Node.js net 模块教程

server/2025/2/26 23:17:10/

Node.js net 模块教程

简介

Node.js 的 net 模块提供了用于实现 TCP 服务器和客户端的异步网络 API。它是 Node.js 网络功能的核心,为上层模块如 HTTP、HTTPS 等提供了基础支持。本教程将全面介绍 net 模块的使用方法和最佳实践。

引入 net 模块

const net = require('net');

核心概念

TCP (传输控制协议)

TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议。net 模块主要处理 TCP 通信。

Socket

Socket 是网络通信的端点,在 Node.js 中表示为 net.Socket 类的实例。它可以是服务器与客户端之间建立的连接,也可以是客户端主动创建的连接。

服务器

服务器使用 net.Server 类创建,负责监听连接并处理客户端请求。

TCP 服务器创建

基本服务器

const net = require('net');// 创建服务器
const server = net.createServer((socket) => {console.log('客户端已连接');// 接收数据socket.on('data', (data) => {console.log(`接收到数据: ${data}`);// 发送响应socket.write('服务器已收到你的消息');});// 连接关闭socket.on('end', () => {console.log('客户端已断开连接');});// 处理错误socket.on('error', (err) => {console.error('连接错误:', err);});
});// 监听端口
server.listen(3000, () => {console.log('服务器启动成功,监听端口 3000');
});

服务器配置选项

创建服务器时可以传递配置选项:

const server = net.createServer({allowHalfOpen: false, // 当另一端发送 FIN 包时自动发送 FIN (默认)pauseOnConnect: false // 是否在连接时暂停套接字 (默认)
});

服务器事件

net.Server 类继承自 EventEmitter,支持以下主要事件:

  • listening: 服务器开始监听连接时触发
  • connection: 新客户端连接建立时触发
  • error: 发生错误时触发
  • close: 服务器关闭时触发
server.on('listening', () => {console.log('服务器开始监听连接');
});server.on('connection', (socket) => {console.log('新客户端连接');
});server.on('error', (err) => {console.error('服务器错误:', err);
});server.on('close', () => {console.log('服务器已关闭');
});

TCP 客户端创建

基本客户端

const net = require('net');// 创建连接
const client = net.createConnection({ host: 'localhost',port: 3000 
}, () => {console.log('已连接到服务器');// 发送数据client.write('你好,服务器');
});// 接收数据
client.on('data', (data) => {console.log(`接收到服务器响应: ${data}`);// 关闭连接client.end();
});// 连接结束
client.on('end', () => {console.log('已断开与服务器的连接');
});// 错误处理
client.on('error', (err) => {console.error('连接错误:', err);
});

客户端配置选项

创建客户端连接时可以传递多种配置选项:

const client = net.createConnection({host: 'localhost', // 主机名port: 3000,        // 端口号localAddress: '192.168.1.100', // 本地接口family: 4,         // IP 版本 (4 或 6)timeout: 5000      // 连接超时(毫秒)
});

Socket 对象

net.Socket 是 TCP 连接的抽象,具有流(Duplex Stream)的特性,既可读又可写。

创建 Socket

除了服务器自动创建外,也可以手动创建:

const socket = new net.Socket();
socket.connect(3000, 'localhost', () => {console.log('连接成功');
});

Socket 属性

  • socket.remoteAddress: 远程 IP 地址
  • socket.remotePort: 远程端口
  • socket.localAddress: 本地 IP 地址
  • socket.localPort: 本地端口
  • socket.bytesRead: 接收的字节数
  • socket.bytesWritten: 发送的字节数
socket.on('connect', () => {console.log(`连接到 ${socket.remoteAddress}:${socket.remotePort}`);console.log(`本地端口: ${socket.localPort}`);
});

Socket 方法

  • socket.write(data[, encoding][, callback]): 发送数据
  • socket.end([data][, encoding][, callback]): 结束连接
  • socket.destroy([error]): 强制关闭连接
  • socket.pause(): 暂停数据读取
  • socket.resume(): 恢复数据读取
  • socket.setKeepAlive([enable][, initialDelay]): 设置 keepalive
  • socket.setNoDelay([noDelay]): 禁用 Nagle 算法

事件处理

服务器事件

server.on('listening', () => {const address = server.address();console.log(`服务器监听 ${address.address}:${address.port}`);
});server.on('error', (err) => {if (err.code === 'EADDRINUSE') {console.error('端口已被占用');}
});

Socket 事件

  • connect: 成功建立连接时触发
  • data: 接收到数据时触发
  • end: 对方结束发送数据时触发
  • timeout: 连接超时时触发
  • error: 发生错误时触发
  • close: 连接完全关闭时触发
socket.on('data', (data) => {console.log(`接收到数据: ${data.toString()}`);
});socket.on('timeout', () => {console.log('连接超时');socket.end();
});socket.on('close', (hadError) => {console.log(`连接关闭${hadError ? ',发生错误' : ''}`);
});

