Node系列 · Node基础:net 模块

Node系列 · Node基础:net 模块

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

一、net 模块定位

net 在 Node 模块体系里的位置如下图------所有上层网络通信都建立在它之上:
#mermaid-svg-ac7vYqSRbNp3Rjdx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ac7vYqSRbNp3Rjdx .error-icon{fill:#552222;}#mermaid-svg-ac7vYqSRbNp3Rjdx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ac7vYqSRbNp3Rjdx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .marker.cross{stroke:#333333;}#mermaid-svg-ac7vYqSRbNp3Rjdx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ac7vYqSRbNp3Rjdx p{margin:0;}#mermaid-svg-ac7vYqSRbNp3Rjdx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .cluster-label text{fill:#333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .cluster-label span{color:#333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .cluster-label span p{background-color:transparent;}#mermaid-svg-ac7vYqSRbNp3Rjdx .label text,#mermaid-svg-ac7vYqSRbNp3Rjdx span{fill:#333;color:#333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .node rect,#mermaid-svg-ac7vYqSRbNp3Rjdx .node circle,#mermaid-svg-ac7vYqSRbNp3Rjdx .node ellipse,#mermaid-svg-ac7vYqSRbNp3Rjdx .node polygon,#mermaid-svg-ac7vYqSRbNp3Rjdx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ac7vYqSRbNp3Rjdx .rough-node .label text,#mermaid-svg-ac7vYqSRbNp3Rjdx .node .label text,#mermaid-svg-ac7vYqSRbNp3Rjdx .image-shape .label,#mermaid-svg-ac7vYqSRbNp3Rjdx .icon-shape .label{text-anchor:middle;}#mermaid-svg-ac7vYqSRbNp3Rjdx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ac7vYqSRbNp3Rjdx .rough-node .label,#mermaid-svg-ac7vYqSRbNp3Rjdx .node .label,#mermaid-svg-ac7vYqSRbNp3Rjdx .image-shape .label,#mermaid-svg-ac7vYqSRbNp3Rjdx .icon-shape .label{text-align:center;}#mermaid-svg-ac7vYqSRbNp3Rjdx .node.clickable{cursor:pointer;}#mermaid-svg-ac7vYqSRbNp3Rjdx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .arrowheadPath{fill:#333333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ac7vYqSRbNp3Rjdx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ac7vYqSRbNp3Rjdx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ac7vYqSRbNp3Rjdx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ac7vYqSRbNp3Rjdx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ac7vYqSRbNp3Rjdx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ac7vYqSRbNp3Rjdx .cluster text{fill:#333;}#mermaid-svg-ac7vYqSRbNp3Rjdx .cluster span{color:#333;}#mermaid-svg-ac7vYqSRbNp3Rjdx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ac7vYqSRbNp3Rjdx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ac7vYqSRbNp3Rjdx rect.text{fill:none;stroke-width:0;}#mermaid-svg-ac7vYqSRbNp3Rjdx .icon-shape,#mermaid-svg-ac7vYqSRbNp3Rjdx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ac7vYqSRbNp3Rjdx .icon-shape p,#mermaid-svg-ac7vYqSRbNp3Rjdx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ac7vYqSRbNp3Rjdx .icon-shape .label rect,#mermaid-svg-ac7vYqSRbNp3Rjdx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ac7vYqSRbNp3Rjdx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ac7vYqSRbNp3Rjdx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ac7vYqSRbNp3Rjdx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 基础层
应用层
基于
http
websocket
数据库驱动
自定义 TCP 服务
net 模块

TCP / IPC
tls 模块

TLS/SSL
操作系统 / 内核 / 网卡

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

二、Socket 概念

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

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

三、创建 TCP 客户端

3.1 最小客户端

javascript:net-client.js 复制代码
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:net-server.js 复制代码
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 关键事件

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

::: warning

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

:::

4.3 显式监听 vs 回调式创建

