粘包和拆包产生的原因
- 发送端原因:
- Nagle算法:为了提高网络利用率,减少网络传输小包的开销,TCP协议默认启用Nagle算法。该算法会将小的数据包积累到一定大小或者等待一定时间后再发送。例如,应用层连续调用多次
send
方法发送数据,TCP协议可能会将这些小数据包合并成一个大的数据包发送出去,这就导致在接收端可能会出现粘包问题。
- 接收端原因:
- 缓冲区机制:接收端的TCP缓冲区在接收到数据后,应用层可能不会立即读取,或者一次读取的数据量小于缓冲区中的数据量。这样就会导致缓冲区中剩余的数据和下一次接收的数据粘在一起,从而产生粘包问题。另外,如果发送的数据量大于接收端缓冲区的大小,数据就会被拆分成多个部分进行传输,在接收端就会出现拆包问题。
处理粘包与拆包问题的方法及代码示例
- 定长包处理方法:
- 原理:每个数据包都固定长度。发送端发送固定长度的数据包,接收端每次按固定长度读取数据。
- 代码示例:
const net = require('net');
const server = net.createServer((socket) => {
let buffer = '';
socket.on('data', (data) => {
buffer += data.toString('utf8');
while (buffer.length >= 10) { // 假设每个包固定长度为10
const packet = buffer.substr(0, 10);
buffer = buffer.substr(10);
console.log('Received:', packet);
}
});
});
server.listen(8080, () => {
console.log('Server listening on port 8080');
});
const client = net.connect({ port: 8080 }, () => {
const packets = ['HelloWorld', 'NodejsTCP'];
packets.forEach((packet) => {
// 假设每个包固定长度为10,不足10补空格
let fixedPacket = packet.padEnd(10,' ');
client.write(fixedPacket);
});
});
- 包头 + 包体处理方法:
- 原理:数据包由包头和包体组成,包头中包含包体的长度等信息。发送端先发送包头,接收端先接收包头获取包体长度,再根据包体长度接收包体。
- 代码示例:
const net = require('net');
const server = net.createServer((socket) => {
let buffer = Buffer.alloc(0);
socket.on('data', (data) => {
buffer = Buffer.concat([buffer, data]);
while (buffer.length >= 4) { // 假设包头长度为4,存储包体长度
const bodyLength = buffer.readUInt32BE(0);
if (buffer.length >= 4 + bodyLength) {
const body = buffer.slice(4, 4 + bodyLength);
buffer = buffer.slice(4 + bodyLength);
console.log('Received:', body.toString('utf8'));
} else {
break;
}
}
});
});
server.listen(8080, () => {
console.log('Server listening on port 8080');
});
const client = net.connect({ port: 8080 }, () => {
const messages = ['Hello Nodejs', 'TCP Communication'];
messages.forEach((message) => {
const body = Buffer.from(message, 'utf8');
const header = Buffer.alloc(4);
header.writeUInt32BE(body.length, 0);
client.write(Buffer.concat([header, body]));
});
});