TCP 拥塞控制:慢启动、拥塞避免、快速重传和快速恢复
围绕TCP 拥塞控制章节,讲清 cwnd、ssthresh、慢启动、拥塞避免、快速重传、快速恢复,以及它和流量控制的区别。
相关工具
拥塞控制保护的是网络
上一篇讲流量控制时,重点是保护接收方:接收端缓冲区不够,发送方就要少发。TCP 拥塞控制看的是另一件事:网络中间的链路、路由器队列、交换设备和路径容量,能不能承受当前发送速度。
资料 对拥塞控制的描述是:TCP 可以在网络出现拥塞时动态调整数据传输速率,以防止网络过载。这里的“拥塞”可以理解为路上太挤了。发送方发得太猛,中间设备排队变长,延迟升高,最终开始丢包。
TCP 不能直接问每台路由器“你堵不堵”。它主要从反馈里推断网络状态:ACK 回来得顺不顺,有没有超时,有没有重复 ACK,丢包是不是出现了。然后它调整拥塞窗口 `cwnd`,控制自己在未收到确认前最多能放多少数据到网络里。
cwnd 是发送方对网络容量的估计
拥塞窗口 `cwnd` 是发送方维护的变量。它不是接收方告诉发送方的,而是发送方根据网络反馈自己调出来的。网络反馈好,ACK 持续回来,发送方就逐步增大 `cwnd`;网络出现丢包或超时,发送方就认为可能拥塞,降低 `cwnd`。
这和接收窗口 `rwnd` 不同。`rwnd` 来自接收方,表达的是接收缓冲区还有多少空间;`cwnd` 来自发送方拥塞控制算法,表达的是网络路径大概还能承受多少未确认数据。真实发送窗口通常取两者较小值。
所以带宽看起来很大,不代表 TCP 可以马上拉满。TCP 会一点点试探。试探太慢,带宽浪费;试探太猛,网络拥塞。拥塞控制就是在这两者之间找平衡。
慢启动:从小窗口开始试探
已有内容提到慢启动时说,TCP 发送方会以较小的发送窗口开始传输数据,随着每次成功收到确认,逐渐增加发送窗口大小,呈指数级增长。名字叫慢启动,但它的增长并不慢,只是起点很小。
为什么不能一开始就大窗口?因为发送方刚建立连接时,并不知道这条路径真实容量有多大。贸然把大量数据灌进去,可能直接把中间队列打满。慢启动先用小窗口试水,ACK 顺利回来,就快速扩大窗口。
可以把它想成开车上高速时的加速过程。你不会一脚油门到底,而是先确认路况和车距。慢启动就是 TCP 的前期试探:只要网络反馈正常,就把发送能力较快地提起来。
拥塞避免:接近阈值后别再猛冲
慢启动不会无限指数增长。TCP 有一个慢启动阈值 `ssthresh`。当 `cwnd` 增长到这个阈值附近,就进入拥塞避免阶段。文中写到,在拥塞避免阶段,发送方以线性增加的方式增加发送窗口,而不是继续指数级增长。
这一步很重要。刚开始小窗口时指数增长能快速利用带宽,但窗口已经比较大时,再翻倍就危险了。拥塞避免把增长节奏放缓,每个 RTT 只小步增加,让发送方接近网络容量时更谨慎。
如果网络一直稳定,`cwnd` 会在线性增长中慢慢变大,吞吐也会提升。直到出现丢包、超时或重复 ACK,发送方才会认为网络可能到了承受边界,需要降速。

