Skip to content

TCP 拥塞控制典型计算题

2026 大纲 五(三)5 TCP 拥塞控制。本篇是《TCP 拥塞控制》的计算配套篇,只处理"给定初始参数与事件序列,逐轮推出 cwnd / ssthresh / 发送窗口"这一类问题;四种算法的机制与推导一概不重讲、只引用。

一、八成的错来自口径,不是算错

上一篇把四种算法讲完了,规则本身并不难。可这类题的失分率却很高——因为规则的边界处有四个含糊点,题面往往不说明,而不同的取法会让整张表整体错开

所以做题的第一件事不是算,是把四个口径钉死。

口径①:「第 n 轮末发生事件」的语义。n 轮的 cwnd 仍按事件前的规则算出(该轮窗口在发数据时就定了),事件的影响从第 n+1 轮开始体现。但若题面写的是"当 cwnd = X 时发生超时",就直接在这一点处理事件。两种措辞的答案整体错开一轮——审题的第一个动作永远是分清题面用的是哪一种。

口径②:cwnd = ssthresh 时算哪个阶段。 教材原话是"既可使用慢开始算法,也可使用拥塞避免算法"(标准留下的两可)。本站统一按拥塞避免,即从这一轮起 +1。题面另有说明以题面为准。

口径③:慢开始翻倍会被 ssthresh 封顶。 依据是教材图 5-25 的点③——超时后 ssthresh 被调成 12、cwnd 从 1 重新慢开始,教材写的是"当拥塞窗口 cwnd = ssthresh = 12 时,改为执行拥塞避免算法"。若允许无脑翻倍,8×2=16 就永远取不到 12 这个值。 换个角度也讲得通——ssthresh 的字面意思就是"慢开始门限",冲过天花板再往回退,门限就没有意义了。

口径④:ssthresh 除 2 向下取整。 cwnd 为奇数时取 cwnd/2:24→12、25→12、15→7、11→5、7→3、3→1。题面另有说明以题面为准。

两条最容易踩空的边界

其一:事件处理一律用"事件发生时的旧 cwnd"算 ssthresh,顺序不能反。 先算 ssthresh,再设 cwnd。反过来先设 cwnd 再算 ssthresh,结果差一倍。

其二:rwnd 从不参与拥塞控制的状态更新。 发送窗口=min(cwnd, rwnd),rwnd 只影响"实际能发多少",完全不影响 cwnd 自身的演变;事件处理(算 ssthresh、设 cwnd)一律用 cwnd。慢开始封顶用的也是 ssthresh 而不是 rwnd。

还有两处小提醒。cwnd 的单位多数题目用 MSS,也可能用字节——用字节时"翻倍""+1"都要换算成"加一个 MSS",审题时先确认。ssthresh 只在事件发生时改变,平时保持不变;所以只问"某一轮的 ssthresh"时不必逐轮算 cwnd,找到最近一次事件发生在哪一轮、当时 cwnd 是多少即可。

二、两张表 + 四步流程

规则转自《TCP 拥塞控制》,本篇不另立口径。

逐轮规则(正常传输时):

本轮阶段判定条件下一轮 cwnd
慢开始cwnd < ssthresh×2,但封顶到 ssthresh(长到 ssthresh 就停住)
拥塞避免cwnd ≥ ssthresh+1

事件分支:

事件TCP RenoTCP Tahoe
超时ssthresh=cwnd/2cwnd=1,回慢开始与 Reno 完全相同
3 个重复确认ssthresh=cwnd/2cwnd=ssthresh,进拥塞避免(快恢复)当超时处理cwnd=1,回慢开始

做题四步:

第一步,列出初始参数(cwnd 初值、ssthresh 初值、有无 rwnd),并确认题面的事件措辞属于口径 ① 的哪一种。

第二步,画表格,列固定为——轮次 / cwnd / ssthresh / 阶段 / 事件 / 发送窗口。有 rwnd 才写最后一列。

第三步,逐行推进,每行的动作顺序是:① 先按 cwnd 与 ssthresh 的大小关系判定本轮阶段;② 填本轮的 cwnd、ssthresh、发送窗口;③ 看本轮末有没有事件——有事件就先算新 ssthresh 再设新 cwnd无事件就按本轮阶段的规则算下一轮 cwnd(慢开始翻倍且封顶到 ssthresh,拥塞避免 +1)。

