NetCalcLab

TCP 吞吐与带宽时延积

高速链路为什么不一定给你高速传输:带宽时延积、窗口天花板、窗口扩大选项,以及一点点丢包所带来的惊人代价。

为什么高速链路也可能很慢

两个相隔 100 ms 的办公点之间有一条 1 Gbps 链路,本该在不到一秒内传完一个文件。实际上单条 TCP 连接往往只能跑到十分之一。问题不在链路 —— 是协议在等。TCP 要求数据被确认之后才能在途位置上被替换,而在长路径上,每一个确认都要走完一个完整的往返才能回来。如果发送方在第一个确认回来之前就用光了允许未确认的数据量,链路在每个往返里都会有一部分时间处于空闲。

决定这件事是否发生的量就是带宽时延积:要让管道被填满,需要有多少数据同时在途。它就是带宽乘以往返时延。

带宽时延积
BDP(比特) = 带宽(比特/秒) × RTT(秒)
BDP(字节) = BDP(比特) ÷ 8

1 Gbps、RTT 100 ms:
  BDP = 1,000,000,000 × 0.1      = 100,000,000 比特
      = 100,000,000 ÷ 8          = 12,500,000 字节
      = 12.5 MB

也就是说,这条链路需要始终有 12.5 MB 的数据在途才能保持繁忙。这个数字就是后面每一项调优决策所追求的目标。

窗口天花板

TCP 发送方在途的未确认数据量,不能超过接收方通告的窗口允许的值。由此得出一个与链路速率毫无关系的吞吐上限:

窗口受限的吞吐
吞吐(字节/秒) = 窗口(字节) ÷ RTT(秒)

窗口 65,535 字节、RTT 100 ms:
  吞吐 = 65,535 ÷ 0.1 = 655,350 字节/秒
       = 655,350 × 8  = 5,242,800 比特/秒 ≈ 5.24 Mbps

这是 TCP 性能中最重要的一个数字。在 100 ms 路径上使用出厂默认的 65,535 字节窗口,会把单条流限制在约 5.24 Mbps —— 无论链路本身有多快。同样的窗口在 10 ms 的往返时延下能到 52.4 Mbps,这就是为什么这个问题在局域网上完全看不出来,而跨洋时却非常严重。

16 位带来的问题

TCP 首部中窗口字段原本只有 16 位宽。这不是实现选择,而是格式本身:能表达的最大值是 2^16 − 1 = 65,535。TCP 设计之初,这个值远大于任何路径的带宽时延积,所以不构成任何代价。而当链路在卫星或洲际距离上跑到几兆比特每秒之后,它就变成了新的硬约束。

窗口扩大:TCP 如何突破这个限制

RFC 7323(最早是 RFC 1323)引入了窗口扩大选项。握手期间双方各自通告一个移位计数,此后发送的每一个窗口值都按值 × 2^scale 解释。扩大因子为 7 时,天花板乘以 128:

100 ms RTT 下不同扩大因子对应的窗口天花板
扩大因子倍数有效窗口吞吐天花板
0(不扩大)×165,535 B约 5.24 Mbps
4×161,048,560 B约 83.9 Mbps
6×644,194,240 B约 335 Mbps
7×1288,388,480 B约 671 Mbps
8×25616,776,960 B约 1.34 Gbps
10×102467,107,840 B约 5.37 Gbps

由于移位计数是协商出来的,两端都必须支持 —— 一台现代 Linux 或 macOS 主机与老旧设备通信时,仍可能拿到未扩大的窗口。另外要注意,这个扩大因子只作用于窗口字段,不作用于握手期间窗口更新里的那个值,这正是读抓包时经常差 128 倍的经典来源。

丢包:另一个天花板

即便窗口很大,丢包也会施加自己的限制。拥塞控制把丢包视为拥塞的证据并降低发送速率,而恢复是渐进的。Mathis 公式给出了单条 Reno 式 AIMD 流的数量级上限:

