You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS net.Socket客户端:已写入数据却无响应意外关闭连接

Socket连接写入后未收到响应意外关闭的问题排查

背景

我在公司开发了一个小型程序,用于与PLC上的服务器Socket建立连接,程序工作流程如下:

  • 每10秒与服务器建立新的Socket连接
  • 客户端程序与PLC上的服务器Socket建立连接
  • 连接初始化完成后立即写入数据
  • 服务器Socket响应写入请求并返回数据
  • 收到服务器响应后关闭Socket连接

无论写入成功、服务器返回空响应、出现错误或超时,我都会将连接开始时间、写入时间、服务器响应时间、写入帧、服务器返回帧等信息记录到数据库中。我使用NodeJS原生模块net.Socket来定义客户端Socket,并管理服务器响应、错误及超时。

代码

"use strict";

const
    net = require('net'),
    {convertToHex} = require("./services/utils"),
    {db_insert} = require("./services/db_query"),
    {log_socket_client} = require("./services/logger"),
    crypto = require("crypto"),
    cmdCommandList = {/* ... */};


let timeout_interval_socket_client = setInterval(
    () => {
        let socket_client = new net.Socket();
        socket_client.uuid = crypto.randomUUID();

        const date_start_task = new Date();
        let
            frame_input_in_hex = null,
            date_socket_write = null;

        socket_client
            .on("connect", function() {
                buf_command_automat = /* _AUTOMATE_BUFFER_COMMAND_ */;

                frame_input_in_hex = convertToHex(buf_command_automat);
                socket_client.write(buf_command_automat, function() {
                    date_socket_write = new Date();
                });
            })
            .on('data', function(message) {
                /* INSERT TO BDD MESSAGE : frame output received by socket server */
                socket_client.destroy();
            })
            .on('close', function(hadError) {
                (async() => {
                    if(socket_client.bytesWritten === 0) {
                        /* INSERT TO BDD MESSAGE : noBytesWritten */
                    }

                    if(socket_client.bytesRead === 0) {
                        /* INSERT TO BDD MESSAGE : no packet received */
                    }

                    await socket_client.destroy();
                })();
            })
            .on('error', function(error) {
                /* INSERT TO BDD error.toString()  */
                if(socket_client) socket_client.close();
            })
            .on('timeout', () => {
                /* INSERT TO BDD MESSAGE : timeout  */
                if(socket_client) socket_client.close();
            });

            
        socket_client.setTimeout(15000);
        socket_client.connect(/* _AUTOMATE_PORT_ */, /* _AUTOMATE_IP_ */);

    }, (10000)
);


process.on('SIGINT', () => {
    if(timeout_interval_socket_client) {
        clearInterval(timeout_interval_socket_client);
    }
});

问题

当前遇到的问题是:部分连接在已完成客户端Socket写入操作的情况下,未收到服务器响应就意外关闭了。请问可能的原因是什么?

已尝试的解决措施

  • 排查是否为动态关闭的Socket
  • 尝试用.destroy()替代.close()关闭Socket
  • 为Socket添加ID,排查是否为setInterval的副作用

补充信息

  • NodeJS版本:v18.17.1
  • npm版本:v9.6.7
  • Socket超时设置为15秒

可能的原因分析

1. PLC服务器端限制

PLC的Socket服务器可能存在连接数上限、请求频率限制,或内部资源不足,导致接收写入后主动关闭连接而不返回响应。比如部分PLC对并发连接或每秒请求数有严格限制,若程序每10秒发起一次连接,服务器端未正确处理旧连接残留时,可能触发保护机制直接断开连接。

2. 网络层面问题

  • 中间设备规则拦截:交换机、防火墙等中间设备可能设置了短于15秒的空闲超时,或对特定数据包内容进行过滤,导致服务器响应无法到达客户端,连接被强制关闭。
  • 数据包丢失:写入的请求成功到达服务器,但服务器的响应在传输过程中丢失,客户端未收到数据,最终触发超时或连接被动关闭。

3. Node.js Socket处理逻辑缺陷

  • 重复销毁Socket:close事件回调中重复调用destroy(),而close事件本身就是连接关闭时触发,重复销毁会导致异常状态;同时回调中的await无实际作用(Node.js事件回调不等待异步操作),可能引发资源泄漏。
  • 超时逻辑冲突:setTimeout(15000)设置的是Socket空闲超时,若服务器处理请求的时间接近15秒,可能在响应发出前触发超时,客户端主动关闭连接,此时服务器的响应可能还在传输中,但客户端已无法接收。
  • 写入完成的判断偏差:socket_client.write的回调仅表示数据写入操作系统发送缓冲区,不代表数据已成功发送到服务器。若此时网络异常,数据未到达服务器就被断开连接,客户端会误以为写入完成,但服务器实际未收到完整请求。

4. 数据帧格式错误

写入PLC的buf_command_automat可能存在格式问题,比如校验位错误、帧长度不符合协议要求,PLC服务器接收到后无法解析,会直接关闭连接而不返回响应。

5. 资源泄漏累积

虽然调用了destroy()或close(),但部分Socket在错误或超时后未彻底释放资源,累积后导致后续连接出现异常关闭的情况。

内容的提问来源于stack exchange,提问作者Mathieu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 19:57:33