FreeModbus释放底层的 TCP 监听端口

背景

有Modbus和scpi两种协议,在使用过程中要切换,每次切换要给给对应协议绑定端口

问题

在每次切换协议之前都这样操作,把所有连接都关闭仔进行重新绑定

c 复制代码
    // 尝试禁用Modbus
    eMBDisable();

    // 尝试关闭SCPI服务器的监听和客户端连接
    if (server_state.listen_pcb != NULL) {
        tcp_close(server_state.listen_pcb);
        server_state.listen_pcb = NULL;
  
    }
    if (server_state.client_pcb != NULL) {
        tcp_close(server_state.client_pcb);
        server_state.client_pcb = NULL;
        server_state.connected = 0;
    
    }

但是失败

结果:这样修改后,开机后protocol=1能直接连上设置的 192.168.1.111 port: 501 Modbus

面板改成选scpi后两种协议都不能使用,再改回Modbus后modbus可以使用,scpi不能用

开机后protocol=0能直接连上设置的 192.168.1.111 port: 501 scpi , 此时端口成功绑定

改成Modbus后modbus可以使用,scpi不能用

面板再改成选scpi后两种协议都不能使用,此时端口绑定失败

原因

这次失败的原因在于 FreeModbus 库的 eMBDisable() 函数默认只停止协议状态机,并不会释放底层的 TCP 监听端口(Listening Socket)。

  1. 资源未释放 :在 Modbus TCP 模式下,eMBDisable() 最终调用的是 vMBTCPPortDisable(),而在 porttcp.c 中,这个函数仅尝试关闭已连接的客户端 PCB,完全没有处理正在监听的 PCB (pxPCBListen)。
  2. 库配置限制 :FreeModbus 默认将 MB_PORT_HAS_CLOSE 设置为 0,这导致 eMBClose() 函数实际上是空的。
  3. 现象解释 :
    • 当从 Modbus 切换到 SCPI 时,Modbus 的监听端口依然在占用(因为它没被关闭)。
    • SCPI 尝试 tcp_bind 同一个端口,自然返回 8 (ERR_USE)。
    • 当切回 Modbus 时,Modbus 重新初始化并覆盖了之前的指针,但旧的监听 PCB 依然挂在协议栈里(资源泄露),虽然能连上,但此时协议栈状态已经非常混乱。

最终解决方法

我们需要强制开启 Modbus 库的资源释放功能,并在切换时显式调用它。

步骤 1:开启 Modbus 库的关闭功能

在 mbconfig.h中,将 MB_PORT_HAS_CLOSE 定义为 1。

添加了:

c 复制代码
#define MB_PORT_HAS_CLOSE ( 1 )

步骤 2:在切换逻辑中添加彻底关闭调用

在 user_mb_app_m.c的 cleanup_all_protocols 函数中,添加对 eMBClose() 的调用。

核心修改如下:

c 复制代码
static void cleanup_all_protocols(void)
{
    // 1. 禁用 Modbus 状态机
    eMBDisable();
    // 2. 彻底关闭 Modbus 端口监听(关键:释放 pxPCBListen)
    eMBClose();

    // 3. 彻底清理 SCPI 资源
    if (server_state.client_pcb != NULL) {
        tcp_abort(server_state.client_pcb);
        server_state.client_pcb = NULL;
        server_state.connected = 0;
    }
    if (server_state.listen_pcb != NULL) {
        tcp_close(server_state.listen_pcb);
        server_state.listen_pcb = NULL;
    }
}

验证说明

  • 为什么这次会成功?
    • 因为 eMBClose() 会通过 pvMBFrameCloseCur 指针最终调用到 porttcp.c 中的 vMBTCPPortClose()。
    • vMBTCPPortClose() 会显式调用 prvvMBPortReleaseClient( pxPCBListen ),这才会真正向 LwIP 发出关闭监听端口的指令。
  • 后续测试 :
    再次切换。切换到 SCPI 后,观察到端口顺利绑定。
相关推荐
贞明电子7 小时前
工业三色报警灯怎么接入项目系统?开关量与RS485控制应用指南
运维·网络·微信公众平台·贞明电子
HideF7 小时前
Linux
linux·服务器·网络
万联WANFLOW7 小时前
海外网络故障怎么排查?Cloudflare 三季度断网报告解读与 mtr 实战
运维·网络·故障分析
sbjdhjd7 小时前
网工进阶笔记:H3CSE 园区网全栈技术与协议汇总
网络·笔记·云计算·ssh·网络工程·vlan·aaa
Ruiery9 小时前
Linux 6.6内核内存管理深度解析(十三):slab/slub 分配器
linux·运维·服务器
taktok_ai10 小时前
用 cc-switch 连接 Taktok
网络
乌恩大侠10 小时前
从 AI-RAN 到 AI Agent:基于 NVIDIA Nemotron 构建 5G/6G 智能网络故障诊断系统
服务器·人工智能·5g·o-ru·ai-ran·电信大模型
小嘉丶学长12 小时前
Day14 · 字典与集合
java·开发语言·网络·python·学习·1024程序员节
迪菲赫尔曼12 小时前
【基础篇 11】把官方源码放到服务器:版本、解压层级和安装位置,一次弄清楚
运维·服务器·开发语言·人工智能·python·yolo·目标检测
LCG米12 小时前
STM32驱动W5500以太网模块实测:TCP吞吐瓶颈定位、TX指针回绕陷阱与断线重连保活落地
stm32·单片机·嵌入式硬件·tcp/ip