bigywater.tech
网络协议 · 51

窗口缩放选项:为什么 64 KiB 不够

为什么 64 KiB 的窗口不够用?窗口缩放选项到底把窗口放大了多少?macOS 的接收缓冲区自动调整多久把窗口放大到上限,上限又怎样决定吞吐?这篇用握手里的真实选项、100 ms 往返的回环传输和真实公网的 RTT 来回答。

41.8 MB/s
往返 100 ms 的回环上,接收窗口自动放大到 4 MiB 上限后的稳定吞吐(= 4,194,240 ÷ 0.1 s)
s = 6 与 s = 13
本机和 example.com 在握手里协商的窗口缩放移位数(最大窗口 4,194,240 与 536,862,720 字节)
0.27 MB/s
不缩放时到 example.com(RTT 243 ms)的吞吐上限;缩放之后上限 17 MB/s

窗口缩放

实测 · 本机执行输出
    移位 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,与带宽无关。

分步演示

窗口缩放
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第二节抓的是 lo0 与 en0 上的真实握手;第三节的 100 ms 往返由 netlab 注入;第四节的 RTT 用 connect 耗时的中位数近似(5 次),Wi-Fi 与路径变化会让数字每次不同;窗口 ÷ RTT 是上限的计算值
    • 没有验证:关闭窗口缩放后的实测(需要改 net.inet.tcp.rfc1323 之类的开关或 root);发送方拥塞窗口对吞吐的限制(见拥塞控制);autorcvbufmax 调大后的效果;其它系统的 autotune 策略
    京ICP备2026067502号-1京公网安备11010502063487号