node.js的http.createServer过程深入解析
下面是开发创建一个服务器的代码。接下来我们一起分析这个过程。
var http=require(' http ');函数(请求,响应){回应。end(' Hello World ');}).听(9297);首先我们去到lib/http.js模块看一下这个函数的代码。
函数createServer(request listener){ 0返回新的服务器(请求侦听器);}只是对_http_server.js做了些封装。我们继续往下看。
函数服务器(requestListener) { if(!(服务器的这个实例))返回新的服务器(请求侦听器);净服务器.调用(这个,{ allowalfopen : true });//收到超文本传送协议(超文本传输协议的缩写)请求时执行的回调if(请求侦听器){ this。on(' request ',请求侦听器);} this . httpallowhalfold=false//建立传输控制协议连接的回调this.on('connection ',connectionListener);this . time out=2 * 60 * 1000 this . keepalivetimeout=5000这个_ pending response data=0;this . MaxHeadersCount=null } util . inherits(服务器,网络。服务器);发现_http_server.js也没有太多逻辑,继续看lib/net.js下的代码。
函数服务器(选项,connectionListener) { if(!(服务器的这个实例))返回新的服务器(选项,连接侦听器);事件发射器。叫(这个);//connectionListener在http.js处理过了if(选项类型==' function '){ connectionListener=options;options={ };this.on('connection ',connectionListener);} else if(options==null | | options的类型==' object '){ options=options | | { };if(connectionListener的类型==' function '){ this。on(' connection ',connectionListener);} } else {抛出新错误TYPE错误(' ERR _ INVALID _ ARG _ TYPE ',' options ',' Object ',options);}这个_ connections=0;这个[async _ id _ symbol]=-1;这个_ handle=null这个_ usingWorkers=false这个_ workers=[];这个_ unref=falsethis。allowarfopen=options。allowarfopen | | falsethis.pauseOnConnect=!options.pauseOnConnect}至此http.createServer就执行结束了,我们发现这个过程还没有涉及到很多逻辑,并且还是停留到射流研究…层面。接下来我们继续分析听函数的过程。该函数是网模块提供的。我们只看关键的代码。
服务器。原型。listen=function(.args) { //处理入参,根据文档我们知道听可以接收好几个参数,我们这里是只传了端口号9297 var normalized=normalizeArgs(args);//normalized=[{port: 9297},null];var options=normalized[0];var cb=归一化[1];//第一次听的时候会创建,如果非空说明已经听过如果(这个_ handle){ 0抛出新错误。错误(' ERR _ SERVER _ EVENT _ LISTEN ');} .listeningcluster(this,null,options.port | 0,4,backlog,undefined,options。独占);}函数listeningcluster(){ 0.服务器_listen2(地址、端口、地址类型、积压、FD);} _ listen 2=setupListenHandle=function(){ 0.这个_handle=createServerHandle(.);这个_手柄。听(backlog | | 511);}函数createserverhandlee(){ handle=新的TCP(TCpconstats).SERVER);handle.bind(地址、端口);}到这我们终于看到了传输控制协议连接的内容,每一个服务器新建一个处理并且保存他,该处理是一个三氯苯酚对象。然后执行约束和听函数。接下来我们就看一下三氯苯酚类的代码TCP。是C提供的类。对应的文件是tcp_wrap.cc。我们看看新的三氯苯酚的时候发生了什么。
void tcpwrap :3360 new(const FunctionCallbackInfoValue args){//此构造函数不应公开给公共javascript .//因此,我们断言,我们不是试图将它称为//正常函数。检查(参数. IsConstructCall());CHECK(args[0]-IsInt32());environment * env=environment :3360 get current(args);int type _ Value=args[0]。asint 32()-Value();tcpwrap :3360套接字类型=static _ casttcpwrap 33603360套接字类型(type _ value);ProviderType提供程序;开关(类型){ case SOCKET : PROVIDER=PROVIDER _ TCpwrap;打破;case SERVER : PROVIDER=PROVIDER _ tcpserverwrap;打破;默认值: RECLEATE();}新的TCPWrap(环境,参数.这个(),提供程序);} tcpwrap :3360 cpwrap(Environment * env,LocalObject,ProviderType提供程序): ConnectionWrap(env,Object,provider){ int r=uv _ TCP _ init(env-event _ loop(),handle _);CHECK_EQ(r,0);}我们看到,新的三氯苯酚的时候其实是执行libuv的uv_tcp_init函数,初始化一个uv_tcp_t的结构体。首先我们先看一下uv_tcp_t结构体的结构。
uv_tcp_tuv_tcp_t//初始化一个传输控制协议流的结构体int uv_tcp_init(uv_loop_t* loop,uv_tcp_t* tcp) { //未指定未指定协议返回uv_tcp_init_ex(循环,tcp,AF _ untec);} int uv _ TCP _ init _ ex(uv _ loop _ t * loop,uv_tcp_t* tcp,无符号(同Internationalorganizations)国际组织标志){ int domain/*使用域的低8位*///低八位是域域=标志0xFFif(域!=AF_INET域!=AF_INET6域!=AF _ UNEXC)返回UV _ EINVAL//除了第八位的其他位是标志if(标志~0xFF)返回UV _ EINVALUV _ stream _ init(循环,(uv_stream_t*)tcp,UV _ TCP);/*如果超过这个点有任何失败,我们需要从句柄队列中删除句柄,因为它是由uv _ stream _初始化中的紫外线_手柄_初始化添加的*/if(域!=AF _ UNTERC){ int err=me _ new _ socket(TCP,域,0);if (err) { //出错则把该处理移除环队列QUEUE _ REMOVE(TCP-handle _ QUEUE);返回错误;} }返回0;}我们接着看uv__stream_init做了什么事情。
void uv _ stream _ init(uv _ loop _ t * loop,uv_stream_t* stream,uv _ handle _ type){ int err;uv _ handle _ init(循环,(uv_handle_t*)流,类型);stream-read_cb=空;stream-alloc _ CB=NULL;流-close_cb=空;流-连接_cb=空;流连接请求=空;流-关闭_请求=空;stream-accepted _ FD=-1;流-排队_fds=空;流延迟误差=0;QUEUE _ INIT(stream-write _ QUEUE);QUEUE _ INIT(stream-write _ completed _ QUEUE);stream-write _ queue _ size=0;if(loop-em file _ FD==-1){ err=uv _ open _ cloexec('/dev/null ',O _ RDONLY);if (err 0) /*在极少数情况下,没有将"/dev/null "装载到打开"/"中*/err=uv__open_cloexec('/',O _ RDONLY);if(err=0)loop-em file _ FD=err;} #如果已定义(__APPLE__)流-select=空;#endif /*已定义(__APPLE_) *///初始化超正析象管观察者uv__io_init(stream-io_watcher,uv__stream_io,-1);}void uv__io_init(uv__io_t* w,uv__io_cb,int fd) { assert(cb!=空);assert(FD=-1);//初始化队列,回调,需要监听的FD _ QUEUE _ INIT(w-pending _ QUEUE);QUEUE _ INIT(w-watcher _ QUEUE);w-CB=CB;w-FD=FD;w-事件=0;w-PE通风口=0;#如果已定义(UV _ HAVE _ KQUEUE)w-r计数=0;w-w计数=0;#endif /*已定义(UV_HAVE_KQUEUE) */}从代码可以知道,只是对uv_tcp_t结构体做了一些初始化操作。到这,新的三氯苯酚的逻辑就执行完毕了。接下来就是继续分类开发里调用约束和听的逻辑nodejs。的约束对应libuv的函数是uv__tcp_bind,听着对应的是uv _ tcp _侦听。先看一个约束的核心代码。
/*无法在非IPv6套接字上设置仅IPv6模式*/if((仅标记UV _ TCP _ IPv6)addr-sa _ family!=AF_INET6)返回UV _ EINVAL//获取一个窝并且设置某些标记err=me _ new _ socket(TCP,addr-sa_family,0);如果(错误)返回呃;on=1;//设置在端口可重用if(setsockopt(TCP-io _ watcher。FD,SOL_SOCKET,SO_REUSEADDR,on,sizeof(on)))返回UV _ ERR(errno);bind(tcp-io_watcher.fd,addr,addrlen) errno!=EADDRINUSEstatic int也许_new_socket(uv_tcp_t*句柄,int域,无符号长标志){ struct sockaddr _ storage saddrsocklen _ t slenif(domain==AF _ untec){ handle-flags |=flags;返回0;}返回new_socket(句柄、域、标志);}静态int new_socket(uv_tcp_t*句柄,int域,无符号长标志){ struct sockaddr _ storage saddrsocklen _ t slenint sockfdint err//获取一个socket err=uv__socket(domain,SOCK_STREAM,0);if (err 0)返回呃;sockfd=err//设置选项和保存窝的文件描述符到超正析象管观察者中err=uv _ stream _ open((uv _ stream _ t *)句柄、sockfd、flags);if(err){ uv _ _ close(sockfd);返回错误;} .返回0;} int uv _ stream _ open(uv _ stream _ t * stream,int fd,int flags) { if(!(stream-io _ watcher。FD==-1 | | stream-io _ watcher。FD==FD))返回UV _ EBUSYassert(FD=0);stream-flags |=flags;if(stream-type==UV _ TCP){ if((stream-flags UV _ HANDLE _ TCP _ NODELAY)UV _ TCP _ NODELAY(FD,1))返回UV _ ERR _ errno(errno);/* TODO使用用户传入的延迟*/if((stream-flags UV _ HANDLE _ TCP _ KEEPALIVE)UV _ _ TCP _ KEEPALIVE(FD,1,60)){ return UV _ _ ERR(errno);} } .//保存窝对应的文件描述符到超正析象管观察者中,libuv会在超正析象管轮询阶段监听该文件描述符流io观察器。FD=FD返回0;}上面的一系列操作主要是新建一个窝文件描述符,设置一些旗帜,然后把文件描述符保存到超正析象管(图像或图标)观察者中,libuv在轮询输入输出阶段会监听该文件描述符,如果有事件到来,会执行设置的回调函数,该函数是在uv__stream_init里设置的uv _ stream _ io。最后执行约束函数进行绑定操作。最后我们来分析一下听函数。首先看下tcp_wrapper.cc的代码。
void TCPWrap :3360 listen(const FunctionCallbackInfoValue args){ TCPWrap * wrap;分配或返回打开(换行,参数Holder(),args .GetReturnValue().set(UV _ ebdf));int backlog=args[0]-int 32 value();int err=uv _ listen(re explore _ cast uv _ stream _ t *(wrap-handle _)、backlog、OnConnection);啊GetReturnValue().set(err);}代码中有个很重要的地方就是OnConnection函数,nodejs给听函数设置了一个回调函数在连接上,该函数在超正析象管(图像或图标)观察者里保存的文件描述符有连接到来时会被调用OnConnection。函数是在connection_wrap.cc定义的,tcp包装继承了连接包装。下面我们先看一下紫外线听着。该函数调用了uv _ tcp _侦听。该函数的核心代码如下。
if(侦听(tcp-io_watcher.fd,backlog))返回UV _ _ ERR(errno);//cb即OnConnection TCP-connection _ CB=CB;TCP-flags |=UV _ HANDLE _ BOUND;//有连接到来时的libuv层回调,覆盖了紫外线流初始化时设置的值TCP-io _ watcher。CB=uv _ server _ io//注册事件uv__io_start(tcp-loop,tcp-io_watcher,POLLIN);在libuv的轮询输入输出阶段,epoll_wait会监听到到来的连接,然后调用uv__server_io。下面是该函数的核心代码。
//继续注册事件,等待连接uv__io_start(stream-loop,stream-io_watcher,POLLIN);err=uv _ _ accept(uv _ _ stream _ FD(stream));//保存连接对应的套接字流-accepted _ FD=err;//执行开发层回调stream-connection_cb(stream,0);libuv会摘下一个连接,得到对应的插座。然后执行开发层的回调,这时候我们来看一下OnConnection的代码。
OnConnection(uv_stream_t*句柄,int状态)如果(状态==0) { //新建一个uv_tcp_t结构体本地对象client _ obj=wrap type :实例(env,wrap_data,wrap type : socket);wrap type * wrap assign _ OR _ RETURN _ UNWRAP(wrap,client _ obj);uv _ stream _ t * client _ handle=re explore _ cast uv _ stream _ t *(wrap-handle _);//紫外线_接受返回0表示成功if (uv_accept(句柄,客户端_句柄))返回;argv[1]=client _ obj;} //执行上层的回调,该回调是net.js设置的onconnection wrap _ data-make回调(env-onconnection _ string()、arraysize(argv)、argv);OnConnection新建了一个uv_tcp_t结构体。代表这个连接。然后调用紫外线接受。
int uv_accept(uv_stream_t*服务器,uv_stream_t*客户端){ 0.//新建的uv_tcp_t结构体关联接受_fd,注册读写事件UV _ stream _ open(客户端,服务器接受_fd,UV _ HANDLE _ ready | UV _ HANDLE _ written);}最后执行开发的回调。
函数onconnection(err,客户端句柄){ var handle=thisvar self=handle . ownerif(err){ self . emit(' error ',errnoException(err,' accept ');返回;} if (self.maxConnections self ._连接=自我。MaxConnections){客户端句柄。close();返回;} var Socket=new Socket({ handle :客户端句柄,allowalfopen :自身。allowalfopen,pauseoncreate : self。pauseonconnect });插座。可读=套接字。written=true自我。_连接;socket.server=self插座_ SERVER=SelfDTrace _ NET _ SERVER _ CONNECTION(套接字);LTTNG_NET_SERVER_CONNECTION(套接字);COUNTER_NET_SERVER_CONNECTION(套接字);//触发_http_server.js里设置的connectionListener回调self.emit('connection ',socket);}听着函数总体的逻辑就是把窝设置为可监听,然后注册事件,等待连接的到来,连接到来的时候,调用接受获取新建立的连接,tcp_wrapper.cc的回调新建一个uv_tcp_t结构体,代表新的连接,然后设置可读写事件,并且设置回调为uv__stream_io,等待数据的到来。最后执行_http_server.js设置的回调连接监听器。至此,服务器启动并且接收连接的过程就完成了。接下来就是对用户数据的读写。当用户传来数据时,处理数据的函数是uv _ stream _ io。后面继续解析数据的读写。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。
版权声明:node.js的http.createServer过程深入解析是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。