这篇文章主要介绍了nodejs处理tcp连接的核心流程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

前几天和一个小伙伴交流了一下nodejs中epoll和处理请求的一些知识,今天简单来聊一下nodejs处理请求的逻辑。我们从listen函数开始。

int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {// 设置处理的请求的策略,见下面的分析if (single_accept == -1) {const char* val = getenv("UV_TCP_SINGLE_ACCEPT");single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */}if (single_accept)tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT;// 执行bind或设置标记err = maybe_new_socket(tcp, AF_INET, flags);// 开始监听if (listen(tcp->io_watcher.fd, backlog))return UV__ERR(errno);// 设置回调tcp->connection_cb = cb;tcp->flags |= UV_HANDLE_BOUND;// 设置io观察者的回调,由epoll监听到连接到来时执行tcp->io_watcher.cb = uv__server_io;// 插入观察者队列,这时候还没有增加到epoll,poll io阶段再遍历观察者队列进行处理(epoll_ctl)uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN);return 0;
}

我们看到,当我们createServer的时候,到Libuv层就是传统的网络编程的逻辑。这时候我们的服务就启动了。在poll io阶段,我们的监听型的文件描述符和上下文(感兴趣的事件、回调等)就会注册到epoll中。正常来说就阻塞在epoll。那么这时候有一个tcp连接到来,会怎样呢?epoll首先遍历触发了事件的fd,然后执行fd上下文中的回调,即uvserver_io。我们看看uvserver_io。

void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {// 循环处理,uv__stream_fd(stream)为服务器对应的fdwhile (uv__stream_fd(stream) != -1) {// 通过accept拿到和客户端通信的fd,我们看到这个fd和服务器的fd是不一样的err = uv__accept(uv__stream_fd(stream));// uv__stream_fd(stream)对应的fd是非阻塞的,返回这个错说明没有连接可用accept了,直接返回if (err < 0) {if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK))return;}// 记录下来stream->accepted_fd = err;// 执行回调stream->connection_cb(stream, 0);/*stream->accepted_fd为-1说明在回调connection_cb里已经消费了accepted_fd,否则先注销服务器在epoll中的fd的读事件,等待消费后再注册,即不再处理请求了*/if (stream->accepted_fd != -1) {uv__io_stop(loop, &stream->io_watcher, POLLIN);return;}/*ok,accepted_fd已经被消费了,我们是否还要继续accept新的fd,如果设置了UV_HANDLE_TCP_SINGLE_ACCEPT,表示每次只处理一个连接,然后睡眠一会,给机会给其他进程accept(多进程架构时)。如果不是多进程架构,又设置这个,就会导致处理连接被延迟了一下*/if (stream->type == UV_TCP &&(stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) {struct timespec timeout = { 0, 1 };nanosleep(&timeout, NULL);}}
}

从uv__server_io,我们知道Libuv在一个循环中不断accept新的fd,然后执行回调,正常来说,回调会消费fd,如此循环,直到没有连接可处理了。接下来,我们重点看看回调里是如何消费fd的,大量的循环会不会消耗过多时间导致Libuv的事件循环被阻塞一会。tcp的回调是c++层的OnConnection。

// 有连接时触发的回调
template
void ConnectionWrap::OnConnection(uv_stream_t* handle,int status) {// 拿到Libuv结构体对应的c++层对象                          WrapType* wrap_data = static_cast(handle->data);CHECK_EQ(&wrap_data->handle_, reinterpret_cast(handle));Environment* env = wrap_data->env();HandleScope handle_scope(env->isolate());Context::Scope context_scope(env->context());// 和客户端通信的对象Local client_handle;if (status == 0) {// Instantiate the client javascript object and handle.// 新建一个js层使用对象Local client_obj;if (!WrapType::Instantiate(env, wrap_data, WrapType::SOCKET).ToLocal(&client_obj))return;// Unwrap the client javascript object.WrapType* wrap;// 把js层使用的对象client_obj所对应的c++层对象存到wrap中ASSIGN_OR_RETURN_UNWRAP(&wrap, client_obj);// 拿到对应的handleuv_stream_t* client = reinterpret_cast(&wrap->handle_);// 从handleaccpet到的fd中拿一个保存到client,client就可以和客户端通信了if (uv_accept(handle, client))return;client_handle = client_obj;} else {client_handle = Undefined(env->isolate());}// 回调js,client_handle相当于在js层执行new TCPLocal argv[] = { Integer::New(env->isolate(), status), client_handle };wrap_data->MakeCallback(env->onconnection_string(), arraysize(argv), argv);
}

代码看起来很复杂,我们只需要关注uv_accept。uv_accept的参数,第一个是服务器对应的handle,第二个是表示和客户端通信的对象。

