当前位置: 首页 > news >正文

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

        在嵌入式开发中,使用 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

 

http://www.lryc.cn/news/591510.html

相关文章:

  • 当综艺IP跨界咖啡餐饮,《微笑一号店》重塑行业经营逻辑
  • 从零开始:C++ UDP通信实战教程
  • 【Python】通过cmd的shell命令获取局域网内所有IP、MAC地址,通过主机名获取IP
  • CCLink IE转ModbusTCP网关配置无纸记录器(上篇)
  • Redis 生产实战 7×24:容量规划、性能调优、故障演练与成本治理 40 条军规
  • Baumer工业相机堡盟工业相机如何通过YoloV8模型实现人物识别(C#)
  • MacOS安装linux虚拟机
  • Kubernetes架构原理与集群环境部署
  • Spring Boot 自动配置:从 spring.factories 到 AutoConfiguration.imports 的演变
  • MySQL安全修改表结构、加索引:ON-Line-DDL工具有哪些
  • 数据产品结构:从数据接入到可视化的完整架构指南
  • 学习C++、QT---27(QT中实现记事本项目实现行列显示、优化保存文件的功能的讲解)
  • Spring Boot 参数校验:@Valid 与 @Validated
  • 关于vector中的erase的强调
  • Leetcode刷题营第二十八题:二叉树的前序遍历
  • Effective Python 条款7 用列表推导来取代map和filter
  • c++之 KMP 讲解
  • 网络原理 —— HTTP
  • 深入理解Collections.addAll方法
  • Python 离线安装 PyInstaller 的完整步骤(以python3.11.4-amd64.exe为例)
  • Trae IDE:打造完美Java开发环境的实战指南
  • 产品经理如何绘制服务蓝图(Service Blueprint)
  • 基于5G系统的打孔LDPC编码和均匀量化NMS译码算法matlab性能仿真
  • Oracle 成本优化器(CBO)与数据库统计信息:核心原理与实践
  • 线程(三) linux 同步
  • .NET Framework版本信息获取(ASP.NET探针),获取系统的.NET Framework版本
  • pycharm提交项目到github及问题解决
  • ubuntu基础搭建
  • 【Android代码】绘本翻页时通过AI识别,自动通过手机/pad朗读绘本
  • 基于单片机公交车报站系统/报站器