Mathis 公式
吞吐 ≈ (MSS ÷ RTT) × (C ÷ √p)

  MSS = 最大报文段长度(字节)
  RTT = 往返时延(秒)
  p   = 丢包概率
  C   ≈ 1.22,对应简单的 AIMD(Reno)发送方

MSS 1460 B、RTT 100 ms、p = 1%:
  1460 ÷ 0.1        = 14,600
  1.22 ÷ √0.01      = 1.22 ÷ 0.1 = 12.2
  吞吐              = 14,600 × 12.2 = 178,120 字节/秒
                    ≈ 1.42 Mbps

丢包项位于平方根之下,这让它的惩罚格外严厉。从 0.01% 的丢包升到 1% —— 增加一百倍 —— 只让吞吐下降十倍,而不是一百倍。这也意味着,一条存在少量持续性丢包的路径,有一个无论怎么调窗口都抬不起来的硬上限。

MSS 1460 B、RTT 100 ms 下的 Mathis 上限
丢包率√pC ÷ √p吞吐天花板
0.001%(1e-5)0.00316386约 45.1 Mbps
0.01%(1e-4)0.01122约 14.3 Mbps
0.1%(1e-3)0.031638.6约 4.51 Mbps
1%(1e-2)0.112.2约 1.42 Mbps
5%(5e-2)0.2245.45约 636 kbps

真正卡住的是哪一个限制?

当传输比预期慢时,真正有用的问题是:这三个限制里,哪一个正在起约束作用。每一个的应对方式都不同,用错了只是浪费时间。

诊断一次慢速传输
症状可能的限制该检查什么
吞吐 × RTT ≈ 64 KB窗口,且未启用扩大握手过程中双方是否都出现了窗口扩大选项?
吞吐 × RTT ≈ 某个整数 MB窗口,容量小于 BDP把通告的窗口与 BDP 作比较;检查 tcp_rmem / tcp_wmem 自动调优。
吞吐 × RTT ≈ BDP,但依然很慢丢包查重传与重复 ACK;要在路径上测丢包,而不是只在两端测。
吞吐远低于窗口与丢包两个天花板应用程序或磁盘可能是发送方没有数据可发,或者接收方写入太慢。
单条流慢,多条流合计很快每流限制,而不是链路多条流各有自己的窗口;用单条流测往往测错了对象。

最快的粗判方法:把观测到的吞吐乘以 RTT。如果结果可疑地接近 64 KB,或者接近某个整数的兆字节数,那限制就在窗口。如果接近 BDP 却远低于链路速率,就去查丢包。

这个模型没有覆盖什么

上面的数字都是单条理想化流的上限。实际传输还会受 TCP 慢启动限制 —— 连接以一个很小的拥塞窗口开始,每个往返翻一倍,直到达到路径容量,所以短传输根本摸不到天花板。延迟确认、接收窗口自动调优、竞争流量、负载下排队时延推高实际 RTT,以及 CUBIC 对丢包的不同反应,都会让真实行为偏离模型。

请用这些数字判断"哪个限制在起作用"以及"调优还能换来多少余量",而不是用它预测某次具体传输要多久。带宽计算器处理的是更基础的问题:给定速率下传一个文件要多久;而 MTU / MSS 计算器处理的是会降低这些公式所依赖的 MSS 的封装开销。

带宽时延积参考

填满常见链路需要多少数据在途。这也是单条 TCP 流跑到满速所需的窗口大小 —— 把它和未扩大时的 65,535 字节上限对比,就能看出什么时候必须启用窗口扩大。

不同链路速率与 RTT 下的 BDP(字节)
链路速率1 ms10 ms50 ms100 ms200 ms
10 Mbps1.25 KB12.5 KB62.5 KB125 KB250 KB
100 Mbps12.5 KB125 KB625 KB1.25 MB2.5 MB
1 Gbps125 KB1.25 MB6.25 MB12.5 MB25 MB
10 Gbps1.25 MB12.5 MB62.5 MB125 MB250 MB
100 Gbps12.5 MB125 MB625 MB1.25 GB2.5 GB

