TCP拥塞控制核心四算法:慢启动、拥塞避免、快重传与快恢复实战
2026/9/11 1:34:53 网站建设 项目流程

1. 先把拥塞控制放回真实场景里

期末复习周,总有人拿着“TCP 的拥塞控制”这一章犯愁:慢启动、拥塞避免、快重传、快恢复到底谁先谁后?不瞒你说,408考过,保研面试问过,工作后排查线上服务变慢的时候也照样用得上。这一章是 TCP 里最像“黑盒博弈”的部分——你永远不知道网络中间发生了什么,只能靠丢包和延迟去猜,然后动态调整发送速度。

这篇文章不打算按课本顺序平铺直叙,我尽量用“现场排查”和“备考刷题”两条线串起来讲。不管是计算机网络期末复习、考研 408,还是保研复试准备,又或者你只是个想把 TCP 基础补扎实的开发者,都可以照着下面的思路来读。读完之后再看课本,你会觉得那四个算法不再是孤立名词,而是一套完整的“堵车应对策略”。

2. 拥塞控制和流量控制,别再傻傻分不清

2.1 一个堵车例子讲清拥塞

想象你开车出门,导航显示前方五公里处有事故,但你还是按照原计划一路加速往那儿开。结果就是你和大家一起堵死在路上,谁也没办法按时到达。网络里的拥塞是同一个道理:TCP 发送方不知道中间路由器已经排队排到极限了,还在一个劲儿地发数据包,路由器缓冲区被塞满,只能把后来的包丢掉。

拥塞控制的本质,就是让 TCP 发送方学会“根据网络的承载能力调整自己的发送速率”。它不是靠某个中心节点发号施令,而是每个发送端通过观察丢包、延迟、重复确认这些间接信号,自己判断当前网络是不是“堵了”。这和你开车时不是靠交通广播而是靠导航上的红色路段去猜前方路况,非常像。

之所以要专门做拥塞控制,是因为如果每个 TCP 连接都只管自己拼命发,网络很快就会进入“拥塞崩溃”:数据包被路由器丢弃,发送方等不到 ACK 就重传,重传又加剧拥塞,最后吞吐量反而趋近于零。计算机网络的教材里常说的“拥塞窗口 cwnd”,就是发送方主动限制自己能飞多少数据的一个“油门”。

2.2 拥塞窗口和接收窗口,两个窗口别搞混

很多人刚学到这里会卡住:TCP 不是已经有滑动窗口做流量控制了吗?接收方会通过窗口字段告诉发送方“你能发多少”,为什么还要另搞一个拥塞窗口?

关键区别在于是谁在限速。流量控制的窗口叫接收窗口(rwnd),是接收方根据自己缓冲区剩余容量给出的限制,防止发送方把接收方内存撑爆。拥塞窗口(cwnd)则是发送方自己给自己加的限制,防止把网络中间的路由器撑爆。真正的发送上限是这两个窗口里较小的那个:

实际可发送的数据量 = min(cwnd, rwnd)

这是一个特别核心的公式。我见过不少面试者能把慢启动算法背得滚瓜烂熟,但一问他“发送窗口到底由谁决定”就答不上来,就是没把两个窗口的关系吃透。接收方只能约束它那一端,中间网络里有多少带宽、多少队列、哪些路由器正在高负载,只有发送方通过丢包和延迟去推断。

2.3 为什么拥塞控制设计起来这么难

TCP 发送方看不到网络内部状态,这是一个典型的“信息不完整”问题。它拿到的反馈信号非常有限:收到 ACK 说明数据正在平稳送达,收到重复 ACK 或者超时没等到 ACK,则说明网络可能出问题了。麻烦的是,这些信号并不绝对,无线网络里偶尔丢包和信号不好,不代表网络真的拥塞。

所以拥塞控制算法本质上是一套“推理机制”:用极少的观测信号,去猜一个看不见的状态。这也解释了为什么 TCP 版本迭代了这么多年,从 Tahoe、Reno、NewReno 到现在 Linux 默认的 CUBIC,以及 Google 后来搞的 BBR,算法始终在“如何更快地探测可用带宽”和“如何避免把网络塞爆”之间找平衡。基础课里学的四个算法虽然是老技术,但它们包含的设计思想——加性增、乘性减、慢启动探测、快速恢复——至今仍是所有拥塞控制算法的底子。