数据传输

发送数据

// 发送字符串
socket.write('Hello', 'utf8');// 发送 Buffer
const buffer = Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]);
socket.write(buffer);// 使用回调确认数据已被发送
socket.write('World', () => {console.log('数据已发送');
});

接收数据

let chunks = [];socket.on('data', (chunk) => {chunks.push(chunk);
});socket.on('end', () => {const data = Buffer.concat(chunks).toString();console.log(`完整数据: ${data}`);
});

处理二进制数据

socket.on('data', (chunk) => {// 假设前两个字节表示消息长度const messageLength = chunk.readUInt16BE(0);const message = chunk.slice(2, 2 + messageLength);console.log(`消息内容: ${message.toString()}`);
});

高级特性

IPC (进程间通信)

除了 TCP 通信,net 模块也支持通过 Unix 域套接字或命名管道进行进程间通信:

// 服务器
const server = net.createServer().listen('/tmp/echo.sock');// 客户端
const client = net.createConnection({ path: '/tmp/echo.sock' });

多连接管理

实际应用中,服务器通常需要管理多个连接:

const connections = new Map();server.on('connection', (socket) => {const id = `${socket.remoteAddress}:${socket.remotePort}`;connections.set(id, socket);socket.on('close', () => {connections.delete(id);console.log(`客户端 ${id} 已断开,当前连接数: ${connections.size}`);});
});// 向所有客户端广播消息
function broadcast(message) {for (const socket of connections.values()) {socket.write(message);}
}

重连机制

客户端断线重连示例:

function createClient() {const client = net.createConnection({ port: 3000 });client.on('error', (err) => {console.error('连接错误:', err);});client.on('close', () => {console.log('连接关闭,尝试重连...');setTimeout(() => {createClient();}, 3000); // 3秒后重连});return client;
}const client = createClient();

实际应用案例

简单聊天服务器

const net = require('net');
const clients = [];const server = net.createServer((socket) => {// 为新连接分配昵称socket.name = `用户${clients.length + 1}`;// 广播新用户连接消息const message = `${socket.name} 已加入聊天室`;broadcast(message, socket);// 添加到客户端列表clients.push(socket);// 欢迎消息socket.write(`欢迎来到聊天室,${socket.name}!\n`);// 接收消息socket.on('data', (data) => {broadcast(`${socket.name}: ${data}`, socket);});// 断开连接socket.on('end', () => {clients.splice(clients.indexOf(socket), 1);broadcast(`${socket.name} 已离开聊天室`, socket);});// 处理错误socket.on('error', (err) => {console.error(`${socket.name} 发生错误:`, err);});
});// 广播消息给所有客户端
function broadcast(message, sender) {clients.forEach((client) => {// 不发送给消息发送者if (client !== sender) {client.write(message);}});console.log(message);
}server.listen(3000, () => {console.log('聊天服务器已启动,监听端口 3000');
});

简单的 HTTP 服务器

使用 net 模块实现基础 HTTP 服务器:

const net = require('net');const server = net.createServer((socket) => {socket.on('data', (data) => {const request = data.toString();console.log('收到请求:', request);// 简单的 HTTP 响应const response = ['HTTP/1.1 200 OK','Content-Type: text/html','Connection: close','','<html><body><h1>Hello from Node.js net module</h1></body></html>'].join('\r\n');socket.write(response);socket.end();});socket.on('error', (err) => {console.error('Socket 错误:', err);});
});server.listen(8080, () => {console.log('HTTP 服务器运行在 http://localhost:8080/');
});

性能优化

使用 Buffer 池

对于高性能应用,可以使用 Buffer 池避免频繁创建新 Buffer:

const bufferPool = Buffer.allocUnsafe(1024 * 100); // 100KB 池
let offset = 0;function allocateBuffer(size) {if (offset + size > bufferPool.length) {offset = 0; // 重置偏移}const buffer = bufferPool.slice(offset, offset + size);offset += size;return buffer;
}// 使用预分配的 buffer 发送数据
const dataToSend = "Hello";
const buffer = allocateBuffer(dataToSend.length);
buffer.write(dataToSend);
socket.write(buffer);

避免小包发送

合并小数据包可以提高网络效率:

const queue = [];
let isFlushing = false;function queueData(socket, data) {queue.push(data);if (!isFlushing) {isFlushing = true;process.nextTick(flushQueue, socket);}
}function flushQueue(socket) {if (queue.length > 0) {const data = Buffer.concat(queue);queue.length = 0;socket.write(data);}isFlushing = false;
}

调整 Socket 参数

针对不同场景优化 Socket 设置:

// 低延迟应用 (禁用 Nagle 算法)
socket.setNoDelay(true);// 长连接应用
socket.setKeepAlive(true, 60000); // 60秒// 设置超时
socket.setTimeout(30000); // 30秒
socket.on('timeout', () => {console.log('连接超时');socket.end();
});

常见问题解答

Q: 如何处理 EADDRINUSE 错误?

A: 这个错误表示端口已被占用,可以通过以下方式处理:

server.on('error', (err) => {if (err.code === 'EADDRINUSE') {console.log('端口已被占用,尝试其他端口...');server.close();server.listen(port + 1);}
});

Q: 如何实现心跳机制?

A: 通过定时发送心跳包确保连接活跃:

// 服务端心跳检测
const clients = new Map();server.on('connection', (socket) => {const id = `${socket.remoteAddress}:${socket.remotePort}`;clients.set(id, { socket, lastHeartbeat: Date.now() });socket.on('data', (data) => {if (data.toString() === 'PING') {clients.get(id).lastHeartbeat = Date.now();socket.write('PONG');}});
});// 每10秒检查一次客户端心跳
setInterval(() => {const now = Date.now();for (const [id, client] of clients.entries()) {// 如果客户端30秒没有心跳,断开连接if (now - client.lastHeartbeat > 30000) {console.log(`客户端 ${id} 心跳超时,断开连接`);client.socket.destroy();clients.delete(id);}}
}, 10000);// 客户端心跳
const client = net.createConnection({ port: 3000 });
setInterval(() => {client.write('PING');
}, 10000);

Q: 如何处理大量数据传输?

A: 使用流控制和数据分块:

const fs = require('fs');// 发送大文件
function sendLargeFile(socket, filePath) {const fileStream = fs.createReadStream(filePath);fileStream.on('data', (chunk) => {// 检查缓冲区是否已满const canContinue = socket.write(chunk);if (!canContinue) {// 如果缓冲区已满,暂停读取fileStream.pause();// 当缓冲区清空后,恢复读取socket.once('drain', () => {fileStream.resume();});}});fileStream.on('end', () => {console.log('文件发送完成');});
}

http://www.ppmy.cn/server/170874.html

相关文章

Day28 第八章 贪心算法 part01

一. 学习文章及资料 理论基础 455.分发饼干 376.摆动序列 53.最大子序和 二. 学习内容 1. 理论基础 贪心算法无规律&#xff01; 一般如想到局部最优&#xff0c;好像能推出全局最优&#xff0c;并且无明显反例&#xff0c;那就试一试&#xff01; 2. 分发饼干 (1) 解题思…

从零开始开发纯血鸿蒙应用之网页浏览

从零开始开发纯血鸿蒙应用 〇、前言一、优化菜单交互1、BuilderFunction.ets2、改造 PageTitleBar 二、网址打开1、方式选择1、使用浏览器打开2、内部打开2.1、声明权限2.2、封装 WebViewPage2.2.1、组件字段2.2.2、aboutToAppear2.2.3、onBackPress2.2.4、标题栏2.2.4、网页内…

c/c++蓝桥杯经典编程题100道(22)最短路径问题

最短路径问题 ->返回c/c蓝桥杯经典编程题100道-目录 目录 最短路径问题 一、题型解释 二、例题问题描述 三、C语言实现 解法1&#xff1a;Dijkstra算法&#xff08;正权图&#xff0c;难度★★&#xff09; 解法2&#xff1a;Bellman-Ford算法&#xff08;含负权边&a…

【数据挖掘】数据仓库

数据仓库 目录&#xff1a;数据仓库相关知识点笔记4.2 数据仓库建模&#xff1a;数据立方体与 OLAP4.2.1 数据立方体&#xff1a;一种多维数据模型4.2.2 星形、雪花形和事实星座&#xff1a;多维数据模型的模式4.2.3 维&#xff1a;概念分层的作用4.2.4 度量的分类和计算4.2.5 …

深度学习之特征提取

前言 深度学习就是把输入转换成一个高维的向量&#xff0c;之后利用这个向量去完成分类、回归等任务。 深度学习特征工程知识图谱 1. 特征提取的本质 核心目标&#xff1a;将原始数据→高维语义特征向量 监督驱动&#xff1a;标签决定特征提取方向 典型架构&#xff1a; …

matlab 海浪模型和舰艇动力学模型

1、内容简介 matlab148-海浪模型和舰艇动力学模型 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

墨刀列表突然变得很小怎么办?

换个浏览器就好了 或者调整原浏览器比例

ubuntu 安全策略(等保)

windows 三个帐号屏保设置组策略,密码超时次数/审计记录&#xff1b; linux 应具有登录失败处理功能&#xff0c;应配置并启用结束会话、限制非法登录次数和当登录连接超时自动退出等相关措施。 1、在系统中新建测试用户&#xff0c;使用此用户登录时多次输入错误密码&…