所有大于 65,535 字节的值,单条 TCP 流在不使用窗口扩大选项时都达不到。这张表里除了第一列之外几乎每一格都超过了它 —— 这就是该选项在现代互联网上实际上是强制要求的原因。

值得记住的关键常量

决定 TCP 吞吐的几个数字
量数值为什么重要
未扩大时的最大窗口65,535 字节16 位字段的上限;不启用扩大时就是天花板
窗口扩大的最大因子×2^14RFC 7323 把移位上限设为 14,最大窗口约 1 GB
以太网默认 MSS1460 字节1500 MTU − 20 IPv4 − 20 TCP
Mathis 常量 C约 1.22对应简单 AIMD(Reno)发送方的经验系数
建议的最小 MTU576 字节(IPv4)任何路径都必须能在不分片的情况下转发它
IPv6 最小 MTU1280 字节RFC 8200 抬高了底线;路由器从不分片

相关:TCP 吞吐计算器、带宽计算器、MTU / MSS 计算器、MTU 与 MSS 指南。

常见问题

带宽时延积是什么?

带宽时延积(BDP)是为了让链路完全跑满,需要同时在途的数据量 —— 已发出但还没被确认。它就是带宽乘以往返时延。1 Gbps 链路、100 ms RTT 的 BDP 是 1e9 × 0.1 = 1e8 比特 = 12.5 MB。在线上有 12.5 MB 未确认数据之前,链路总有一部分时间处于空闲。

为什么吞吐会被 TCP 窗口卡住?

发送方的未确认在途数据不能超过接收窗口允许的量。若窗口为 W 字节、往返时延为 R 秒,则每个 RTT 最多只能有 W 字节通过链路,所以吞吐上限是 W ÷ R。窗口 65,535 字节、RTT 100 ms 时,这个上限是 655,350 字节/秒 ≈ 5.24 Mbps —— 与底层链路有多快无关。

TCP 窗口扩大是什么,为什么需要它?

原始 TCP 首部中窗口字段只有 16 位,所以在没有扩展选项时最大窗口是 65,535 字节。在长距离或高速链路上,这个天花板会把吞吐压得很惨。RFC 7323 引入了窗口扩大选项,把通告值按协商好的位数左移 —— 扩大因子 7 会把 65,535 变成 8,388,480 字节。两端都必须支持,否则不会启用。

丢包为什么对吞吐影响这么大?

拥塞控制把丢包解读为需要把发送速率减半的信号,而恢复过程是缓慢的。Mathis 公式把由此产生的上限近似为 (MSS ÷ RTT) × (1.22 ÷ √p),其中 p 是丢包概率。由于丢包项在平方根之下,在 100 ms RTT 路径上 1% 的丢包率,会把一条 MSS 1460 字节的单流限制在约 2.8 Mbps —— 哪怕链路是千兆的。

什么是长肥网络?

长肥网络(LFN,long fat network / long fat pipe)指带宽时延积很大的路径 —— 通常是跨大陆或跨洲链路,带宽与 RTT 的乘积很大。这类路径正是窗口扩大、选择确认与更大缓冲区最关键的场合,也正是在这里,未经调优的主机性能会远低于链路容量。

窗口越大就一定越快吗?

不是。窗口必须有两端真实的缓冲区和内核自动调优上限作为支撑,而且只有当路径确实能把数据送过去时才有意义。在短距离或丢包路径上,更大的窗口主要只是增加了丢包后需要重传的数据量。窗口应当按带宽时延积来定,不要更大。

为什么这些计算器给的是上限而不是预测值?

它们建模的是一条理想化的单流:没有竞争流量、没有排队、没有丢包。真实吞吐会因慢启动、拥塞控制爬升、延迟确认、接收窗口自动调优、竞争流以及应用层瓶颈而更低。请把这些数字当作调优能够逼近的天花板,以及判断「到底是窗口、丢包还是路径在起限制作用」的工具。