3. 核心四件套:慢启动、拥塞避免、快重传、快恢复

3.1 慢启动:不是一直慢,是探路

先记住一个小前提:TCP 建立连接后,发送方并不知道当前网络到底能承受多大的发送速率。如果一上来就按接收窗口的最大值猛发,网络很可能直接被冲垮。所以 TCP 采用“慢启动”的策略:发送速率从很小开始,每收到一个 ACK,拥塞窗口就增加一个 MSS。

这个策略的关键词是“指数增长”。每经过一个 RTT(往返时间),cwnd 就翻一倍:初始窗口如果是 1 个 MSS,经过 1 个 RTT 变成 2,再经过 1 个 RTT 变成 4,再来一个 RTT 变成 8。3 个 RTT 之后是初始的 8 倍,10 个 RTT 后是 1024 倍。这就是为什么教材上说慢启动“增长很快”,它只是在初始阶段慢慢试水,真正跑起来后增长效率非常惊人。

慢启动不会无限地指数增长下去,它遇到一个阈值就叫 ssthresh(慢启动阈值)。当 cwnd 小于 ssthresh 时,处于慢启动阶段;当 cwnd 达到或超过 ssthresh 时,就转入拥塞避免阶段。具体到 Linux 实现,初始 ssthresh 可以很大,甚至大于接收窗口,所以实际连接建立后,通常先经历一段很短的慢启动,等 cwnd 撞到 ssthresh 或者出现丢包才开始“收敛”。

注意:现代 TCP 的初始拥塞窗口已经不限于 1 个 MSS,RFC 6928 建议初始窗口设为 10 个 MSS。所以“慢启动”名字听着慢,实际上一开始就有一定的突发能力,适用于绝大多数宽带和移动网络。

3.2 拥塞避免:从指数换成线性

进入拥塞避免阶段后,cwnd 不再每个 RTT 翻倍,而是每个 RTT 只增加 1 个 MSS。如果以“收到每个 ACK 增加”来算,就是每收到一个 ACK 增加MSS * (MSS / cwnd)的字节数。这个线性增长很克制,让发送速率慢慢逼近网络瓶颈,不至于像慢启动那样瞬间冲过头。

为什么把指数改成线性?因为慢启动本来就是用来快速“探路”的,探到 ssthresh 附近后,说明我们已经接近网络能承受的边界了,再指数增长就太危险。拥塞避免阶段的原则是:小步试探,平稳靠近。这个阶段最理想的情况是 cwnd 一直缓慢增长,直到某一天网络出现丢包,然后进入下一轮的“减窗”过程。

教材里常写“加法增大、乘法减小”,前者就是拥塞避免阶段每个 RTT 增加 1 个 MSS,后者指的是发生拥塞后 cwnd 按比例减半甚至清零。这套机制把网络推断和速率调整绑定在一起:没丢包就证明当前速率还受得住,那就继续加;一旦丢包,就默认网络已经到了临界点,立刻降速,给网络“喘口气”的时间。

3.3 快重传:别傻等超时

正常传输中,接收方每收到一个数据段,就会回复一个 ACK 表示“下一个期待收到的字节序号”。如果中间某个数据段丢了,接收方每收到一个乱序的数据段,都会重复发送同一个 ACK,告诉发送方“我还在等我缺的那一段”。当发送方收到 3 个重复的 ACK 时,几乎可以断定那个数据段已经丢了,这时无需等待超时,立即重传丢失的数据段,这就是快重传。

这里有个容易忽略的细节:为什么不等到超时再重传,非要搞个“3 个重复 ACK”?因为超时等待时间 RTO 通常比 RTT 大不少,现代网络的 RTO 一般至少 1 秒,而 3 个重复 ACK 可能在几个 RTT 之内就会出现,比如 10 毫秒到几百毫秒不等。早一点重传,就能早一点把数据补上,避免整个连接因为一个丢包一直卡着不动。所以快重传本质上是“用重复 ACK 代替超时作为丢包信号”。

值得注意的是,收到重复 ACK 不一定代表网络拥塞,也可能只是数据包乱序到达。但因为一个 TCP 连接里同一时刻往往有多个数据段在途,连续 3 个重复 ACK 已经足够说明问题大概率不是偶然乱序,而是真丢了。教材上把这个阈值定为 3,既有理论依据,也经过了大量实测验证,不要随便改成 2 或者 4。