第四步,用下面四条自查法扫一遍。

三、四条自查法

  1. 慢开始段是不是纯翻倍。 应该是 124816。中间出现不是翻倍的值,只有一种合法解释——被 ssthresh 封顶,且那个值必须恰好等于当前 ssthresh;不等就是算错了。
  2. 事件后的 ssthresh 一定小于事件发生时的 cwnd(因为它是那个 cwnd 的一半再向下取整)。算出比它还大的必错。
  3. 拥塞避免段必须每轮恰好 +1。 出现跳跃或翻倍说明阶段判定错了——大概率是"cwnd = ssthresh 那一轮"判成了慢开始。
  4. 快恢复后 cwnd 绝不为 1。 Reno 的 3 个重复确认后 cwnd=ssthresh>1cwnd 降到 1 只有两种可能:要么这是超时事件,要么题目用的是 Tahoe。

再补一条口径自查:把最终答案整体前移或后移一轮,看看是不是更符合题面措辞——若题面是"当 cwnd = X 时发生超时"而你按"第 X 轮末"处理了,结果会整体错开一轮。

Reno 的完整逐轮推演与同题换 Tahoe 的对照(第一次做这类题、或想看清两类事件怎么落在表里时展开)

某 TCP 连接使用 Reno 算法,初始 cwnd=1 MSS,ssthresh=8 MSS。第 6 轮末发送方收到 3 个重复确认,第 13 轮末发生超时。求第 0~18 轮每一轮的 cwnd 与 ssthresh。

轮次cwndssthresh本轮阶段本轮末事件下一轮 cwnd 的由来
018慢开始1×2=2
128慢开始2×2=4
248慢开始4×2=8
388拥塞避免(cwnd = ssthresh,按口径②)8+1=9
498拥塞避免9+1=10
5108拥塞避免10+1=11
6118拥塞避免3 个重复确认ssthresh=11/2=5;② cwnd=5
755拥塞避免(cwnd = ssthresh)5+1=6
865拥塞避免7
975拥塞避免8
1085拥塞避免9
1195拥塞避免10
12105拥塞避免11
13115拥塞避免超时ssthresh=11/2=5;② cwnd=1
1415慢开始1×2=2
1525慢开始2×2=4
1645慢开始4×2=8>5封顶到 5(口径③)
1755拥塞避免5+1=6
1865拥塞避免7

cwnd 序列1 2 4 8 9 10 11 | 5 6 7 8 9 10 11 | 1 2 4 5 6

三处关键步骤的"为什么":

  • 第 3 轮为什么直接进拥塞避免。 慢开始从 4 翻倍到 8,恰好等于 ssthresh,按口径② 执行拥塞避免,所以第 4 轮 +1 而不是翻倍。这一步定错后面每一行都会错——"翻倍到刚好等于 ssthresh 的那一轮"算慢开始还是拥塞避免,直接决定下一轮是 16 还是 9。
  • 第 6 轮为什么用 11 除 2 而不是用 5。 ssthresh 要用事件发生时的 cwnd(第 6 轮那个 11)算:11/2=5,然后才把 cwnd 设成 5。反过来先设 cwnd 再算 ssthresh,会得到 5/2=2,整条后续全错。
  • 第 16 轮为什么是 5 不是 8。 慢开始翻倍 4×2=8 已越过 ssthresh = 5,按口径③ 封顶到 5。全表只有这一格用到"封顶"规则,也是最容易写成 8 的地方。

同一题其余条件不变、改用 TCP Tahoe: 唯一改动是 3 个重复确认当超时处理ssthresh=cwnd/2cwnd=1、回慢开始),超时的处理两者完全相同。

轮次6789101112131415161718
Reno cwnd11(事件)567891011(超时)12456
Tahoe cwnd11(事件)1245678(超时)12456
  • Reno:1 2 4 8 9 10 11 5 6 7 8 9 10 11 1 2 4 5 6
  • Tahoe:1 2 4 8 9 10 11 1 2 4 5 6 7 8 1 2 4 5 6

