前言
当客户端向http server 发起TCP链接时,server端会发起一系列的callback调用,这是一个逆向调用的过程;开始于libuv,终止于js代码里的callback(promise then)函数。
如下图所示,http server 正向调用过程,实际大部分的时间花在net.js上,直到最下面的红框,才调用了关键函数createTCP()
1 2 3 4 5 | function createTCP() { //绑定tcp_wrap模块,调用tcp constructor。 var TCP = process.binding( 'tcp_wrap' ).TCP; return new TCP(); } |
tcp_wrap模块
我们看一下tcpwrap::initialize()的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | void TCPWrap::Initialize(Handle<Object> target, Handle<Value> unused, Handle<Context> context) { Environment* env = Environment::GetCurrent(context); Local<FunctionTemplate> t = FunctionTemplate::New(env->isolate(), New); t->SetClassName(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP" )); t->InstanceTemplate()->SetInternalFieldCount(1); // Init properties t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "reading" ),Boolean::New(env->isolate(), false )); t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "owner" ),Null(env->isolate())); t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "onread" ),Null(env->isolate())); t->InstanceTemplate()->Set(String::NewFromUtf8(env->isolate(), "onconnection" ),Null(env->isolate())); NODE_SET_PROTOTYPE_METHOD(t, "open" , Open); NODE_SET_PROTOTYPE_METHOD(t, "bind" , Bind); NODE_SET_PROTOTYPE_METHOD(t, "listen" , Listen); NODE_SET_PROTOTYPE_METHOD(t, "getsockname" , GetSockName); target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "TCP" ), t->GetFunction()); } NODE_MODULE_CONTEXT_AWARE_BUILTIN(tcp_wrap, node::TCPWrap::Initialize) |
利用v8 engine的 functionTemplate 创建一个js类。
类的constructor 是 TCP(),
类的prototype是NODE_SET_PROTOTYPE_METHOD(),
类的属性是t->InstanceTemplate()->Set().
在执行函数createTCP()调用process.binding('tcp_wrap')时,其实会调用到下图。在红框内,可以看到参数target被设置成了export对象,也就是说,tcp_wrap模块真正导出的是TCP函数。
Server.prototype._listen2
结合上图,我们可以看到函数_listen2中调用createServerHandle() 返回了一个_handle对象,并且self._handle.onconnection = onconnection,这一步也非常重要。
AsyncWrap准备工作
AsyncWrap 和Env的代码截图:
准备工作及流程:
callBack的逆向调用
前面都是铺垫,这里才是正题。
正向调用过程,从createServer()开始,到listen()结束,为了创建一个基于TCP的http server。了解socket流程的都知道,到此为止,创建工作实际已经完成,剩下的就是等待客户connect。
而所有的callback执行的目的是对应用程序构造出一个socket的对象,并且基于此对象完成面向连接的数据流读取操作。
下图为调用流程:
First callback
TCP::Listen()通过libuv提供的uv_listen()实现了listen异步调用,并且指定了callback回调函数TCPWrap::OnConnection()。
OnConnection()由libuv的event loop调用。
在看Nodejs Env.h 和 Env-inl.h 中可找到PER_ISOLATE_STRING_PROPERTIES(v)若干引用,就像上面图所示,env->onconnection_string()会返回symbole onconnection。
MakeCall中,通过object()->Get()获取symbole的对应函数。
Other callback
net.js中的onconnection()会被调用,如下图所示:
两个要点,一是创建了socket对象,二是发出了connection时间。
开发者调用createServer()时,其实是在执行new Server(),而类Server中对connection之间有一个监听者,那就是connectionListener(),也就是第三个callback。
通过前文,我想后面的事情就不在赘述了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?