3.4 快恢复:丢了包不一定要回到解放前

较早的 TCP Tahoe 版本遇到拥塞时很“暴力”:只要发生丢包,就把 cwnd 降到 1 个 MSS,重新进入慢启动。这种方式简单可靠,但代价很大,好不容易探测到的带宽全被清零,网络吞吐会断崖式下跌。于是 TCP Reno 加入了快恢复:当收到 3 个重复 ACK 触发快重传时,发送方认为网络“有点堵,但没堵死”,没必要从头再来。

快恢复的具体行为可以概括为“乘性减,但保留一部分”:ssthresh 被设置为当前 cwnd 的一半,cwnd 也先设置为 ssthresh 加上 3 个 MSS(因为这 3 个重复 ACK 说明已经有 3 个数据段被接收方正常收到了,等于网络里腾出了 3 个 MSS 的“额度”)。之后每收到一个重复 ACK,cwnd 临时增加 1 个 MSS,允许发送方补发一部分新数据;直到收到新的 ACK,才把 cwnd 降到调整后的 ssthresh,然后进入拥塞避免阶段。

这段逻辑背起来有点绕,我用大白话翻译一下:网络虽然丢了包,但还能收到重复 ACK,说明通信链路没有完全中断,所以不用跌到 1,而是砍半之后再慢慢往上加。对比 Tahoe 的“清零重启”,Reno 的“减半恢复”显然更高效,这也是为什么考试里经常拿这两个版本做对比。NewReno 后来又改进了处理一个窗口内多个数据包丢失的情况,但基础思想仍然是“减半 + 线性恢复”。

4. 从卷子到网络:用抓包和实验把算法看明白

4.1 Wireshark 里看到的不只是“重传”

很多人以为抓包就能直接看到 cwnd,这是个误解。Wireshark 抓的是网卡上的数据包,而 cwnd 是 TCP 发送端内部的一个状态变量,不会直接写在报文头里。你抓包能看到的,是 cwnd 变化后的“结果”——比如突然出现大量重传、重复 ACK、RTO 超时。

我建议复习时这样玩:开两个虚拟机,或者直接在本机用 tcpdump 抓包,然后用tc模拟网络丢包和延迟。先正常下载一个大文件,再在链路上增加丢包,对比前后抓包文件的变化。你会发现当有 10% 的丢包时,抓包里会出现成片的 duplicate ACK,发送方很快重传缺失数据段,同时传输速度明显下降。这个现象,就是教材里快重传、快恢复在现实中的映射。

Wireshark 里我用得最多的过滤器是tcp.analysis.retransmissiontcp.analysis.duplicate_ack。前者直接标记出重传的数据段,后者标记重复确认。打开一个带丢包的 pcap 文件,先用tcp.analysis.flags全局筛一遍,再按时间排序,很容易看到一个丢包引发的连锁反应:重复 ACK 出现若干条,随后一条重传包跟上,接着恢复传输。

4.2 用 tc 模拟丢包,观察吞吐和重传

想亲手感受拥塞控制,不需要花钱搭复杂环境,Linux 上用tc这个命令就够了。比如给本地网卡加 10% 丢包和 100ms 延迟:

sudo tc qdisc add dev eth0 root netem loss 10% delay 100ms

加完之后用任何 TCP 下载工具拉一次数据,然后对比正常情况下的速度。你会发现加了丢包后,TCP 传输的吞吐量远远不是“正常速度的 90%”,而可能直接掉到原来的三分之一甚至更低。原因就是拥塞控制算法一旦检测到丢包,会把 cwnd 减半甚至重新进入慢启动,发送速率被一次次“打回去”。

测试完记得把规则删掉:

sudo tc qdisc del dev eth0 root

如果你用的是ss -tin,还能看到更细的信息。ss -i会打印 TCP 连接的窗口、RTT、RTO 等参数,其中cwndssthresh都会直接显示出来。在丢包场景下,你会看到 cwnd 的数值像过山车一样上下跳动:丢包时骤降,无丢包时又按拥塞避免的节奏慢慢爬升。这段实验做完,你再回头看课本里的公式,会通透很多。