三个值得注意的差别:① 第 7 轮就分岔,Reno 回到 5、Tahoe 回到 1;② Tahoe 的第 9→10 轮出现封顶4×2=8>5,封顶到 5);③ 第 13 轮超时时两者的 cwnd 已经不同(Reno 11、Tahoe 8),所以算出的新 ssthresh 也不同11/2=58/2=4)。

第 ③ 点最容易漏:事件本身的处理规则相同,但代入的 cwnd 不同,结果自然不同。 不要以为"超时处理一样"就可以直接抄 Reno 的数。第 14 轮之后 Tahoe 的 ssthresh 是 4,慢开始 1,2,4 到第 16 轮就等于 ssthresh 了,数值上与 Reno 恰好重合,但阶段判定的理由不同


题目给了 rwnd 时:表多一列,但 cwnd 的演变一点不变。 设 Reno、cwnd=1ssthresh=14、接收方始终通告 rwnd=12第 10 轮末发生超时。慢开始 1,2,4,8 之后 8×2=16>14封顶到 14——注意封顶用的是 ssthresh 而不是 rwnd(rwnd 管"能发多少",ssthresh 管"cwnd 长到哪儿换算法",两者不是一回事);此后拥塞避免 14,15,,20

轮次45678910
cwnd14151617181920
发送窗口 = min(cwnd, 12)12 ← 受限121212121212 ← 受限

第 4~10 轮的发送窗口全部被 rwnd 卡在 12(共 7 轮)。而第 10 轮末超时时,新 ssthresh=20/2=10——不是拿发送窗口 12 去除得到的 6。

这一格最容易踩空:发送窗口只有 12,但拥塞控制的状态变量是 cwnd,事件处理一律用 cwnd。 用 12 去除会得到 6,整条后续全错。rwnd 从不参与拥塞控制的状态更新。


问"前 n 轮共发送多少"时,用分段闭式互证,别逐个累加。 每轮发送的数据量等于该轮的 cwnd(假设每轮都能把窗口用满),而 慢开始段是等比数列、拥塞避免段是等差数列,两段各有闭式,可以互相印证——这是通用的求和自查法,不限于某一道题。

cwnd0=1 MSS、ssthresh=32 MSS、全程无拥塞事件为例,前 10 轮(第 0~9 轮)的 cwnd 是 1 2 4 8 16 32 33 34 35 36(第 5 轮 16×2=32=ssthresh,按口径② 转拥塞避免,故第 6 轮起 +1):

1+2+4+8+16慢开始段=251=31,32+33+34+35+36拥塞避免段=5×34=17031+170=201 MSS

与逐个累加的结果一致 ✓。两段闭式对不上,就说明阶段分界点判错了——最容易出问题的还是"cwnd = ssthresh 那一轮"归错了阶段。

给出 cwnd 逐轮取值,反推事件类型与各次 ssthresh(想掌握反推题的通用套路时展开)

某 TCP Reno 连接的 cwnd 逐轮取值(单位 MSS):1, 2, 4, 8, 16, 17, 18, 19, 20, 10, 11, 12, 13, 1, 2, 4, 6, 7。求初始 ssthresh、各次事件的轮次与类型、以及每次事件后的新 ssthresh。

第 1 步:按"相邻两轮怎么变"分类。 判据只有五条:

相邻变化判定
翻倍b=2a慢开始
加 1b=a+1拥塞避免
降到 1超时
降到大于 1 的值3 个重复确认(快恢复)
既不翻倍也不 +1 的上升慢开始被 ssthresh 封顶,此时 b=ssthresh

第 2 步:逐段判。

轮次段cwnd 变化判定
0→41,2,4,8,16 翻倍慢开始
4→516→17 加 1转入拥塞避免 ⟹ 初始 ssthresh = 16
5→817,18,19,20 加 1拥塞避免
8→920 → 10,降到大于 1第 8 轮末:3 个重复确认;新 ssthresh=20/2=10cwnd=10 ✓ 与序列一致
9→1210,11,12,13 加 1拥塞避免(cwnd = ssthresh 起步即 +1)
12→1313 → 1第 12 轮末:超时;新 ssthresh=13/2=6
13→151,2,4 翻倍慢开始
15→164 → 6,既非翻倍(应为 8)也非 +1慢开始被封顶,故 ssthresh=6 ✓ 与上一步算出的 6 吻合
16→176→7 加 1拥塞避免

