Node 版本
10.24.1
引入模块
const net = require('net');
创建 Socket 服务
方式一
net.createServer([options][,connectionlistener]);
也可以用方式二
new net.Server(options, connectionListener);
在 net.js 源码里面,createServer 其实也是做了 new Server 的的操作
function createServer(options, connectionListener) {
return new Server(options, connectionListener);
}
这里 server 返回值是 net.Server,继承了 Nodejs 的事件触发器。
net.Server 有如下事件
- connection
- listening
- error
- close
connection
当客户端 连接服务器的时候,会触发 connection 事件
服务端
const server = net.createServer((socket) => {
socket.on('data', (data) => {
console.log("receive_data:" + data);
socket.write(data);
});
});
server.on('connection', (socket) => {
socket.write('create connect');
});
server.listen(8686, () => {
console.log(8686);
});
server.on('listening', () => {
console.log('listening');
});
客户端发起请求
➜ ~ telnet 127.0.0.1 8686
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
create connect // 这里是建立连接后服务器返回的结果
在这里,我们可以更深层次的了解服务端连接事件触发的过程
当客户端与服务端建立起连接是,Nodejs 在系统层面会调度 onconnection 方法

onconnection 方法主要做了两件事情
- 创建 socket
- 触发 connection 事件,并将 socket 对象作为参数
......
var 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);
;
self.emit('connection', socket); // 触发 connection 事件
创建 socket 对象有一部分很重要,是关于服务端接受接收客户端处理
stream.Duplex.call(this, options);
这里通过 call 的方式,让 Socket 对象拥有了 stream 对象的属性及方法。
在 steam.js 内,addChunk 这个方法触发了 data 事件。
系统层面调度如下

// stream.js 285 行
function addChunk(stream, state, chunk, addToFront) {
if (state.flowing && state.length === 0 && !state.sync) {
state.awaitDrain = 0;
stream.emit('data', chunk);
} else {
// update the buffer info.
state.length += state.objectMode ? 1 : chunk.length;
if (addToFront)
state.buffer.unshift(chunk);
else
state.buffer.push(chunk);
if (state.needReadable)
emitReadable(stream);
}
maybeReadMore(stream, state);
}
listening
当调用 listen 方法的时候,会触发 listening 事件
服务端
server.listen(8686);
server.on('listening', () => {
console.log('listening');
});
net.Server 有如下属性和方法
- method: address
监听 IP 套接字,则返回操作系统报告的服务器的绑定
address
、地址family
名称和port
- method: getConnections
- property: maxConnections
- ...
客户端连接 Socket
建立连接
const client = net.createConnection(options[, connectListener])
返回的是 Socket 对象
值得注意的是,创建连接的时候,会在本地随机分配端口。
const client = net.createConnection(8686, () => {
console.log("client 1");
});
const client2 = net.createConnection(8686, () => {
console.log("client 2");
});
> client2.localPort
53866 //
> client.localPort
53725
因为服务端和客户端都是本地的,我们也可以通过 lsof -i :8686 查看连接的情况
➜ ~ lsof -i :8686
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 57010 chenxiaochi 25u IPv6 0xa4e6a264cb60551 0t0 TCP *:distinct (LISTEN)
node 57010 chenxiaochi 26u IPv6 0xa4e6a264cb5f891 0t0 TCP localhost:distinct->localhost:53725 (ESTABLISHED)
node 57010 chenxiaochi 27u IPv6 0xa4e6a265642f231 0t0 TCP localhost:distinct->localhost:53866 (ESTABLISHED)
node 64411 chenxiaochi 27u IPv4 0xa4e6a2653a2c8b9 0t0 TCP localhost:53725->localhost:distinct (ESTABLISHED)
node 64411 chenxiaochi 28u IPv4 0xa4e6a26539ffd09 0t0 TCP localhost:53866->localhost:distinct (ESTABLISHED)
事件
- connect
- data
- end
- white
- ...
data 事件
当服务端向客户端发送消息的时候,会触发此事件
客户端
➜ telnet localhost 8686
Trying ::1...
Connected to localhost.
Escape character is '^]'.
create connect
hello world
hello world // 服务端返回结果
属性
socket.localPort
socket.localAddress
...
总结
可以多写一些例子,加强理解。Net 模块是 Nodejs 比较重要的模块。看源码的过程中,也看到了对 stream 模块, async_hook 模块的依赖,更深一层还涉及到系统层面 libuv 的调度。这些更底层,能了解多一些,对 nodejs 服务的运行会有更清晰的认知。
网友评论