高速公路的监控大屏上一排绿色图标中间忽然跳出一个红色感叹号——某辆测试车的控制信号中断了。工程师小李盯着大屏心里默数一秒、两秒……五秒后红色图标又变回绿色。他打开日志发现这已经是今天第七次了每次持续 2 到 8 秒不等间隔 3 到 5 分钟规律得像闹钟。他拿起对讲机问现场T-Box 信号怎么样两张卡都满格。前排的测试工程师拿着手机比划4G 信号和 5G 信号显示都是五格。这个 bug 卡了两周。每次排查都绕进同一个死循环车端信号正常、网络层抓包没丢包、重启 T-Box 无效、换路段无效。直到后来用ip monitor address盯着网卡 IP 变化才找到了真正的凶手——不是信号不是网络是传输层的一个根本性缺陷。而最终的解法和所有人最初的直觉完全相反。第一章双卡的三种典型组合以及为什么信号格骗了你1.1 T-Box 双卡的三种常见部署方式T-Box 上的双 SIM 卡不是简单地插两张卡那么简单不同的运营商组合决定了完全不同的信号行为。工程上常见三种说明以下提到的5G均指5G/4G自适应模组/SIM卡——模组会根据信号强度在 5G NR 和 4G LTE 之间自动切换上层看到的是一张5G卡但底层随时可能在跑 4G。这个细节在后续 IP 变化分析中至关重要。组合一移动5G 联通5G最常见两张卡来自不同运营商分别是移动和联通的 5G/4G 自适应模组。这是覆盖互补最好的组合——移动覆盖农村和高速联通覆盖城区和地铁两家的 5G 基站建设侧重点不同双卡叠加后盲区最少。但不同运营商意味着两张卡处于两家完全独立的 CGNAT 网络每张卡都有自己的 IP 地址IP 地址空间互不重叠。这是后续一切问题的根源。组合二移动5G 联通4G准确说5G卡 4G模组两张卡的套餐都是 5G 卡但联通侧的 T-Box 硬件槽位用的是 4G 模组而不是 5G 模组。4G 模组比 5G 模组便宜约 50%在硬件 BOM 成本敏感的 T-Box 设计里这是常见的降本选择。5G 卡插入 4G 模组只能跑 4G 速率但覆盖范围和组合一一样好。这里顺带给出两种制式的性能参考制式空口理论延迟实测延迟下行实测带宽4G LTE~30-50ms40-80ms10-50Mbps5G NR~1-10ms10-30ms20-100Mbps注意5G 的实测带宽上限100Mbps并没有宣传的那么夸张在城区密集覆盖区才能跑到高值高速公路上通常在 30-60Mbps 区间。对控制信号50Kbps来说两种制式的带宽都绰绰有余真正的差异在延迟。组合三移动4G 联通4G两个槽位都用 4G 模组成本最低。信号行为最稳定但峰值带宽和延迟都受限于 4G。重要说明三种组合的业务场景完全相同。这不是不同场景选不同方案——而是业界不同厂商根据成本和硬件供应链做出的不同选型最终跑在车上的业务都一样云控指令、OTA 升级、诊断数据、日志上报一个都不少。本文后续分析的断连问题三种组合都会遇到只是组合一的 5G 路径延迟更低、信号降级概率略有差异。三种组合的共同点两张卡属于不同运营商IP 地址可能在你没有察觉的情况下悄悄变化。实测关键发现深圳5G/4G自适应模组这里有一个被大多数文章忽略的细节直接影响断连根因分析。在深圳的实测结果4G → 5G 升级模组内网口 IP不变TCP 连接通常能保持5G → 4G 降级模组内网口 IP会变TCP 连接必然断开两个关键数字5G 降级到 4G 的概率并不高在深圳这种 5G 覆盖较好的城市实测约在1% 以下。但一旦发生断连率是 **100%**——没有例外。这个组合意味着什么绝大多数时候车辆稳定跑在 5G 上一切正常。但每次进入 5G 覆盖空洞隧道口、高架桥下、地下道附近降级发生IP 变化连接断。工程师小李遇到的每隔 3-5 分钟断一次正好对应测试路段上几个固定的 5G 弱覆盖点——不是随机的基站切换是可预测的地理位置触发的降级事件。换句话说断连不是随机的是确定性的。只要路线固定断连点也固定。这个特征让问题看起来规律得像闹钟。这个发现改变了排查方向与其监控基站切换事件不如直接监控模组内网口 IP 变化# 监控模组内网口 IP 变化rmnet0 是移动模组网卡rmnet1 是联通模组网卡 ip monitor address | grep -E rmnet[01] # 看到 IP 变化的时间戳和控制信号断连日志对比几乎一一对应1.2 信号格是路TCP 连接是车牌号要理解为什么信号满格但连接断需要先把两个概念分开。信号格衡量的是无线电信号强度告诉你这里有没有路。四格信号意味着手机和基站之间有良好的无线链路数据包可以在空气中传输。TCP 连接是完全不同的东西。一个 TCP 连接由五个字段标识源 IP、源端口、目标 IP、目标端口、协议。这就是所谓的五元组。服务端用五元组来区分不同客户端的连接就像快递公司用寄件地址收件地址单号来追踪包裹。现在问题清楚了。当 T-Box 从基站 A 切换到基站 B运营商 CGNAT 重新分配了一个新的公网 IP比如从121.4.0.1变成了221.7.0.2。信号依然满格——路还在。但 TCP 连接的源 IP 变了五元组不再匹配了——旧的车牌号在新路上不认。服务端还在等来自121.4.0.1:45678的数据T-Box 却在用221.7.0.2:45678发包。服务端把这些包当成陌生连接不理。T-Box 也不知道 IP 变了还在用老连接发心跳收不到 ACK。双方陷入沉默直到 keepalive 超时才触发重连。这个过程就像你开车换了号牌收费站系统还在等旧号牌过闸两边都在原地等。用命令眼见为实# 在 T-Box 上实时观察 IP 地址变化rmnet0 是 4G 模组网卡rmnet1 是 5G 模组网卡 ip monitor address # 同时在另一个终端观察已建立的 TCP 连接 watch -n 1 ss -tn state established # 可以看到ip monitor 显示 IP 变化的那一刻ss 里的连接还在但几十秒后会消失第二章传输层切换机制的根本缺陷2.1 TCP 五元组失效的完整时序4G/5G 公网环境下IP 变化的概率有多高这里要区分两个维度维度一降级事件本身发生的频率在深圳这种 5G 覆盖较好的城市5G 降级到 4G 的概率约1% 以下按时间占比维度二降级发生后 IP 变化的概率4G → 5G 升级模组内网口 IP 基本不变IP 变化概率 10%TCP 连接通常能保持5G → 4G 降级模组内网口 IP 大概率变化IP 变化概率 60%-80%TCP 连接必然失效把两个维度合起来**日常行驶中 99% 的时间一切正常但每次进入 5G 弱覆盖区发生降级断连概率高达 60-80%**。断连不频繁但一旦发生就是确定性的而且发生在固定的地理位置——这就是为什么问题规律得像闹钟。失效之后发生什么这里有一个被很多工程师忽视的延迟链IP 变化t0 ↓ T-Box 继续用旧连接发包服务端不响应 ↓ 应用层心跳超时默认 30-60st30s 到 60s ↓ 应用层触发重连 ↓ TCP 三次握手1 RTT ≈ 100ms ↓ TLS 握手1-2 RTT ≈ 200-400ms取决于会话复用 ↓ 应用层重新建立上下文认证、状态同步 ↓ 控制信号恢复发送t 30s 到 90s 后这就是为什么断连会持续 30 秒甚至更长。IP 变化这件事本身只有几百毫秒但上层感知到这件事需要等 keepalive 或者心跳超时。有些团队把心跳间隔调到 5 秒断连感知时间降到了 5 秒但仍然不够。控制信号的 SLA 要求端到端 200ms感知延迟 5 秒是不可接受的。2.2 现有方案为什么都不够好面对这个问题工程上通常会先试几个直觉上合理的方案。以下是真实踩过的坑方案工作原理表面上看实际缺陷双卡冷备份主卡断了切备卡换 default route有备用路径切换感知 2-8s期间控制信号中断还要处理路由切换带来的连接重建应用层心跳每 5s 发一个 ping超时触发重连简单可靠只能感知断连不能预防最差 5s 感知 重连时间共 6-7sMPTCP 内核多路径内核维护多条子流路径失效自动切换内核原生支持零改造移动公网 APN 中间设备剥离 TCP OptionsMP_CAPABLE 握手失败公网 APN 上成功率难以保证工程上不可依赖。 云端需要改造支持对应的mptcp协议。SO_KEEPALIVE内核级 TCP 保活比应用层心跳更底层Linux 默认 keepalive_time 7200s调小也只是感知快一些IP 变化后 keepalive 包也是无效的MPTCP 这条路是最反直觉的坑。工程师通常会这样想MPTCP 是内核原生的多路径传输应该最可靠——为什么在 4G 公网不好用原因很简单MPTCP 的多路径协商依赖 TCP Options 字段MP_CAPABLE、MP_JOIN 等扩展头而移动运营商公网 APN 上的 NAT 和 DPI 设备对不认识的 TCP Options 直接清零。握手包发出去Options 被剥掉服务端收到的是普通 TCP 连接MPTCP 特性根本不生效。这是一个很重要的结论我们后面还会展开MPTCP 是内核态协议反而比用户态 QUIC 更难部署在 4G 公网。2.3 应用层重连风暴一个被忽视的放大效应上面讨论的是单个连接的断连问题。真实场景更糟糕T-Box 上同时跑着多个服务——云控指令通道、OTA 升级通道、诊断数据上报、日志采集。IP 变化的那一刻这些连接全部同时失效。然后每个服务各自感知到断连各自触发重连同时发出 TCP SYN 包。服务端在同一秒内收到来自同一个 T-Box 的四五个 SYN 包每个包还要做 TLS 握手、身份认证、状态同步。这个瞬间的请求峰值可以比正常流量高出 10 倍以上。如果车队有 100 辆测试车同时过了一个基站切换点服务端的 SYN 风暴会更严重。这是一个被很多团队忽视的级联故障风险。# 在 T-Box 上抓 SYN 包观察重连风暴 tcpdump -i rmnet0 tcp[tcpflags] tcp-syn ! 0 -n -l | \ awk {print $1} | cut -d. -f1 | uniq -c # 输出格式每秒的 SYN 包数量 # 正常每秒 0-1 个 # 重连风暴每秒 5-10 个 # 同时抓所有网卡观察是否同时触发 tcpdump -i any tcp[tcpflags] tcp-syn ! 0 -n 2/dev/null | head -50第三章QUIC 为什么天然适合这个场景3.1 Connection ID把连接从四元组里解放出来QUIC 解决 IP 变化问题的方式从根本上不同于 TCP。TCP 连接的标识是五元组这是写死在协议设计里的。IP 地址不仅是路由地址还是连接标识的一部分——这在 1981 年 TCP 诞生的时候是合理的那时候 IP 地址不会变。但移动互联网时代IP 地址随时会变。QUIC 引入了Connection IDCID。CID 是一个独立于 IP 地址和端口之外的连接标识符由双方协商生成嵌在 QUIC 包头里。服务端用 CID 来识别连接不用 IP 地址。用类比来说TCP 连接像车牌号车道绑定的通行证换了车道IP 变了通行证就作废了。QUIC 的 CID 像车辆识别码VIN 码换了车道甚至换了城市服务端还认得出这辆车。IP 变化时发生什么T-Box IP: 121.4.0.1 → 221.7.0.2IP 变化 ↓ T-Box 继续用相同的 CID 发 QUIC 包 ↓ 服务端收到来自新 IP 的包但 CID 一致 ↓ 服务端触发连接迁移Connection Migration流程 ↓ Path Validation双方确认新路径可用 ↓ 连接无缝继续总耗时通常 100msIP 切换感知可以做到 50ms这个过程对应用层完全透明控制信号无需中断。# 验证 QUIC 连接迁移用 Wireshark/tcpdump 观察 IP 变化前后的 Connection ID # QUIC 包都是加密的但可以从 packet number 的连续性判断连接是否迁移成功 tcpdump -i rmnet0 udp port 443 -n -v 2/dev/null | grep length # 连接迁移前后包长度分布应保持一致没有握手包的大包突现3.2 MPTCP 在 4G 公网为什么更难部署现在可以展开解释这个反直觉的结论。MPTCP 协议的多路径特性依赖 TCP 头部的 Options 字段来传递协商信息。具体地说MPTCP 的MP_CAPABLE选项在三次握手时携带 Key用来建立多路径连接。这个过程完全在内核协议栈内完成不需要应用层感知。问题在于移动运营商公网 APN 网络里有大量中间设备——NAT 网关、DPI深度包检测、负载均衡器——这些设备对不认识的 TCP Options 处理方式因厂商而异有的透传、有的清零、有的报错。移动公网 APN 的工程实践情况移动 CMNET公网 APNCGNAT 设备对未知 TCP Options 处理激进MP_CAPABLE 在多数省份被剥离工程上不能依赖 MPTCP 建连联通 3gnet/uninet公网 APN部分省份中间设备相对宽松但同样无法保证稳定透传运营商专线 APN车载专用不经过公网 CGNAT中间设备干扰极少MPTCP 可用性高这里没有精确的运营商级统计数字——目前没有公开的学术论文专门测量中国各运营商 APN 对 MPTCP 的干扰率。全球范围内最权威的测量Honda et al., IMC 2011显示约 29% 的互联网路径会修改 TCP Options而移动运营商公网 APN 的中间设备密度更高工程经验判断干扰率远高于这个全球均值。T-Box 通常用公网 APN成本考虑——专线 APN 每月费用是公网 APN 的 10 倍以上。在公网 APN 上MPTCP 能否生效是运气问题工程上不可依赖。更讽刺的是MPTCP 是操作系统内核级别的协议需要双端内核都支持Linux 5.6 合入主线对应用层完全透明。但恰恰是因为它在内核里、在 TCP 层它的扩展信息暴露在中间设备面前被随意处置。QUIC 的情况完全不同。QUIC 头部是加密的。QUIC 使用 AEAD 加密包头包括任何扩展信息对中间设备不透明中间设备只能看到 UDP 包看不到里面是什么。MPQUICQUIC 的多路径扩展的协商信息同样受到加密保护运营商 DPI 无法识别也无法剥离。这就是 QUIC 相对于 MPTCP 的关键部署优势——不是因为协议设计更好而是因为在运营商网络里活得更好。# 验证 T-Box 的 4G 网络是否剥离 TCP Options # 抓 TCP SYN 包检查 Options 字段 tcpdump -i rmnet0 -v tcp[tcpflags] tcp-syn ! 0 2/dev/null | \ grep -A 2 options # 如果 MPTCP 生效会看到 mptcp unknown-76 或 MP_CAPABLE 之类的 options # 如果被剥离只会看到标准的 MSS/WScale/SACK/Timestamp options # 对比QUIC 包对中间设备完全不透明 tcpdump -i rmnet0 udp port 443 -n -v 2/dev/null | head -20 # 只能看到 UDP 头部信息内容全是加密数据3.3 QUIC 的另一个优势更快的握手顺带提一下QUIC 的握手效率本身也比 TCPTLS 快。TCP 连接需要 1 RTT 三次握手然后 TLS 1.3 需要 1 RTT合计 2 RTT 才能发送第一个应用数据包。如果 RTT 是 100ms重连代价就是 200ms——已经超过了控制信号的 SLA 边界。QUIC 的 1-RTT 握手把 QUIC 层和 TLS 层合并首次连接 1 RTT 就能发应用数据。更重要的是QUIC 支持 0-RTT 会话恢复——如果 T-Box 之前连接过同一台服务器可以直接把第一个请求附在握手包里握手延迟降到接近 0。控制信号重连场景IP 变化后触发用 0-RTT 恢复重连耗时约 10-30ms比 TCPTLS 的 200-600ms 快一个数量级。第四章不做切换做冗余——架构决策的推导4.1 切换思维 vs 冗余思维到这里工程师通常会问既然 QUIC 支持连接迁移IP 变化时自动迁移到新路径问题不就解决了吗还不够。连接迁移是被动的——必须等到 IP 变化、新路径建立、Path Validation 完成之后才能恢复。这个过程虽然比 TCP 重连快很多但在迁移完成之前还是有一段窗口期会丢包或者延迟上升。真正的解决思路是把问题从如何快速切换变成如何不需要切换。切换思维的逻辑链是主路径失效 → 检测到失效有延迟→ 切换到备路径有耗时→ 恢复中间总有一段不可压缩的感知切换窗口。冗余思维的逻辑链是两条路径同时发送 → 云端取先到者 → 任一路径失效 → 另一条已经到了没有感知窗口没有切换耗时从应用层看从未中断。这个转变的关键洞察是对于控制信号带宽不是瓶颈延迟确定性才是。控制信号的典型数据量每秒 10-50 条指令每条 1KB总带宽 50Kbps。双发之后翻倍到 100Kbps。4G 网络带宽是 10-50Mbps弱网环境下实际可用带宽在 2-10Mbps——100Kbps 只占 1%-5%完全可以接受。4.2 冗余发送的工作原理与量化收益冗余发送Redundant Scheduling的架构如下T-Box ├── SIM1 (移动 5G/4G自适应) → QUIC MPQUIC 路径 A ──┐ └── SIM2 (联通 5G/4G自适应) → QUIC MPQUIC 路径 B ──┤→ 云控网关去重 → 应用层 └── 丢弃重复包按 QUIC Packet Number 去重T-Box 端QUIC 库的 MPQUIC 冗余模式会把每个 QUIC 包同时在两条路径上发送两份包携带相同的内容和相同的 Packet Number。云控网关收到两份包后用 Packet Number 做去重——先到的处理后到的丢弃。这个去重窗口一般设为 200ms具体推导见第 02 篇《云控 SLA 的数学》。延迟收益的计算很直接单路径方案延迟 路径 A 的延迟唯一路径 冗余发送方案延迟 min(路径 A 延迟, 路径 B 延迟)假设路径 A RTT 均值 80ms路径 B RTT 均值 120ms各自 P99 150ms 和 200ms单路径 P99150ms走路径 A冗余发送 P99约 90-100ms哪条快用哪条更重要的是尾延迟和断连概率。当路径 A 在基站切换时出现 2-8 秒断连冗余发送方案中路径 B 还在正常工作控制信号从未中断。这才是真正解决了开头的问题。4.3 为什么需要专门的 QUIC 库支持冗余发送实现 MPQUIC 冗余发送需要一个支持 MPQUIC 的 QUIC 库。市场上主流的 QUIC 实现大多还不支持 MPQUIC或者支持程度不够——MPQUIC 至今没有正式 RFC只有草案draft-ietf-quic-multipath各库的实现进度不一。选型时需要重点考察MPQUIC 草案支持程度、ARM 交叉编译可行性、C/C 集成接口是否完整。选型细节各库的功能对比、T-Box ARM 编译踩坑见本系列第 04 篇。4.4 冗余发送的适用边界——它不是万能的诚实地说冗余发送不是所有场景的正确答案。适合用冗余发送的场景控制信号小包1KB、低频10-50次/秒、高可靠要求、延迟 200ms SLA关键状态同步心跳、位置上报不适合用冗余发送的场景视频回传每秒几十 MB双发会直接把 4G 带宽打满这是不可接受的。视频走独立通道见第 03 篇《为什么控制信号和视频走不同的传输通道》OTA 升级文件大双发带宽代价高而且 OTA 不要求低延迟断点续传就够用日志上报大量数据对延迟不敏感两路都断的情况进隧道、地下停车场、或者两家运营商同时网络故障时两条路径都断了冗余发送也没用。这时需要降级策略——本地缓存指令、切换到 WiFi 或短距离通信。这个场景在第 12 篇专门讨论。# 在 T-Box 上验证双路径是否同时在发包 # 方法同时抓两个网卡看是否有相同内容的 UDP 包 # 终端 1抓 SIM14G的 QUIC 包 tcpdump -i rmnet0 udp port 443 -n -l -w /tmp/cap_rmnet0.pcap PID1$! # 终端 2抓 SIM25G的 QUIC 包 tcpdump -i rmnet1 udp port 443 -n -l -w /tmp/cap_rmnet1.pcap PID2$! # 等 30 秒 sleep 30 kill $PID1 $PID2 # 对比两个抓包文件的包大小分布 # 如果冗余发送生效两个文件的包大小分布应高度相似 tcpdump -r /tmp/cap_rmnet0.pcap -n | awk {print $NF} | sort | uniq -c | sort -rn | head -10 tcpdump -r /tmp/cap_rmnet1.pcap -n | awk {print $NF} | sort | uniq -c | sort -rn | head -10第五章把所有方案放在一起比较经过前面四章的分析我们可以给出一张完整的方案对比表。方案切换感知时间部署难度运营商兼容性带宽代价适合云控双卡冷备份2-8 秒低路由脚本好无额外❌ 感知太慢应用层心跳 重连5-30 秒低应用改造好无额外❌ 感知太慢TCP SO_KEEPALIVE10-120 秒低sysctl好无额外❌ 更慢MPTCP 内核多路径500ms理论高内核版本双端支持差公网 APN 不可依赖无额外❌ 运营商兼容性差QUIC 连接迁移100ms中QUIC 库集成好UDP 加密无额外勉强可用QUIC MPQUIC 冗余发送0ms无感知中QUIC 库 服务端去重好UDP 加密2x 控制信号带宽✅感知时间 0ms不是夸张是架构上的精确描述冗余发送不需要感知路径失效因为另一条路径已经先到了。结尾回到高速公路上的那个场景。测试车的控制信号为什么每隔 3-5 分钟断一次因为高速公路上每隔几公里就有一次基站切换切换时 CGNAT 重新分配 IPTCP 五元组失效应用层要等 30 秒才感知到然后再花 500ms 重连。信号格一直是五格但 TCP 连接已经死了。最终的解法是把 T-Box 上的云控指令通道换成 QUIC MPQUIC 冗余发送模式——4G 和 5G 双路同时发云端网关去重取先到者。某条路径切换的那段时间另一条路径的包已经到了控制信号从未中断。这个解法改变了问题的思路框架**不是切换多快而是需不需要切换**。但这只是解决了控制信号会不会断的问题。下一个问题是就算连接不断200ms 的端到端 SLA 传输层能保证吗实际上传输层在延迟预算里能用的空间可能只有 20-60ms——因为 qdisc 队列和进程调度才是真正的大头。这个延迟分解见下一篇《云控 SLA 的数学200ms 端到端延迟预算怎么分配给传输层》。如果你也遇到过类似的断连问题欢迎在留言区分享你们的排查思路——是什么帮你找到了根因
T-Box双SIM卡的“幻觉“:为什么有两张卡却还在断连?
发布时间:2026/6/6 4:14:23
高速公路的监控大屏上一排绿色图标中间忽然跳出一个红色感叹号——某辆测试车的控制信号中断了。工程师小李盯着大屏心里默数一秒、两秒……五秒后红色图标又变回绿色。他打开日志发现这已经是今天第七次了每次持续 2 到 8 秒不等间隔 3 到 5 分钟规律得像闹钟。他拿起对讲机问现场T-Box 信号怎么样两张卡都满格。前排的测试工程师拿着手机比划4G 信号和 5G 信号显示都是五格。这个 bug 卡了两周。每次排查都绕进同一个死循环车端信号正常、网络层抓包没丢包、重启 T-Box 无效、换路段无效。直到后来用ip monitor address盯着网卡 IP 变化才找到了真正的凶手——不是信号不是网络是传输层的一个根本性缺陷。而最终的解法和所有人最初的直觉完全相反。第一章双卡的三种典型组合以及为什么信号格骗了你1.1 T-Box 双卡的三种常见部署方式T-Box 上的双 SIM 卡不是简单地插两张卡那么简单不同的运营商组合决定了完全不同的信号行为。工程上常见三种说明以下提到的5G均指5G/4G自适应模组/SIM卡——模组会根据信号强度在 5G NR 和 4G LTE 之间自动切换上层看到的是一张5G卡但底层随时可能在跑 4G。这个细节在后续 IP 变化分析中至关重要。组合一移动5G 联通5G最常见两张卡来自不同运营商分别是移动和联通的 5G/4G 自适应模组。这是覆盖互补最好的组合——移动覆盖农村和高速联通覆盖城区和地铁两家的 5G 基站建设侧重点不同双卡叠加后盲区最少。但不同运营商意味着两张卡处于两家完全独立的 CGNAT 网络每张卡都有自己的 IP 地址IP 地址空间互不重叠。这是后续一切问题的根源。组合二移动5G 联通4G准确说5G卡 4G模组两张卡的套餐都是 5G 卡但联通侧的 T-Box 硬件槽位用的是 4G 模组而不是 5G 模组。4G 模组比 5G 模组便宜约 50%在硬件 BOM 成本敏感的 T-Box 设计里这是常见的降本选择。5G 卡插入 4G 模组只能跑 4G 速率但覆盖范围和组合一一样好。这里顺带给出两种制式的性能参考制式空口理论延迟实测延迟下行实测带宽4G LTE~30-50ms40-80ms10-50Mbps5G NR~1-10ms10-30ms20-100Mbps注意5G 的实测带宽上限100Mbps并没有宣传的那么夸张在城区密集覆盖区才能跑到高值高速公路上通常在 30-60Mbps 区间。对控制信号50Kbps来说两种制式的带宽都绰绰有余真正的差异在延迟。组合三移动4G 联通4G两个槽位都用 4G 模组成本最低。信号行为最稳定但峰值带宽和延迟都受限于 4G。重要说明三种组合的业务场景完全相同。这不是不同场景选不同方案——而是业界不同厂商根据成本和硬件供应链做出的不同选型最终跑在车上的业务都一样云控指令、OTA 升级、诊断数据、日志上报一个都不少。本文后续分析的断连问题三种组合都会遇到只是组合一的 5G 路径延迟更低、信号降级概率略有差异。三种组合的共同点两张卡属于不同运营商IP 地址可能在你没有察觉的情况下悄悄变化。实测关键发现深圳5G/4G自适应模组这里有一个被大多数文章忽略的细节直接影响断连根因分析。在深圳的实测结果4G → 5G 升级模组内网口 IP不变TCP 连接通常能保持5G → 4G 降级模组内网口 IP会变TCP 连接必然断开两个关键数字5G 降级到 4G 的概率并不高在深圳这种 5G 覆盖较好的城市实测约在1% 以下。但一旦发生断连率是 **100%**——没有例外。这个组合意味着什么绝大多数时候车辆稳定跑在 5G 上一切正常。但每次进入 5G 覆盖空洞隧道口、高架桥下、地下道附近降级发生IP 变化连接断。工程师小李遇到的每隔 3-5 分钟断一次正好对应测试路段上几个固定的 5G 弱覆盖点——不是随机的基站切换是可预测的地理位置触发的降级事件。换句话说断连不是随机的是确定性的。只要路线固定断连点也固定。这个特征让问题看起来规律得像闹钟。这个发现改变了排查方向与其监控基站切换事件不如直接监控模组内网口 IP 变化# 监控模组内网口 IP 变化rmnet0 是移动模组网卡rmnet1 是联通模组网卡 ip monitor address | grep -E rmnet[01] # 看到 IP 变化的时间戳和控制信号断连日志对比几乎一一对应1.2 信号格是路TCP 连接是车牌号要理解为什么信号满格但连接断需要先把两个概念分开。信号格衡量的是无线电信号强度告诉你这里有没有路。四格信号意味着手机和基站之间有良好的无线链路数据包可以在空气中传输。TCP 连接是完全不同的东西。一个 TCP 连接由五个字段标识源 IP、源端口、目标 IP、目标端口、协议。这就是所谓的五元组。服务端用五元组来区分不同客户端的连接就像快递公司用寄件地址收件地址单号来追踪包裹。现在问题清楚了。当 T-Box 从基站 A 切换到基站 B运营商 CGNAT 重新分配了一个新的公网 IP比如从121.4.0.1变成了221.7.0.2。信号依然满格——路还在。但 TCP 连接的源 IP 变了五元组不再匹配了——旧的车牌号在新路上不认。服务端还在等来自121.4.0.1:45678的数据T-Box 却在用221.7.0.2:45678发包。服务端把这些包当成陌生连接不理。T-Box 也不知道 IP 变了还在用老连接发心跳收不到 ACK。双方陷入沉默直到 keepalive 超时才触发重连。这个过程就像你开车换了号牌收费站系统还在等旧号牌过闸两边都在原地等。用命令眼见为实# 在 T-Box 上实时观察 IP 地址变化rmnet0 是 4G 模组网卡rmnet1 是 5G 模组网卡 ip monitor address # 同时在另一个终端观察已建立的 TCP 连接 watch -n 1 ss -tn state established # 可以看到ip monitor 显示 IP 变化的那一刻ss 里的连接还在但几十秒后会消失第二章传输层切换机制的根本缺陷2.1 TCP 五元组失效的完整时序4G/5G 公网环境下IP 变化的概率有多高这里要区分两个维度维度一降级事件本身发生的频率在深圳这种 5G 覆盖较好的城市5G 降级到 4G 的概率约1% 以下按时间占比维度二降级发生后 IP 变化的概率4G → 5G 升级模组内网口 IP 基本不变IP 变化概率 10%TCP 连接通常能保持5G → 4G 降级模组内网口 IP 大概率变化IP 变化概率 60%-80%TCP 连接必然失效把两个维度合起来**日常行驶中 99% 的时间一切正常但每次进入 5G 弱覆盖区发生降级断连概率高达 60-80%**。断连不频繁但一旦发生就是确定性的而且发生在固定的地理位置——这就是为什么问题规律得像闹钟。失效之后发生什么这里有一个被很多工程师忽视的延迟链IP 变化t0 ↓ T-Box 继续用旧连接发包服务端不响应 ↓ 应用层心跳超时默认 30-60st30s 到 60s ↓ 应用层触发重连 ↓ TCP 三次握手1 RTT ≈ 100ms ↓ TLS 握手1-2 RTT ≈ 200-400ms取决于会话复用 ↓ 应用层重新建立上下文认证、状态同步 ↓ 控制信号恢复发送t 30s 到 90s 后这就是为什么断连会持续 30 秒甚至更长。IP 变化这件事本身只有几百毫秒但上层感知到这件事需要等 keepalive 或者心跳超时。有些团队把心跳间隔调到 5 秒断连感知时间降到了 5 秒但仍然不够。控制信号的 SLA 要求端到端 200ms感知延迟 5 秒是不可接受的。2.2 现有方案为什么都不够好面对这个问题工程上通常会先试几个直觉上合理的方案。以下是真实踩过的坑方案工作原理表面上看实际缺陷双卡冷备份主卡断了切备卡换 default route有备用路径切换感知 2-8s期间控制信号中断还要处理路由切换带来的连接重建应用层心跳每 5s 发一个 ping超时触发重连简单可靠只能感知断连不能预防最差 5s 感知 重连时间共 6-7sMPTCP 内核多路径内核维护多条子流路径失效自动切换内核原生支持零改造移动公网 APN 中间设备剥离 TCP OptionsMP_CAPABLE 握手失败公网 APN 上成功率难以保证工程上不可依赖。 云端需要改造支持对应的mptcp协议。SO_KEEPALIVE内核级 TCP 保活比应用层心跳更底层Linux 默认 keepalive_time 7200s调小也只是感知快一些IP 变化后 keepalive 包也是无效的MPTCP 这条路是最反直觉的坑。工程师通常会这样想MPTCP 是内核原生的多路径传输应该最可靠——为什么在 4G 公网不好用原因很简单MPTCP 的多路径协商依赖 TCP Options 字段MP_CAPABLE、MP_JOIN 等扩展头而移动运营商公网 APN 上的 NAT 和 DPI 设备对不认识的 TCP Options 直接清零。握手包发出去Options 被剥掉服务端收到的是普通 TCP 连接MPTCP 特性根本不生效。这是一个很重要的结论我们后面还会展开MPTCP 是内核态协议反而比用户态 QUIC 更难部署在 4G 公网。2.3 应用层重连风暴一个被忽视的放大效应上面讨论的是单个连接的断连问题。真实场景更糟糕T-Box 上同时跑着多个服务——云控指令通道、OTA 升级通道、诊断数据上报、日志采集。IP 变化的那一刻这些连接全部同时失效。然后每个服务各自感知到断连各自触发重连同时发出 TCP SYN 包。服务端在同一秒内收到来自同一个 T-Box 的四五个 SYN 包每个包还要做 TLS 握手、身份认证、状态同步。这个瞬间的请求峰值可以比正常流量高出 10 倍以上。如果车队有 100 辆测试车同时过了一个基站切换点服务端的 SYN 风暴会更严重。这是一个被很多团队忽视的级联故障风险。# 在 T-Box 上抓 SYN 包观察重连风暴 tcpdump -i rmnet0 tcp[tcpflags] tcp-syn ! 0 -n -l | \ awk {print $1} | cut -d. -f1 | uniq -c # 输出格式每秒的 SYN 包数量 # 正常每秒 0-1 个 # 重连风暴每秒 5-10 个 # 同时抓所有网卡观察是否同时触发 tcpdump -i any tcp[tcpflags] tcp-syn ! 0 -n 2/dev/null | head -50第三章QUIC 为什么天然适合这个场景3.1 Connection ID把连接从四元组里解放出来QUIC 解决 IP 变化问题的方式从根本上不同于 TCP。TCP 连接的标识是五元组这是写死在协议设计里的。IP 地址不仅是路由地址还是连接标识的一部分——这在 1981 年 TCP 诞生的时候是合理的那时候 IP 地址不会变。但移动互联网时代IP 地址随时会变。QUIC 引入了Connection IDCID。CID 是一个独立于 IP 地址和端口之外的连接标识符由双方协商生成嵌在 QUIC 包头里。服务端用 CID 来识别连接不用 IP 地址。用类比来说TCP 连接像车牌号车道绑定的通行证换了车道IP 变了通行证就作废了。QUIC 的 CID 像车辆识别码VIN 码换了车道甚至换了城市服务端还认得出这辆车。IP 变化时发生什么T-Box IP: 121.4.0.1 → 221.7.0.2IP 变化 ↓ T-Box 继续用相同的 CID 发 QUIC 包 ↓ 服务端收到来自新 IP 的包但 CID 一致 ↓ 服务端触发连接迁移Connection Migration流程 ↓ Path Validation双方确认新路径可用 ↓ 连接无缝继续总耗时通常 100msIP 切换感知可以做到 50ms这个过程对应用层完全透明控制信号无需中断。# 验证 QUIC 连接迁移用 Wireshark/tcpdump 观察 IP 变化前后的 Connection ID # QUIC 包都是加密的但可以从 packet number 的连续性判断连接是否迁移成功 tcpdump -i rmnet0 udp port 443 -n -v 2/dev/null | grep length # 连接迁移前后包长度分布应保持一致没有握手包的大包突现3.2 MPTCP 在 4G 公网为什么更难部署现在可以展开解释这个反直觉的结论。MPTCP 协议的多路径特性依赖 TCP 头部的 Options 字段来传递协商信息。具体地说MPTCP 的MP_CAPABLE选项在三次握手时携带 Key用来建立多路径连接。这个过程完全在内核协议栈内完成不需要应用层感知。问题在于移动运营商公网 APN 网络里有大量中间设备——NAT 网关、DPI深度包检测、负载均衡器——这些设备对不认识的 TCP Options 处理方式因厂商而异有的透传、有的清零、有的报错。移动公网 APN 的工程实践情况移动 CMNET公网 APNCGNAT 设备对未知 TCP Options 处理激进MP_CAPABLE 在多数省份被剥离工程上不能依赖 MPTCP 建连联通 3gnet/uninet公网 APN部分省份中间设备相对宽松但同样无法保证稳定透传运营商专线 APN车载专用不经过公网 CGNAT中间设备干扰极少MPTCP 可用性高这里没有精确的运营商级统计数字——目前没有公开的学术论文专门测量中国各运营商 APN 对 MPTCP 的干扰率。全球范围内最权威的测量Honda et al., IMC 2011显示约 29% 的互联网路径会修改 TCP Options而移动运营商公网 APN 的中间设备密度更高工程经验判断干扰率远高于这个全球均值。T-Box 通常用公网 APN成本考虑——专线 APN 每月费用是公网 APN 的 10 倍以上。在公网 APN 上MPTCP 能否生效是运气问题工程上不可依赖。更讽刺的是MPTCP 是操作系统内核级别的协议需要双端内核都支持Linux 5.6 合入主线对应用层完全透明。但恰恰是因为它在内核里、在 TCP 层它的扩展信息暴露在中间设备面前被随意处置。QUIC 的情况完全不同。QUIC 头部是加密的。QUIC 使用 AEAD 加密包头包括任何扩展信息对中间设备不透明中间设备只能看到 UDP 包看不到里面是什么。MPQUICQUIC 的多路径扩展的协商信息同样受到加密保护运营商 DPI 无法识别也无法剥离。这就是 QUIC 相对于 MPTCP 的关键部署优势——不是因为协议设计更好而是因为在运营商网络里活得更好。# 验证 T-Box 的 4G 网络是否剥离 TCP Options # 抓 TCP SYN 包检查 Options 字段 tcpdump -i rmnet0 -v tcp[tcpflags] tcp-syn ! 0 2/dev/null | \ grep -A 2 options # 如果 MPTCP 生效会看到 mptcp unknown-76 或 MP_CAPABLE 之类的 options # 如果被剥离只会看到标准的 MSS/WScale/SACK/Timestamp options # 对比QUIC 包对中间设备完全不透明 tcpdump -i rmnet0 udp port 443 -n -v 2/dev/null | head -20 # 只能看到 UDP 头部信息内容全是加密数据3.3 QUIC 的另一个优势更快的握手顺带提一下QUIC 的握手效率本身也比 TCPTLS 快。TCP 连接需要 1 RTT 三次握手然后 TLS 1.3 需要 1 RTT合计 2 RTT 才能发送第一个应用数据包。如果 RTT 是 100ms重连代价就是 200ms——已经超过了控制信号的 SLA 边界。QUIC 的 1-RTT 握手把 QUIC 层和 TLS 层合并首次连接 1 RTT 就能发应用数据。更重要的是QUIC 支持 0-RTT 会话恢复——如果 T-Box 之前连接过同一台服务器可以直接把第一个请求附在握手包里握手延迟降到接近 0。控制信号重连场景IP 变化后触发用 0-RTT 恢复重连耗时约 10-30ms比 TCPTLS 的 200-600ms 快一个数量级。第四章不做切换做冗余——架构决策的推导4.1 切换思维 vs 冗余思维到这里工程师通常会问既然 QUIC 支持连接迁移IP 变化时自动迁移到新路径问题不就解决了吗还不够。连接迁移是被动的——必须等到 IP 变化、新路径建立、Path Validation 完成之后才能恢复。这个过程虽然比 TCP 重连快很多但在迁移完成之前还是有一段窗口期会丢包或者延迟上升。真正的解决思路是把问题从如何快速切换变成如何不需要切换。切换思维的逻辑链是主路径失效 → 检测到失效有延迟→ 切换到备路径有耗时→ 恢复中间总有一段不可压缩的感知切换窗口。冗余思维的逻辑链是两条路径同时发送 → 云端取先到者 → 任一路径失效 → 另一条已经到了没有感知窗口没有切换耗时从应用层看从未中断。这个转变的关键洞察是对于控制信号带宽不是瓶颈延迟确定性才是。控制信号的典型数据量每秒 10-50 条指令每条 1KB总带宽 50Kbps。双发之后翻倍到 100Kbps。4G 网络带宽是 10-50Mbps弱网环境下实际可用带宽在 2-10Mbps——100Kbps 只占 1%-5%完全可以接受。4.2 冗余发送的工作原理与量化收益冗余发送Redundant Scheduling的架构如下T-Box ├── SIM1 (移动 5G/4G自适应) → QUIC MPQUIC 路径 A ──┐ └── SIM2 (联通 5G/4G自适应) → QUIC MPQUIC 路径 B ──┤→ 云控网关去重 → 应用层 └── 丢弃重复包按 QUIC Packet Number 去重T-Box 端QUIC 库的 MPQUIC 冗余模式会把每个 QUIC 包同时在两条路径上发送两份包携带相同的内容和相同的 Packet Number。云控网关收到两份包后用 Packet Number 做去重——先到的处理后到的丢弃。这个去重窗口一般设为 200ms具体推导见第 02 篇《云控 SLA 的数学》。延迟收益的计算很直接单路径方案延迟 路径 A 的延迟唯一路径 冗余发送方案延迟 min(路径 A 延迟, 路径 B 延迟)假设路径 A RTT 均值 80ms路径 B RTT 均值 120ms各自 P99 150ms 和 200ms单路径 P99150ms走路径 A冗余发送 P99约 90-100ms哪条快用哪条更重要的是尾延迟和断连概率。当路径 A 在基站切换时出现 2-8 秒断连冗余发送方案中路径 B 还在正常工作控制信号从未中断。这才是真正解决了开头的问题。4.3 为什么需要专门的 QUIC 库支持冗余发送实现 MPQUIC 冗余发送需要一个支持 MPQUIC 的 QUIC 库。市场上主流的 QUIC 实现大多还不支持 MPQUIC或者支持程度不够——MPQUIC 至今没有正式 RFC只有草案draft-ietf-quic-multipath各库的实现进度不一。选型时需要重点考察MPQUIC 草案支持程度、ARM 交叉编译可行性、C/C 集成接口是否完整。选型细节各库的功能对比、T-Box ARM 编译踩坑见本系列第 04 篇。4.4 冗余发送的适用边界——它不是万能的诚实地说冗余发送不是所有场景的正确答案。适合用冗余发送的场景控制信号小包1KB、低频10-50次/秒、高可靠要求、延迟 200ms SLA关键状态同步心跳、位置上报不适合用冗余发送的场景视频回传每秒几十 MB双发会直接把 4G 带宽打满这是不可接受的。视频走独立通道见第 03 篇《为什么控制信号和视频走不同的传输通道》OTA 升级文件大双发带宽代价高而且 OTA 不要求低延迟断点续传就够用日志上报大量数据对延迟不敏感两路都断的情况进隧道、地下停车场、或者两家运营商同时网络故障时两条路径都断了冗余发送也没用。这时需要降级策略——本地缓存指令、切换到 WiFi 或短距离通信。这个场景在第 12 篇专门讨论。# 在 T-Box 上验证双路径是否同时在发包 # 方法同时抓两个网卡看是否有相同内容的 UDP 包 # 终端 1抓 SIM14G的 QUIC 包 tcpdump -i rmnet0 udp port 443 -n -l -w /tmp/cap_rmnet0.pcap PID1$! # 终端 2抓 SIM25G的 QUIC 包 tcpdump -i rmnet1 udp port 443 -n -l -w /tmp/cap_rmnet1.pcap PID2$! # 等 30 秒 sleep 30 kill $PID1 $PID2 # 对比两个抓包文件的包大小分布 # 如果冗余发送生效两个文件的包大小分布应高度相似 tcpdump -r /tmp/cap_rmnet0.pcap -n | awk {print $NF} | sort | uniq -c | sort -rn | head -10 tcpdump -r /tmp/cap_rmnet1.pcap -n | awk {print $NF} | sort | uniq -c | sort -rn | head -10第五章把所有方案放在一起比较经过前面四章的分析我们可以给出一张完整的方案对比表。方案切换感知时间部署难度运营商兼容性带宽代价适合云控双卡冷备份2-8 秒低路由脚本好无额外❌ 感知太慢应用层心跳 重连5-30 秒低应用改造好无额外❌ 感知太慢TCP SO_KEEPALIVE10-120 秒低sysctl好无额外❌ 更慢MPTCP 内核多路径500ms理论高内核版本双端支持差公网 APN 不可依赖无额外❌ 运营商兼容性差QUIC 连接迁移100ms中QUIC 库集成好UDP 加密无额外勉强可用QUIC MPQUIC 冗余发送0ms无感知中QUIC 库 服务端去重好UDP 加密2x 控制信号带宽✅感知时间 0ms不是夸张是架构上的精确描述冗余发送不需要感知路径失效因为另一条路径已经先到了。结尾回到高速公路上的那个场景。测试车的控制信号为什么每隔 3-5 分钟断一次因为高速公路上每隔几公里就有一次基站切换切换时 CGNAT 重新分配 IPTCP 五元组失效应用层要等 30 秒才感知到然后再花 500ms 重连。信号格一直是五格但 TCP 连接已经死了。最终的解法是把 T-Box 上的云控指令通道换成 QUIC MPQUIC 冗余发送模式——4G 和 5G 双路同时发云端网关去重取先到者。某条路径切换的那段时间另一条路径的包已经到了控制信号从未中断。这个解法改变了问题的思路框架**不是切换多快而是需不需要切换**。但这只是解决了控制信号会不会断的问题。下一个问题是就算连接不断200ms 的端到端 SLA 传输层能保证吗实际上传输层在延迟预算里能用的空间可能只有 20-60ms——因为 qdisc 队列和进程调度才是真正的大头。这个延迟分解见下一篇《云控 SLA 的数学200ms 端到端延迟预算怎么分配给传输层》。如果你也遇到过类似的断连问题欢迎在留言区分享你们的排查思路——是什么帮你找到了根因