慢启动让 cwnd 从小窗口快速增长;拥塞避免改为线性增长;丢包后通过超时或重复 ACK 触发降速、快速重传和快速恢复。
超时:最强烈的拥塞信号
发送方发出数据后,会等待 ACK。如果超过重传超时时间还没收到确认,它会认为数据可能丢失。资料 在重传机制部分也提到,发送方会为每个发送的数据段设置定时器,超时后触发重传。
在拥塞控制里,超时通常被看作比较严重的拥塞信号。因为 ACK 长时间没有回来,可能说明数据包或 ACK 丢了,也可能说明网络排队已经很严重。发送方会明显降低 `cwnd`,重新从较小窗口开始探测。
这种处理会让吞吐突然下降,但它保护了网络。如果所有发送方在丢包后还继续猛发,拥塞会越来越严重,更多包丢失,更多重传加入,整个网络会更慢。TCP 选择退让,是为了让路径有机会恢复。
快速重传:不用等超时才重传
已有内容提到快速重传时说,当接收方连续收到相同确认号时,会发送冗余确认,触发发送方进行重传。这个机制的意义在于:有些丢包不需要等到定时器超时才能发现。
举个例子,发送方连续发送 1、2、3、4、5 号数据段,2 号丢了,但 3、4、5 到了。接收方因为缺少 2,只能反复确认“我还在等 2”。发送方收到多个重复 ACK,就能推断某个中间段丢了,于是立刻重传。
快速重传比超时更温和。它说明网络仍然在传输后续包,ACK 也能回来,只是某个包丢了。发送方需要降速,但不一定要像超时那样完全回到起点。
快速恢复:降速,但不完全归零
快速恢复通常和快速重传连在一起。文中写到,发送方收到快速重传的确认后,不需要等到超时再次发送,而是可以使用快速恢复算法继续发送未丢失的数据。
直观理解,快速恢复是在承认网络出了问题的同时,也承认网络没有完全瘫掉。因为重复 ACK 能回来,说明接收方还在收到后续数据,路径还有传输能力。所以发送方会把阈值降下来,把 `cwnd` 调小,然后在较低水平继续恢复。
这比超时后从很小窗口重新慢启动更平滑。它既减少发送压力,又不把吞吐打到最低。对长连接和大文件传输来说,快速恢复能让连接在偶发丢包后更快回到稳定状态。
rwnd 和 cwnd 一起决定能发多少
上一章说过,流量控制的接收窗口 `rwnd` 来自接收方。现在加上拥塞窗口 `cwnd`,发送方真实能发送多少,就要同时看两边。接收方能接很多,但网络拥塞,不能多发;网络不拥塞,但接收方缓冲区很小,也不能多发。
常见表达是:实际发送窗口受 `min(rwnd, cwnd)` 限制。比如 `rwnd=64KB`、`cwnd=16KB`,发送方最多按 16KB 来;如果 `rwnd=8KB`、`cwnd=64KB`,发送方最多按 8KB 来。
这个区别对排查很有用。吞吐低不一定是带宽低。如果抓包里接收窗口很小,可能是接收端应用读得慢;如果接收窗口很大但丢包、重传、RTT 抖动明显,可能是网络拥塞或链路质量差。

rwnd 看接收方缓冲区能力,cwnd 看网络拥塞程度。发送方实际窗口受两者共同限制,取较小值。
为什么拥塞控制会让速度上下波动
很多 TCP 传输的速度不是一条平滑直线,而是上升、下降、再上升。原因就在拥塞控制。网络反馈好,`cwnd` 增大,发送速度提升;出现丢包或超时,`cwnd` 降低,发送速度下降;网络恢复后,又继续增加。
这种波动不是坏事。它是 TCP 在共享网络中寻找合适速率的过程。互联网上有很多连接同时使用同一条链路,如果每个连接都只追求自己最快,整体就会变得更差。拥塞控制让每个连接在发现拥塞时后退一点。
从应用层看,这意味着大文件下载、接口响应、跨地域同步都会受 RTT、丢包率、链路竞争影响。即使服务器性能很好,只要路径上丢包明显,TCP 也会通过拥塞控制把发送窗口压下来。
工程排查:先看丢包、重传和 RTT
怀疑拥塞控制影响吞吐时,可以先看几个现象。第一是重传率。如果抓包或监控里 TCP Retransmission 明显增加,说明发送方确实在补发数据。第二是重复 ACK,如果大量出现,可能触发快速重传。
第三是 RTT。拥塞初期常常先表现为排队延迟增加,RTT 抖动变大,然后才开始丢包。第四是发送窗口。如果 `rwnd` 不小,但吞吐上不去,同时重传和 RTT 都异常,就更像网络拥塞而不是接收端处理慢。
排查时也要看路径。内网、跨机房、跨运营商、跨境链路的表现差异很大。拥塞控制是在端到端连接上运行的,问题可能不在应用服务器,也不在客户端,而在中间链路。
面试里怎么讲
如果被问 TCP 拥塞控制,可以先说目的:它是在网络出现拥塞时动态调整发送速率,防止发送方把网络链路和路由队列打满。发送方通过拥塞窗口 `cwnd` 控制未确认数据量。
然后讲四个机制:慢启动从小窗口开始,收到 ACK 后指数增长;到达阈值后进入拥塞避免,改为线性增长;收到多个重复 ACK 时触发快速重传,不等超时就重传丢失段;快速恢复在降速后不完全回到起点,而是从较低窗口继续恢复。
最后讲和流量控制的区别:流量控制看接收方能力,用 `rwnd` 防止接收缓冲区溢出;拥塞控制看网络拥塞程度,用 `cwnd` 防止网络过载。实际发送窗口通常取 `rwnd` 和 `cwnd` 的较小值。
常见问题
慢启动为什么叫慢启动?
因为它从很小的拥塞窗口开始试探网络,而不是一开始就大发送量。但窗口增长本身通常是指数级的,所以它并不是一直很慢。
快速重传和超时重传有什么区别?
超时重传要等定时器超时,通常说明拥塞信号更严重;快速重传依靠多个重复 ACK 提前发现丢包,不必等超时,恢复也相对平滑。
吞吐低一定是拥塞控制导致的吗?
不一定。吞吐低可能是接收窗口小、应用处理慢、带宽限制、丢包、RTT 高、服务端限速等多种原因。要结合 rwnd、cwnd、重传率和 RTT 一起看。