简介
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]): 设置 keepalivesocket.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('文件发送完成');
});
}到此这篇关于node.js net模块的使用示例的文章就介绍到这了,更多相关node.js net模块内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论