lwIP WebSocket 客户端 TCP PCB 泄漏问题分析与解决

发布于:2025-07-18 ⋅ 阅读:(13) ⋅ 点赞:(0)

        在嵌入式开发中,使用 lwIP 实现 WebSocket 客户端时,偶尔会遇到反复连接导致 TCP PCB(Protocol Control Block)泄漏,最终连接数达到上限(如 4)后无法再建立新连接的问题。本文将结合实际案例,分析问题原因并给出彻底解决方案。

问题现象

        设备端 WebSocket 客户端反复连接服务器,运行一段时间后,发现无法再建立新连接。通过调试 lwIP,发现 TCP PCB 数量不断增加,达到最大值后,后续连接全部失败。

#define MEMP_NUM_TCP_PCB                4

原因分析

        lwIP 的 TCP PCB 用于管理每个 TCP 连接的状态。正常情况下,连接关闭后 PCB 会被释放。但在实际代码中,WebSocket 客户端反复连接时,旧的 PCB 没有被及时释放,导致 PCB 泄漏。主要原因有:

  • 连接关闭时未主动调用 altcp_close 或 altcp_abort 彻底释放 PCB。
  • 新连接初始化前未检查并释放旧 PCB。

解决方案

         1.关闭连接时彻底释放 PCB,在 wsock_close() 函数中,主动调用 altcp_close,如失败则调用 altcp_abort

 if (pws->pcb) {
    altcp_arg(pws->pcb, NULL);
    altcp_recv(pws->pcb, NULL);
    altcp_err(pws->pcb, NULL);
    altcp_poll(pws->pcb, NULL, 0);
    altcp_sent(pws->pcb, NULL);
    if (altcp_close(pws->pcb) != ERR_OK) {
        altcp_abort(pws->pcb);
        close_err = ERR_ABRT;
    }
    pws->pcb = NULL;
}

        2. 修改lwipopts.h的LWIP_SOCKET宏定义:

 #define LWIP_SOCKET                     0

总结

        问题的根本原因是同事一开始没有改LWIP_SOCKET这个宏,默认为1,出现连接失败会自动调用wsock_close()导致出现HardFault_handler,然后他把这段释放处理屏蔽了,能正常使用,但又导致TCP PCB未能正确释放。

#define LWIP_SOCKET                     1

 

    if(pws->pcb)

    {

        altcp_arg(pws->pcb, NULL);

        altcp_recv(pws->pcb, NULL);

        altcp_err(pws->pcb, NULL);

        altcp_poll(pws->pcb, NULL, 0);

        altcp_sent(pws->pcb, NULL);

        // 主动关闭连接,彻底释放PCB资源

        // if(altcp_close(pws->pcb) != ERR_OK)

        // {

        //     altcp_abort(pws->pcb);

        //     close_err = ERR_ABRT;

        // }

        pws->pcb = NULL;

    }

        因为 lwIP WebSocket 客户端是基于 lwIP 的 TCP/ALTCP 原生 API 和 PCB 机制实现的,而不是基于 Socket API,LWIP_SOCKET 1 使能了 Socket API,导致 lwIP 内部在连接失败时自动调用 wsock_close(),而如果 PCB 或相关资源未正确初始化或已被释放,wsock_close() 内部访问空指针或非法内存就会触发 HardFault。

        正确的做法就是只需要修改lwipopts.h的LWIP_SOCKET宏定义为0,websocket_client.c源文件不需要修改:

#define LWIP_SOCKET                     0

 


网站公告

今日签到

点亮在社区的每一天
去签到