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:
@@ -107,7 +107,14 @@ static void iot_evt_process(void) {
|
||||
static uint8_t _was_ready = 0;
|
||||
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 立即重发 */
|
||||
_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;
|
||||
if (_evt_pend_id) {
|
||||
@@ -221,16 +228,22 @@ int main(void) {
|
||||
iot_evt_handle_ack(_evt_pend_id, 0);
|
||||
CHECK(_evt_count == 10, "T7 ACK 后出队6条");
|
||||
|
||||
/* T8: 断线重连沿 → 未决包作废 + 挂起解除, 剩余事件补报 */
|
||||
iot_evt_process(); /* 发下一包 (4条? 6条上限内=6... 剩10条→6) */
|
||||
/* T8: 断线重连 → 未决包用【同 msg_id/ts】重发 (V1.04 §5.3-2, 平台去重不失效) */
|
||||
iot_evt_process(); /* 发下一包 */
|
||||
uint32_t pend_before = _evt_pend_id;
|
||||
uint32_t ts_before = _evt_pend_ts;
|
||||
CHECK(pend_before != 0, "T8 有未决包");
|
||||
g_iot_state = IOT_STATE_DISCONNECTED;
|
||||
iot_evt_process(); /* 断线 */
|
||||
iot_evt_process(); /* 断线 (未 ACK) */
|
||||
g_iot_state = IOT_STATE_READY;
|
||||
_mock_ms += 100;
|
||||
iot_evt_process(); /* 重连沿: 作废旧包, 立即新包 */
|
||||
CHECK(_evt_pend_id == pend_before + 1, "T8 重连后新 msg_id 补报");
|
||||
int pub_before = pub_count;
|
||||
iot_evt_process(); /* 重连沿: 同 msg_id 立即重发 */
|
||||
CHECK(_evt_pend_id == pend_before, "T8 重连后保持同 msg_id (不换号)");
|
||||
CHECK(_evt_pend_ts == ts_before, "T8 重连后保持原 ts");
|
||||
CHECK(pub_count == pub_before + 1, "T8 重连触发一次重发");
|
||||
{ char idbuf[32]; snprintf(idbuf, sizeof(idbuf), "\"msg_id\":%u", pend_before);
|
||||
CHECK(strstr(last_payload, idbuf) != NULL, "T8 重发报文含原 msg_id"); }
|
||||
/* 全部确认清空 */
|
||||
while (_evt_count) { iot_evt_handle_ack(_evt_pend_id, 0); _mock_ms += 100; iot_evt_process(); }
|
||||
CHECK(_evt_count == 0, "T8 清空");
|
||||
|
||||
Reference in New Issue
Block a user