4.3 理解“丢包 = 拥塞”这个基本假设

所有经典拥塞控制算法都建立在一个假设上:网络丢包是因为中间路由器太忙,缓冲区一旦满了就把新到的数据包丢弃。这个假设在有线网络上大体成立,但在无线网络、卫星链路、共享 Wi-Fi 环境下并不完全准确,因为电磁干扰、信号衰弱也会造成丢包。

这个知识点在很多期末题里不会直接考,但实际工作中特别重要。我记得有一次排查一个远程服务“时快时慢”的问题,抓包一看,大量重传,重传率超过 5%。第一反应是带宽不够,后来查无线网卡信号,发现是天线老化导致的不稳定丢包。如果当时无脑认为是拥塞,调低发送速率或者加宽带,问题根本治不好。

从算法层面说,这就是“丢包不完全是拥塞”的现实意义。CUBIC、BBR 这些新算法,本质上都是在尝试修正“丢包就等于拥塞”这个生硬假设,让 TCP 在丢包率较高的链路上也能保持比较好的吞吐。基础课可以只讲 Reno 系列,但心里要知道这些假设的边界在哪。

5. 期末、408、保研、面试的高频考点速查

5.1 五道必背简答题

第一题:简述慢启动的原理。答出“指数增长”“从 1 个 MSS 开始”“每个 RTT 翻倍”“遇到 ssthresh 转入拥塞避免”就能拿分,最好再补充一句“现代实现初始窗口可能是 10 个 MSS”。

第二题:拥塞发生后 cwnd 和 ssthresh 怎么变?分两种情况:如果是超时重传,ssthresh 设为当前 cwnd 的一半,cwnd 重置为 1 个 MSS,重新慢启动;如果是收到 3 个重复 ACK,则 ssthresh 设为 cwnd 的一半,然后进入快恢复。考试最坑的就是把这两种情况混在一起说。

第三题:为什么快重传用 3 个重复 ACK 而不是 1 个?因为 1 个重复 ACK 很可能是数据包乱序到达导致的,不足以判定丢包;3 个重复 ACK 说明后面至少有三个数据段已经到达,丢包概率高,可以安全重传。

第四题:快恢复和慢启动的区别。快恢复恢复后的起点是新的 ssthresh,而不是 1;它不重新执行指数增长,而是从 ssthresh 附近直接进入线性增长。核心是“减半但不归零”。

第五题:TCP 如何判断网络发生拥塞?主要通过两个信号:RTO 超时,以及收到 3 个重复 ACK。前者认为是严重拥塞,后者认为是一般性拥塞。有些教材还会补充说 RTT 增大也是一种拥塞信号,但在经典算法里不作为主要触发条件。

5.2 一张表搞定核心参数变化

事件cwndssthresh后续状态
慢启动阶段,收到 ACK每个 RTT 翻倍不变cwnd 到 ssthresh 后进入拥塞避免
拥塞避免阶段,收到 ACK每个 RTT +1 MSS不变保持线性增长
发生超时重置为 1 MSS设为丢包前 cwnd 的一半重新慢启动
收到 3 个重复 ACK设为 ssthresh + 3 MSS设为丢包前 cwnd 的一半进入快恢复
快恢复后收到新 ACK设为 ssthresh不变进入拥塞避免

背这张表之前,先理解一个关系:cwnd 是发送速率的核心,ssthresh 是“快慢状态”的分界线。超时等于“道路完全堵死”,所以要回到起点重新探路;重复 ACK 等于“堵了一部分但还在走”,所以只是减半再缓慢恢复。

5.3 最容易丢分的三个误区

第一个误区:把流量控制和拥塞控制混为一谈。流量控制是接收方怕自己处理不过来,拥塞控制是网络怕自己被塞爆,两者由不同的“信号”驱动,一个是接收窗口,一个是丢包和 RTT。答题时一定要分清楚。

第二个误区:认为收到重复 ACK 时 cwnd 直接减半。准确说法是:先更新 ssthresh,进入快恢复之后 cwnd 先等于 ssthresh + 3 MSS,不是简单地减半。这个“+3”很容易被忽略,但它恰好体现了快恢复的设计用意:已经有 3 个数据段被接收,网络空出了空间。

