简单了解 node net 模块
文章记录了对net 模块的简单理解分析。
- net模块
- 简单使用
- net.Server 类
- net.Socket 类
- 总结
1.1 net模块
Node.js 的 Net模块用于创建基于流的 TCP 或 IPC 的服务器(net.createServer())与客户端(net.createConnection())。
Node.js 的 Net模块是基于TCP协议的socket网路编程模块。
主要的两个部分
- net.Server 类
- net.Socket 类
TCP是面向连接的传输层协议,重要的特征是建立传输前的三次握手
当两端都确认了,才开始相互传输数据,会话创建过后,服务端和客户端分别提供一个套接字,形成此次的连接。
套接字 socket 是一个 双工流,既可以通过 data事件读取数据另一方发过来的数据,又可以通过write()方法向另一方发送数据。
1.2. 简单使用
先写个小例子:
TCP服务端:
let net = require('net')
let server = net.createServer({}, socket => {
// socket 是一个双工流
console.log('client connected')
socket.on('data', data => {
console.log(data.toString())
socket.write('server: hello server')
})
// 服务器收到客户端发出的关闭请求时,会触发end事件
socket.on('end', () => {
console.log('client disconnected')
})
socket.on('close', () => {
console.log('client closed')
})
})
server.listen(8080, () => {
console.log('server start');
})
TCP客户端:
let net = require('net')
// new net.Socket() 返回的是一个双工流
let client = new net.Socket()
client.connect(8080, 'localhost', () => {
console.log('connected server')
client.write('client: hello server');
})
client.on('data', function (data) {
console.log(data.toString())
});
setTimeout(()=>{
client.end()
},5000)
上面流程调试代码画个简图:(假设此时有两个客户端连接)
TCP服务是以connection 为单位进行服务的。
每一次连接都 会创建一个socket 实例
1.3. net.Server 类
createServer就是一个语法糖,帮助new生成server实例,
server 对象继承了EventEmitter对象 。
this.on('connection', connectionListener); // 此处订阅了connection,等待有连接的时候,发布connecttionListener就会被调用
截取部分代码如下
function Server(options, connectionListener) {
if (!(this instanceof Server))
return new Server(options, connectionListener);
// 调用 EventEmitter 获得属性
EventEmitter.call(this);
// 订阅connection 事件
this.on('connection', connectionListener);
// 统计连接数量
this._connections = 0;
this[async_id_symbol] = -1;
// 会挂载TCP对象
this._handle = null;
this._usingWorkers = false;
this._workers = [];
this._unref = false;
this.allowHalfOpen = options.allowHalfOpen || false;
this.pauseOnConnect = !!options.pauseOnConnect;
}
每当有客户端连接时,就会调用callback函数onconnection函数,创建socket实例,将由c++部分的TCP、Pipe类创建的实例clientHandle挂载在_handle属性上。
然后发布connection事件传递socket 实例
所以server 对象更多的是对socket连接的管理。截取代码如下
function onconnection(err, clientHandle) {
const handle = this;
const self = handle[owner_symbol];
// 超出最大链接数 不让客户端连接
if (self.maxConnections && self._connections >= self.maxConnections) {
clientHandle.close();
return;
}
const socket = new Socket({
handle: clientHandle,
allowHalfOpen: self.allowHalfOpen,
pauseOnCreate: self.pauseOnConnect,
readable: true,
writable: true
});
// 连接数增加
self._connections++;
socket.server = self;
socket._server = self;
DTRACE_NET_SERVER_CONNECTION(socket);
// 发布 connection
self.emit('connection', socket);
}
clientHandle对象如下,它是一个TCP 实例:
onconnection执行的目的是对应用程序构的连接造出一个socket的实例。
self.emit('connection', socket)// 发布 connecttion 事件,传递socket
执行connectionListener (socket),然后就基于此socket实例完成面向connection的数据流读取操作。
补充
如果.createServer 没有传递connectionListener 函数
也可以使用 server.on('connection',connectionListener) // .createServer 传递connectionListener 函数更像语法糖
1.4. net.Socket对象
socket(通过 socket=new Socket() ) 是个双工流源码截取如下:
socket._handle上挂载的实例对象是由C++中的Pipe、TCP实现,大概截取代码如下
//<------Socket函数----->
this._handle = createHandle(fd, false);
//<------createHandle函数----->
function createHandle(fd, is_server) {
validateInt32(fd, 'fd', 0);
const type = guessHandleType(fd);
if (type === 'PIPE') {
return new Pipe(
is_server ? PipeConstants.SERVER : PipeConstants.SOCKET
);
}
if (type === 'TCP') {
return new TCP(
is_server ? TCPConstants.SERVER : TCPConstants.SOCKET
);
}
throw new ERR_INVALID_FD_TYPE(type);
}
由c++部分的TCP、Pipe类创建由internalBinding('tcp_wrap')导出的TCP对象,如下:
Node.js 的 Net模块是对 c++部分的TCP、Pipe类创建的socket 进行了抽象封装。截取个代码如下
所以socke实例有Writable,Readable,和TCP的相关功能函数。
1.5. 总结
TCP服务是一connection 为单位进行服务的。
理解net模块可以多看文档,代码实践。
还可以看看TCP协议,链接建立握手,慢启动拥塞控制,Nagle算法解决的问题等等。
文章记录了对net 模块的简单理解分析。例子用词比较粗糙,理解不准确之处,还请教正。