网络不稳定因素:丢包、延迟、乱序、带宽波动、NAT穿透
做跨网络Socket通信,说白了就是跟「不确定性」打交道。你写的代码再漂亮,网络一抖,全白搭。我这些年踩过的坑,十有八九都跟下面这几个因素有关。
咱们一个一个说清楚。
2.1 丢包——最直接的杀手
丢包,就是数据包发出去,对方没收到。原因很多:线路噪声、路由器缓存满了、中间节点直接丢弃。TCP遇到丢包会重传,但重传带来延迟。UDP更惨——丢了就丢了,应用层不处理就是数据空洞。
核心影响:
- TCP吞吐量断崖式下降(拥塞控制窗口减半)
- UDP实时流出现花屏、卡顿、静音
- 重传风暴可能拖垮整个链路
我的经验:有一次做跨国视频会议,丢包率从0.1%跳到3%,画面直接变成马赛克。后来加了前向纠错(FEC),丢包率容忍度提到了5%左右,效果立竿见影。
2.2 延迟——看不见的墙
延迟就是数据包从A到B的时间。局域网延迟通常1ms以内,跨省可能20-50ms,跨国轻松200ms+。延迟高了,TCP的握手、慢启动、拥塞控制全受影响。
为什么会这样?因为光速是物理极限,绕地球半圈就要66ms。再加上路由器处理、排队、协议栈开销,200ms很常见。
| 场景 | 典型延迟 | 对Socket的影响 |
|---|---|---|
| 同机房内网 | 0.1-0.5ms | 几乎无感 |
| 同城跨运营商 | 5-20ms | TCP慢启动效率下降 |
| 跨省骨干网 | 30-80ms | 实时交互开始有卡顿感 |
| 跨国(中美) | 150-300ms | 必须做延迟补偿和预测 |
注意:延迟不是固定值。晚高峰、海底光缆故障、路由绕路都会让延迟飙升。我曾经见过中美链路从180ms跳到800ms,持续了半小时。如果应用层不做超时重试和降级,用户直接断连。
2.3 乱序——TCP也扛不住
乱序,就是先发的包后到,后发的包先到。TCP虽然能重组,但乱序会触发快速重传——接收端收到乱序包会发重复ACK,发送端以为丢包了,赶紧重传。结果呢?带宽浪费,吞吐量下降。
UDP更直接——乱序就是乱序,应用层自己排。
乱序的典型场景:
- 多路径传输(MPTCP、多网卡绑定)
- 负载均衡器分发到不同后端
- 无线网络切换(WiFi切4G)
我个人习惯在应用层加序列号,不管TCP还是UDP。TCP虽然有序,但序列号能帮你快速定位乱序程度,方便做监控和告警。
2.4 带宽波动——忽快忽慢
带宽不是固定的。共享链路、突发流量、运营商限速,都会让可用带宽像过山车。TCP的拥塞控制会主动探测带宽,但反应慢——先升后降,来回震荡。
UDP如果不做流控,直接往链路上猛发,结果就是路由器丢包,然后应用层收到一堆空洞。
我曾经踩过的坑:一个文件传输服务,用了默认TCP,带宽从100Mbps掉到2Mbps,用户抱怨传个10MB文件要40秒。后来加了带宽探测和动态调整并发数,问题解决。说白了,别让协议自己瞎猜,应用层要主动感知。
2.5 NAT穿透——最难搞的拦路虎
NAT(网络地址转换)让内网设备共用公网IP。但P2P通信时,两边都在NAT后面,直接连不上。常见的NAT类型有四种:
| NAT类型 | 特点 | 穿透难度 |
|---|---|---|
| 完全锥形 | 映射后任何外网IP都能访问 | 容易 |
| 限制锥形 | 只允许曾经通信过的IP访问 | 中等 |
| 端口限制锥形 | 限制IP+端口 | 较难 |
| 对称NAT | 每个目标IP+端口都新建映射 | 极难 |
对称NAT基本没法用传统打洞方式穿透。我建议的方案:先用STUN探测NAT类型,对称NAT直接走中继(TURN),别浪费时间打洞。
注意:NAT穿透不是一次性的。NAT映射有超时时间,通常30秒到5分钟不等。如果长时间没心跳,映射会失效。我曾经遇到过打洞成功,但30秒没发数据,映射就掉了,连接中断。解决方案是定期发心跳包,保持映射存活。
2.6 知识体系总览
下面这张图把五个因素的关系画清楚了。你看一眼,心里就有谱了。
你看,五个因素不是孤立的。丢包会加剧延迟,延迟会触发乱序,带宽波动会让丢包更严重,NAT穿透失败直接连不上。所以做稳定性方案,不能只盯着一个点。
我的建议:先摸清你的网络环境。用工具测一下延迟、丢包率、带宽,搞清楚NAT类型。数据说话,别靠猜。然后针对性地选策略——延迟高就加缓存和预测,丢包多就上FEC,NAT对称就走中继。
嗯,这一章的内容就到这儿。记住这五个因素,后面讲具体方案时,你会反复用到它们。