--------------------------有问题的写法(故意发送很长的字符串,让客户端多次接收)---------------------------
1)Server.js(注意必须实现 "error"的监听, 不然客户端断开,服务端就宕机了, 当然也要实现 "close"事件监听)
var net = require("net")
var server = net.createServer(function (socket) {
console.log("client connect");
socket.on("error", function () {
console.log("error");
});
socket.on("close", function () {
console.log("client disconnect");
socket.end();
});
socket.on("data", function (data) {
socket.write(data);
});
});
server.on("close", function () {
console.log("client close");
});
server.listen({
port: 6080,
exclusive: true
});
console.log("server start!");
2)Client.js
var net = require("net")
var socket = net.connect({
host: "127.0.0.1",
port: 6080
});
socket.on("connect", function () {
console.log("connect to server success");
var str = "";
for(var i = 0; i < 100000; i++){
str += "0123456789";
}
socket.write(str);
});
socket.on("close", function () {
console.log("disconnect");
socket.end();
});
socket.on("data", function (data) {
// console.log("recv from server:", data);
// 解析为字符串
var str = data.toString("utf8", 0, data.length);
console.log(str.length)
});
socket.on("error", function () {
console.log("error");
});
3)运行结果
可见客户端打印了多次说明分多次包才把服务器发来的数据包收完
--------------------------解决粘包问题的写法(故意发送很长的字符串,让客户端多次接收)---------------------------
1.TcpPkg.js
var TcpPkg = {
readPkgSize: function (pkgData, offset) {
if (offset > pkgData.length - 4) {
return -1;
}
var len = pkgData.readUInt32LE(offset);
return len;
},
packageData: function (data) {
var buf = Buffer.allocUnsafe(4 + data.length);
buf.writeUInt32LE(4 + data.length, 0);
buf.fill(data, 4);
return buf;
}
};
module.exports = TcpPkg;
2.Server.js
var net = require("net")
var TcpPkg = require("./TcpPkg.js");
var server = net.createServer(function (socket) {
console.log("client connect");
socket.on("error", function () {
console.log("error");
});
socket.on("close", function () {
console.log("client disconnect");
socket.end();
});
socket.on("data", function (data) {
if (!Buffer.isBuffer(data)) {
console.error("收到的不是Buffer");
return;
}
console.log("temp data:", data);
var lastPkg = socket.lastPkg;
if (lastPkg != null) {
var buf = Buffer.concat([lastPkg, data], lastPkg.length + data.length);
lastPkg = buf;
} else {
lastPkg = data;
}
var offset = 0;
var pkgLen = TcpPkg.readPkgSize(lastPkg, offset);
if (pkgLen < 0) {
socket.lastPkg = lastPkg;
return;
}
while (offset + pkgLen <= lastPkg.length) {
var cmdBuf = Buffer.allocUnsafe(pkgLen - 4);
// 读取到一个完整的命令包放到cmdBuf
lastPkg.copy(cmdBuf, 0, offset + 4, offset + pkgLen);
// 编码加上包头后发送过去
console.log("接收到数据的长度:", cmdBuf.length)
var finalBuf = TcpPkg.packageData(cmdBuf);
socket.write(finalBuf);
offset += pkgLen;
if (offset >= lastPkg.length) {
break;
}
pkgLen = TcpPkg.readPkgSize(lastPkg, offset);
if (pkgLen < 0) {
break;
}
}
if (offset >= lastPkg.length) {
lastPkg = null;
} else {
var buf = Buffer.allocUnsafe(lastPkg.length - offset);
lastPkg.copy(buf, 0, offset, lastPkg.length);
lastPkg = buf;
}
socket.lastPkg = lastPkg;
});
});
server.on("close", function () {
console.log("client close");
});
server.listen({
port: 6080,
exclusive: true
});
console.log("server start!");
3.Client.js
var net = require("net");
var TcpPkg = require("./TcpPkg.js");
var socket = net.connect({
host: "127.0.0.1",
port: 6080
});
socket.on("connect", function () {
console.log("connect to server success");
var str = "";
for (var i = 0; i < 1000; i++) {
str += "0123456789";
}
var cmdBuf = Buffer.from(str);
var finalData = TcpPkg.packageData(cmdBuf);
socket.write(finalData);
});
socket.on("close", function () {
console.log("disconnect");
socket.end();
});
socket.on("data", function (data) {
if (!Buffer.isBuffer(data)) {
console.error("收到的不是Buffer");
return;
}
var lastPkg = socket.lastPkg;
if (lastPkg != null) {
var buf = Buffer.concat([lastPkg, data], lastPkg.length + data.length);
lastPkg = buf;
} else {
lastPkg = data;
}
var offset = 0;
var pkgLen = TcpPkg.readPkgSize(lastPkg, offset);
if (pkgLen < 0) {
socket.lastPkg = lastPkg;
return;
}
while (offset + pkgLen <= lastPkg.length) {
var cmdBuf = Buffer.allocUnsafe(pkgLen - 4);
// 读取到一个完整的命令包放到cmdBuf
lastPkg.copy(cmdBuf, 0, offset + 4, offset + pkgLen);
// 原封不动的发送过去
console.log("客户端收到服务器的字节长度:", cmdBuf.length);
var str = cmdBuf.toString("utf8", 0, cmdBuf.length);
console.log(str);
offset += pkgLen;
if (offset >= lastPkg.length) {
break;
}
pkgLen = TcpPkg.readPkgSize(lastPkg, offset);
if (pkgLen < 0) {
break;
}
}
if (offset >= lastPkg.length) {
lastPkg = null;
} else {
var buf = Buffer.allocUnsafe(lastPkg.length - offset);
lastPkg.copy(buf, 0, offset, lastPkg.length);
lastPkg = buf;
}
socket.lastPkg = lastPkg;
});
socket.on("error", function () {
console.log("error");
});
4.效果
可见服务器依然分了2次才把客户端的包收完,但是没关系,被暂存了,所以最后服务器将收到的包合并为完整的包,一次性发送给客户端,客户端也采用暂存包的方式,最终收到完整包时一次性解析出来命令。