RT-Thread论坛
直播中

李玉鑫

8年用户 1346经验值
私信 关注
[问答]

libmodbus库问题:TCP模式下客户端超时断开后无法再次重连怎么解决?

情况1 :用libmodbus的TCP demo,发现用客户端软件连接收发如果超时后在启动连接就无法连接上libmodbus服务器了,声明:客户端此时不会主动断开连接。
情况2:客户端用短连接进行一次收发后断开连接,再次启动短连接进行通讯时被提示:”服务器拒绝“
综上2种情况,libmodbus该如何修改才能让支持让断开的客户端再次连接?

=

回帖(1)

李红

2025-10-24 17:59:35

解决 libmodbus TCP 模式下客户端断开后无法重连的问题


问题分析


两种情况的根本原因都是 服务端未正确处理连接关闭后的资源释放,导致:



  1. 情况1:客户端超时后未主动断开,服务端仍持有失效连接,阻塞在 modbus_receive()

  2. 情况2:短连接结束后,服务端未关闭旧连接,导致新连接被拒绝。


解决方案


修改服务端代码逻辑,核心思路:超时检测 + 连接资源释放 + 循环等待新连接


关键修改步骤



  1. 设置响应超时时间


    modbus_t *ctx = modbus_new_tcp("127.0.0.1", 1502);
    struct timeval timeout;
    timeout.tv_sec = 1;  // 1秒超时(根据需求调整)
    timeout.tv_usec = 0;
    modbus_set_response_timeout(ctx, &timeout);



  2. 重构服务端主循环


    int server_socket = modbus_tcp_listen(ctx, 1);  // 创建监听socket
    while (1) {
       // 1. 等待客户端连接到
       if (modbus_tcp_accept(ctx, &server_socket) == -1) {
           fprintf(stderr, "Accept failed: %sn", modbus_strerror(errno));
           continue;  // 继续等待新连接
       }

       // 2. 连接建立后处理请求
       while (1) {
           uint8_t req[MODBUS_TCP_MAX_ADU_LENGTH];
           int rc = modbus_receive(ctx, req);

           if (rc == -1) {
               // 错误处理:超时或连接断开
               if (errno == ETIMEDOUT) {
                   printf("Client timeout. Closing connection.n");
               } else {
                   printf("Connection reset: %sn", modbus_strerror(errno));
               }
               break;  // 退出内部循环,准备接受新连接
           }

           // 处理Modbus请求(示例)
           if (req[7] == 0x03) {  // 功能码03
               uint16_t reg[2] = {0x1234, 0x5678};
               modbus_reply(ctx, req, 5, reg);  // 返回数据
           }
       }

       // 3. 关闭当前失效连接
       modbus_close(ctx);
       printf("Connection closed. Waiting for new client...n");
    }



  3. 编译时链接库


    gcc server.c -o modbus_server -lmodbus



原理说明




  1. 超时检测

    modbus_set_response_timeout() 设置读超时后,modbus_receive() 会在指定时间后返回 -1 并设置 errno=ETIMEDOUT,避免永久阻塞。




  2. 双重循环结构  



    • 外层循环:持续接受新连接 (modbus_tcp_accept)

    • 内层循环:处理当前连接的请求

    • 当检测到超时/断开时,跳出内层循环并清理连接




  3. 资源清理

    modbus_close() 释放当前连接资源,使服务端能重新绑定端口监听新连接。




处理流程


graph TD
    A[启动服务] --> B[监听端口]
    B --> C{有新连接?}
    C -- 是 --> D[建立连接]
    D --> E{接收数据}
    E -- 成功 --> F[处理请求]
    F --> E
    E -- 超时/错误 --> G[关闭连接]
    G --> C
    C -- 否 --> C

补充建议




  1. 客户端短连接优化

    客户端每次请求后调用 modbus_close() 主动断开,避免服务端等待超时。




  2. 服务端健壮性增强


    // 在 modbus_tcp_accept 前添加 SO_REUSEADDR 避免端口占用
    int reuse = 1;
    setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));



  3. 处理连接中断信号


    // 在 modbus_receive 后检查特定错误码
    if (rc == -1 && (errno == ECONNRESET || errno == EPIPE)) {
       printf("Client forcibly closed connectionn");
       break;
    }




关键点:服务端必须主动释放旧连接资源(modbus_close),才能接受新连接。超时设置避免了服务端永久阻塞,双重循环结构保证了连接重置能力。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分