Nodejs TCP客户端遇到问题求解释
Nodejs TCP客户端遇到问题求解释
写了一个小例子。TCP服务器是在vc里实现的,循环等待客户端的数据连接,接收到客户端传来的特定值(STOP_CTRL: 4848)时断开连接。客户端里TCP连接的启停由我手动控制。
function ctrl_start() {
socket.connect(TCP_PORT, TCP_HOST, function() {
console.log("connected to: " + TCP_HOST + ":" + TCP_PORT);
});
}
function ctrl_stop() {
buf.writeUInt32LE(STOP_CTRL, 0);
socket.end(buf);
socket.destroy();
console.log("destroy "+STOP_CTRL);
}
经测试,连接的启停都进行的很好。可是命令行中打印结果如下:
connected to: 192.168.1.126:6969
destroy 4848
TCP Connection closed
connected to: 192.168.1.126:6969
connected to: 192.168.1.126:6969
destroy 4848
TCP Connection closed
即每次启动时,都会增加一条socket连接信息。可是我在停止连接的时候调用了socket.destroy()的啊?这是为什么?
根据你描述的情况,问题出在你如何处理TCP连接的启停逻辑上。当你调用 socket.end()
和 socket.destroy()
时,这些方法确实会关闭当前的连接,但如果你在短时间内频繁地启动和停止连接,Node.js 并不会自动清理之前的连接状态。因此,每次调用 ctrl_start
函数时,实际上都会创建一个新的连接。
为了更好地理解这个问题,我们先来看一下你的代码片段:
function ctrl_start() {
socket.connect(TCP_PORT, TCP_HOST, function() {
console.log("connected to: " + TCP_HOST + ":" + TCP_PORT);
});
}
function ctrl_stop() {
buf.writeUInt32LE(STOP_CTRL, 0);
socket.end(buf);
socket.destroy();
console.log("destroy " + STOP_CTRL);
}
解决方案
为了解决这个问题,我们需要确保在停止连接后不再重新创建新的连接。你可以通过以下方式来改进你的代码:
- 检查是否已经存在一个活动的连接:在
ctrl_start
方法中,先检查是否有活动的连接,如果有则不创建新的连接。 - 使用一个标志变量:使用一个标志变量来跟踪连接的状态,避免重复创建连接。
下面是修改后的代码示例:
let isConnected = false;
function ctrl_start() {
if (!isConnected) {
socket = new net.Socket(); // 创建一个新的Socket对象
socket.connect(TCP_PORT, TCP_HOST, function() {
console.log("connected to: " + TCP_HOST + ":" + TCP_PORT);
isConnected = true;
});
socket.on('close', function(had_error) {
console.log("TCP Connection closed");
isConnected = false;
});
} else {
console.log("Already connected.");
}
}
function ctrl_stop() {
buf.writeUInt32LE(STOP_CTRL, 0);
socket.end(buf);
socket.destroy();
console.log("destroy " + STOP_CTRL);
}
解释
- isConnected 变量:用于跟踪当前是否有活动的连接。
- 检查 isConnected:在
ctrl_start
方法中,如果已经有活动的连接,则不会重新创建新的连接。 - socket.on(‘close’):监听连接关闭事件,并在连接关闭后将
isConnected
置为false
。
这样可以避免重复创建连接的问题,并且能更好地管理连接状态。
一次socket连接ctrl_start却被调用了2次,或者
console.log("connected to: " + TCP_HOST + ":" + TCP_PORT);
此语句也出现代码的其他地方并同样被执行了。 嘛,ctrl_start函数多加个console.log,把输出信息贴出来吧
function ctrl_start() {
socket.connect(TCP_PORT, TCP_HOST, function() {
console.log("connected to: " + TCP_HOST + ":" + TCP_PORT);
});
console.log(new Error().stack);
}
添加了你写的Error().stack,输出的信息如下
Error
at ctrl_start (D:\tools\nodejs\RC\RCserver.js:104:14)
at Socket.<anonymous> (D:\tools\nodejs\RC\RCserver.js:60:4)
at Socket.$emit (events.js:67:17)
at SocketNamespace.handlePacket (D:\tools\nodejs\node_modules\socket.io\lib\
namespace.js:335:22)
at Manager.onClientMessage (D:\tools\nodejs\node_modules\socket.io\lib\manag
er.js:469:38)
at WebSocket.onMessage (D:\tools\nodejs\node_modules\socket.io\lib\transport
.js:387:20)
at Parser.<anonymous> (D:\tools\nodejs\node_modules\socket.io\lib\transports
\websocket\hybi-16.js:39:10)
at Parser.emit (events.js:67:17)
at D:\tools\nodejs\node_modules\socket.io\lib\transports\websocket\hybi-16.j
s:288:16
at Parser.expectHandler (D:\tools\nodejs\node_modules\socket.io\lib\transpor
ts\websocket\hybi-16.js:299:15)
刚才点错了,输出信息在2#
注意堆栈信息第三行
at Socket.<anonymous> (D:\tools\nodejs\RC\RCserver.js:60:4)
如果其他没贴出来的堆栈信息的第三行都一样,猜测就是socket.io的客户端发送了2个事件(其中一个应该就是楼主所谓手动控制的start事件),服务器端重复初始化了2次socket对象,调用了2次ctrl_start。检查socket.io客户端的代码吧。
如果第三行不同,检查发起socket连接的服务器端代码。
其实堆栈信息都打出来了,按藤摸瓜找出原因不难吧。问题既不是出在ctrl_start也不在ctrl_stop,而是在于调用这2个函数的代码。
从你的描述来看,每次调用 ctrl_stop
函数后,TCP 连接并没有被正确关闭,而是反复重新连接。这可能是因为在调用 socket.destroy()
后,你没有正确处理连接关闭后的事件。
以下是一个改进版本的示例代码,帮助你更好地处理连接关闭事件:
const net = require('net');
const TCP_HOST = '192.168.1.126';
const TCP_PORT = 6969;
const STOP_CTRL = 4848;
let socket;
function ctrl_start() {
socket = new net.Socket();
socket.on('connect', () => {
console.log("connected to: " + TCP_HOST + ":" + TCP_PORT);
});
socket.on('data', (data) => {
// 处理数据
console.log("Received data: " + data.toString());
});
socket.on('close', () => {
console.log("Socket closed");
});
socket.on('error', (err) => {
console.error("Socket error: " + err.message);
});
socket.connect(TCP_PORT, TCP_HOST);
}
function ctrl_stop() {
if (socket) {
const buf = Buffer.alloc(4);
buf.writeUInt32LE(STOP_CTRL, 0);
socket.write(buf);
socket.end();
socket.destroy();
console.log("destroy " + STOP_CTRL);
}
}
// 测试示例
ctrl_start();
setTimeout(ctrl_stop, 5000); // 5秒后停止连接
解释:
- 创建
socket
实例:每次ctrl_start
被调用时,创建一个新的socket
实例。 - 监听事件:确保在
socket
上监听connect
、data
、close
和error
事件。 - 发送停止命令:在
ctrl_stop
函数中,先写入并发送停止命令,然后调用socket.end()
和socket.destroy()
来关闭连接。 - 错误处理:通过
error
事件处理可能发生的错误,以防止程序崩溃。
这样可以确保每次停止连接时,TCP 连接能够正确关闭,不会再出现重复连接的情况。