第 3 步:交叉验证。 这一序列里有两处可以互证:第 8 轮末算出 ssthresh=10,而序列里紧接着的 cwnd 正是 10——Reno 快恢复要求 cwnd = ssthresh,对上了;第 12 轮末算出 ssthresh=6,而第 16 轮慢开始被封顶到的值正是 6——两条独立路径给出同一个 ssthresh,说明判断没错

答案:初始 ssthresh = 16;第 8 轮末收到 3 个重复确认(ssthresh 变 10);第 12 轮末超时(ssthresh 变 6)。

反推题的通用套路就是这两步:先用"降到 1 还是降到大于 1"分出事件类型,再用"封顶值"和"快恢复后的 cwnd"回头验证 ssthresh。 只要有一处对不上,就说明前面某一段的阶段判错了。

本节小结

  1. 八成的错来自口径不统一,不是算错。 四个口径必须先钉死:「第 n 轮末发生事件」的影响从第 n+1 轮体现(题面写"当 cwnd = X 时"则直接在该点处理,两种措辞整体错开一轮);cwnd = ssthresh 时按拥塞避免;慢开始翻倍封顶到 ssthresh;ssthresh 除 2 向下取整
  2. 两条最容易踩空的边界:事件处理一律用事件发生时的旧 cwnd 算 ssthresh,顺序反了差一倍;rwnd 只参与 min 取小,从不参与 cwnd 的状态更新,慢开始封顶用的也是 ssthresh。
  3. 表填完必扫四条自查:慢开始段除封顶格外纯翻倍且封顶值恰等于当前 ssthresh/事件后的 ssthresh 一定小于事件时的 cwnd/拥塞避免段每轮恰好 +1/快恢复后 cwnd 绝不为 1(是 1 就只能是超时或 Tahoe)。

考点速记

本篇处理的正是真题里被考过的形式中最难的那一类:cwnd 与 rwnd 两条线同时跟踪。 三道题恰好把三种"该看哪条线"的情形各考了一遍。

① 只有 cwnd 一条线:慢开始要几轮(cn-2017-39)。MSS = 1 KB、RTT = 5 ms、乙的接收缓存 64 KB,问从连接建立成功到发送窗口达到 32 KB 最少需要多久。

先判 rwnd 会不会成为瓶颈:接收缓存 64 KB > 目标 32 KB,全程不受限,所以只跟 cwnd 一条线。慢开始 12481632从 1 涨到 32 需要 5 个 RTT5×5=25 ms,答 A

选项 B(30 ms)是多算了一轮(把"连接建立"那 1 个 RTT 也算进去了,可题面明说"从连接建立成功"起算);C(160 ms)与 D(165 ms)则是按拥塞避免算的——每轮 +1,从 1 涨到 32 要 31 轮。这道题的机关在"最少"两个字:最少就走慢开始。

② 两条线都要跟,rwnd 最后封顶(cn-2014-38)。甲在 t 时刻超时,当时 cwnd = 8 KB;乙每收到一个数据段都通告接收窗口 10 KB,问 10 个 RTT 后甲的发送窗口

先执行事件动作:ssthresh=8/2=4cwnd=1。然后逐轮:

RTT12345678910
本轮 cwnd124567891011
阶段慢开始慢开始拥塞避免拥塞避免拥塞避免拥塞避免拥塞避免拥塞避免拥塞避免拥塞避免

10 个 RTT 之后 cwnd 涨到 12 KB,但 rwnd 一直是 10 KB → 发送窗口 =min(12,10)=10 KB,答 A

这道题两处都能栽:一是第 3 轮 cwnd 恰好等于 ssthresh = 4,按口径② 要转拥塞避免(判成慢开始会得到 8 而不是 4,后面全错);二是最后一步忘了取 min,直接答 12——而 12 恰好不在选项里,反倒提示你漏了一步。选项里的 14、15 则是阶段判错后的产物。

③ 一个 ACK 之后还能发几段:要扣掉已发未确认(cn-2025-38)。甲在 t0 的 cwnd = 发送窗口 = 2000 B,ssthresh = 8000 B,MSS = 1000 B;t1 收到一个确认段,问未收到新确认之前还能继续发几个 TCP 段。