javascript:net-server-explicit.js 复制代码
// 方式 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:net-listen-options.js 复制代码
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:echo-server.js 复制代码
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:echo-client.js 复制代码
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.remoteAddress string 客户端 IP
socket.remotePort number 客户端端口
socket.localAddress string 服务端本地 IP
socket.localPort number 服务端本地端口
socket.bytesRead number 累计接收字节数
socket.bytesWritten number 累计发送字节数
socket.readyState string '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:net-timeout.js 复制代码
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-demo/ 复制代码
ipc-parent.js
ipc-child.js
javascript:ipc-parent.js 复制代码
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:ipc-child.js 复制代码
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:
#mermaid-svg-5b77fY12qjmP9Z7x{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5b77fY12qjmP9Z7x .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5b77fY12qjmP9Z7x .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5b77fY12qjmP9Z7x .error-icon{fill:#552222;}#mermaid-svg-5b77fY12qjmP9Z7x .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5b77fY12qjmP9Z7x .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5b77fY12qjmP9Z7x .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5b77fY12qjmP9Z7x .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5b77fY12qjmP9Z7x .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5b77fY12qjmP9Z7x .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5b77fY12qjmP9Z7x .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5b77fY12qjmP9Z7x .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5b77fY12qjmP9Z7x .marker.cross{stroke:#333333;}#mermaid-svg-5b77fY12qjmP9Z7x svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5b77fY12qjmP9Z7x p{margin:0;}#mermaid-svg-5b77fY12qjmP9Z7x .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-5b77fY12qjmP9Z7x .cluster-label text{fill:#333;}#mermaid-svg-5b77fY12qjmP9Z7x .cluster-label span{color:#333;}#mermaid-svg-5b77fY12qjmP9Z7x .cluster-label span p{background-color:transparent;}#mermaid-svg-5b77fY12qjmP9Z7x .label text,#mermaid-svg-5b77fY12qjmP9Z7x span{fill:#333;color:#333;}#mermaid-svg-5b77fY12qjmP9Z7x .node rect,#mermaid-svg-5b77fY12qjmP9Z7x .node circle,#mermaid-svg-5b77fY12qjmP9Z7x .node ellipse,#mermaid-svg-5b77fY12qjmP9Z7x .node polygon,#mermaid-svg-5b77fY12qjmP9Z7x .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5b77fY12qjmP9Z7x .rough-node .label text,#mermaid-svg-5b77fY12qjmP9Z7x .node .label text,#mermaid-svg-5b77fY12qjmP9Z7x .image-shape .label,#mermaid-svg-5b77fY12qjmP9Z7x .icon-shape .label{text-anchor:middle;}#mermaid-svg-5b77fY12qjmP9Z7x .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5b77fY12qjmP9Z7x .rough-node .label,#mermaid-svg-5b77fY12qjmP9Z7x .node .label,#mermaid-svg-5b77fY12qjmP9Z7x .image-shape .label,#mermaid-svg-5b77fY12qjmP9Z7x .icon-shape .label{text-align:center;}#mermaid-svg-5b77fY12qjmP9Z7x .node.clickable{cursor:pointer;}#mermaid-svg-5b77fY12qjmP9Z7x .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5b77fY12qjmP9Z7x .arrowheadPath{fill:#333333;}#mermaid-svg-5b77fY12qjmP9Z7x .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5b77fY12qjmP9Z7x .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5b77fY12qjmP9Z7x .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5b77fY12qjmP9Z7x .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5b77fY12qjmP9Z7x .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5b77fY12qjmP9Z7x .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5b77fY12qjmP9Z7x .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5b77fY12qjmP9Z7x .cluster text{fill:#333;}#mermaid-svg-5b77fY12qjmP9Z7x .cluster span{color:#333;}#mermaid-svg-5b77fY12qjmP9Z7x div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5b77fY12qjmP9Z7x .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5b77fY12qjmP9Z7x rect.text{fill:none;stroke-width:0;}#mermaid-svg-5b77fY12qjmP9Z7x .icon-shape,#mermaid-svg-5b77fY12qjmP9Z7x .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5b77fY12qjmP9Z7x .icon-shape p,#mermaid-svg-5b77fY12qjmP9Z7x .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5b77fY12qjmP9Z7x .icon-shape .label rect,#mermaid-svg-5b77fY12qjmP9Z7x .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5b77fY12qjmP9Z7x .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5b77fY12qjmP9Z7x .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5b77fY12qjmP9Z7x :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} TCP 三次握手
on data
write
TCP 挥手
浏览器
socket
http.IncomingMessage
业务 handler
http.ServerResponse

理解 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 给出了基础设施,业务层要负责协议层
相关推荐
Diligently_1 小时前
Anolis 系统更新与密码设置&磁盘扩容
java·后端·spring
kyson_1 小时前
大文件切片上传:从“能传”到“可靠”的 Vue 3 + Java 全栈实现
后端
张龙6871 小时前
uv 全面指南:比 pip 快 100 倍的 Python 包管理器,从安装到团队落地
后端·python
用户40966601317512 小时前
Guava 不止有 Lists 和 Maps:Cache、EventBus、Multimap 实战
java·后端
一只叫煤球的猫2 小时前
从 Java 21 到 Java 25:ThreadLocal 以外的选择—— ScopedValue
java·后端·架构
用户3169353811833 小时前
SpringBoot + Vue 项目生产环境部署完整指南
前端·后端
霸道流氓气质3 小时前
Spring 事务同步机制 —— AfterTransactionActionCollector 的异步解耦原理
java·后端·spring
Csvn3 小时前
📊 SQL 入门 Day 19:事务与隔离级别
后端·sql