TCP 滑动窗口是什么?它和流量控制、拥塞控制有什么关系?
简化版
TCP 滑动窗口允许发送方在未收到前一个 ACK 前连续发送多个字节段,用来提升吞吐。真正能发送的窗口通常取 min(接收窗口 rwnd, 拥塞窗口 cwnd),所以它既支撑可靠传输,也同时受流量控制和拥塞控制约束。
详细版
滑动窗口解决的是“不能发一个包等一个 ACK”的低效率问题。发送方维护一个发送窗口,窗口内的数据可以发送但尚未全部确认;接收方维护接收窗口,告诉对方自己还能接收多少数据。
关键点:
- TCP 的序列号按字节编号,不是按包编号。
- ACK 常见是累计确认,
ACK=5001表示 5001 之前的字节都连续收到了。 - 接收窗口
rwnd防止压垮接收方,是流量控制。 - 拥塞窗口
cwnd防止压垮网络,是拥塞控制。 - 发送方实际可发数据量通常受
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 | 暂停发送数据,等待窗口恢复 |
这就是流量控制。它保护的是接收方,不是整个网络。很多人把 rwnd 和 cwnd 混在一起,面试时一定要明确对象不同。
四、拥塞窗口 cwnd 解决网络承压问题
即使接收方还能接收,网络中间链路也可能拥塞。TCP 拥塞控制维护 cwnd,通过慢启动、拥塞避免、快速重传和快速恢复等机制估计网络能承受的发送量。
发送方最终不能只看接收窗口,而要同时考虑接收方和网络:
可发送窗口 = min(rwnd, cwnd) - 已发送未确认数据量
例如 rwnd=64KB、cwnd=16KB、已发送未确认 10KB,那么当前还能新发 min(64,16)-10=6KB。这说明接收方虽然有空间,但网络侧限制更紧。
五、累计 ACK、乱序与窗口滑动
TCP 常见 ACK 是累计确认。接收方收到 1001~2000、2001~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) 后还剩多少未占用空间。这样答题能自然串起可靠传输、流量控制和拥塞控制。