UDP 丢了包、乱了序、收到两份,应用怎么知道?本机能不能复现这些现象?这篇用 netlab 造丢包和限速,用自己写的中继造乱序和重复,再对真实的公共 DNS 服务器连发 UDP,最后用一个最小的停等协议看“自己做可靠”要花多少代价。
设置丢包 5%:发 2000 个,收到 1906 个(1906 个不同序号),丢失 94 个 = 4.7%;连续丢失的段 89 段,最长 2;长度分布 1个连丢×84,2个连丢×5;收到的序号是否严格递增:True 设置丢包 20%:发 2000 个,收到 1620 个(1620 个不同序号),丢失 380 个 = 19.0%;连续丢失的段 305 段,最长 7;长度分布 1个连丢×246,2个连丢×48,3个连丢×9,4个连丢×1,7个连丢×1;收到的序号是否严格递增:True netlab 的丢包是独立随机丢的(没有突发),所以连续丢失的段大多长度为 1;真实的无线 / 拥塞丢包往往是成串的
netlab 能造随机丢包,实测与设置值接近;丢包不会改变收到的包的顺序,而且是各自独立的,单个丢失居多。
突发:8 ms 内发完 1000 个(约 971 Mbit/s 的速度),收到 100 个,丢失 900 个;收到的延迟 最小 67 / 中位 881 / 最大 1695 ms;收到的序号范围 0~99 慢发(每 20 ms 一个 1000 字节 = 400 Kbit/s,低于 500 Kbit/s 的管道):2.0 s 发完 100 个,收到 100 个,丢失 0 个;延迟 最小 66 / 中位 79 / 最大 80 ms(50 ms 延迟 + 1000 字节在 500 Kbit/s 上的发送时间 16 ms) 超过管道带宽的突发会在队列里排队,队满以后后面的包直接丢弃(尾部丢弃),队里的包延迟也大幅增加;UDP 自己不会降速
UDP 不会因为管道窄就降速:突发 1000 个包进 500 Kbit/s 的管道,只通过前 100 个,延迟涨到几百毫秒;慢发则没有丢失。
发 2000 个;中继复制了 40 份;接收端收到 2040 个(不同序号 2000 个,丢失 0 个) 接收端检测:乱序(比已见到的最大序号小的新序号)1467 个,重复 40 个,最大“落后”距离 23;中继发出顺序里的真实值:乱序 1467、重复 40、最大落后 23;两者一致 乱序占 73.4%——随机延迟 0~30 ms、每 1 ms 一个包,所以相邻的包经常互换位置
回环上不会自己出现,用中继人为制造后,按序号的检测算法与真实记录一致。
公共 DNS A,慢发 每 20 ms 一个:发 300 个,收到 260 个不同的应答,丢失 40 个(13.3%),重复 0 个,乱序 0 个 公共 DNS A,突发 不留间隔:发 300 个,收到 285 个不同的应答,丢失 15 个(5.0%),重复 0 个,乱序 22 个(最大落后 46) 公共 DNS B,慢发 每 20 ms 一个:发 300 个,收到 300 个不同的应答,丢失 0 个(0.0%),重复 0 个,乱序 1 个(最大落后 1) 公共 DNS B,突发 不留间隔:发 300 个,收到 300 个不同的应答,丢失 0 个(0.0%),重复 0 个,乱序 5 个(最大落后 12)
向两个公共 DNS 连发 UDP:一个丢 13.3%,一个不丢;突发时能看到几个到几十个的乱序,没有重复。
每个方向丢 0%:发了 200 次(200 个数据报,重发 0 次),服务端收到 200 个不同序号,总用时 0.02 s;理论平均发送次数 200/(1−0.0)² = 200 次(计算值) 每个方向丢 5%:发了 225 次(200 个数据报,重发 25 次),服务端收到 200 个不同序号,总用时 0.80 s;理论平均发送次数 200/(1−0.05)² = 222 次(计算值) 每个方向丢 20%:发了 287 次(200 个数据报,重发 87 次),服务端收到 200 个不同序号,总用时 2.72 s;理论平均发送次数 200/(1−0.2)² = 312 次(计算值) 每个方向丢 40%:发了 532 次(200 个数据报,重发 332 次),服务端收到 200 个不同序号,总用时 10.32 s;理论平均发送次数 200/(1−0.4)² = 556 次(计算值)
停等协议把丢包变成重发,丢得越多越慢;真实的实现(滑动窗口、快速重传、拥塞控制)在 TCP 与 QUIC 的部分展开。