COMP5416 Week 06 TCP 与拥塞控制讲课总结

COMP5416 Week 06 讲课总结:TCP

课程:COMP5416/COMP4416 — Advanced Network Technologies,Semester 2 2026 讲师:Professor Vincent Gramoli 来源:讲义 W6-TCP.pdf(56 页)。本周没有转录稿,以下内容全部依据讲义整理,必要处用 Kurose & Ross 教材补全解释。 教材:Kurose & Ross 9th ed. —— 第 3 章 3.5 / 3.6 / 3.7 节

本周覆盖:面向连接的传输(TCP)→ 拥塞控制原理 → TCP 拥塞控制。 它直接接在 Week 05 传输层 的 rdt / GBN / SR 之后:上周讲「原理」,本周讲「TCP 是怎么把这些原理落地的」。

配套练习:Week 8 文件夹里的 W8-TCP-lab(滑动窗口序号空间 + TCP Reno 拥塞窗口图)专门练本周内容, 完整解答写在 Week 08 讲课总结 的第八部分。

讲义大纲(p2)

部分 内容
一 面向连接的传输:TCP(段结构、序号/ACK、RTT 与超时、可靠传输、流量控制、连接管理)
二 拥塞控制原理(三个场景、两类方法)
三 TCP 拥塞控制(AIMD、慢启动、Tahoe/Reno、吞吐量、公平性)

第一部分:TCP 概览与段结构

一、TCP 的六个特性(讲义 p4)

TCP 由 RFC 793、1122、1323、2018、2581 定义。

特性 含义
点到点(point-to-point) 一个发送方、一个接收方(所以 TCP 不支持多播)
可靠、按序的字节流 没有「消息边界」:应用写两次 100 字节,对方可能一次读到 200 字节
流水线(pipelined) 拥塞控制和流量控制共同决定窗口大小
全双工(full duplex) 同一连接上数据双向流动;MSS = 最大段长度
面向连接 握手(交换控制消息)在数据交换前初始化双方状态
流量受控 发送方不会淹没接收方

「无消息边界」是 TCP 与 UDP 的一个经典区别:UDP 一次 sendto() 就是一个数据报,对方一次 recvfrom() 拿到完整一个; TCP 只保证字节顺序,应用层必须自己定义消息怎么分隔(例如 HTTP 用 Content-Length 或 \r\n\r\n)。

二、TCP 段结构(讲义 p5)

 0                   16                  31
┌───────────────────┬───────────────────┐
│ source port # │ dest port # │
├───────────────────┴───────────────────┤
│ sequence number │ ← 按「字节」计数
├───────────────────────────────────────┤
│ acknowledgement number │
├──────┬──────┬─────────┬───────────────┤
│ head │ not │U A P R S F│ receive window│ ← rwnd:接收方愿意再收多少字节
│ len │ used │ │ │
├──────┴──────┴─────────┼───────────────┤
│ checksum │ urg data ptr │
├───────────────────────┴───────────────┤
│ options (variable length) │
├───────────────────────────────────────┤
│ application data (variable) │
└───────────────────────────────────────┘
字段 / 标志 作用
序号、确认号 按字节计数,而不是按段计数(讲义特别标出 "counting by bytes of data (not segments!)")
head len 首部长度(因为有可变长 options)
ACK 位 确认号字段有效
RST, SYN, FIN 连接建立和拆除命令
receive window 接收方愿意接受的字节数(流量控制用)
checksum Internet 校验和,与 UDP 相同
URG / PSH 紧急数据 / 立即推送 —— 讲义注明「一般不使用」

首部最少 20 字节(无 options 时),对比 UDP 的 8 字节。

三、序号与 ACK(讲义 p6–7)

概念 定义
序号(sequence number) 段中数据第一个字节在字节流中的编号
确认号(ACK number) 期望从对方收到的下一个字节的序号
ACK 性质 累积确认(cumulative ACK)

讲义的问题:接收方怎么处理乱序到达的段? 答:TCP 规范没有规定,由实现者决定。(实际实现几乎都会缓存乱序段 —— 所以 TCP 是 GBN 与 SR 的混合体,见 Week 05 末尾的对照表。)

讲义 p7:Telnet 回显例子(必须会推)

方向 Seq ACK 数据 含义
A → B 42 79 'C' 用户在 A 敲了 'C';A 期待 B 的第 79 字节
B → A 79 43 'C' B 确认收到 'C'(期待 43),并回显 'C'
A → B 43 80 无 A 确认收到回显(期待 80)

推导规律: - 自己下一段的 Seq = 自己上一段的 Seq + 上一段数据长度 - 自己发的 ACK = 对方上一段的 Seq + 对方上一段的数据长度

ACK 可以「捎带(piggyback)」在数据段里 —— 第二行 B 既在发数据又在确认。


第二部分:RTT 估计与超时

四、超时值该设多大(讲义 p8)

设置 后果
必须比 RTT 长 但 RTT 会变化
太短 过早超时,不必要的重传
太长 对丢包反应太慢

怎么估计 RTT: - SampleRTT:从段发出到收到其 ACK 的时间 - 忽略重传段(因为不知道 ACK 对应的是哪一次发送 —— 这就是 Karn 算法的出发点) - SampleRTT 抖动大,所以要对最近若干次测量做平均,得到更平滑的 EstimatedRTT

五、三个公式(必背)

EstimatedRTT —— 指数加权移动平均(EWMA):

「过去样本的影响按指数速度衰减」:第 次之前的样本权重是 。

DevRTT —— RTT 偏差的 EWMA:

超时间隔 = 估计值 + 安全余量:

为什么乘 4:RTT 波动越大,安全余量就越大;波动小时超时值会贴近 EstimatedRTT。

算例(自己构造,用来练手)

设当前 ms、 ms,新测得 ms。

按 RFC 6298 的顺序(先用旧的 EstimatedRTT 更新 DevRTT):

⚠️ 顺序会影响结果:如果先更新 EstimatedRTT 到 105 再算 DevRTT,得到 ,超时 ms。 讲义没有规定先后顺序;考试时把你用的顺序写清楚,题目若给了顺序就按题目。


第三部分:TCP 的可靠数据传输

六、TCP rdt 的组成(讲义 p12)

TCP 在 IP 的不可靠服务之上构建 rdt 服务,用到:

机制 来源(Week 05)
流水线发送 GBN / SR
累积 ACK GBN
单个重传定时器 GBN
重传触发:超时 和 重复 ACK rdt3.0 / rdt2.2

讲义先考虑一个简化发送方:忽略重复 ACK、忽略流量控制和拥塞控制。

七、简化 TCP 发送方的三个事件(讲义 p13–14)

事件 动作
收到应用数据 用 NextSeqNum 建段;交给 IP;NextSeqNum += length(data);若定时器未运行则启动(把定时器看作是给「最老的未确认段」的)
超时 只重传「序号最小的未确认段」;重启定时器
收到 ACK,值为 y 若 y > SendBase:SendBase = y;若仍有未确认段则重启定时器,否则停止

与 GBN 的关键区别:GBN 超时重传整个窗口,TCP 超时只重传一个段(最老的那个)。 SendBase − 1 就是「最后一个被累积确认的字节」。

八、三个重传场景(讲义 p15–16)

场景 过程 结论
ACK 丢失 A 发 Seq=92(8 字节);B 回 ACK=100 丢失;A 超时重传 Seq=92;B 再回 ACK=100 重传一次,B 收到重复数据丢弃但仍确认
过早超时 A 连发 Seq=92(8B)、Seq=100(20B);ACK=100、ACK=120 都在路上;A 超时重传 Seq=92;随后收到 ACK=100、ACK=120 → SendBase=120;B 对重传的 92 回 ACK=120 因为累积 ACK,B 回的是 120 而不是 100
累积 ACK 挽救丢失的 ACK ACK=100 丢了,但 ACK=120 在超时前到达 A 不需要重传任何东西,直接发 Seq=120

九、TCP 接收方的 ACK 生成规则(讲义 p17,RFC 1122/2581)

接收方事件 动作
按序段到达,之前数据都已确认 延迟 ACK:最多等 500 ms 看下一个段;没来就发 ACK
按序段到达,另有一个段的 ACK 正在等待 立即发一个累积 ACK,同时确认这两个段
乱序段到达(序号高于期望值),检测到空洞 立即发重复 ACK,指明下一个期望字节
到达的段部分或全部填补空洞 立即发 ACK(前提是该段从空洞的低端开始)

第三行就是快速重传的「原料」:每来一个乱序段,接收方就重复确认一次「我还在等 X」。

十、快速重传(讲义 p18–19)

问题:超时间隔往往比较长 → 丢包后要等很久才重传。

思路:通过重复 ACK 发现丢失的段。发送方常常背靠背发很多段,若其中一个丢了,后面每个段都会引发一个重复 ACK。

规则:发送方收到对同一数据的 3 个重复 ACK(triple duplicate ACKs)时,立即重传序号最小的未确认段,不等超时。

讲义 p19 的时序:A 发 Seq=92、100、…,其中 Seq=100 丢失 → B 不断回 ACK=100 → A 收到第 1 个 ACK=100 是正常 ACK,再收到 3 个重复的 ACK=100 → 立即重传 Seq=100。

「3 个重复」= 一共 4 个相同的 ACK(第 1 个是正常确认)。这是常见的计数陷阱。 为什么是 3 而不是 1:1–2 个重复 ACK 可能只是网络乱序,3 个就很可能是真丢了。


第四部分:流量控制

十一、问题与机制(讲义 p21–22)

流量控制:接收方控制发送方,使发送方不会因为发得太多、太快而溢出接收方的缓冲区。

场景:TCP 往 socket 缓冲区里放数据的速度 > 应用从缓冲区取数据的速度。

要点 说明
接收方通告空闲缓冲 在发回给发送方的段的首部里填 rwnd
RcvBuffer 通过 socket 选项设置(典型默认 4096 字节);很多操作系统会自动调整
发送方的约束 在途(未确认)数据量 ≤ rwnd
效果 保证接收缓冲永不溢出

流量控制 vs 拥塞控制(必考区分):

流量控制 拥塞控制
保护谁 接收方(缓冲区) 网络(路由器缓冲与链路)
信号来源 接收方显式通告 rwnd 发送方从丢包/时延推断
控制变量 rwnd cwnd

实际发送窗口 = min(rwnd, cwnd)。


第五部分:连接管理

十二、握手要协商什么(讲义 p24)

握手双方要: - 同意建立连接(都知道对方愿意) - 同意连接参数:双向的初始序号、双方的 rcvBuffer 大小

讲义 p24 给出 Java 风格的 socket 代码:客户端 new Socket("hostname","port number"),服务器 welcomeSocket.accept()。

十三、两次握手为什么不行(讲义 p25–26)

两次握手:客户端发 req_conn(x),服务器回 acc_conn(x),双方进入 ESTAB。

在网络中会遇到:可变时延、因丢失而重传的消息、乱序、看不到对方状态。

两个失败场景:

场景 过程 后果
半开连接 客户端的 req_conn(x) 延迟,客户端重传;连接完成后客户端终止,服务器也忘了 x;旧的 req_conn(x) 姗姗来迟 服务器再次 ESTAB,但已没有客户端 —— half open connection
重复数据被接受 同上,还有一个迟到的 data(x+1) 也被重传过 服务器把旧数据当新连接的数据接受

根本原因:服务器无法区分「新的连接请求」和「旧连接残留的重复请求」,因为它从不确认客户端是否真的在线。

十四、三次握手(讲义 p27–28)

步骤 方向 内容 状态变化
1 客户端 → 服务器 SYN=1, Seq=x(选择初始序号 x) 客户端 LISTEN → SYNSENT
2 服务器 → 客户端 SYN=1, Seq=y, ACK=1, ACKnum=x+1(SYNACK) 服务器 → SYN RCVD
3 客户端 → 服务器 ACK=1, ACKnum=y+1(这个段可以携带客户端到服务器的数据) 客户端收到 SYNACK → ESTAB(说明服务器活着);服务器收到 ACK → ESTAB(说明客户端活着)

为什么三次就够:第三步让服务器确认「客户端此刻真的在、而且确认的是我新选的 y」。 迟到的旧 SYN 即使到达,服务器回的 SYNACK 携带新的 y,客户端不会用 y+1 去确认它(客户端会发 RST),连接就不会被建立。

SYN 和 FIN 各消耗一个序号,所以确认号是 x+1、y+1。

十五、关闭连接(讲义 p29–30)

规则:客户端和服务器各自关闭自己这一侧(发 FIN=1 的段);收到 FIN 回 ACK(ACK 可与自己的 FIN 合并);同时发 FIN 也能处理。

步骤 方向 段 客户端状态 服务器状态
1 C → S FIN=1, seq=x FIN_WAIT_1(不能再发,还能收) —
2 S → C ACK=1, ACKnum=x+1 FIN_WAIT_2(等服务器关闭) CLOSE_WAIT(还能发数据)
3 S → C FIN=1, seq=y — LAST_ACK(不能再发)
4 C → S ACK=1, ACKnum=y+1 TIMED_WAIT → 等 2 × 最大段寿命 → CLOSED CLOSED

为什么客户端要等 2MSL: ① 如果最后那个 ACK 丢了,服务器会重发 FIN,客户端还在就能再回一次 ACK; ② 让本连接残留在网络中的旧段全部过期,避免干扰下一个使用相同四元组的连接。

半关闭:步骤 2 和 3 之间,服务器仍可单向发数据给客户端 —— 这正是「各自关闭自己一侧」的含义。


第六部分:拥塞控制原理

十六、什么是拥塞(讲义 p32)

非正式定义:「太多的源、太快地发送太多数据,网络处理不过来」。 不同于流量控制! 而且是网络领域「十大问题」之一。

表现 原因
丢包 路由器缓冲溢出
长时延 在路由器缓冲中排队

十七、拥塞的三个场景与代价(讲义 p33–41)

场景 1:两个发送方、一个路由器、无限缓冲、无重传

  • 输出链路容量 ,每个连接最大吞吐
  • 代价 1:到达速率 逼近容量时,时延趋于无穷大

场景 2:一个路由器、有限缓冲、发送方会重传超时的分组

符号: = 原始数据速率; = 原始数据 + 重传数据(offered load), 。

理想化程度 假设 结果
完美知识 只有路由器有空缓冲时才发 ,可达
已知丢失 分组会因缓冲满被丢,只在确定丢失时重传 发到 时有一部分是重传,渐近 goodput 仍为
现实:重复 还会过早超时,同一分组发两份且都被交付 goodput 低于

代价 2、3: - 为得到同样的 goodput 要做更多工作(重传) - 不必要的重传:链路承载同一分组的多个副本 → goodput 下降

场景 3:四个发送方、多跳路径、超时重传

问题: 和 增大会怎样? 答:红色流的 增大时,到达上游队列的蓝色分组全被丢弃,蓝色吞吐量 → 0。

代价 4:分组被丢弃时,它在上游已经用掉的所有传输容量都被浪费了!

总结拥塞的四个代价(考试常让你列举): 1. 接近容量时排队时延剧增 2. 丢包导致重传,额外工作 3. 过早超时导致不必要的重复传输 4. 下游丢包浪费上游带宽 —— 极端时整体吞吐趋于 0(拥塞崩溃)

十八、两类拥塞控制方法(讲义 p42)

端到端拥塞控制 网络辅助拥塞控制
网络反馈 没有显式反馈 路由器向端系统提供反馈
如何得知拥塞 端系统根据观察到的丢包、时延推断 单比特指示(SNA、DECbit、TCP/IP ECN、ATM)或 显式给出发送速率
代表 TCP 采用的方法 ECN、ATM ABR

第七部分:TCP 拥塞控制

十九、AIMD(讲义 p44)

方法:发送方不断增大发送速率(窗口),探测可用带宽,直到发生丢包。

阶段 规则
加性增(Additive Increase) 每个 RTT 把 cwnd 增加 1 MSS,直到检测到丢包
乘性减(Multiplicative Decrease) 丢包后把 cwnd 减半

图形:锯齿(saw tooth) —— 「探测带宽」。

二十、cwnd 与发送速率(讲义 p45)

发送方约束:

(结合流量控制,实际是 。)

粗略地说:发 cwnd 字节,等一个 RTT 收 ACK,再发更多,所以

cwnd 是动态的,是「感知到的网络拥塞程度」的函数。

二十一、慢启动(讲义 p46)

