网易首页 > 网易号 > 正文 申请入驻

简单总结nodejs处理tcp连接的核心流程

0
分享至

这篇文章主要介绍了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)为对应服务器的fd
while (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);
// 拿到对应的handle
uv_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 TCP
Local 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;
// 标记已经消费了fd
server->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连接的核心流程的文章就介绍到这了,感谢大家的支持。

本文地址:https://www.linuxprobe.com/nodejs-linux-seven.htm

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
金球奖这一夜,国际女星个个风情万种,但都被一位中国女星艳压了

金球奖这一夜,国际女星个个风情万种,但都被一位中国女星艳压了

八卦南风
2026-01-12 16:25:50
贾玲巴黎周“一脸男相”!不爱笑也没梨涡眼神犀利,梳大背头好酷

贾玲巴黎周“一脸男相”!不爱笑也没梨涡眼神犀利,梳大背头好酷

轩逸阿II
2026-01-20 07:54:29
12斤肉3个月甩净!全红婵减重到离谱,这哪是减肥,是拿命拼金牌

12斤肉3个月甩净!全红婵减重到离谱,这哪是减肥,是拿命拼金牌

做一个合格的吃瓜群众
2025-12-31 07:41:05
泰国等严查柬埔寨入境中国籍人员

泰国等严查柬埔寨入境中国籍人员

原某报记者
2026-01-19 14:00:13
94岁胡枫寿宴变明星演唱会!四个曾孙相伴,张学友下跪谢恩太炸裂

94岁胡枫寿宴变明星演唱会!四个曾孙相伴,张学友下跪谢恩太炸裂

一盅情怀
2026-01-20 14:37:45
韩网友提问:在铁证面前,中国人为什么还不承认汉字起源于韩国?

韩网友提问:在铁证面前,中国人为什么还不承认汉字起源于韩国?

芳芳历史烩
2026-01-20 20:53:46
宋骧曝梁小龙死因,凌晨胸痛,去医院神志清醒,一个举动害了他

宋骧曝梁小龙死因,凌晨胸痛,去医院神志清醒,一个举动害了他

观察鉴娱
2026-01-20 10:12:36
湖北女子花60块买水果,老公怪她大手大脚,网友:不要花穷人的钱

湖北女子花60块买水果,老公怪她大手大脚,网友:不要花穷人的钱

心轩专栏
2026-01-19 23:36:38
张家辉梁家辉亮相米兰时装周展现独特绅士风度

张家辉梁家辉亮相米兰时装周展现独特绅士风度

孤傲何妨初
2026-01-21 01:59:04
谁是如今欧洲最强的国家?法国:全世界都只有三个比我强

谁是如今欧洲最强的国家?法国:全世界都只有三个比我强

西府赵王爷
2024-07-06 10:54:36
萨莉亚,我劝你别太离谱

萨莉亚,我劝你别太离谱

餐观局
2026-01-19 21:16:06
很多人以为殉葬就是把活人关进地宫,门一关,他们只能哭喊着等死

很多人以为殉葬就是把活人关进地宫,门一关,他们只能哭喊着等死

忠于法纪
2026-01-18 17:42:24
向华炎灵堂现争议,孙女当众大笑,77岁向华强披麻戴孝反差刺眼

向华炎灵堂现争议,孙女当众大笑,77岁向华强披麻戴孝反差刺眼

银河史记
2026-01-20 19:14:27
杭州百岁夫妻,同年同月同日生!一个爱吃糖,一个爱喝酒,不吃保健品,不管闲事

杭州百岁夫妻,同年同月同日生!一个爱吃糖,一个爱喝酒,不吃保健品,不管闲事

极目新闻
2026-01-20 12:30:27
考古王菲李亚鹏的微博,竟然诡异的磕到了

考古王菲李亚鹏的微博,竟然诡异的磕到了

金牌舆情官
2026-01-20 21:19:04
去了北京才发现:没人穿大衣、皮草,满大街都是“海淀风3件套”

去了北京才发现:没人穿大衣、皮草,满大街都是“海淀风3件套”

小虎新车推荐员
2026-01-13 11:24:22
“迷人”的愚蠢——反智盛行的五大原因

“迷人”的愚蠢——反智盛行的五大原因

听哲学
2026-01-18 21:44:12
向余望赛后庆祝:不贪心,再赢一场就回家

向余望赛后庆祝:不贪心,再赢一场就回家

懂球帝
2026-01-21 02:19:12
她因身材发育过猛,无奈退出国家游泳队,被三流导演看中捧成顶流

她因身材发育过猛,无奈退出国家游泳队,被三流导演看中捧成顶流

卿子书
2025-12-31 08:15:41
回顾许家印被抓捕现场,奋力反抗,怒吼不已,被抓捕人员抬出去

回顾许家印被抓捕现场,奋力反抗,怒吼不已,被抓捕人员抬出去

干史人
2026-01-08 22:47:00
2026-01-21 02:39:00
linux系统运维之家
linux系统运维之家
科技资讯
1392文章数 7549关注度
往期回顾 全部

科技要闻

收藏|这可能是CES2026最清醒一份复盘

头条要闻

丹麦首相:主权不谈判 准备贸易战

头条要闻

丹麦首相:主权不谈判 准备贸易战

体育要闻

勇士遭暴击!巴特勒重伤赛季报销

娱乐要闻

网红版闫学晶!600万粉博主阿爆翻车

财经要闻

李迅雷:2026买房不如租房

汽车要闻

奇瑞张贵兵:墨甲不做秀技术的企业 只做痛点终结者

态度原创

家居
亲子
教育
游戏
公开课

家居要闻

隽永之章 清雅无尘

亲子要闻

这是真的先天法医圣体阿!

教育要闻

学习不再靠死磕,是从初二开始的

《秘奥:秘宇奥忆》评测:因简洁而特别"/> 主站 商城 论坛 自运营 登录 注册 《秘奥:秘宇奥忆》评测:因简洁而特别 Marvin 2026-01-2...

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版