You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 创建socket服务器constnet=require('net');letclients=0;constserver=net.createServer(client=>{clients++;letclientId=clients;console.log('Client connect:',clientId);client.on('end',()=>{console.log('Client disconnected:',clientId);});console.log(client)client.write('Welcome client: '+clientId+' \r\n');client.pipe(client);// 把客户端的数据返回给客户端});server.listen(8000,()=>{console.log('Server started on port 8000');});// 创建socket客户端constnet=require('net');constclient=net.connect(8000);client.on('data',data=>{console.log('data>>>',data.toString());});client.on('end',()=>{console.log('Client disconnected');});
Server.prototype.listen=function(...args){// ...// 监听listening事件if(cb!==null){this.once('listening',cb);}// ...if(optionsinstanceofTCP){// ...listenInCluster(this,null,-1,-1,backlogFromArgs);returnthis;}if(typeofoptions.fd==='number'&&options.fd>=0){listenInCluster(this,null,null,null,backlogFromArgs,options.fd);returnthis;}letbacklog;if(typeofoptions.port==='number'||typeofoptions.port==='string'){// ...if(options.host){lookupAndListen(this,options.port|0,options.host,backlog,options.exclusive,flags);}else{listenInCluster(this,null,options.port|0,4,backlog,undefined,options.exclusive);}returnthis;}if(options.path&&isPipeName(options.path)){// ...listenInCluster(this,pipeName,-1,-1,backlog,undefined,options.exclusive);if(!this._handle){// Failed and an error shall be emitted in the next tick.// Therefore, we directly return.returnthis;}// ...returnthis;}// ...};
net
net
模块用于创建基于TCP或IPC的服务端或客户端。这里有一个
net
创建TCP服务器的基本例子,以此执行服务端和客户端即可在终端看到输出。源码
net.createServer
通过上面的例子,我可以看到,服务端是通过
net.createServer
这个工厂函数来进行创建的。在源码中,是通过new了一个Sever
实例来进行创建的。Server方法做了几件很简单的事情
connection
事件的绑定connections
的get/set方法,对其进行改造,似的不直接访问_connections
属性在这里可以看到,实例的创建非常简单,但是这样还不能创建一个TCP服务,还需要对端口进行监听。
从源码里可以看到,
listen
方法做了各种各样的判断条件,以适配各种各样入参条件。这些我们都可以忽略,最重要的是看到了一个方法,listenInCluster
。不管什么样的情况都会调用到这个方法,即使是lookupAndListen
方法,内部也是listenInCluster
。那么接下来看看
listenInCluster
方法做了些什么事情listenInCluster
在主进程调用了_listen2
方法,并且处理了在集群中的的监听事件,这里cluster
做了什么先不去追究,只要知道最后在回调方法里,也是调用了_listen2
方法即可。cluster的会单独在cluster
上去做研究。下面来看下
_listen2
方法setupListenHandle
方法它通过createServerHandle
创建了一个服务句柄,并绑定在了实例上,这里来看下createServerHandle
的内部实现通过入参判断是否是监听文件标识符还是管道服务还是TCP,然后通过
address
判断是否要用默认地址,再判断用ipv4
还是ipv6默认的IP地址在上面有定义
在
setupListenHandle
方法中,有这么一段代码,用于触发connection
事件从这里可以看到,socket其实是一个双工流,用于监听和实现流的读写。这个方法的引用,在c++的内建模块,这里不再深入赘述
总结
最后总结下net的整体流程
listen
方法上,根据入参的变化优化参数后,最终都调用的listenInCluster
方法listenInCluster
内,做了cluster集群的一些优化处理(这里在cluster模块再深入了解)。不管是cluster还是master,最终是调用的_listen2
方法,寻找根源,其实是setupListenHandle
方法setupListenHandle
内,调用了createServerHandle
方法,并绑定了onconnection
事件onconnection
实际上由c++的内建模块调用,绑定在handle.onconnection
。在内部emit了onconnection
事件,并创建了一个socket
,从代码得知其实socket
是一个双工流实例,并回传到回调方法里,所以我们才可以对socket进行读写和数据监听操作createServerHandle
方法,改方法通过不同的入参,可创建fd文件描述符
的句柄,还有windows下的pipe管道
句柄,当然还有ipv4
/ipv6
的TCP
句柄,最后当顶在Server
实例的_handle
属性上。结语
在核心模块中,JavaScript对服务的处理比较浅薄,只是在TCP层上封装了监听事件,最终调用的还是c++的内建模块来实现服务的监听,后面有机会,可以再往内建模块继续深入研究。
The text was updated successfully, but these errors were encountered: