4、通信中断排查(Wi-Fi):信号、TCP、MQTT/HTTP
固件升级最怕什么?我最怕升级到一半,Wi-Fi 断了。
设备变砖还是小事,关键是用户那边已经显示“升级中”,结果等了一小时还是 50%。这种问题,十有八九出在通信链路上。今天我们就来聊聊 Wi-Fi 信号、TCP 连接、以及应用层协议这几个环节,到底该怎么查。
核心思路:通信中断排查,要分层看。物理层(Wi-Fi 信号)→ 传输层(TCP 重传/超时)→ 应用层(MQTT/HTTP 心跳与断连)。哪一层断了,就从哪一层补。
4.1 Wi-Fi 信号强度与稳定性
先看物理层。Wi-Fi 信号不好,上层协议再优化也没用。我习惯在固件里加一个信号监控任务,每 30 秒采样一次 RSSI 和信噪比。
| 信号等级 | RSSI 范围 (dBm) | 信噪比 (dB) | 升级建议 |
|---|---|---|---|
| 优秀 | > -50 | > 40 | 正常升级 |
| 良好 | -50 ~ -65 | 25 ~ 40 | 正常升级,建议监控 |
| 一般 | -65 ~ -75 | 15 ~ 25 | 降低速率,增加重试 |
| 差 | < -75 | < 15 | 暂缓升级,提示用户 |
我在项目中遇到过一个问题:设备在实验室信号很好,一到客户现场就频繁断连。后来发现是 2.4G 信道干扰太严重。周围十几个 AP 挤在信道 1、6、11 上,设备自动扫描又慢,导致连接不稳定。
我的做法:在升级前做一次信道扫描,选干扰最小的信道连接。如果信号低于 -75 dBm,直接弹窗提示用户“Wi-Fi 信号弱,建议靠近路由器”。别让设备在弱信号下硬扛,重传会拖死整个升级流程。
4.2 TCP 连接超时与重传机制
Wi-Fi 信号没问题,但升级还是断?那就要看 TCP 层了。说白了,TCP 重传是网络自愈机制,但重传次数太多、超时太长,升级就会卡死。
我习惯在固件里抓三个关键指标:
- 重传率:重传包数 / 总发包数。超过 5% 就要警惕。
- RTO(重传超时):初始一般是 1 秒,指数退避到 64 秒封顶。
- 零窗口事件:接收方缓冲区满了,发送方被迫等待。
为什么会重传?最常见的原因是 Wi-Fi 丢包。你想想看,TCP 发送一个数据段,等不到 ACK,就会重传。如果连续重传 3 次以上,RTO 会指数增长,从 1 秒到 2 秒、4 秒、8 秒……直到 64 秒。这段时间里,升级进度条一动不动。
我曾经踩过的坑:某款芯片的 TCP 重传阈值默认是 15 次。在弱信号下,一个包重传 15 次才放弃,每次间隔指数增长,总耗时超过 2 分钟。升级任务被阻塞,看门狗都来不及喂。后来我把重传阈值改成 5 次,配合应用层快速重连,问题就解决了。
排查方法:用 tcpdump 抓包,导入 Wireshark 看重传分布。重点关注:
- 是否有大量
TCP Retransmission或TCP Fast Retransmission - RTO 是否频繁触发指数退避
- 是否有
TCP ZeroWindow或TCP Window Full
# 抓取升级过程中的 TCP 流量
tcpdump -i wlan0 host 192.168.1.100 and port 8883 -w upgrade.pcap
# 用 tshark 统计重传率
tshark -r upgrade.pcap -q -z io,stat,1,"COUNT(tcp.analysis.retransmission) tcp.analysis.retransmission"
4.3 MQTT/HTTP 协议层面的中断处理
TCP 层没问题,但应用层还是断?嗯,这里要注意:MQTT 和 HTTP 有自己的心跳和超时机制,跟 TCP 的 Keep-Alive 不是一回事。
MQTT 层面
MQTT 靠 PINGREQ/PINGRESP 保活。我建议:
- 心跳间隔:设为 30 秒,比服务器超时时间短一半(比如服务器 60 秒断开,客户端就 30 秒发一次心跳)。
- 会话清理:升级场景用
Clean Session = false,配合Session Expiry Interval,断连后恢复会话,不用重新订阅。 - QoS 选择:固件升级包建议用 QoS 1,至少保证一次送达。QoS 0 丢包不重发,升级包缺了块就麻烦了。
避坑指南:我曾经遇到一个案例,设备 MQTT 心跳正常,但升级到一半就断连。抓包发现是 Broker 的 keepalive 设置比客户端短。客户端 60 秒发一次心跳,Broker 45 秒就超时断开了。两边参数没对齐,这种问题查起来最头疼。
HTTP 层面
HTTP 升级通常用分块下载或断点续传。我建议:
- 连接超时:HTTP 客户端超时设为 10 秒,不要用默认的 60 秒。10 秒没响应就重试,比傻等快得多。
- 断点续传:用
Range头实现。每次重连后从上次中断的偏移量继续下载,不用从头开始。 - 重试策略:指数退避 + 随机抖动。第一次重试等 1 秒,第二次 2 秒,第三次 4 秒……最多 5 次。加随机抖动是为了避免多个设备同时重试,打崩服务器。
// HTTP 断点续传示例(伪代码)
int retry_count = 0;
int max_retries = 5;
int base_delay = 1000; // 1秒
while (retry_count < max_retries) {
http_response = http_get(url, range_start);
if (http_response.status == 200 || http_response.status == 206) {
// 下载成功,写入 flash
break;
} else if (http_response.status == 408 || http_response.status >= 500) {
// 超时或服务器错误,重试
retry_count++;
delay = base_delay * (1 << retry_count) + random(0, 500);
sleep(delay);
} else {
// 其他错误,直接退出
break;
}
}
我的习惯:在固件里加一个“通信健康度”日志。每次升级结束后,把 Wi-Fi 信号、TCP 重传率、MQTT 断连次数、HTTP 重试次数都记录下来。这样下次出问题,直接看日志就能定位是哪一层的问题,不用从头抓包。
总结一下:Wi-Fi 信号差就提示用户或自动切换信道;TCP 重传多就调低重传阈值、缩短超时;MQTT/HTTP 断连就用心跳和断点续传。三层都守住,升级中断的概率能降 80% 以上。