第三个误区:把三次握手、四次挥手当成拥塞控制的一部分。三次握手解决的是连接建立时的“双方是否在线”问题,四次挥手解决的是连接释放问题,拥塞控制解决的是“连接建立后发多快”的问题。考试里的简答题如果串了,正确答案也容易被判定为逻辑不清。

还有一个面试中常问的延伸题:如果网络一直不丢包,拥塞窗口会无限增长吗?答案是理论上会,但实际到达接收窗口或者链路瓶颈时,要么被 rwnd 卡住,要么网络开始排队丢包,所以不可能无限增长。这个问题考的就是 min(cwnd, rwnd) 有没有真正理解。

6. 实际网络排查里的拥塞控制影子

6.1 先看重传率,别一上来就 ping

遇到“应用卡顿”“下载慢”“视频加载不出来”这类问题,很多人的第一反应是 ping 一下看通不通。但 ping 用的是 ICMP,不能代表 TCP 的真实传输质量。更稳妥的做法是用ss -tin查看 TCP 连接的重传、RTT、cwnd,或者用 tcpdump 抓包统计重传率。

重传率高,通常意味着网络中确实存在丢包,TCP 的拥塞控制已经把 cwnd 压低了很多,所以吞吐上不去。不要小看 cwnd 这个数字,它低的时候,即使链路带宽再大,发送方也只能像挤牙膏一样一点点发。检查的时候我会同时看两个指标:RTO 平均值和重传包占比。如果重传占比超过 2%,网卡或链路很可能已经有明显问题。

排查现场经常用到的一个命令组合:

ss -tin | grep -E "cwnd|rtt|retrans"

当然ss的输出里没有叫 “retrans” 的字段,实际上要看重传计数,更常用的还是抓包统计。用 tcpdump 抓 30 秒流量,再用 Wireshark 打开统计tcp.analysis.retransmission标记数量,比靠肉眼看命令行输出直观得多。

6.2 用 cwnd 判断“为什么快不起来”

我记得帮朋友排查过一个内部工具上传慢的问题:客户端到服务器在同一机房,按理说带宽很足,但文件传输速度始终上不去。抓包后发现一个奇怪现象:发送方的 cwnd 长期只有几百字节,远小于接收窗口。

后来一查,是客户端所在虚拟机网卡模式的丢包率高得离谱,TCP 每次还没把 cwnd 涨起来就触发丢包,被一次次打回原型。拥塞控制的“探测机制”在这种场景下反而成了拖后腿的因素——它把随机丢包当成了网络饱和信号,不断降速。最后换了虚拟网卡驱动,上传速度马上恢复正常。

这个案例给我们的启发是:拥塞控制是保护网络的重要手段,但当成因不在“拥塞”而在“链路质量”时,不能只靠调 TCP 参数解决,还得从物理层、链路层找原因。普通业务方遇到这种问题,优先检查网卡丢包、交换机端口统计、无线信号强度,都比调内核参数更有效。

6.3 线上服务常见的“拥塞窗口恢复慢”

还有一种很常见的坑:TCP 长连接长时间空闲后,再次发数据时 cwnd 已经降回初始值,如果数据量比较大,就会感觉第一波请求明显偏慢。Linux 内核里有一个tcp_slow_start_after_idle参数默认开启,就是用来控制这种情况的。

如果公司内部网络质量稳定,RTT 很低,可以关掉空闲后慢启动,让连接保留下来的 cwnd:

sysctl -w net.ipv4.tcp_slow_start_after_idle=0

这个参数不是银弹,它本身也是“拥塞状态是否应该被遗忘”的策略选择。对于内网非常稳定的服务,关掉确实能减少首包延迟;但在公网、移动网络下,直接沿用旧 cwnd 可能造成突发发送,反而触发丢包。所以调这个参数一定要结合具体场景,先观察再动手。

我个人在实际排查中的体感是,拥塞控制就像一个“交通管制系统”,平时感觉不到它的存在,一旦网络出现抖动,它的反应速度和恢复能力直接决定了用户体验。学这一章的时候,别只背那几个算法的名字和公式,多想想每个决策背后的取舍。等你哪天真在线上看到重传率飙升、传输速率骤降,会突然明白课本里那些简洁的图示,原来描述的正是这些真实世界里的网络故事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询