为什么 64 KiB 的窗口不够用?窗口缩放选项到底把窗口放大了多少?macOS 的接收缓冲区自动调整多久把窗口放大到上限,上限又怎样决定吞吐?这篇用握手里的真实选项、100 ms 往返的回环传输和真实公网的 RTT 来回答。
移位 s 最大窗口(字节) RTT 10 ms RTT 100 ms RTT 200 ms
0 65,535 6.6 MB/s 0.7 MB/s 0.3 MB/s
3 524,280 52.4 MB/s 5.2 MB/s 2.6 MB/s
6 4,194,240 419.4 MB/s 41.9 MB/s 21.0 MB/s
7 8,388,480 838.8 MB/s 83.9 MB/s 41.9 MB/s
9 33,553,920 3355.4 MB/s 335.5 MB/s 167.8 MB/s
13 536,862,720 53686.3 MB/s 5368.6 MB/s 2684.3 MB/s
14 1,073,725,440 107372.5 MB/s 10737.3 MB/s 5368.6 MB/s
没有窗口缩放(s = 0)时,窗口最大 65535 字节:RTT 100 ms 的链路上吞吐最多 0.66 MB/s ≈ 5.2 Mbit/s,不管带宽多大;窗口缩放让这个上限随 s 指数增长。选项只出现在 SYN / SYN-ACK 里,而且 SYN 里的窗口字段本身不缩放窗口字段 16 位,选项里的移位数 s 把窗口扩大到 2^s 倍(最大 s = 14,约 1 GiB)。
本机 lo0:我的 SYN 窗口缩放 6,窗口字段 65535(不缩放);对方 SYN-ACK 窗口缩放 6,窗口字段 65535;握手完成后我的第一个 ACK 窗口字段 6380 → 实际 408320 字节;我能通告的最大窗口 4,194,240,对方 4,194,240 example.com:我的 SYN 窗口缩放 6,窗口字段 65535(不缩放);对方 SYN-ACK 窗口缩放 13,窗口字段 65535;握手完成后我的第一个 ACK 窗口字段 2061 → 实际 131904 字节;我能通告的最大窗口 4,194,240,对方 536,862,720 另一个公网主机:我的 SYN 窗口缩放 6,窗口字段 65535(不缩放);对方 SYN-ACK 窗口缩放 13,窗口字段 65535;握手完成后我的第一个 ACK 窗口字段 2070 → 实际 132480 字节;我能通告的最大窗口 4,194,240,对方 536,862,720 两边各有各的移位数,互不影响;要启用缩放,SYN 和 SYN-ACK 里都必须带窗口缩放选项,只有一边带的话两边都不缩放
选项只在 SYN / SYN-ACK 里协商,双方各有自己的 s:本机 6,example.com 13。
net.inet.tcp.doautorcvbuf: 1
net.inet.tcp.autorcvbufmax: 4194304
net.inet.tcp.recvspace: 131072
客户端的窗口缩放系数 6
时间(秒) 这 0.5 秒的吞吐(MB/s) SO_RCVBUF 读数(字节) 这一时刻 ACK 里的窗口(字节,取该秒内最大)
0.5 4.44 4194240 1241024
1.0 29.53 4194240 4194240
1.5 42.01 4194240 4194240
2.0 41.81 4194240 4194240
2.5 41.81 4194240 4194240
3.0 41.81 4194240 4194240
3.5 41.81 4194240 4194240
4.0 43.31 4194240 4194240
4.5 42.18 4194240 4194240
5.0 34.65 4194240 4194240
5.5 38.74 4194240 4194240
6.0 41.81 4194240 4194240
窗口(缓冲区)随着传输进行自动长大,吞吐随之上升:内核根据"最近一个 RTT 内收到了多少数据"来放大接收缓冲区,上限是 net.inet.tcp.autorcvbufmax(4 MiB)macOS 默认开启接收缓冲区自动调整,约 1 秒内把窗口放大到 autorcvbufmax(4 MiB);之后吞吐由窗口 ÷ RTT 封顶。
到example.com:connect 耗时(≈ 一个 RTT)中位 243 ms:不缩放时 65535 字节窗口 ÷ RTT = 0.27 MB/s ≈ 2.2 Mbit/s;缩放 2^6 时本机能通告的窗口 4,194,240 字节 → 上限 17 MB/s 到另一个公网主机:connect 耗时(≈ 一个 RTT)中位 293 ms:不缩放时 65535 字节窗口 ÷ RTT = 0.22 MB/s ≈ 1.8 Mbit/s;缩放 2^6 时本机能通告的窗口 4,194,240 字节 → 上限 14 MB/s 缩放选项几乎是必须的:今天的链路动辄 100 Mbit/s 以上、RTT 几十到几百毫秒,不缩放的 64 KiB 窗口会把带宽浪费掉绝大部分
往返 200~300 ms 的链路上,64 KiB 窗口只有 0.2~0.3 MB/s,与带宽无关。