要点 说明
开始时 cwnd = 1 MSS
增长 每个 RTT 翻倍
实现方式 每收到一个 ACK,cwnd 增加 1 MSS
总结 初始速率很慢,但指数级快速上升

「每个 ACK 加 1 MSS」为什么等于「每 RTT 翻倍」:一个 RTT 内发出 cwnd 个段,收回 cwnd 个 ACK,cwnd 就增加 cwnd,即翻倍。

慢启动的「慢」指的是起点低(1 MSS),不是增长慢。

二十二、何时从慢启动切到拥塞避免(讲义 p48)

问:指数增长什么时候切换成线性增长? 答:当 cwnd 达到「超时前 cwnd 的一半」时。

实现:变量 ssthresh;丢包事件发生时,ssthresh = 丢包前 cwnd 的 1/2。

拥塞避免阶段的增长:每收到一个新 ACK,cwnd += MSS · (MSS / cwnd),合起来就是每 RTT 增加 1 MSS。

二十三、对丢包的反应:Tahoe vs Reno(讲义 p47)

丢包信号 TCP Reno TCP Tahoe
超时 cwnd = 1 MSS,慢启动到 ssthresh,再线性增长 cwnd = 1 MSS
3 个重复 ACK cwnd 减半,然后线性增长(快速恢复) cwnd = 1 MSS(不区分)

为什么 Reno 对 3 个重复 ACK 「手下留情」:重复 ACK 说明网络还能交付一些段(后面的段都到了),拥塞没有超时那么严重。 超时则说明「什么都没回来」,是更严重的拥塞信号,所以两种 TCP 都回到 1。

二十四、TCP 拥塞控制完整 FSM(讲义 p49)

三个状态:slow start、congestion avoidance、fast recovery。

初始化:cwnd = 1 MSS,ssthresh = 64 KB,dupACKcount = 0。

当前状态 事件 动作 下一状态
慢启动 新 ACK cwnd += MSS;dupACKcount = 0;发新段 慢启动
慢启动 cwnd ≥ ssthresh — 拥塞避免
拥塞避免 新 ACK cwnd += MSS·(MSS/cwnd);dupACKcount = 0 拥塞避免
慢启动 / 拥塞避免 重复 ACK dupACKcount++ 不变
慢启动 / 拥塞避免 dupACKcount == 3 ssthresh = cwnd/2;cwnd = ssthresh + 3;重传丢失段 快速恢复
快速恢复 重复 ACK cwnd += MSS;发新段 快速恢复
快速恢复 新 ACK cwnd = ssthresh;dupACKcount = 0 拥塞避免
任意状态 超时 ssthresh = cwnd/2;cwnd = 1 MSS;dupACKcount = 0;重传丢失段 慢启动

cwnd = ssthresh + 3 的「+3」:那 3 个重复 ACK 说明已有 3 个段离开了网络(被接收方收下了),所以窗口可以「膨胀」3 个 MSS。 出了快速恢复,cwnd 回落到 ssthresh —— 这就是「减半」的最终效果。

做题时的简化:大多数题(包括 W8 lab)把 Reno 的 3-dupACK 反应简化为「cwnd 减半、ssthresh = 减半值、进入线性增长」,不考虑 +3 的膨胀。

自己推一遍:Reno 的 cwnd 轨迹(单位 MSS)

假设 ssthresh = 8,每轮无丢包时:

轮次 1 2 3 4 5 6 7 8 9 10 11 12
cwnd 1 2 4 8 9 10 11 5 6 7 8 1
阶段 SS SS SS →CA CA CA CA 后 3-dup CA CA CA CA 后超时 SS
  • 第 7 轮末 3 个重复 ACK:ssthresh = ⌊11/2⌋ = 5,cwnd → 5,进入线性增长
  • 第 11 轮末 超时:ssthresh = 8/2 = 4,cwnd → 1,重新慢启动(1, 2, 4, 然后线性)

如果是 Tahoe:第 7 轮末的 3-dup 也会让 cwnd → 1,然后慢启动到 ssthresh = 5。

二十五、TCP 平均吞吐量(讲义 p50)

