目录
正在加载目录…
专栏文章
专栏文章
网络算法系列
1. 轮询与加权轮询 2. 最小连接数 3. Jump Consistent Hash 4. Rendezvous Hashing 5. TCP 拥塞控制(CUBIC 与 BBR) 6. QUIC 丢包恢复与拥塞控制 7. 经典一致性哈希(虚拟节点) 8. P2C(Power of Two Choices) 9. TCP 滑动窗口与流量控制 10. Maglev Hashing

TCP 滑动窗口与流量控制

发布于 2026-06-08 09:02 · 最后编辑于 2026-07-31 15:54 · 字数 1,577 👁 84 次阅读

TCP 滑动窗口是接收方向发送方通告「我还能接收多少字节」的机制,防止发送方淹没接收方缓冲区;与拥塞控制的 cwnd 共同决定有效发送速率,是 TCP 可靠传输的基础组成部分。

相关文章TCP 拥塞控制(CUBIC 与 BBR)

tcp sliding window

目录

章节说明
流量控制 vs 拥塞控制两者的本质区别
滑动窗口模型四个缓冲区区域,三个指针
窗口通告机制ACK 携带 rwnd,Zero Window 死锁
Window Scaling 扩展高带宽延迟网络的 16bit 上限突破
Silly Window Syndrome小窗口综合征及 Nagle/Clark 解法
有效窗口计算min(rwnd, cwnd) 实际控制逻辑
参考资料RFC 与文档

流量控制 vs 拥塞控制

TCP 有两套独立的速率控制机制,经常被混淆:

维度流量控制(rwnd)拥塞控制(cwnd)
保护对象接收方缓冲区网络链路
由谁决定接收方(广播 rwnd)发送方(本地估算)
信号来源ACK 中的 Window 字段丢包 / ECN / 延迟变化
算法简单通告,无须估算CUBIC、BBR 等复杂算法
RFCRFC 793RFC 5681(Reno)等

有效发送窗口 = min(rwnd, cwnd):发送方受两者中较小值约束。

滑动窗口模型

发送方缓冲区的四个区域

|<── 已发送已确认 ──>|<── 已发送未确认 ──>|<── 可发送未发送 ──>|<── 窗口外 ──>|
                    ↑                   ↑                   ↑
                 SND.UNA             SND.NXT          SND.UNA + rwnd
区域状态可操作性
已发送已确认数据已到达接收方可释放缓冲
已发送未确认在途,等待 ACK必须保留(重传用)
可发送未发送窗口内,待发可立即发送
窗口外超出 rwnd必须等待窗口滑动

三个关键指针(RFC 793)

指针含义
SND.UNA最早未确认的字节序号(窗口左边界)
SND.NXT下一个待发字节序号
SND.WND当前发送窗口大小(= rwnd)

窗口滑动:每收到一个 ACK,SND.UNA 前移,窗口向右滑动,释放空间让新数据进入可发送区域。

接收方缓冲区

|<── 已消费 ──>|<── 已接收未消费 ──>|<── 空闲(可接收)──>|<── 超出 ──>|
                                    ↑                    ↑
                                 RCV.NXT           RCV.NXT + rwnd

rwnd = 空闲缓冲大小 = 总缓冲 − 已接收未消费字节数

应用层每次 read() 消费数据后,rwnd 增大,通过下一个 ACK 通告给发送方。

窗口通告机制

正常流程

发送方                               接收方
  │── DATA(seq=1, len=1000) ────────→│  缓冲: 4096→3096
  │── DATA(seq=1001, len=1000) ──→   │  缓冲: 3096→2096
  │← ACK(ack=2001, rwnd=2096) ───────│  通告剩余缓冲
  │(窗口右移,SND.UNA=2001)         │
  │                                  │  应用读取 2096 字节
  │← ACK(ack=2001, rwnd=4096) ───────│  rwnd 恢复

Zero Window 与持续定时器

当接收方缓冲满时发送 rwnd=0

发送方                               接收方
  │← ACK(ack=X, rwnd=0) ─────────────│  缓冲满,停止接收
  │  (停止发送)                     │
  │  持续定时器触发(通常 1~60s)    │
  │── Zero Window Probe ────────────→│  探测包(1字节)
  │← ACK(ack=X, rwnd=0) ─────────────│  仍然满
  │  (指数退避重试)                 │
  │                                  │  应用消费数据
  │← ACK(ack=X, rwnd=4096) ──────────│  窗口重新打开

死锁风险:若 rwnd=0 的通告包丢失,发送方永久等待——持续定时器(Persist Timer)正是为此设计的防死锁机制。

Window Scaling 扩展

TCP header 的 Window 字段只有 16 bit,最大值 65535 字节(64KB)。

在高带宽延迟网络(如 10Gbps 跨洲链路,RTT=100ms)下:

带宽延迟积(BDP)= 10Gbps × 100ms = 125MB

64KB 远小于 125MB,链路利用率不足 0.05%。

解决方案:RFC 1323 定义 Window Scaling 选项(三次握手时协商):

实际窗口大小 = Window 字段值 × 2^shift_cnt
shift_cnt ∈ [0, 14],最大窗口 = 65535 × 2^14 ≈ 1GB

Linux 默认启用 Window Scaling(net.ipv4.tcp_window_scaling=1)。

Silly Window Syndrome

问题描述

发生场景:接收方应用层每次只读 1 字节,导致:

① 接收方缓冲空出 1 字节 → 通告 rwnd=1
② 发送方发送 1 字节数据(40 字节 TCP/IP header + 1 字节数据)
③ 98% 带宽浪费在头部

解决方案

接收方(Clark 算法):rwnd=0 后,等到缓冲空出的空间 ≥ min(MSS, 缓冲总量/2) 时再通告非零 rwnd(RFC 1122 §4.2.3.3)。

发送方(Nagle 算法)

if 待发数据 >= MSS 或 所有在途数据已确认:
    立即发送
else:
    等待(积累数据或等 ACK 回来)

交互式应用(SSH、游戏)通常禁用 Nagle(TCP_NODELAY)以降低延迟。

有效窗口计算

cwnd:发送方根据网络状况估算的拥塞窗口
rwnd:接收方通告的接收窗口
effective_window = min(rwnd, cwnd)

实际可发送字节数 = effective_window − (SND.NXT − SND.UNA)
                                      ↑
                                   已发未确认字节数

典型瓶颈场景

场景瓶颈现象
接收方内存不足rwnd 小发送速率受接收方限制
网络拥塞cwnd 小发送速率受网络限制
长肥管道两者都大但 BDP 需要大窗口需要 Window Scaling

参考资料

参考资料

  • RFC 793 — Transmission Control Protocol(TCP 原始规范,滑动窗口定义)
  • RFC 1323 — TCP Extensions for High Performance(Window Scaling)
  • RFC 9293 — Transmission Control Protocol (TCP)(2022 年最新整合版)
  • TCP 拥塞控制(CUBIC 与 BBR)(拥塞控制 cwnd 的对应算法)
← 返回列表

评论 (0)

暂无评论,来留下第一条吧。
登录注册 后才能发表评论