这道题最容易错的不是拥塞控制,是对 ack_seq 的理解。 三步:

第一步,cwnd 涨多少。 2000<8000,仍在慢开始;慢开始的规则是"每收到一个 ACK,cwnd += 1 MSS"t1 只收到 1 个 ACK → cwnd=2000+1000=3000 B。

慢开始不是"翻倍",是"每 ACK 加 1 个 MSS"。 只有当一个 RTT 内发出的段被同时确认时,效果上才等于翻倍。本题只收到 1 个 ACK,所以只加 1 个 MSS。

第二步,发送窗口。 min(cwnd,rwnd)=min(3000,4000)=3000 B。

第三步,扣掉已发未确认。 甲在 t0~t1 之间发了 2 个段:seq=2001(占 2001~3000)和 seq=3001(占 3001~4000)。收到的确认是 ack_seq=3001——它表示"期待 3001",也就是 3001 那一段还没被确认。所以已发未确认 = 1000 B

还能发=30001000=2000 B=2 MSS

Aack_seq=3001 读成"seq=3001 那段已确认"的人会算出 3 段甚至更多——ack_seq = N 永远表示"期待 N",意味着 <N 的已确认、N 及以后的还没有。

本篇练习区里还会出现四道题:cn-2009-39、cn-2020-38、cn-2022-38 只跟 cwnd 一条线,讲在 TCP 拥塞控制;cn-2015-39 的 rwnd 会被一路榨干到 1 KB,讲在 TCP 流量控制

本篇的四个口径与两个折叠推演在真题里不单独成题,但它们是上面三道题不出错的前提——尤其是口径②(cwnd = ssthresh 归拥塞避免),cn-2014-38 的第 3 轮正卡在这一格上。

易错cwnd 恰好等于 ssthresh 那一轮按拥塞避免算(口径②),判成慢开始会让后面每一行都错。

易错先用事件发生时的旧 cwnd 算 ssthresh,再设新 cwnd。 顺序反了差一倍。

易错最后一步别忘了取 min(cwnd,rwnd) 题面给了接收缓存或 rwnd 就一定要跟这条线。

易错rwnd 只参与取 min,从不参与 cwnd 的状态更新。 算 ssthresh 一律用 cwnd。

易错慢开始的规则是"每收到一个 ACK 加 1 个 MSS",收到几个就加几个;"翻倍"只是一个 RTT 内全部被确认时的效果。

易错ack_seq = N 表示"期待 N",N 及以后的还没被确认——算已发未确认时全靠这一条。

易错慢开始翻倍会被 ssthresh 封顶(口径③),不会冲过去再退回来。

易错问"最少时间"走慢开始、问"最长时间"走拥塞避免。

教材出处
  • 本篇的全部动作规则、口径依据与教材页码,见 TCP 拥塞控制 · 教材出处。本篇不重复列出,只标注三处直接支撑本篇口径的原文:
  • 谢希仁《计算机网络》(第 8 版)p243"当 cwnd < ssthresh 时,使用上述的慢开始算法。当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞避免算法。" ——这是口径②的来源(标准留两可,本站统一取拥塞避免)。
  • 同书 p243:图 5-25 点③的叙述 "按照慢开始算法,发送方每收到一个对新报文段的确认 ACK,就把拥塞窗口值加 1。当拥塞窗口 cwnd = ssthresh = 12 时(图中的点③,这是 ssthresh 第 1 次调整后的数值),改为执行拥塞避免算法" ——超时后 ssthresh 被调为 12,cwnd 从 1 重新慢开始,若允许无脑翻倍则序列为 1,2,4,8,16,永远取不到 12 这个值;教材明确写 cwnd 到达 12,即慢开始被 ssthresh 封顶。这是口径③的直接依据。
  • 同书 p244:快恢复的三步 "发送方第 2 次调整门限值,使 ssthresh = cwnd / 2 = 8,同时设置拥塞窗口 cwnd = ssthresh = 8,并开始执行拥塞避免算法" ——先算 ssthresh 再设 cwnd 的顺序,以及"Reno 快恢复后 cwnd 等于新 ssthresh"这条自查依据。

相关知识

TCP 拥塞控制TCP 流量控制TCP 可靠传输

真题练习