← 返回题目列表

TCP 滑动窗口是什么?它和流量控制、拥塞控制有什么关系?

高频 困难 第 18 / 30 题 更新于 2026/07/30
TCP滑动窗口流量控制拥塞控制ACK

简化版

TCP 滑动窗口允许发送方在未收到前一个 ACK 前连续发送多个字节段,用来提升吞吐。真正能发送的窗口通常取 min(接收窗口 rwnd, 拥塞窗口 cwnd),所以它既支撑可靠传输,也同时受流量控制和拥塞控制约束。

详细版

滑动窗口解决的是“不能发一个包等一个 ACK”的低效率问题。发送方维护一个发送窗口,窗口内的数据可以发送但尚未全部确认;接收方维护接收窗口,告诉对方自己还能接收多少数据。

关键点:

  1. TCP 的序列号按字节编号,不是按包编号。
  2. ACK 常见是累计确认,ACK=5001 表示 5001 之前的字节都连续收到了。
  3. 接收窗口 rwnd 防止压垮接收方,是流量控制。
  4. 拥塞窗口 cwnd 防止压垮网络,是拥塞控制。
  5. 发送方实际可发数据量通常受 min(rwnd, cwnd) 限制。

所以面试里要说明:滑动窗口是基础机制,流量控制和拥塞控制是限制窗口大小的两类规则。

完整版教学

一、滑动窗口为什么比停等协议高效

如果每发送一个 TCP 段就停下来等 ACK,链路利用率会很低。假设 RTT 是 100ms,每个段 1KB,停等模式下一秒最多大约发送 10KB,哪怕带宽是 100Mbps 也吃不满。

滑动窗口的思路是:在一个 RTT 内先连续发一批数据,ACK 回来后窗口向前滑动,再继续发下一批。这样吞吐不再只由单个报文的等待时间决定,而由“窗口大小 / RTT”决定。

理论吞吐上限约为:
throughput <= window_size / RTT

如果窗口 64KB,RTT 100ms:
throughput <= 64KB / 0.1s = 640KB/s

记忆钩子:滑动窗口的价值不是“窗口会动”,而是“允许在等待 ACK 的同时继续把链路填满”。

二、发送窗口里的四段区域

发送方可以把字节流切成四段:已经确认、已发送未确认、可发送未发送、暂时不可发送。窗口覆盖的是中间两段,左边界随着 ACK 前进,右边界由窗口大小决定。

字节序列:
| 已确认 | 已发送未确认 | 可发送未发送 | 不可发送 |
          ^                         ^
        send base               window end

举例:发送窗口大小是 5000 字节,send base=1001,那么窗口右边界是 6000。如果已经发出 1001~3000,但还没收到 ACK,那么 3001~6000 仍可继续发送。收到 ACK=3001 后,左边界滑到 3001,右边界也滑到 8000。

这个过程解释了为什么 TCP 可以高吞吐:它不用等每个小包单独确认,只要累计 ACK 推进,窗口就能成批前移。

三、接收窗口 rwnd 解决接收方承压问题

接收方的缓存不是无限的。如果发送方只顾高速发送,接收方应用来不及读取,内核接收缓冲区会被塞满。TCP 通过报文首部里的窗口字段通告 rwnd,告诉发送方“我还能接收多少字节”。

接收方状态通告窗口发送方行为
缓冲区空闲较多rwnd=64KB可以继续发送较多数据
应用读取变慢rwnd=8KB收缩发送量
缓冲区满rwnd=0暂停发送数据,等待窗口恢复

这就是流量控制。它保护的是接收方,不是整个网络。很多人把 rwndcwnd 混在一起,面试时一定要明确对象不同。

四、拥塞窗口 cwnd 解决网络承压问题

即使接收方还能接收,网络中间链路也可能拥塞。TCP 拥塞控制维护 cwnd,通过慢启动、拥塞避免、快速重传和快速恢复等机制估计网络能承受的发送量。

发送方最终不能只看接收窗口,而要同时考虑接收方和网络:

可发送窗口 = min(rwnd, cwnd) - 已发送未确认数据量

例如 rwnd=64KBcwnd=16KB、已发送未确认 10KB,那么当前还能新发 min(64,16)-10=6KB。这说明接收方虽然有空间,但网络侧限制更紧。

五、累计 ACK、乱序与窗口滑动

TCP 常见 ACK 是累计确认。接收方收到 1001~20002001~3000 后回 ACK=3001,表示 3001 之前都连续到了。如果 3001~4000 丢了,但 4001~5000 到了,接收方通常仍回 ACK=3001,因为中间缺口没补上。

收到情况:
1001-2000  ok
2001-3000  ok
3001-4000  lost
4001-5000  out-of-order

ACK 仍然是 3001

这种累计 ACK 让协议简单可靠,但也带来信息不足:发送方不知道后面的乱序段是否已到。SACK 可以补充“哪些非连续区间已收到”,从而减少不必要重传。

六、窗口太小、太大分别有什么问题

窗口太小会让吞吐上不去,尤其是高带宽、高 RTT 链路。窗口太大也不是无成本:接收端缓存压力更高,丢包后重传范围可能更大,拥塞控制也要更谨慎地探测网络容量。

可以用带宽时延积估算需要多大窗口:

BDP = bandwidth * RTT

带宽 100Mbps,RTT 50ms:
BDP = 100Mb/s * 0.05s = 5Mb = 625KB

如果窗口只有 64KB,就很难跑满这条链路。这也是为什么现代 TCP 会用窗口扩大选项支持更大的窗口。

七、常见误区与追问

  • 误区:滑动窗口就是流量控制。 滑动窗口是机制,流量控制用 rwnd 限制它,拥塞控制用 cwnd 限制它。
  • 误区:ACK 确认的是某个包编号。 TCP 序列号按字节编号,ACK 表示下一个期望收到的字节序号。
  • 误区:接收窗口越大越好。 窗口要匹配链路和应用处理能力,过大可能增加缓存占用和丢包恢复成本。
  • 追问:实际可发送窗口怎么算? 通常看 min(rwnd, cwnd),再减去已经发送但尚未确认的数据。
  • 追问:乱序包来了窗口会不会滑动? 如果缺口没补上,累计 ACK 不会越过缺口,接收端可以缓存乱序数据等待补洞。
  • 追问:为什么高延迟链路需要大窗口? 因为吞吐受 window_size / RTT 约束,RTT 越大,想跑满带宽就需要更大的在途数据量。

八、加强记忆

滑动窗口可以按“三个数字”记:send base 决定左边界,rwnd 表示接收方余量,cwnd 表示网络承受量。真正能发多少,不是只看一个窗口,而是看 min(rwnd, cwnd) 后还剩多少未占用空间。这样答题能自然串起可靠传输、流量控制和拥塞控制。