fix(mqtt): 修复 loop_data>512B 溢出发垃圾包致 broker RST 重连风暴 (现场P0)

根因(非平台推测的环形缓冲/event_report队列):
  mqtt_publish 的 mqttBuf=512B < loop_data(4通道)604B
  → MQTTSerialize_publish 返回 -2 不写 buf
  → len 为 uint32_t, -2 变 42.9亿
  → WCHNET 把清零缓冲+越界相邻全局(temp_guide=report_config→report_c)
    当 520B 垃圾包发出 → broker 见非法类型0x00 RST → 重连风暴

修复(net_srv.c):
  - MAX_MQTTBUF_LEN 512→1024 (容纳 604B loop_data)
  - mqtt_publish: len uint32→int + 守卫 if(len<=0)return (序列化失败绝不发残缓冲)
  - keepalive 9→60 (报告建议, 9s过激)

event_report 协议违规修复(iot_mqtt_srv.c):
  - 重连重发保持原 msg_id/ts (V1.04 §5.3-2), 不再作废换号致平台去重失效

验证: tests/test_mqtt_publish_overflow.c 复现旧垃圾包+验证守卫/大缓冲;
      tests/test_event_report.c T8 断言重连同 msg_id. 均全过.
遗留: ts=上电秒数非Unix时间戳, 待定方案(设备无RTC/SNTP)

Refs: docs/incidents/2026-07-15-DC045A49718F-protocol-error.md
This commit is contained in:
wangfq
2026-07-15 14:58:03 +08:00
parent e54334e761
commit e3eaa111fe
7 changed files with 243 additions and 16 deletions
@@ -103,7 +103,7 @@ typedef struct _IOT_TOPIC_
} IOT_Topic;
extern IOT_Topic g_iot_topic;
#define MQTT_KEEPALIVE_INTERVAL 9
#define MQTT_KEEPALIVE_INTERVAL 60 // 9s 过于激进(易误断+PINGREQ风暴); broker超时=1.5×=90s, 60s ping留30s余量
typedef struct _NET_STATE_
@@ -210,9 +210,17 @@ static void iot_evt_process(void) {
uint8_t ready = (g_iot_state == IOT_STATE_READY);
uint32_t now = mstick();
if (ready && !_was_ready) { // 重连沿: 作废未决包, 解除挂起, 全量重报
_evt_pend_id = 0;
_evt_gaveup = 0;
if (ready && !_was_ready) { // 重连沿
_evt_gaveup = 0;
if (_evt_pend_id) {
/* 有未决包 → 用【原 msg_id/原 ts】立即重发 (协议 V1.04 §5.3-2:
跨重连也须保持同 msg_id, 否则平台 (sn,msg_id) 去重失效致重复入库) */
_evt_retry = 0; // 重连后重试计数归零, 不吃掉本次
iot_evt_send(_evt_pend_id, _evt_pend_ts, _evt_pend_n);
_evt_sent_ms = now;
_was_ready = ready;
return;
}
}
_was_ready = ready;
if (!ready) return;
@@ -64,7 +64,7 @@ uint8_t MyBuf[RECE_BUF_LEN];
#define MAX_TMP_BUF_LEN 64
#define MAX_MQTTBUF_LEN 512 //384 //512
#define MAX_MQTTBUF_LEN 1024 //512: loop_data(4通道)≈604B > 512 → 序列化失败发全零垃圾包被broker RST (2026-07-15)
char mqtt_username[64] = {0};
char mqtt_password[32] = {0};
char mqtt_clientid[64] = {0};
@@ -160,21 +160,29 @@ void MQTT_Pingreq()
void mqtt_publish(char *topic, char *message, int req_qos)
{
uint32_t len = 0;
int len = 0; // 必须有符号: MQTTSerialize_publish 缓冲不足返回 -2(BUFFER_TOO_SHORT)
clear_mqtt_buf();
{
PRINT("\nWill_publish_topid:%s, message:%s\n", topic, message);
}
MQTTString topicString = MQTTString_initializer;
int msglen = strlen(message);
topicString.cstring = topic;
static uint16_t s_mqtt_pkt_id = 0;
uint16_t pkt_id = (req_qos > 0) ? ++s_mqtt_pkt_id : 0;
len = MQTTSerialize_publish(mqttBuf, sizeof(mqttBuf), 0, req_qos, 0, pkt_id, topicString, (unsigned char *)message, msglen);
// Transport_SendPacket(mqttBuf, len);
WCHNET_SocketSend(SocketId_TCP, (uint8_t *)mqttBuf, &len);
/* 守卫: 序列化失败(缓冲不足/参数错)绝不发送残缓冲。
否则 len 转 uint32 成天文数字, WCHNET 把清零的 mqttBuf + 越界相邻内存当垃圾包发出,
broker 见非法报文类型 0x00 → RST → 重连风暴 (2026-07-15 现场事故根因) */
if (len <= 0) {
PRINT("mqtt_publish: serialize FAIL rc=%d msglen=%d buf=%d (dropped)\n",
len, msglen, (int)sizeof(mqttBuf));
return;
}
uint32_t slen = (uint32_t)len;
WCHNET_SocketSend(SocketId_TCP, (uint8_t *)mqttBuf, &slen);
}
void dev_initialize_pub(void)