Skip to content

Node系列 · Node基础:net 模块

net 是 Node 最底层的网络通信模块,提供基于 TCP / Unix 套接字的能力。HTTP、WebSocket、数据库驱动等模块几乎都建立在它之上。理解 net 就理解了 Node 网络编程的核心模型——socket 即文件、流即 socket。

一、net 模块定位

net 在 Node 模块体系里的位置如下图——所有上层网络通信都建立在它之上:

通信类型适用Node 模块
TCP/IP 网络通信跨主机、跨进程net
进程间通信(IPC)同一台机器多进程net.connect({ path }) 用 Unix 域套接字
HTTP 协议Web 服务http(基于 net
TLS 加密HTTPStls(基于 net

二、Socket 概念

socket 是 TCP 通信的端点——一个虚拟的文件,可通过读写收发数据。

维度说明
本质操作系统分配的资源标识(文件描述符)
Node 表现net.Socket 实例,继承 Duplex 流(既可读也可写)
通信方式一端 write() 数据 → 另一端 data 事件触发
生命周期connectdata × N → end / errorclose

三、创建 TCP 客户端

3.1 最小客户端

javascript
const net = require('node:net');

const client = net.createConnection({ host: '127.0.0.1', port: 3000 }, () => {
  console.log('已连接到服务端');
  client.write('hello from client\n');
});

client.on('data', (data) => {
  console.log('服务端说:', data.toString().trim());
  client.end();
});

client.on('end', () => {
  console.log('服务端关闭连接');
});

client.on('error', (err) => {
  console.error('连接出错:', err.message);
});

3.2 返回值

net.createConnection 返回 net.Socket 实例(同时也是 Duplex 流)。所以:

  • 可以 client.write(data) 发数据
  • 可以监听 data / end / error / close 事件
  • 可以 client.pipe(...) 串到别处

四、创建 TCP 服务端

4.1 最简服务端

javascript
const net = require('node:net');

const server = net.createServer((socket) => {
  console.log(`新客户端连接: ${socket.remoteAddress}:${socket.remotePort}`);

  socket.write('欢迎连接\n');

  socket.on('data', (data) => {
    console.log(`收到: ${data.toString().trim()}`);
    socket.write(`echo: ${data}`);
  });

  socket.on('end', () => {
    console.log('客户端断开');
  });

  socket.on('error', (err) => {
    console.error('socket 错误:', err.message);
  });
});

server.listen(3000, () => {
  console.log('服务端监听 3000 端口');
});

4.2 关键事件

事件触发时机用途
listeningserver.listen() 成功后服务可用通知
connection新客户端连接(回调中第一个参数)给 socket 注册处理
close服务关闭且所有连接断开资源清理
error出错(端口占用等)错误处理

WARNING

端口占用是 EADDRINUSE 错误。换端口或先 server.close() 再重启。

4.3 显式监听 vs 回调式创建

javascript
// 方式 1:callback 内处理(前面用的)
net.createServer((socket) => { /* ... */ }).listen(3000);

// 方式 2:先建 server,再 listen
const server = net.createServer();
server.on('connection', (socket) => { /* ... */ });
server.listen(3000, () => console.log('listening'));

两种等价;项目里选一种保持风格统一。

4.4 端口监听选项

javascript
server.listen({
  port: 3000,
  host: '0.0.0.0',     // 监听所有网卡(默认);'127.0.0.1' 只本机
  backlog: 511,        // TCP 等待连接队列长度(默认 511)
  exclusive: false,    // Node 14+:独占端口(cluster 场景)
}, () => {
  const addr = server.address();
  console.log(`监听 ${addr.address}:${addr.port}`);
});

五、回显服务端:完整示例

把客户端发来的数据原样返回:

text
echo-server/
├── server.js
└── client.js
javascript
const net = require('node:net');

const server = net.createServer((socket) => {
  socket.setEncoding('utf-8');
  socket.write('欢迎连接 echo server,输入任意内容回显\n');

  socket.on('data', (chunk) => {
    const text = chunk.trim();
    if (text === 'bye') {
      socket.write('再见\n');
      socket.end();
      return;
    }
    socket.write(`echo: ${text}\n`);
  });
});

server.listen(3000);
javascript
const net = require('node:net');
const readline = require('node:readline');

const client = net.createConnection({ port: 3000 }, () => {
  console.log('已连接,输入内容回显(输入 bye 退出)');
});

const rl = readline.createInterface({ input: process.stdin });
rl.on('line', (line) => {
  client.write(`${line}\n`);
});

client.setEncoding('utf-8');
client.on('data', (chunk) => console.log(chunk.trim()));
client.on('end', () => {
  console.log('连接关闭');
  rl.close();
  process.exit(0);
});
bash
$ node echo-server.js
$ node echo-client.js
已连接,输入内容回显(输入 bye 退出)
hello
echo: hello
bye
再见
连接关闭

六、socket 关键属性与方法

6.1 属性

属性类型说明
socket.remoteAddressstring客户端 IP
socket.remotePortnumber客户端端口
socket.localAddressstring服务端本地 IP
socket.localPortnumber服务端本地端口
socket.bytesReadnumber累计接收字节数
socket.bytesWrittennumber累计发送字节数
socket.readyStatestring'opening' / 'open' / 'readOnly' / 'writeOnly' / 'closed'

6.2 方法

方法作用
socket.write(data, [encoding], [cb])发数据,返回 boolean(背压信号)
socket.end([data], [encoding])半关闭(FIN),后续只能接收
socket.destroy()强制关闭(不发 FIN)
socket.setEncoding(encoding)设置接收数据编码
socket.setTimeout(ms, cb)空闲超时回调(无数据收发 ms 毫秒后触发)
socket.setNoDelay(noDelay)true 关闭 Nagle 算法(适合实时通信)
socket.setKeepAlive(enable, initialDelay)启用 TCP keep-alive

6.3 空闲超时实战

javascript
const server = net.createServer((socket) => {
  socket.setTimeout(30_000); // 30 秒无数据则超时
  socket.on('timeout', () => {
    console.log('客户端空闲超时,断开');
    socket.end('idle timeout\n');
  });
});

七、进程间通信(IPC)

同一台机器上的两个 Node 进程,可以用 Unix 域套接字通信:

text
ipc-parent.js
ipc-child.js
javascript
const net = require('node:net');
const { fork } = require('node:child_process');

// 父进程:起服务
const server = net.createServer((socket) => {
  socket.on('data', (data) => {
    console.log('收到子进程:', data.toString().trim());
    socket.write('ack from parent\n');
  });
});
server.listen('/tmp/my-ipc.sock', () => {
  fork('./ipc-child.js');
});
javascript
const net = require('node:net');

const client = net.createConnection('/tmp/my-ipc.sock', () => {
  client.write('hello parent\n');
});

client.on('data', (data) => {
  console.log('父进程响应:', data.toString().trim());
  client.end();
});

TIP

实际项目里用 child_process.fork 自带的 IPC 通道(process.send / process.on('message'))就够了,不需要自己写 Unix 域套接字。上面的例子只用于"必须用 raw net" 的场景。

八、socketEventEmitter 的关系

net.Socket 继承自 stream.Duplexstream.Duplex 又继承自 stream.Stream,最终继承自 events.EventEmitter。这就是为什么 socket 能用 on('data', ...) 这种事件 API——本质上都是 EventEmitter。

后续学 http.ServerRequest / http.ServerResponse 时会发现它们也是同样的继承链。

九、net 与 http 的关系

http.Server 内部就是 net.Server,每条 HTTP 连接对应一个 socket:

理解 net 后,HTTP 的 req / res 就只是"加了 HTTP 协议解析的 socket"——一切 socket 方法(write / setTimeout / destroy)在 req / res 上也都能用。

十、最佳实践

场景推荐
TCP 服务端net.createServer(cb).listen(port)
TCP 客户端net.createConnection({ host, port })
数据结束判断协议层自定义结束符(如 \n)或固定长度
大数据传输用流(socket.pipe(writable)
错误处理必须监听 'error'——不监听未捕获异常直接 crash
空闲连接socket.setTimeout 主动断开
高并发cluster 模块或反向代理,不要单进程硬扛

十一、小结

  • net 是 Node 网络通信的最底层;http / tls 都基于它
  • 服务端 net.createServer + listen,客户端 net.createConnection
  • net.Socket 是 Duplex 流,既能写也能读,事件 API 与 EventEmitter 一致
  • 同一进程内的多进程通信可用 IPC(path 选项的 Unix 域套接字)
  • TCP 通信要解决四个问题:连接管理、协议解析、背压、空闲超时——net 给出了基础设施,业务层要负责协议层