Node.js之TCP服务(net模块)
引言
TCP(传输控制协议)是互联网的基础通信协议之一,它提供可靠的、面向连接的字节流服务。在Node.js中,net模块为创建TCP服务器和客户端提供了强大的API支持。
TCP与 UDP 区别
核心对比
| 特性 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 可靠传输 | 不可靠传输 |
| 传输方式 | 字节流 | 数据报 |
| 速度 | 较慢 | 较快 |
| 头部开销 | 20字节 | 8字节 |
| 顺序保证 | 保证顺序 | 不保证顺序 |
| 流量控制 | 支持 | 不支持 |
| 拥塞控制 | 支持 | 不支持 |
TCP连接建立过程
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: TCP三次握手
C->>+S: SYN seq=100
S-->>+C: SYN-ACK seq=200, ack=101
C->>S: ACK seq=101, ack=201
Note over C,S: 连接建立完成
Note over C,S: 数据传输阶段
C->>S: Data seq=101
S-->>C: ACK ack=102
Note over C,S: TCP四次挥手
C->>+S: FIN seq=102
S-->>+C: ACK ack=103
S->>+C: FIN seq=201
C-->>S: ACK ack=202
Note over C,S: 连接关闭完成
适用场景对比
// TCP 适用场景
const tcpScenarios = [
'HTTP/HTTPS 网站访问',
'文件传输 (FTP)',
'邮件服务 (SMTP/IMAP)',
'数据库连接',
'远程登录 (SSH)',
'聊天应用(需要消息完整性)'
];
// UDP 适用场景
const udpScenarios = [
'DNS 查询',
'视频/音频流媒体',
'在线游戏',
'实时监控',
'DHCP 服务',
'广播/组播通信'
];
创建 TCP 服务器
创建简单的TCP 服务器
const net = require('net');
// 创建基础TCP服务器
const server = net.createServer((socket) => {
console.log('客户端连接成功:', socket.remoteAddress + ':' + socket.remotePort);
// 向客户端发送欢迎消息
socket.write('欢迎连接到TCP服务器!\n');
// 监听客户端数据
socket.on('data', (data) => {
console.log('接收到数据:', data.toString().trim());
// 回显数据给客户端
socket.write('服务器收到: ' + data);
});
// 监听连接关闭
socket.on('close', () => {
console.log('客户端断开连接');
});
// 监听错误事件
socket.on('error', (err) => {
console.error('Socket错误:', err);
});
});
// 启动服务器监听
const PORT = 8080;
const HOST = '127.0.0.1';
server.listen(PORT, HOST, () => {
console.log(`TCP服务器启动,监听 ${HOST}:${PORT}`);
});
// 处理服务器错误
server.on('error', (err) => {
console.error('服务器错误:', err);
});
服务器的事件
const net = require('net');
class TCPServer {
constructor(options = {}) {
this.port = options.port || 8080;
this.host = options.host || '127.0.0.1';
this.server = null;
this.connections = new Map(); // 存储连接信息
this.connectionId = 0;
}
start() {
this.server = net.createServer();
// 监听连接事件
this.server.on('connection', (socket) => {
this.handleConnection(socket);
});
// 监听服务器启动事件
this.server.on('listening', () => {
const address = this.server.address();
console.log(`TCP服务器启动成功: ${address.address}:${address.port}`);
});
// 监听关闭事件
this.server.on('close', () => {
console.log('TCP服务器已关闭');
});
// 监听错误事件
this.server.on('error', (err) => {
console.error('TCP服务器错误:', err);
if (err.code === 'EADDRINUSE') {
console.error(`端口 ${this.port} 已被占用`);
}
});
// 开始监听
this.server.listen(this.port, this.host);
}
handleConnection(socket) {
const connectionId = ++this.connectionId;
const clientInfo = `${socket.remoteAddress}:${socket.remotePort}`;
// 存储连接信息
this.connections.set(connectionId, {
socket: socket,
clientInfo: clientInfo,
connectedAt: new Date(),
bytesReceived: 0,
bytesSent: 0
});
console.log(`[${connectionId}] 新连接: ${clientInfo}`);
console.log(`当前连接数: ${this.connections.size}`);
// 设置socket编码
socket.setEncoding('utf8');
// 数据接收事件
socket.on('data', (data) => {
this.handleData(connectionId, data);
});
// 连接关闭事件
socket.on('close', (hadError) => {
this.handleDisconnection(connectionId, hadError);
});
// 错误事件
socket.on('error', (err) => {
this.handleSocketError(connectionId, err);
});
// 超时事件
socket.on('timeout', () => {
console.log(`[${connectionId}] 连接超时`);
socket.end('连接超时\n');
});
// 设置超时时间(30秒)
socket.setTimeout(30000);
// 发送欢迎消息
this.sendMessage(connectionId, `欢迎连接! 连接ID: ${connectionId}\n`);
}
handleData(connectionId, data) {
const connection = this.connections.get(connectionId);
if (!connection) return;
const message = data.toString().trim();
connection.bytesReceived += data.length;
console.log(`[${connectionId}] 收到数据: ${message}`);
// 处理特殊命令
if (message.startsWith('/')) {
this.handleCommand(connectionId, message);
} else {
// 普通消息回显
this.sendMessage(connectionId, `回显: ${message}\n`);
}
}
handleCommand(connectionId, command) {
const connection = this.connections.get(connectionId);
if (!connection) return;
switch (command) {
case '/info':
const info = {
connectionId: connectionId,
clientInfo: connection.clientInfo,
connectedAt: connection.connectedAt,
bytesReceived: connection.bytesReceived,
bytesSent: connection.bytesSent,
uptime: Date.now() - connection.connectedAt.getTime()
};
this.sendMessage(connectionId, `连接信息: ${JSON.stringify(info, null, 2)}\n`);
break;
case '/list':
const activeConnections = Array.from(this.connections.keys());
this.sendMessage(connectionId, `活跃连接: ${activeConnections.join(', ')}\n`);
break;
case '/quit':
this.sendMessage(connectionId, '再见!\n');
connection.socket.end();
break;
default:
this.sendMessage(connectionId, `未知命令: ${command}\n`);
}
}
handleDisconnection(connectionId, hadError) {
const connection = this.connections.get(connectionId);
if (connection) {
console.log(`[${connectionId}] 连接断开: ${connection.clientInfo}${hadError ? ' (有错误)' : ''}`);
this.connections.delete(connectionId);
}
console.log(`当前连接数: ${this.connections.size}`);
}
handleSocketError(connectionId, err) {
console.error(`[${connectionId}] Socket错误:`, err);
const connection = this.connections.get(connectionId);
if (connection) {
connection.socket.destroy();
this.connections.delete(connectionId);
}
}
sendMessage(connectionId, message) {
const connection = this.connections.get(connectionId);
if (connection && !connection.socket.destroyed) {
connection.socket.write(message);
connection.bytesSent += Buffer.byteLength(message);
}
}
// 广播消息给所有连接
broadcast(message, excludeId = null) {
this.connections.forEach((connection, id) => {
if (id !== excludeId && !connection.socket.destroyed) {
connection.socket.write(message);
connection.bytesSent += Buffer.byteLength(message);
}
});
}
// 优雅关闭服务器
close() {
if (this.server) {
// 关闭所有连接
this.connections.forEach((connection) => {
connection.socket.end('服务器即将关闭\n');
});
// 关闭服务器
this.server.close();
}
}
}
// 使用示例
const server = new TCPServer({ port: 8080, host: '127.0.0.1' });
server.start();
// 优雅关闭处理
process.on('SIGINT', () => {
console.log('\n正在关闭服务器...');
server.close();
process.exit(0);
});
处理多个客户端
const net = require('net');
class MultiClientTCPServer {
constructor(options = {}) {
this.port = options.port || 8080;
this.host = options.host || '127.0.0.1';
this.server = null;
this.clients = new Map();
this.rooms = new Map(); // 聊天室功能
this.clientCounter = 0;
}
start() {
this.server = net.createServer((socket) => {
this.addClient(socket);
});
this.server.on('listening', () => {
console.log(`多客户端TCP服务器启动: ${this.host}:${this.port}`);
});
this.server.on('error', (err) => {
console.error('服务器错误:', err);
});
this.server.listen(this.port, this.host);
}
addClient(socket) {
const clientId = ++this.clientCounter;
const clientInfo = {
id: clientId,
socket: socket,
address: socket.remoteAddress,
port: socket.remotePort,
nickname: `用户${clientId}`,
room: null,
connectedAt: new Date()
};
this.clients.set(clientId, clientInfo);
console.log(`客户端 ${clientId} 连接: ${clientInfo.address}:${clientInfo.port}`);
// 设置socket事件
socket.setEncoding('utf8');
socket.setTimeout(60000); // 60秒超时
socket.on('data', (data) => {
this.handleMessage(clientId, data.toString().trim());
});
socket.on('close', () => {
this.removeClient(clientId);
});
socket.on('error', (err) => {
console.error(`客户端 ${clientId} 错误:`, err);
this.removeClient(clientId);
});
socket.on('timeout', () => {
console.log(`客户端 ${clientId} 超时`);
this.sendToClient(clientId, '连接超时,断开连接\n');
socket.end();
});
// 发送欢迎消息和帮助信息
this.sendWelcomeMessage(clientId);
}
removeClient(clientId) {
const client = this.clients.get(clientId);
if (client) {
// 如果在房间中,离开房间
if (client.room) {
this.leaveRoom(clientId);
}
console.log(`客户端 ${clientId} (${client.nickname}) 断开连接`);
this.clients.delete(clientId);
// 通知其他客户端
this.broadcastToAll(`${client.nickname} 离开了服务器\n`, clientId);
}
}
handleMessage(clientId, message) {
const client = this.clients.get(clientId);
if (!client) return;
console.log(`[${client.nickname}]: ${message}`);
// 处理命令
if (message.startsWith('/')) {
this.handleCommand(clientId, message);
} else {
// 普通消息处理
if (client.room) {
// 房间内广播
this.broadcastToRoom(client.room, `[${client.nickname}]: ${message}\n`, clientId);
} else {
// 全局广播
this.broadcastToAll(`[${client.nickname}]: ${message}\n`, clientId);
}
}
}
handleCommand(clientId, command) {
const client = this.clients.get(clientId);
if (!client) return;
const parts = command.split(' ');
const cmd = parts[0].toLowerCase();
const args = parts.slice(1);
switch (cmd) {
case '/help':
this.sendHelpMessage(clientId);
break;
case '/nick':
if (args.length > 0) {
const oldNick = client.nickname;
client.nickname = args.join(' ');
this.sendToClient(clientId, `昵称已更改为: ${client.nickname}\n`);
this.broadcastToAll(`${oldNick} 更改昵称为 ${client.nickname}\n`, clientId);
} else {
this.sendToClient(clientId, '用法: /nick <新昵称>\n');
}
break;
case '/list':
this.sendClientList(clientId);
break;
case '/rooms':
this.sendRoomList(clientId);
break;
case '/join':
if (args.length > 0) {
this.joinRoom(clientId, args[0]);
} else {
this.sendToClient(clientId, '用法: /join <房间名>\n');
}
break;
case '/leave':
this.leaveRoom(clientId);
break;
case '/whisper':
case '/w':
if (args.length >= 2) {
this.sendPrivateMessage(clientId, args[0], args.slice(1).join(' '));
} else {
this.sendToClient(clientId, '用法: /whisper <用户> <消息>\n');
}
break;
case '/quit':
this.sendToClient(clientId, '再见!\n');
client.socket.end();
break;
default:
this.sendToClient(clientId, `未知命令: ${cmd}. 输入 /help 查看帮助\n`);
}
}
sendWelcomeMessage(clientId) {
const welcomeMsg = `
╔══════════════════════════════════╗
║ 欢迎来到TCP聊天服务器 ║
║ ║
║ 您的ID: ${clientId.toString().padEnd(24)} ║
║ 输入 /help 查看可用命令 ║
╚══════════════════════════════════╝
`;
this.sendToClient(clientId, welcomeMsg + '\n');
}
sendHelpMessage(clientId) {
const helpMsg = `
可用命令:
/help - 显示帮助信息
/nick <名称> - 更改昵称
/list - 显示在线用户
/rooms - 显示房间列表
/join <房间> - 加入房间
/leave - 离开当前房间
/whisper <用户> <消息> - 私聊
/quit - 退出服务器
`;
this.sendToClient(clientId, helpMsg + '\n');
}
sendClientList(clientId) {
const clients = Array.from(this.clients.values());
let list = '\n在线用户列表:\n';
list += '─'.repeat(30) + '\n';
clients.forEach(client => {
const status = client.room ? `[房间: ${client.room}]` : '[大厅]';
list += `${client.id}. ${client.nickname} ${status}\n`;
});
list += `\n总计: ${clients.length} 人在线\n`;
this.sendToClient(clientId, list);
}
sendRoomList(clientId) {
let list = '\n房间列表:\n';
list += '─'.repeat(30) + '\n';
if (this.rooms.size === 0) {
list += '当前没有活跃的房间\n';
} else {
this.rooms.forEach((members, roomName) => {
list += `${roomName} (${members.size} 人)\n`;
});
}
this.sendToClient(clientId, list);
}
joinRoom(clientId, roomName) {
const client = this.clients.get(clientId);
if (!client) return;
// 离开当前房间
if (client.room) {
this.leaveRoom(clientId);
}
// 加入新房间
if (!this.rooms.has(roomName)) {
this.rooms.set(roomName, new Set());
}
this.rooms.get(roomName).add(clientId);
client.room = roomName;
this.sendToClient(clientId, `已加入房间: ${roomName}\n`);
this.broadcastToRoom(roomName, `${client.nickname} 加入了房间\n`, clientId);
}
leaveRoom(clientId) {
const client = this.clients.get(clientId);
if (!client || !client.room) return;
const roomName = client.room;
const room = this.rooms.get(roomName);
if (room) {
room.delete(clientId);
// 通知房间内其他用户
this.broadcastToRoom(roomName, `${client.nickname} 离开了房间\n`, clientId);
// 如果房间为空,删除房间
if (room.size === 0) {
this.rooms.delete(roomName);
}
}
client.room = null;
this.sendToClient(clientId, `已离开房间: ${roomName}\n`);
}
sendPrivateMessage(senderId, targetNick, message) {
const sender = this.clients.get(senderId);
if (!sender) return;
// 查找目标用户
const target = Array.from(this.clients.values()).find(
client => client.nickname.toLowerCase() === targetNick.toLowerCase()
);
if (target) {
this.sendToClient(target.id, `[私聊来自 ${sender.nickname}]: ${message}\n`);
this.sendToClient(senderId, `[私聊发送给 ${target.nickname}]: ${message}\n`);
} else {
this.sendToClient(senderId, `用户 "${targetNick}" 不在线\n`);
}
}
sendToClient(clientId, message) {
const client = this.clients.get(clientId);
if (client && !client.socket.destroyed) {
client.socket.write(message);
}
}
broadcastToAll(message, excludeId = null) {
this.clients.forEach((client, clientId) => {
if (clientId !== excludeId && !client.socket.destroyed) {
client.socket.write(message);
}
});
}
broadcastToRoom(roomName, message, excludeId = null) {
const room = this.rooms.get(roomName);
if (room) {
room.forEach(clientId => {
if (clientId !== excludeId) {
this.sendToClient(clientId, message);
}
});
}
}
close() {
this.clients.forEach(client => {
client.socket.end('服务器关闭\n');
});
if (this.server) {
this.server.close();
}
}
}
// 启动服务器
const chatServer = new MultiClientTCPServer({ port: 8080 });
chatServer.start();
// 优雅关闭
process.on('SIGINT', () => {
console.log('\n关闭聊天服务器...');
chatServer.close();
process.exit(0);
});
创建 TCP 客户端
创建简单的 TCP 客户端
const net = require('net');
// 创建基础TCP客户端
const client = net.createConnection({ port: 8080, host: '127.0.0.1' });
// 连接成功事件
client.on('connect', () => {
console.log('成功连接到服务器');
// 发送数据到服务器
client.write('Hello Server!');
});
// 接收服务器数据
client.on('data', (data) => {
console.log('收到服务器数据:', data.toString());
});
// 连接关闭事件
client.on('close', () => {
console.log('与服务器断开连接');
});
// 错误事件
client.on('error', (err) => {
console.error('连接错误:', err);
});
// 5秒后关闭连接
setTimeout(() => {
client.end();
}, 5000);
客户端的事件
const net = require('net');
const readline = require('readline');
class TCPClient {
constructor(options = {}) {
this.host = options.host || '127.0.0.1';
this.port = options.port || 8080;
this.client = null;
this.connected = false;
this.reconnectDelay = options.reconnectDelay || 3000;
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
this.reconnectAttempts = 0;
this.autoReconnect = options.autoReconnect !== false;
// 创建命令行接口
this.rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
}
connect() {
console.log(`正在连接到 ${this.host}:${this.port}...`);
this.client = net.createConnection({
port: this.port,
host: this.host
});
this.setupEventHandlers();
}
setupEventHandlers() {
// 连接成功事件
this.client.on('connect', () => {
console.log(`✅ 成功连接到服务器 ${this.host}:${this.port}`);
this.connected = true;
this.reconnectAttempts = 0;
this.onConnect();
});
// 接收数据事件
this.client.on('data', (data) => {
this.onData(data);
});
// 连接关闭事件
this.client.on('close', (hadError) => {
console.log('❌ 连接已关闭' + (hadError ? ' (有错误)' : ''));
this.connected = false;
this.onClose(hadError);
});
// 连接结束事件
this.client.on('end', () => {
console.log('📡 服务器结束了连接');
this.connected = false;
this.onEnd();
});
// 错误事件
this.client.on('error', (err) => {
console.error('💥 连接错误:', err.message);
this.connected = false;
this.onError(err);
});
// 超时事件
this.client.on('timeout', () => {
console.log('⏰ 连接超时');
this.onTimeout();
});
// Lookup事件 (DNS解析)
this.client.on('lookup', (err, address, family, host) => {
if (err) {
console.error('DNS解析失败:', err);
} else {
console.log(`🔍 DNS解析: ${host} -> ${address} (IPv${family})`);
}
});
// Ready事件
this.client.on('ready', () => {
console.log('🚀 连接就绪,可以发送数据');
this.onReady();
});
}
// 事件处理方法
onConnect() {
this.startInputHandler();
}
onData(data) {
const message = data.toString().trim();
// 清除当前行,显示服务器消息,然后重新显示提示符
process.stdout.write('\r\x1b[K'); // 清除当前行
console.log(message);
this.showPrompt();
}
onClose(hadError) {
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
this.attemptReconnect();
} else {
this.cleanup();
}
}
onEnd() {
if (this.autoReconnect) {
this.attemptReconnect();
} else {
this.cleanup();
}
}
onError(err) {
switch (err.code) {
case 'ECONNREFUSED':
console.error('🚫 连接被拒绝,请检查服务器是否运行');
break;
case 'ENOTFOUND':
console.error('🔍 找不到主机,请检查主机名');
break;
case 'ETIMEDOUT':
console.error('⏱️ 连接超时');
break;
case 'ECONNRESET':
console.error('🔄 连接被重置');
break;
default:
console.error('❌ 未知错误:', err.code);
}
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
this.attemptReconnect();
} else {
this.cleanup();
}
}
onTimeout() {
console.log('断开超时连接...');
this.client.destroy();
}
onReady() {
// 可以在这里发送初始化数据
}
attemptReconnect() {
this.reconnectAttempts++;
console.log(`🔄 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
setTimeout(() => {
this.connect();
}, this.reconnectDelay);
}
startInputHandler() {
this.showPrompt();
this.rl.on('line', (input) => {
const command = input.trim();
if (command === '/quit' || command === '/exit') {
this.disconnect();
return;
}
if (command === '/status') {
this.showStatus();
this.showPrompt();
return;
}
if (command === '/clear') {
console.clear();
this.showPrompt();
return;
}
if (command.length > 0) {
this.sendMessage(command);
}
this.showPrompt();
});
this.rl.on('close', () => {
this.disconnect();
});
}
sendMessage(message) {
if (this.connected && this.client && !this.client.destroyed) {
this.client.write(message + '\n');
} else {
console.log('❌ 未连接到服务器,无法发送消息');
}
}
showPrompt() {
if (this.connected) {
process.stdout.write('💬 > ');
} else {
process.stdout.write('❌ [断开] > ');
}
}
showStatus() {
console.log('\n📊 连接状态:');
console.log(` 服务器: ${this.host}:${this.port}`);
console.log(` 状态: ${this.connected ? '✅ 已连接' : '❌ 未连接'}`);
console.log(` 重连次数: ${this.reconnectAttempts}/${this.maxReconnectAttempts}`);
console.log(` 自动重连: ${this.autoReconnect ? '启用' : '禁用'}`);
if (this.client) {
console.log(` 本地地址: ${this.client.localAddress}:${this.client.localPort}`);
console.log(` 字节读取: ${this.client.bytesRead}`);
console.log(` 字节写入: ${this.client.bytesWritten}`);
}
console.log();
}
disconnect() {
console.log('\n👋 正在断开连接...');
this.autoReconnect = false;
if (this.client && !this.client.destroyed) {
this.client.end();
}
this.cleanup();
}
cleanup() {
if (this.rl) {
this.rl.close();
}
setTimeout(() => {
process.exit(0);
}, 100);
}
// 设置超时
setTimeout(timeout) {
if (this.client) {
this.client.setTimeout(timeout);
}
}
// 设置保持活跃
setKeepAlive(enable, initialDelay) {
if (this.client) {
this.client.setKeepAlive(enable, initialDelay);
}
}
// 设置无延迟
setNoDelay(noDelay) {
if (this.client) {
this.client.setNoDelay(noDelay);
}
}
}
// 使用示例
const client = new TCPClient({
host: '127.0.0.1',
port: 8080,
autoReconnect: true,
maxReconnectAttempts: 5,
reconnectDelay: 3000
});
console.log('TCP客户端启动');
console.log('命令: /quit - 退出, /status - 状态, /clear - 清屏');
client.connect();
// 优雅关闭处理
process.on('SIGINT', () => {
console.log('\n收到中断信号...');
client.disconnect();
});
客户端与服务器的交互
// 客户端与服务器交互流程
const interactionFlow = {
// 连接建立流程
connectionFlow: `
客户端 -> 服务器: 建立TCP连接
服务器 -> 客户端: 发送欢迎消息
客户端 -> 服务器: 发送认证信息
服务器 -> 客户端: 确认认证结果
`,
// 数据交换流程
dataExchangeFlow: `
客户端 -> 服务器: 发送请求数据
服务器: 处理请求
服务器 -> 客户端: 返回响应数据
客户端: 处理响应
`
};
sequenceDiagram
participant C as TCP Client
participant S as TCP Server
Note over C,S: 连接建立阶段
C->>+S: TCP连接请求
S-->>-C: 连接确认
S->>C: 发送欢迎消息
Note over C,S: 认证阶段
C->>S: 发送用户信息
S->>S: 验证用户信息
S-->>C: 认证结果
Note over C,S: 正常通信阶段
loop 消息交换
C->>S: 发送消息/命令
S->>S: 处理消息
S-->>C: 返回响应
end
Note over C,S: 连接关闭阶段
C->>S: 发送关闭请求
S-->>C: 确认关闭
Note over C,S: 连接终止
TCP 数据传输
数据流处理
const net = require('net');
const { Transform } = require('stream');
class DataProcessor {
constructor() {
this.buffer = Buffer.alloc(0);
this.expectedLength = 0;
this.headerSize = 4; // 4字节长度头
}
// 处理粘包和拆包问题
processData(chunk) {
// 将新数据追加到缓冲区
this.buffer = Buffer.concat([this.buffer, chunk]);
const messages = [];
while (this.buffer.length >= this.headerSize) {
// 如果还没有读取消息长度
if (this.expectedLength === 0) {
// 读取消息长度(前4字节)
this.expectedLength = this.buffer.readUInt32BE(0);
this.buffer = this.buffer.slice(this.headerSize);
}
// 检查是否有完整的消息
if (this.buffer.length >= this.expectedLength) {
// 提取完整消息
const messageData = this.buffer.slice(0, this.expectedLength);
this.buffer = this.buffer.slice(this.expectedLength);
this.expectedLength = 0;
// 解析消息
try {
const message = JSON.parse(messageData.toString());
messages.push(message);
} catch (error) {
console.error('消息解析失败:', error);
}
} else {
// 数据不完整,等待更多数据
break;
}
}
return messages;
}
// 封装消息
packMessage(data) {
const message = JSON.stringify(data);
const messageBuffer = Buffer.from(message);
const lengthBuffer = Buffer.alloc(this.headerSize);
// 写入消息长度
lengthBuffer.writeUInt32BE(messageBuffer.length, 0);
// 组合长度头和消息体
return Buffer.concat([lengthBuffer, messageBuffer]);
}
}
// TCP服务器 - 数据流处理
class StreamTCPServer {
constructor(port = 8080) {
this.port = port;
this.server = null;
this.clients = new Map();
}
start() {
this.server = net.createServer((socket) => {
const processor = new DataProcessor();
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
this.clients.set(clientId, { socket, processor });
console.log(`客户端连接: ${clientId}`);
// 数据处理
socket.on('data', (chunk) => {
const messages = processor.processData(chunk);
messages.forEach(message => {
this.handleMessage(clientId, message);
});
});
// 连接关闭
socket.on('close', () => {
this.clients.delete(clientId);
console.log(`客户端断开: ${clientId}`);
});
// 错误处理
socket.on('error', (err) => {
console.error(`客户端错误 ${clientId}:`, err);
this.clients.delete(clientId);
});
// 发送欢迎消息
this.sendMessage(clientId, {
type: 'welcome',
message: '欢迎连接到流式TCP服务器',
timestamp: Date.now()
});
});
this.server.listen(this.port, () => {
console.log(`流式TCP服务器启动,端口: ${this.port}`);
});
}
handleMessage(clientId, message) {
console.log(`[${clientId}] 收到消息:`, message);
switch (message.type) {
case 'ping':
this.sendMessage(clientId, {
type: 'pong',
timestamp: Date.now(),
original: message
});
break;
case 'echo':
this.sendMessage(clientId, {
type: 'echo_response',
data: message.data,
timestamp: Date.now()
});
break;
case 'broadcast':
this.broadcast(message, clientId);
break;
default:
this.sendMessage(clientId, {
type: 'error',
message: '未知消息类型',
timestamp: Date.now()
});
}
}
sendMessage(clientId, data) {
const client = this.clients.get(clientId);
if (client && !client.socket.destroyed) {
const packedData = client.processor.packMessage(data);
client.socket.write(packedData);
}
}
broadcast(message, excludeId = null) {
const broadcastData = {
type: 'broadcast_message',
sender: excludeId,
data: message.data,
timestamp: Date.now()
};
this.clients.forEach((client, clientId) => {
if (clientId !== excludeId && !client.socket.destroyed) {
const packedData = client.processor.packMessage(broadcastData);
client.socket.write(packedData);
}
});
}
}
// TCP客户端 - 数据流处理
class StreamTCPClient {
constructor(host = '127.0.0.1', port = 8080) {
this.host = host;
this.port = port;
this.client = null;
this.processor = new DataProcessor();
this.connected = false;
}
connect() {
this.client = net.createConnection({ port: this.port, host: this.host });
this.client.on('connect', () => {
console.log('连接成功');
this.connected = true;
this.onConnect();
});
this.client.on('data', (chunk) => {
const messages = this.processor.processData(chunk);
messages.forEach(message => {
this.handleMessage(message);
});
});
this.client.on('close', () => {
console.log('连接关闭');
this.connected = false;
});
this.client.on('error', (err) => {
console.error('连接错误:', err);
this.connected = false;
});
}
onConnect() {
// 连接成功后发送测试消息
this.sendMessage({
type: 'ping',
message: 'Hello Server',
timestamp: Date.now()
});
// 定期发送心跳
this.heartbeatInterval = setInterval(() => {
if (this.connected) {
this.sendMessage({
type: 'ping',
message: 'heartbeat',
timestamp: Date.now()
});
}
}, 30000);
}
handleMessage(message) {
console.log('收到消息:', message);
switch (message.type) {
case 'welcome':
console.log('服务器欢迎消息:', message.message);
break;
case 'pong':
console.log('收到心跳响应');
break;
case 'broadcast_message':
console.log(`广播消息来自 ${message.sender}:`, message.data);
break;
default:
console.log('其他消息:', message);
}
}
sendMessage(data) {
if (this.connected && this.client && !this.client.destroyed) {
const packedData = this.processor.packMessage(data);
this.client.write(packedData);
}
}
disconnect() {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
}
if (this.client && !this.client.destroyed) {
this.client.end();
}
}
}
传输二进制数据
const net = require('net');
const fs = require('fs');
const path = require('path');
class BinaryDataServer {
constructor(port = 8080) {
this.port = port;
this.server = null;
}
start() {
this.server = net.createServer((socket) => {
console.log('客户端连接:', socket.remoteAddress);
let fileStream = null;
let fileName = '';
let fileSize = 0;
let receivedSize = 0;
let headerReceived = false;
let headerBuffer = Buffer.alloc(0);
socket.on('data', (chunk) => {
if (!headerReceived) {
// 处理文件头信息
headerBuffer = Buffer.concat([headerBuffer, chunk]);
// 查找头部结束标记 (\r\n\r\n)
const headerEnd = headerBuffer.indexOf('\r\n\r\n');
if (headerEnd !== -1) {
const headerData = headerBuffer.slice(0, headerEnd).toString();
const fileData = headerBuffer.slice(headerEnd + 4);
// 解析头部信息
const headers = this.parseHeaders(headerData);
fileName = headers.filename || 'unknown';
fileSize = parseInt(headers.filesize) || 0;
console.log(`开始接收文件: ${fileName}, 大小: ${fileSize} bytes`);
// 创建文件写入流
const uploadsDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadsDir)) {
fs.mkdirSync(uploadsDir, { recursive: true });
}
const filePath = path.join(uploadsDir, fileName);
fileStream = fs.createWriteStream(filePath);
headerReceived = true;
receivedSize = fileData.length;
// 写入首次接收到的文件数据
if (fileData.length > 0) {
fileStream.write(fileData);
}
// 发送接收确认
this.sendResponse(socket, {
status: 'receiving',
filename: fileName,
received: receivedSize,
total: fileSize
});
}
} else {
// 接收文件数据
receivedSize += chunk.length;
fileStream.write(chunk);
// 发送进度更新
if (receivedSize % 1024 === 0) { // 每1KB发送一次进度
this.sendResponse(socket, {
status: 'progress',
received: receivedSize,
total: fileSize,
percentage: Math.round((receivedSize / fileSize) * 100)
});
}
// 文件接收完成
if (receivedSize >= fileSize) {
fileStream.end();
console.log(`文件接收完成: ${fileName}`);
this.sendResponse(socket, {
status: 'completed',
filename: fileName,
size: receivedSize,
message: '文件上传成功'
});
// 重置状态
headerReceived = false;
headerBuffer = Buffer.alloc(0);
receivedSize = 0;
fileSize = 0;
fileName = '';
}
}
});
socket.on('close', () => {
console.log('客户端断开连接');
if (fileStream) {
fileStream.end();
}
});
socket.on('error', (err) => {
console.error('Socket错误:', err);
if (fileStream) {
fileStream.end();
}
});
});
this.server.listen(this.port, () => {
console.log(`二进制数据服务器启动,端口: ${this.port}`);
});
}
parseHeaders(headerString) {
const headers = {};
const lines = headerString.split('\r\n');
lines.forEach(line => {
const [key, value] = line.split(': ');
if (key && value) {
headers[key.toLowerCase()] = value;
}
});
return headers;
}
sendResponse(socket, data) {
const response = JSON.stringify(data) + '\n';
socket.write(response);
}
}
class BinaryDataClient {
constructor(host = '127.0.0.1', port = 8080) {
this.host = host;
this.port = port;
this.client = null;
}
connect() {
return new Promise((resolve, reject) => {
this.client = net.createConnection({ port: this.port, host: this.host });
this.client.on('connect', () => {
console.log('连接成功');
resolve();
});
this.client.on('data', (data) => {
// 处理服务器响应
const responses = data.toString().split('\n').filter(line => line.trim());
responses.forEach(response => {
try {
const parsed = JSON.parse(response);
this.handleServerResponse(parsed);
} catch (error) {
console.log('服务器响应:', response);
}
});
});
this.client.on('error', (err) => {
console.error('连接错误:', err);
reject(err);
});
});
}
handleServerResponse(response) {
switch (response.status) {
case 'receiving':
console.log(`服务器开始接收文件: ${response.filename}`);
break;
case 'progress':
process.stdout.write(`\r上传进度: ${response.percentage}% (${response.received}/${response.total})`);
break;
case 'completed':
console.log(`\n✅ 文件上传成功: ${response.filename}`);
break;
default:
console.log('服务器响应:', response);
}
}
async uploadFile(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error('文件不存在');
}
const fileName = path.basename(filePath);
const fileSize = fs.statSync(filePath).size;
console.log(`准备上传文件: ${fileName}, 大小: ${fileSize} bytes`);
// 构建文件头
const headers = [
`Filename: ${fileName}`,
`Filesize: ${fileSize}`,
`Content-Type: application/octet-stream`
].join('\r\n') + '\r\n\r\n';
// 发送文件头
this.client.write(headers);
// 创建文件读取流
const readStream = fs.createReadStream(filePath);
let uploadedSize = 0;
return new Promise((resolve, reject) => {
readStream.on('data', (chunk) => {
this.client.write(chunk);
uploadedSize += chunk.length;
// 显示上传进度
const percentage = Math.round((uploadedSize / fileSize) * 100);
process.stdout.write(`\r上传进度: ${percentage}% (${uploadedSize}/${fileSize})`);
});
readStream.on('end', () => {
console.log('\n文件数据发送完成');
resolve();
});
readStream.on('error', (err) => {
console.error('文件读取错误:', err);
reject(err);
});
});
}
disconnect() {
if (this.client && !this.client.destroyed) {
this.client.end();
}
}
}
// 使用示例
if (require.main === module) {
const args = process.argv.slice(2);
if (args[0] === 'server') {
// 启动服务器
const server = new BinaryDataServer(8080);
server.start();
process.on('SIGINT', () => {
console.log('\n关闭服务器...');
process.exit(0);
});
} else if (args[0] === 'client' && args[1]) {
// 启动客户端并上传文件
const client = new BinaryDataClient();
(async () => {
try {
await client.connect();
await client.uploadFile(args[1]);
client.disconnect();
} catch (error) {
console.error('错误:', error.message);
client.disconnect();
}
})();
} else {
console.log('使用方法:');
console.log(' 启动服务器: node script.js server');
console.log(' 上传文件: node script.js client <文件路径>');
}
}
半关闭连接
const net = require('net');
// 半关闭连接示例 - 服务器端
class HalfCloseServer {
constructor(port = 8080) {
this.port = port;
this.server = null;
}
start() {
this.server = net.createServer((socket) => {
console.log('客户端连接:', socket.remoteAddress);
socket.on('data', (data) => {
const message = data.toString().trim();
console.log('收到消息:', message);
if (message === 'CLOSE_WRITE') {
// 客户端请求半关闭写端
console.log('客户端请求半关闭写端');
socket.end('服务器确认关闭写端'); // 关闭写端
} else if (message === 'SHUTDOWN') {
// 优雅关闭演示
console.log('开始优雅关闭流程...');
// 发送最终数据
socket.write('开始关闭连接...\n');
socket.write('发送最后的数据...\n');
socket.write('再见!\n');
// 关闭写端,但保持读端开放
socket.end();
} else {
// 普通消息处理
socket.write(`回显: ${message}\n`);
}
});
// 客户端关闭读端事件
socket.on('end', () => {
console.log('客户端关闭了写端');
// 服务器可以继续发送数据
setTimeout(() => {
if (!socket.destroyed) {
socket.write('服务器在客户端关闭写端后发送的数据\n');
// 服务器也关闭写端
setTimeout(() => {
socket.end();
}, 1000);
}
}, 500);
});
socket.on('close', (hadError) => {
console.log('连接完全关闭', hadError ? '(有错误)' : '');
});
socket.on('error', (err) => {
console.error('Socket错误:', err);
});
});
this.server.listen(this.port, () => {
console.log(`半关闭演示服务器启动,端口: ${this.port}`);
});
}
}
// 半关闭连接示例 - 客户端
class HalfCloseClient {
constructor(host = '127.0.0.1', port = 8080) {
this.host = host;
this.port = port;
this.client = null;
}
connect() {
return new Promise((resolve, reject) => {
this.client = net.createConnection({ port: this.port, host: this.host });
this.client.on('connect', () => {
console.log('连接成功');
resolve();
});
this.client.on('data', (data) => {
console.log('收到服务器数据:', data.toString().trim());
});
// 服务器关闭写端
this.client.on('end', () => {
console.log('服务器关闭了写端');
// 客户端可以继续发送数据
setTimeout(() => {
if (!this.client.destroyed) {
console.log('客户端在服务器关闭写端后发送数据');
this.client.write('客户端最后的消息\n');
// 客户端也关闭写端
setTimeout(() => {
this.client.end();
}, 500);
}
}, 200);
});
this.client.on('close', (hadError) => {
console.log('连接完全关闭', hadError ? '(有错误)' : '');
});
this.client.on('error', (err) => {
console.error('连接错误:', err);
reject(err);
});
});
}
// 演示正常通信
async testNormalCommunication() {
console.log('\n=== 正常通信测试 ===');
this.client.write('Hello Server!\n');
await this.sleep(500);
this.client.write('How are you?\n');
}
// 演示半关闭 - 客户端先关闭写端
async testClientHalfClose() {
console.log('\n=== 客户端半关闭测试 ===');
this.client.write('CLOSE_WRITE\n');
await this.sleep(1000);
// 客户端关闭写端
console.log('客户端关闭写端');
this.client.end();
}
// 演示服务器主导的优雅关闭
async testServerGracefulShutdown() {
console.log('\n=== 服务器优雅关闭测试 ===');
this.client.write('SHUTDOWN\n');
// 等待服务器响应并关闭
await this.sleep(3000);
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// 连接状态图
const connectionStates = `
连接状态转换:
ESTABLISHED (建立)
↓ (一方调用 end())
CLOSE_WAIT (等待关闭)
↓ (另一方也调用 end())
CLOSED (关闭)
半关闭状态:
- 客户端调用 end(): 客户端写端关闭,但仍可接收数据
- 服务器调用 end(): 服务器写端关闭,但仍可接收数据
- 双方都调用 end(): 连接完全关闭
`;
console.log(connectionStates);
stateDiagram-v2
[*] --> CLOSED
CLOSED --> LISTEN: server.listen()
LISTEN --> SYN_RCVD: receive SYN
SYN_RCVD --> ESTABLISHED: send SYN-ACK, receive ACK
CLOSED --> SYN_SENT: client.connect()
SYN_SENT --> ESTABLISHED: receive SYN-ACK, send ACK
ESTABLISHED --> FIN_WAIT_1: socket.end()
ESTABLISHED --> CLOSE_WAIT: receive FIN
FIN_WAIT_1 --> FIN_WAIT_2: receive ACK
FIN_WAIT_2 --> TIME_WAIT: receive FIN, send ACK
CLOSE_WAIT --> LAST_ACK: socket.end(), send FIN
LAST_ACK --> CLOSED: receive ACK
TIME_WAIT --> CLOSED: timeout
note right of CLOSE_WAIT
半关闭状态:
可接收数据,
但不能发送
end note
TCP 与心跳机制
实现心跳机制
const net = require('net');
class HeartbeatTCPServer {
constructor(options = {}) {
this.port = options.port || 8080;
this.host = options.host || '127.0.0.1';
this.heartbeatInterval = options.heartbeatInterval || 30000; // 30秒
this.heartbeatTimeout = options.heartbeatTimeout || 60000; // 60秒超时
this.server = null;
this.clients = new Map();
this.heartbeatTimer = null;
}
start() {
this.server = net.createServer((socket) => {
this.handleNewClient(socket);
});
this.server.on('listening', () => {
console.log(`心跳TCP服务器启动: ${this.host}:${this.port}`);
this.startHeartbeatCheck();
});
this.server.on('error', (err) => {
console.error('服务器错误:', err);
});
this.server.listen(this.port, this.host);
}
handleNewClient(socket) {
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
const clientInfo = {
id: clientId,
socket: socket,
lastHeartbeat: Date.now(),
lastPing: 0,
rtt: 0, // 往返时间
isAlive: true,
connectTime: new Date(),
heartbeatCount: 0,
missedHeartbeats: 0
};
this.clients.set(clientId, clientInfo);
console.log(`客户端连接: ${clientId}, 总连接数: ${this.clients.size}`);
socket.setEncoding('utf8');
// 发送欢迎消息
this.sendMessage(clientId, {
type: 'welcome',
message: '欢迎连接到心跳服务器',
clientId: clientId,
heartbeatInterval: this.heartbeatInterval
});
socket.on('data', (data) => {
this.handleMessage(clientId, data.toString().trim());
});
socket.on('close', () => {
console.log(`客户端断开: ${clientId}`);
this.clients.delete(clientId);
});
socket.on('error', (err) => {
console.error(`客户端错误 ${clientId}:`, err);
this.clients.delete(clientId);
});
}
handleMessage(clientId, rawMessage) {
const client = this.clients.get(clientId);
if (!client) return;
try {
const message = JSON.parse(rawMessage);
switch (message.type) {
case 'heartbeat':
case 'pong':
this.handleHeartbeatResponse(clientId, message);
break;
case 'ping':
this.handlePingRequest(clientId, message);
break;
case 'status':
this.sendClientStatus(clientId);
break;
default:
console.log(`[${clientId}] 收到消息:`, message);
this.sendMessage(clientId, {
type: 'echo',
original: message,
timestamp: Date.now()
});
}
} catch (error) {
console.error(`[${clientId}] 消息解析错误:`, error);
}
}
handleHeartbeatResponse(clientId, message) {
const client = this.clients.get(clientId);
if (!client) return;
const now = Date.now();
client.lastHeartbeat = now;
client.isAlive = true;
client.heartbeatCount++;
client.missedHeartbeats = 0;
// 计算RTT(往返时间)
if (message.timestamp) {
client.rtt = now - message.timestamp;
}
console.log(`[${clientId}] 心跳响应, RTT: ${client.rtt}ms`);
}
handlePingRequest(clientId, message) {
// 客户端主动ping,立即响应pong
this.sendMessage(clientId, {
type: 'pong',
timestamp: message.timestamp || Date.now(),
serverTime: Date.now()
});
}
sendClientStatus(clientId) {
const client = this.clients.get(clientId);
if (!client) return;
const status = {
type: 'status_response',
clientId: clientId,
connectTime: client.connectTime,
uptime: Date.now() - client.connectTime.getTime(),
lastHeartbeat: new Date(client.lastHeartbeat),
rtt: client.rtt,
heartbeatCount: client.heartbeatCount,
missedHeartbeats: client.missedHeartbeats,
isAlive: client.isAlive
};
this.sendMessage(clientId, status);
}
sendMessage(clientId, data) {
const client = this.clients.get(clientId);
if (client && !client.socket.destroyed) {
client.socket.write(JSON.stringify(data) + '\n');
}
}
startHeartbeatCheck() {
this.heartbeatTimer = setInterval(() => {
this.checkClientHeartbeats();
}, this.heartbeatInterval);
console.log(`心跳检查启动,间隔: ${this.heartbeatInterval}ms`);
}
checkClientHeartbeats() {
const now = Date.now();
const clientsToRemove = [];
console.log(`\n=== 心跳检查 (${new Date().toLocaleTimeString()}) ===`);
console.log(`当前连接数: ${this.clients.size}`);
this.clients.forEach((client, clientId) => {
const timeSinceLastHeartbeat = now - client.lastHeartbeat;
if (timeSinceLastHeartbeat > this.heartbeatTimeout) {
// 超时,标记为死连接
console.log(`[${clientId}] 心跳超时 (${timeSinceLastHeartbeat}ms)`);
client.isAlive = false;
client.missedHeartbeats++;
// 尝试发送最后一次ping
if (client.missedHeartbeats <= 3) {
console.log(`[${clientId}] 发送紧急ping (第${client.missedHeartbeats}次)`);
this.sendPing(clientId, true);
} else {
// 超过重试次数,断开连接
console.log(`[${clientId}] 超过最大重试次数,断开连接`);
clientsToRemove.push(clientId);
}
} else {
// 正常发送心跳ping
console.log(`[${clientId}] 发送心跳ping, 上次心跳: ${timeSinceLastHeartbeat}ms前`);
this.sendPing(clientId, false);
}
});
// 移除死连接
clientsToRemove.forEach(clientId => {
const client = this.clients.get(clientId);
if (client) {
client.socket.destroy();
this.clients.delete(clientId);
console.log(`[${clientId}] 连接已移除`);
}
});
}
sendPing(clientId, isUrgent = false) {
const ping = {
type: 'ping',
timestamp: Date.now(),
urgent: isUrgent,
message: isUrgent ? '紧急心跳检查' : '心跳检查'
};
this.sendMessage(clientId, ping);
// 记录ping时间用于RTT计算
const client = this.clients.get(clientId);
if (client) {
client.lastPing = Date.now();
}
}
// 广播服务器状态
broadcastServerStatus() {
const serverStatus = {
type: 'server_status',
timestamp: Date.now(),
connectedClients: this.clients.size,
uptime: process.uptime(),
memory: process.memoryUsage(),
heartbeatInterval: this.heartbeatInterval,
heartbeatTimeout: this.heartbeatTimeout
};
this.clients.forEach((client, clientId) => {
this.sendMessage(clientId, serverStatus);
});
}
stop() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
// 通知所有客户端服务器即将关闭
this.clients.forEach(client => {
this.sendMessage(client.id, {
type: 'server_shutdown',
message: '服务器即将关闭',
timestamp: Date.now()
});
setTimeout(() => {
client.socket.end();
}, 1000);
});
if (this.server) {
this.server.close();
}
}
}
// 使用示例
const heartbeatServer = new HeartbeatTCPServer({
port: 8080,
heartbeatInterval: 10000, // 10秒检查一次
heartbeatTimeout: 30000 // 30秒超时
});
heartbeatServer.start();
// 每分钟广播一次服务器状态
setInterval(() => {
heartbeatServer.broadcastServerStatus();
}, 60000);
// 优雅关闭
process.on('SIGINT', () => {
console.log('\n正在关闭心跳服务器...');
heartbeatServer.stop();
setTimeout(() => {
process.exit(0);
}, 2000);
});
客户端响应心跳
const net = require('net');
class HeartbeatTCPClient {
constructor(options = {}) {
this.host = options.host || '127.0.0.1';
this.port = options.port || 8080;
this.client = null;
this.connected = false;
// 心跳配置
this.heartbeatInterval = options.heartbeatInterval || 30000;
this.heartbeatTimer = null;
this.pingTimer = null;
// 连接状态
this.reconnectDelay = options.reconnectDelay || 5000;
this.maxReconnectAttempts = options.maxReconnectAttempts || 5;
this.reconnectAttempts = 0;
this.autoReconnect = options.autoReconnect !== false;
// 统计信息
this.stats = {
connectTime: null,
lastHeartbeat: null,
heartbeatsSent: 0,
heartbeatsReceived: 0,
averageRTT: 0,
rttSamples: []
};
}
connect() {
console.log(`正在连接到 ${this.host}:${this.port}...`);
this.client = net.createConnection({ port: this.port, host: this.host });
this.client.setEncoding('utf8');
this.client.on('connect', () => {
console.log('✅ 连接成功');
this.connected = true;
this.reconnectAttempts = 0;
this.stats.connectTime = new Date();
this.startHeartbeat();
});
this.client.on('data', (data) => {
// 处理可能的多条消息
const messages = data.trim().split('\n').filter(msg => msg.length > 0);
messages.forEach(message => {
try {
const parsed = JSON.parse(message);
this.handleServerMessage(parsed);
} catch (error) {
console.error('消息解析错误:', error);
}
});
});
this.client.on('close', (hadError) => {
console.log('❌ 连接关闭' + (hadError ? ' (有错误)' : ''));
this.connected = false;
this.stopHeartbeat();
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
this.attemptReconnect();
}
});
this.client.on('error', (err) => {
console.error('💥 连接错误:', err.message);
this.connected = false;
this.stopHeartbeat();
if (this.autoReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
this.attemptReconnect();
}
});
}
handleServerMessage(message) {
const now = Date.now();
switch (message.type) {
case 'welcome':
console.log('🎉 服务器欢迎:', message.message);
if (message.heartbeatInterval) {
this.heartbeatInterval = message.heartbeatInterval;
console.log(`⏱️ 心跳间隔设置为: ${this.heartbeatInterval}ms`);
}
break;
case 'ping':
// 服务器发送ping,回复pong
console.log(`💓 收到服务器ping${message.urgent ? ' (紧急)' : ''}`);
this.sendPong(message.timestamp);
this.stats.heartbeatsReceived++;
this.stats.lastHeartbeat = new Date();
break;
case 'pong':
// 服务器回复pong
const rtt = now - (message.timestamp || 0);
console.log(`💓 收到服务器pong, RTT: ${rtt}ms`);
this.updateRTTStats(rtt);
break;
case 'server_status':
console.log('\n📊 服务器状态:');
console.log(` 连接客户端数: ${message.connectedClients}`);
console.log(` 服务器运行时间: ${Math.round(message.uptime)}秒`);
console.log(` 内存使用: ${Math.round(message.memory.rss / 1024 / 1024)}MB`);
break;
case 'server_shutdown':
console.log('🔴 服务器即将关闭:', message.message);
this.autoReconnect = false;
break;
case 'status_response':
this.displayClientStatus(message);
break;
default:
console.log('📨 服务器消息:', message);
}
}
sendPong(timestamp) {
const pong = {
type: 'pong',
timestamp: timestamp,
clientTime: Date.now()
};
this.sendMessage(pong);
}
sendPing() {
const ping = {
type: 'ping',
timestamp: Date.now(),
message: '客户端主动ping'
};
this.sendMessage(ping);
this.stats.heartbeatsSent++;
}
sendMessage(data) {
if (this.connected && this.client && !this.client.destroyed) {
this.client.write(JSON.stringify(data) + '\n');
}
}
startHeartbeat() {
// 定期发送ping到服务器
this.pingTimer = setInterval(() => {
if (this.connected) {
this.sendPing();
}
}, this.heartbeatInterval);
console.log(`💗 心跳机制启动,间隔: ${this.heartbeatInterval}ms`);
}
stopHeartbeat() {
if (this.pingTimer) {
clearInterval(this.pingTimer);
this.pingTimer = null;
}
console.log('💔 心跳机制停止');
}
updateRTTStats(rtt) {
this.stats.rttSamples.push(rtt);
// 保持最近20个样本
if (this.stats.rttSamples.length > 20) {
this.stats.rttSamples.shift();
}
// 计算平均RTT
const sum = this.stats.rttSamples.reduce((a, b) => a + b, 0);
this.stats.averageRTT = Math.round(sum / this.stats.rttSamples.length);
}
displayClientStatus(status) {
console.log('\n📈 客户端状态:');
console.log(` 客户端ID: ${status.clientId}`);
console.log(` 连接时间: ${new Date(status.connectTime).toLocaleString()}`);
console.log(` 运行时间: ${Math.round(status.uptime / 1000)}秒`);
console.log(` 最后心跳: ${new Date(status.lastHeartbeat).toLocaleString()}`);
console.log(` 平均RTT: ${status.rtt}ms`);
console.log(` 心跳计数: ${status.heartbeatCount}`);
console.log(` 错过心跳: ${status.missedHeartbeats}`);
console.log(` 连接状态: ${status.isAlive ? '✅ 正常' : '❌ 异常'}`);
}
displayStats() {
console.log('\n📊 本地统计:');
console.log(` 连接时间: ${this.stats.connectTime?.toLocaleString() || '未连接'}`);
console.log(` 最后心跳: ${this.stats.lastHeartbeat?.toLocaleString() || '无'}`);
console.log(` 发送心跳: ${this.stats.heartbeatsSent}`);
console.log(` 接收心跳: ${this.stats.heartbeatsReceived}`);
console.log(` 平均RTT: ${this.stats.averageRTT}ms`);
console.log(` RTT样本数: ${this.stats.rttSamples.length}`);
}
requestStatus() {
this.sendMessage({ type: 'status' });
}
attemptReconnect() {
this.reconnectAttempts++;
console.log(`🔄 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
setTimeout(() => {
this.connect();
}, this.reconnectDelay);
}
disconnect() {
console.log('👋 主动断开连接...');
this.autoReconnect = false;
this.stopHeartbeat();
if (this.client && !this.client.destroyed) {
this.client.end();
}
}
}
// 心跳机制流程图
const heartbeatFlow = `
心跳机制工作流程:
1. 客户端连接到服务器
2. 服务器发送心跳配置
3. 客户端启动心跳定时器
4. 定期发送ping到服务器
5. 服务器回复pong
6. 客户端计算RTT
7. 服务器定期检查客户端状态
8. 超时则断开连接
`;
console.log(heartbeatFlow);
sequenceDiagram
participant C as Client
participant S as Server
participant T as Timer
Note over C,S: 连接建立
C->>+S: TCP连接请求
S-->>-C: 连接确认 + 心跳配置
Note over C,S: 心跳机制启动
C->>T: 启动心跳定时器
S->>T: 启动心跳检查定时器
Note over C,S: 正常心跳流程
loop 每个心跳周期
T-->>C: 定时器触发
C->>S: 发送 PING
S-->>C: 回复 PONG (包含RTT信息)
C->>C: 更新RTT统计
T-->>S: 心跳检查触发
S->>S: 检查客户端状态
alt 客户端正常
S->>S: 更新客户端活跃时间
else 客户端超时
S->>S: 标记客户端为超时
S->>C: 发送紧急PING
alt 客户端响应
C-->>S: 回复PONG
S->>S: 恢复客户端状态
else 多次无响应
S->>S: 断开客户端连接
S-->>C: 关闭连接
end
end
end
Note over C,S: 异常处理
alt 网络异常
C->>C: 检测到连接断开
C->>C: 启动重连机制
C->>S: 重新建立连接
end
总结
Node.js的net模块为TCP网络编程提供了完整而强大的API支持。通过本文的深入分析,我们可以看到:
核心特性
- 可靠连接 - TCP提供面向连接的可靠数据传输
- 事件驱动 - Node.js采用事件驱动模型处理网络IO
- 流式处理 - 支持数据流的读写操作
- 连接管理 - 完善的连接生命周期管理
实践要点
- 粘包处理 - 合理处理TCP数据流的粘包和拆包问题
- 错误处理 - 全面的错误处理和异常恢复机制
- 性能优化 - 合理设置缓冲区和连接参数
- 资源管理 - 及时清理连接和相关资源
应用场景
TCP服务器特别适用于需要可靠数据传输的场景,如:
- 聊天应用和即时通讯
- 文件传输服务
- 数据库连接
- 物联网设备通信
- 实时数据同步