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

lwIP MQTT 心跳 Bug 分析与修复

一、背景

       在使用 lwIP 内置 MQTT 客户端时,如果你用的是 2.2.0 之前的版本,很可能会遇到一个恼人的问题:客户端和服务器正常连接,但一段时间后 会话被 broker 踢掉

比如常见的现象:

  • Mosquitto / EMQX 日志显示客户端超时断开。

  • lwIP 端没有主动调用 mqtt_disconnect(),却突然进入了 MQTT_DISCONNECTED 状态。

  • 配置的 keep-alive 时间是 60s,但实际上 90s 左右就会掉线。

        经过排查,这其实是 心跳(keep-alive)定时逻辑的 bug。下面来分析一下原因,并给出解决方法

二、问题现象

        在 lwIP 2.1.x 的 mqtt.c 里,心跳定时逻辑在 mqtt_cyclic_timer() 中实现

if (client->keep_alive > 0) {client->server_watchdog++;if ((client->server_watchdog * MQTT_CYCLIC_TIMER_INTERVAL) > (client->keep_alive +             client->keep_alive / 2)) {mqtt_close(client, MQTT_CONNECT_TIMEOUT);restart_timer = 0;}/* keep-alive 超时检测 */if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {// 发送心跳包 PINGREQmqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);client->cyclic_tick = 0;} else {client->cyclic_tick++;}
}

        看似合理,但这里有个细节:

  • 只有在 else 分支中才会执行 cyclic_tick++

  • 如果进入 if (...) 发送了心跳包,就会直接 cyclic_tick = 0,漏掉了一次累加。

结果就是:

  • 心跳计数器实际触发频率比预期低。

  • PINGREQ 的发送比配置的 keep-alive 更晚。

  • Broker 端在 1.5 倍 keep-alive 没收到心跳时,就会断开连接。

三、解决方法

        只需要在进入分支判断之前,提前增加一次 cyclic_tick

client->cyclic_tick++; // 修复点:每个周期都先自增
if ((client->cyclic_tick * MQTT_CYCLIC_TIMER_INTERVAL) >= client->keep_alive) {mqtt_output_append_fixed_header(&client->output, MQTT_MSG_TYPE_PINGREQ, 0, 0, 0, 0);client->cyclic_tick = 0;
} else {client->cyclic_tick++;
}

   这样就保证了:

  • 每次定时器调用,cyclic_tick 都会+1。

  • 不会出现“少算一次”的情况。

  • 心跳严格按照配置的 keep-alive 周期发送。

四、结论

  • lwIP 2.1.x 版本的 MQTT 实现存在心跳 bug,导致 PINGREQ 延迟发送,broker 判定超时。

  • 原因在于 cyclic_tick++ 的位置不对,导致计数器漏算。

  • 解决办法:在 lwIP 2.2.0 中,官方已经调整了 mqtt_cyclic_timer() 的逻辑,把 cyclic_tick 的自增位置放到固定地方,避免了这个 bug。因此,如果你的项目允许,推荐直接升级 lwIP 到 ≥ 2.2.0。如果受限于平台或历史代码,直接修改 mqtt.c 中的计数逻辑也能解决问题。

http://www.dtcms.com/a/354960.html

相关文章:

  • 边缘计算(Edge Computing)+ AI:未来智能世界的核心引擎
  • HarmonyOS 组件与页面生命周期:全面解析与实践
  • Paimon——官网阅读:Flink 引擎
  • 【秋招笔试】2025.08.27华为秋招研发岗真题
  • 【新启航】3D 逆向抄数效率提升:自动化拼接工具与智能建模能力如何缩短 50% 项目周期
  • 聚类准确率计算——标签映射(Kuhn-Munkres匈牙利算法)问题的解决(详细并附完整代码)
  • 大模型RAG(Retrieval-Augmented Generation)
  • Python日期计算完全指南:从上周五到任意日期的高效计算
  • Cubemx+Vscode安装与环境配置
  • 聚焦建筑能源革新!安科瑞 “光储直柔” 方案护航碳中和目标实现
  • 162.在 Vue 3 中使用 OpenLayers 解析 GeoJSON 并为每个 Feature 填充渐变色
  • 如何调试一个EVM合约:实战操作 + 常见报错说明
  • 2025年第五届电子信息工程与计算机科学国际会议(EIECS 2025)
  • IO的最大输出速度
  • Maven 项目单元测试实战指南:从环境搭建到问题排查全解析
  • 一天认识一个神经网络之--CNN卷积神经网络
  • Linux系统之----命名管道模拟实现客户端、服务器
  • ImageToPromptAI-AI图像转提示词生成器
  • ftp命令批量删除服务器上的文件
  • 关于我在一个优惠券系统中rocketMQ消息幂等性自定义注解的处理
  • 使用reCAPTCHA提升WordPress网站安全性
  • 驱动开发系列67 - NVIDIA 开源GPU驱动open-gpu-kernel-modules分析-驱动初始化
  • Java自定义程序使用Ollama实现本地ai调用
  • Java-反射机制
  • Java 多线程环境下的全局变量缓存实践指南
  • PyTorch 张量核心知识点
  • 【物联网】什么是 Arduino Nano 33 IoT?
  • 基于springboot的二手车交易系统
  • WEEX唯客上线C2C交易平台:打造安全便捷的用户交易体验
  • FISCO-BCOS-Python 模板