int uv_accept(uv_stream_t* server, uv_stream_t* client) {int err;switch (client->type) {case UV_NAMED_PIPE:case UV_TCP:// 把fd设置到client中err = uv__stream_open(client,server->accepted_fd,UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);break;// ...}client->flags |= UV_HANDLE_BOUND;// 标记已经消费了fdserver->accepted_fd = -1;return err;
}

uv_accept主要就是两个逻辑,把和客户端通信的fd设置到client中,并标记已经消费,从而驱动刚才讲的while循环继续执行。对于上层来说,就是拿到了一个和客户端的对象,在Libuv层是结构体,在c++层是一个c++对象,在js层是一个js对象,他们三个是一层层封装且关联起来的,最核心的是Libuv的client结构体中的fd,这是和客户端通信的底层门票。最后回调js层,那就是执行net.js的onconnection。onconnection又封装了一个Socket对象用于表示和客户端通信,他持有c++层的对象,c++层对象又持有Libuv的结构体,Libuv结构体又持有fd。

const socket = new Socket({handle: clientHandle,allowHalfOpen: self.allowHalfOpen,pauseOnCreate: self.pauseOnConnect,readable: true,writable: true});
const socket = new Socket({handle: clientHandle,allowHalfOpen: self.allowHalfOpen,pauseOnCreate: self.pauseOnConnect,readable: true,writable: true});

到此这篇关于nodejs处理tcp连接的核心流程的文章就介绍到这了,感谢大家的支持。

简单总结nodejs处理tcp连接的核心流程相关推荐

  1. 关于tcp连接中timewait的作用

    今天简单的谈一下tcp连接中timewait的作用,如果没有timewait会发生什么呢? 我们知道首先请求关闭连接的一方会存在timewait状态. 首先我们来看一下tcp四次挥手的过程示意图: 客 ...

  2. 【Java 网络编程】TCP 连接 断开 机制 ( 三次握手 | 四次挥手 )

    文章目录 I TCP 连接建立流程 ( 三次握手 ) II SYN 和 ACK 中的随机值 III TCP 连接建关闭流程 ( 四次挥手 ) IV TCP 连接断开的保证 V 四次挥手的必要性 I T ...

  3. TCP协议中的核心知识点,SYN Flood?ISN?滑动窗口?数据重传?拆包粘包?单tcp连接多请求?拥塞管理?(个人收藏学习笔记)

    TCP协议中的核心知识点,滑动窗口?数据重传?拆包粘包?单tcp连接多请求? 1.前言 2.TCP/IP四层结构 3. TCP 3.1 TCP 协议头 3.2 TCP通信过程 3.2.1 建立连接的三 ...

  4. 几种TCP连接中出现RST的情况

    UNIX网络编程上说:产生RST的三个条件是:目的地为某端口的SYN到达,然而在该端口上并没有正在监听 的服务器:TCP想取消一个已有链接:TCP接收到一个根本不存在的连接上的分节. 几种TCP连接中 ...

  5. TCP 连接断连问题剖析

    在官方的正式文档中,TCP/IP 协议簇也称为国际互联网协议簇.TCP/IP 协议簇是目前使用最为广泛的全球互联网技术,其分层结构如图 1 所示: 图 1. TCP/IP 协议簇分层结构  如图 1 ...

  6. 将serversocket 写在按钮事件中连接不上_Java服务器的模型—TCP连接/流量优化

    本文很长哦,但请给我一点时间. 通常,我们的应用程序不需要并行处理成千上万的用户,也不需要在一秒钟内处理成千上万的消息.我们只需要应付数十或数百个并发连接的用户,就可以在内部应用程序或某些微服务应用程 ...

  7. linux tcp连接计算机,计算机基础知识——linux socket套接字tcp连接分析

    2016.7.4 今天晚上对项目顶层文件(daemon)进行了分析,对其中的TCP连接进行具体的代码级分析. 1.需求分析 首先得知道我们这里为什么要用TCP连接,我们的整个测试系统是由上位机作为客户 ...

  8. tcp实时传输kafka数据_关于Kafka producer管理TCP连接的讨论

    在Kafka中,TCP连接的管理交由底层的Selector类(org.apache.kafka.common.network)来维护.Selector类定义了很多数据结构,其中最核心的当属java.n ...

  9. 服务器与客户端的TCP连接

    TCP通讯之服务器与客户端的连接 相关的网站: 一.第一个案例 二.Node.js使用TCP通讯 (一).创建TCP Server (二).创建 TCP Client 三.Node.js 搭建TCP服 ...

最新文章

  1. jquery点击元素之外触发事件
  2. 推送改变世界!Push提高用户活跃度的三不原则
  3. python 线程退出_python线程退出
  4. css怎么使用gpu加速,用CSS3开启GPU硬件加速来提升网站的动画渲染性能
  5. Android Studio在Ubuntu下离线安装Gradle
  6. 应用vb编程_VB编程中的列表框综合应用讲解
  7. orm2 中文文档 3. 定义模型
  8. 李开复:多次失败后,我总结出最优秀创业者的4个特点
  9. 5.一个三维数组,如何根据最后一维的数字大小正序排列,当然同时要保证索引的关联
  10. spring boot项目修改启动商标
  11. Qt编写安防视频监控系统25-离线地图
  12. 跌倒检测_使用姿势估计的跌倒检测
  13. Xshell光标消失
  14. CSS_01_选择器
  15. lastb 命令的输出结果中的 ssh:notty 的意思
  16. 第二次上机作业 (数组综合)
  17. 原生js制作扫雷-自定义难度
  18. 【避坑指“难”】页面Top置顶(返回顶部)小图标实现逻辑
  19. FOTA与 SOTA介绍
  20. 30岁了,最近碰到的那些离婚的事儿

热门文章

  1. 算法科普:神秘的 DES 加密算法
  2. Django博客系统(退出登录)
  3. 爬虫之 lxml模块和xpath语法
  4. 小于60的数中能被1到10整除的数量
  5. 我的matlab5个车牌_顶帽_底帽_边缘_腐蚀
  6. 技术18期:数据安全之加密与实现
  7. Python自动化办公系列之Python操作PDF
  8. 收藏 | 卷积神经网络 C++ 从零开始实现
  9. 使用霍夫变换检测车道线
  10. 使用webpack构建多页应用