TCP 流分析82 条

TCP 窗口字段换算

抓包中的窗口值受缩放因子影响:显示值可能已还原(Wireshark 默认),而原始 16 位字段值需乘 2^wscale;核对吞吐上限时务必用还原后的实际窗口。

窗口值为什么要换算

TCP 头里的接收窗口是 16 位字段,理论上限 65535 字节,在高带宽长延迟链路上远远不够用。为此握手阶段会协商窗口缩放因子,之后报文里的窗口值要乘以 2 的缩放次幂才是真实可接收字节数。Wireshark 默认显示已经还原后的计算值,而原始 16 位值另有一个字段;核算吞吐上限时用错字段会得出偏小一个数量级的结论。

基本信息

原始字段tcp.window_size_value,报文头里的 16 位数值
计算字段tcp.window_size,已按缩放因子还原的实际窗口
缩放因子握手选项中的窗口缩放位移值,字段为 tcp.options.wscale.shift
换算公式实际窗口 = 原始值 × 2 的位移次幂
交叉验证用 ss -ti 的窗口相关字段对照,字段名随 iproute2 版本不同
边界情况窗口为 0 时不受缩放影响,零乘任何数仍为零

典型使用场景

  • 吞吐核算:按窗口与往返时间估算单连接吞吐上限,解释为什么大带宽链路上反而跑不快
  • 背压定位:接收窗口持续变小或归零,说明消费端处理不过来,而不是网络丢包
  • 两头对账:把抓包换算出的窗口与主机侧套接字信息对照,确认两端读的是同一口径
  • 协议分析教学:用真实抓包演示 16 位窗口为什么必须配合缩放才能支撑现代带宽
  • 性能回归:改动内核参数前后各抓一段,比较窗口变化趋势

查看与换算

查看与换算
# 握手阶段协商出的缩放位移(两端各发一次)
tshark -r a.pcap -Y 'tcp.options.wscale' -T fields -e ip.src -e tcp.options.wscale.shift
tshark -r a.pcap -Y 'tcp.stream == 7' -T fields -e frame.number -e tcp.window_size_value -e tcp.window_size
# 主机侧交叉验证(字段名随 iproute2 版本不同)
ss -ti dst 10.0.0.9
# 只看零窗窗口的帧
tshark -r a.pcap -Y 'tcp.analysis.zero_window' -T fields -e frame.number -e tcp.window_size

字段与判读

字段含义判读提示
tcp.window_size_value报文里的原始 16 位窗口超过 65535 一定不是原始值
tcp.window_size按缩放还原后的窗口核算吞吐时用这个
wscale 位移握手协商的缩放次幂缺失时窗口无法放大
窗口骤降接收方缓冲区被占满先查消费端处理速度再看网络
窗口归零接收方暂时无法接收常伴随发送方零窗探测
两侧窗口差异大一端读原始值一端读还原值先统一口径再比较
窗口为 0 的判断不受缩放影响,可以直接按字段值判定;有争议的只有非零窗口的换算,此时必须回到握手阶段确认位移值。
不同工具对窗口的口径不一致:抓包工具显示还原值,系统工具可能显示套接字层的可用缓冲,两者混用会得出矛盾结论,出结论前先明确各自的定义。

易错点

  • 用原始 16 位值当实际窗口计算吞吐,结果比真实能力小几百倍,据此得出「必须扩容」的错误建议
  • 窗口骤降时先怀疑网络丢包,忽略接收端应用处理不过来这一更常见的原因,排查方向跑偏
  • 把抓包换算出的窗口与主机侧看到的剩余缓冲直接对比,两者定义不同,数值差异被当成异常
  • 只看单向窗口变化就下结论,忽略另一方向的窗口也在影响整体交互,端到端结论需要两侧一起看

数据来源与参考

  • RFC 7323(窗口缩放)
  • Wireshark User's Guide
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