假设:忽略慢启动,总有数据要发; = 发生丢包时的窗口(字节)。

  • 窗口在 到 之间线性变化 → 平均窗口 =
  • 每个 RTT 发出平均窗口那么多数据

算例: KB、 ms → KB/s ≈ 7.2 Mbps。

二十六、长肥管道与 Mathis 公式(讲义 p51)

例子:1500 字节段、100 ms RTT,想要 10 Gbps 吞吐。

个在途段

用丢包率 表示吞吐量 [Mathis 1997]:

反解 10 Gbps 需要的 :

结论:需要极低的丢包率(每 50 亿个段才丢 1 个)—— 标准 TCP 在高速长距离网络上不够用,于是有了面向高速网络的新版 TCP(如 CUBIC、BBR,讲义只说 "new versions of TCP for high-speed")。 这也呼应 Week 12 QUIC 讲义开头的「TCP 各种 flavor」表格。

二十七、TCP 公平性(讲义 p52–54)

公平目标:K 个 TCP 会话共享带宽为 R 的瓶颈链路时,每个应得到平均 R/K 的速率。

为什么 AIMD 是公平的(两个连接的相图)

横轴 = 连接 1 吞吐,纵轴 = 连接 2 吞吐。

阶段 在图上的运动
加性增 两者同时 +1 → 沿斜率 1 的 45° 线移动(差值不变)
乘性减 两者同时减半 → 沿指向原点的直线移动(差值减半)

反复「斜上 → 朝原点缩」,点会逐渐收敛到「等带宽分享线」与「满载线 」的交点。

关键直觉:加性增不改变差距,乘性减把差距减半 → 差距不断缩小 → 公平。 (如果是「乘性增、乘性减」,差距比例不变,就不会收敛。)

公平性的两个漏洞(讲义 p54)

漏洞 说明
UDP 多媒体应用常不用 TCP:不想被拥塞控制限速,用 UDP 以恒定速率发音视频,容忍丢包 —— 不受 TCP 公平性约束
并行 TCP 连接 应用可在两主机间开多条并行连接(Web 浏览器就这样做)

讲义的并行连接算例:速率 的链路上已有 9 条连接。

新应用开的连接数 新应用得到的速率
1
11

TCP 公平是「每连接公平」,不是「每应用公平」或「每用户公平」。


第八部分:本周考点速记

数字与公式

项 值
TCP 首部最小长度 20 字节
EWMA 系数 α = 0.125,β = 0.25
超时间隔 EstimatedRTT + 4·DevRTT
延迟 ACK 最多 500 ms
快速重传触发 3 个重复 ACK
初始 cwnd / ssthresh(讲义 FSM) 1 MSS / 64 KB
平均吞吐 0.75 W / RTT
Mathis 1.22·MSS / (RTT·√L)
关闭等待 TIME_WAIT 等 2·MSL

易错点

  1. TCP 序号按字节编号,不是按段;ACK 是「下一个期望字节」。
  2. TCP 超时只重传最老的未确认段,不是像 GBN 那样重传整个窗口。
  3. 3 个重复 ACK = 一共 4 个相同 ACK。
  4. 慢启动是指数增长,「慢」只是起点低。
  5. ssthresh = 丢包时 cwnd 的一半,超时和 3-dup 都会更新 ssthresh。
  6. Reno:超时 → 1;3-dup → 减半。Tahoe:两种都 → 1。
  7. 流量控制保护接收方(rwnd),拥塞控制保护网络(cwnd),发送窗口取二者最小值。
  8. 两次握手的问题是半开连接和接受旧数据;三次握手的第三个段可以带数据。
  9. TCP 公平是每连接的,开多条连接或用 UDP 就能「占便宜」。
  10. 拥塞代价「上游带宽被浪费」来自场景 3(多跳),不是场景 1。

与前后周的衔接

周 主题 关系
5 Transport(rdt、GBN、SR) 本周的可靠传输机制都是从这里来的
6 TCP 本文
7 Socket 编程实验 用 Python 写 TCP / UDP 客户端服务器,Wireshark 里看三次握手
8 Multimedia 为什么流媒体/VoIP 常选 UDP;配套 TCP lab 解答
12 5G、移动性与 QUIC QUIC 在 UDP 上重做了 TCP 的可靠传输和拥塞控制