Node.js 创建 TCP 服务
在 Node.js 中,net 模块用于创建底层的 TCP 网络服务。它允许我们建立 TCP 服务器和客户端,处理数据传输等任务。TCP是一种面向连接的协议,确保数据的可靠传输,适用于需要可靠性的数据通信场景。
1. TCP 与 UDP 区别
在探讨 net 模块之前,我们先简单回顾一下 TCP 和 UDP的区别:
TCP 是一种可靠的、面向连接的协议,它确保所有的数据包能够按顺序抵达目标,并且提供数据包的确认机制。通常用于文件传输、邮件、网页等需要高可靠性的场景。
UDP 是一种不可靠的、无连接的协议,数据包发送后不会确认接收状态,适用于对速度要求高而对可靠性要求较低的场景,如视频流、在线游戏等。
在 net 模块中,我们可以使用 TCP 协议来创建高可靠的网络通信应用。
2. 创建 TCP 服务器
使用 net.createServer() 可以创建一个 TCP 服务器,监听客户端的连接请求并处理数据。
2.1. 创建简单的 TCP 服务器
const net = require('net');// 创建 TCP 服务器
const server = net.createServer((socket) => {console.log('client connected');// 当接收到数据时触发socket.on('data', (data) => {console.log(`Received: ${data}`);socket.write(`Echo: ${data}`); // 回传数据给客户端});// 当客户端断开连接时触发socket.on('end', () => {console.log('client disconnected');});
});// 服务器监听端口
server.listen(8080, () => {console.log('TCP server running on port 8080');
});
2.2. 服务器的事件
net.Server 继承了 EventEmitter,因此它可以监听各种事件:
connection:客户端连接时触发。
data:接收到客户端的数据时触发。
end:客户端断开连接时触发。
error:处理任何错误。
server.on('error', (err) => {console.error(`Error: ${err}`);
});
2.3. 处理多个客户端
Node.js 的 net 模块是事件驱动的,因此它支持多个客户端同时连接。在一个 TCP 服务器中,每当有一个客户端连接时,都会触发 connection 事件并创建一个新的 socket 对象,该对象用于与该客户端通信。
const net = require('net');const server = net.createServer((socket) => {console.log('New client connected');socket.on('data', (data) => {console.log(`Received: ${data}`);socket.write(`Hello Client, you said: ${data}`);});socket.on('end', () => {console.log('Client disconnected');});
});server.listen(8080, () => {console.log('TCP server is listening on port 8080');
});
3. 创建 TCP 客户端
net.connect() 或 net.createConnection() 方法可以用于创建 TCP 客户端,并连接到 TCP 服务器。
3.1. 创建简单的 TCP 客户端
const net = require('net');// 创建 TCP 客户端并连接到服务器
const client = net.createConnection({ port: 8080 }, () => {console.log('Connected to server');client.write('Hello, server!');
});// 监听数据
client.on('data', (data) => {console.log(`Received from server: ${data}`);client.end(); // 结束连接
});// 监听连接关闭
client.on('end', () => {console.log('Disconnected from server');
});
3.2. 客户端的事件
与服务器类似,net.Socket 也继承了 EventEmitter,可以监听多个事件:
connect:客户端成功连接到服务器时触发。
data:从服务器接收到数据时触发。
end:服务器关闭连接时触发。
error:发生错误时触发。
3.3. 客户端与服务器的交互
当客户端连接到服务器后,服务器可以读取客户端发送的数据,处理数据,并将结果回传给客户端。
const client = net.createConnection({ port: 8080 }, () => {client.write('Hello from client');
});client.on('data', (data) => {console.log(`Received from server: ${data}`);
});
4. TCP 数据传输
TCP 是基于流的协议,意味着它将数据以连续字节流的形式传输。因此,数据可能会被分割成多个部分传输,客户端和服务器都需要确保数据处理的正确性。
4.1. 数据流处理
socket.on('data', (data) => {// 处理数据流console.log(`Received data: ${data}`);
});
由于 TCP 数据流可能会被分段传输,因此需要注意数据拼接问题,特别是在传输大文件或大块数据时,可能会出现数据片段错乱。
4.2. 传输二进制数据
TCP 不仅可以传输文本数据,还可以传输二进制数据。通过 Buffer,我们可以处理任意类型的二进制数据。
socket.on('data', (buffer) => {console.log('Received binary data:', buffer);
});
4.3. 半关闭连接
在 TCP 中,客户端或服务器可以通过 end() 关闭写操作,但保持读操作。这被称为半关闭(half-duplex),即允许一方停止发送数据,但仍然可以接收数据。
socket.end('Goodbye'); // 关闭写操作,但继续监听数据
5. 进阶:TCP 与心跳机制
在实际的 TCP 应用中,我们可能需要检测连接是否仍然有效。常用的方法是实现心跳机制,通过定期发送心跳消息来确保连接的存活。如果在一段时间内没有收到心跳响应,则可以认为连接已断开。
5.1. 实现心跳机制
const net = require('net');const server = net.createServer((socket) => {console.log('client connected');// 定期发送心跳消息const interval = setInterval(() => {if (socket.destroyed) {clearInterval(interval);return;}socket.write('ping');}, 5000);socket.on('data', (data) => {console.log(`Received: ${data}`);if (data.toString() === 'pong') {console.log('Heartbeat received');}});socket.on('end', () => {console.log('client disconnected');clearInterval(interval);});
});server.listen(8080, () => {console.log('TCP server running on port 8080');
});
5.2. 客户端响应心跳
客户端也可以实现一个简单的机制来响应服务器的心跳。
const client = net.createConnection({ port: 8080 }, () => {console.log('Connected to server');
});client.on('data', (data) => {if (data.toString() === 'ping') {client.write('pong');}
});