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()的啊?这是为什么?


6 回复

根据你描述的情况,问题出在你如何处理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);
}

解决方案

为了解决这个问题,我们需要确保在停止连接后不再重新创建新的连接。你可以通过以下方式来改进你的代码:

  1. 检查是否已经存在一个活动的连接:在 ctrl_start 方法中,先检查是否有活动的连接,如果有则不创建新的连接。
  2. 使用一个标志变量:使用一个标志变量来跟踪连接的状态,避免重复创建连接。

下面是修改后的代码示例:

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秒后停止连接

解释:

  1. 创建 socket 实例:每次 ctrl_start 被调用时,创建一个新的 socket 实例。
  2. 监听事件:确保在 socket 上监听 connectdatacloseerror 事件。
  3. 发送停止命令:在 ctrl_stop 函数中,先写入并发送停止命令,然后调用 socket.end()socket.destroy() 来关闭连接。
  4. 错误处理:通过 error 事件处理可能发生的错误,以防止程序崩溃。

这样可以确保每次停止连接时,TCP 连接能够正确关闭,不会再出现重复连接的情况。

回到顶部