COMP5416 Week 05 传输层讲课总结
COMP5416 Week 05 讲课总结:Transport Layer
课程:COMP5416/COMP4416 — Advanced Network Technologies,Semester 2 2026 讲师:Professor Vincent Gramoli 讲义:
W5-Transport.pdf(60 页) 教材:Kurose & Ross 9th ed. —— 第 3 章,3.1 / 3.2 / 3.3 / 3.4 节⚠️ Midterm quiz 在 Week 6,20 道选择题,占 20%。 ✅ 老师已说明:期中不考 Week 6 的内容,范围就是 Week 1–5 —— 本讲是考纲的最后一周。
本讲是考纲里计算题最密集的一周 —— 利用率公式、校验和手算、GBN/SR 行为推演都在这里。
本讲学习目标(讲义 p3)
理解传输层服务背后的原理:
- 多路复用、分解(multiplexing, demultiplexing)
- 可靠数据传输(reliable data transfer)
- 流量控制(flow control)
- 拥塞控制(congestion control)
了解互联网的传输层协议:
- UDP:无连接传输
- TCP:面向连接的可靠传输
- TCP 拥塞控制
注意:本周只讲到 rdt 原理与流水线协议。流量控制、拥塞控制、TCP 协议本身要到 Week 6。
第一部分:传输层服务
一、传输层做什么
传输层在运行于不同主机上的应用进程之间提供逻辑通信。 传输协议运行在端系统里(不在网络核心设备上)。
发送侧与接收侧的动作
| 侧 | 动作 |
|---|---|
| 发送侧 | 把应用消息拆成段(segments),交给网络层 |
| 接收侧 | 把段重组成消息,交给应用层 |
术语要分清(考试会用词区分层次):
层 数据单元的名字 应用层 message(消息) 传输层 segment(段) 网络层 datagram(数据报) 链路层 frame(帧)
应用可用的传输协议不止一个 —— 互联网上是 TCP 和 UDP。
二、传输层 vs 网络层(必考区分)
| 网络层 | 传输层 | |
|---|---|---|
| 通信对象 | 主机到主机(host-to-host) | 进程到进程(process-to-process) |
| 性质 | 尽力而为、不可靠 | 依赖并增强网络层的服务 |
一句话记住:网络层把数据送到「那台机器」,传输层把数据送到「那个进程」。
为什么说「增强」:网络层的 IP 只能保证「尽量把包送到那台机器」,丢包、乱序、重复都可能发生。 TCP 在这个基础上加了序号、ACK、重传、定时器,把不可靠的 IP 变成可靠的字节流 —— 这就是本讲后半段 rdt 要讲的东西。
三、互联网的两个传输层协议
| 服务 | TCP | UDP |
|---|---|---|
| 可靠、按序交付 | ✅ | ❌ |
| 拥塞控制 | ✅ | ❌ |
| 流量控制 | ✅ | ❌ |
| 连接建立 | ✅ | ❌ |
| 时延保证 | ❌ | ❌ |
| 带宽保证 | ❌ | ❌ |
UDP 的定位(讲义原文):对「尽力而为」IP 的无修饰扩展(no-frills extension of "best-effort" IP)。
两者都不提供的服务是高频陷阱选项:时延保证、带宽保证。 无论选 TCP 还是 UDP,都拿不到「延迟不超过 X 毫秒」或「带宽至少 Y Mbps」的承诺 —— 那需要网络层的 QoS 支持。
第二部分:多路复用与分解
四、基本概念
| 侧 | 定义 |
|---|---|
| 发送方的多路复用(multiplexing) | 处理来自多个套接字的数据,添加传输层首部(供之后分解用) |
| 接收方的分解(demultiplexing) | 用首部信息把收到的段交付给正确的套接字 |
分解的工作流程
- 主机收到 IP 数据报,每个数据报有源 IP 地址、目的 IP 地址
- 每个数据报携带一个传输层段
- 每个段有源端口号、目的端口号
- 主机用 IP 地址和端口号把段导向正确的套接字
段格式中与分解相关的部分:
┌────────────────┬────────────────┐ |
五、无连接分解(UDP)
UDP 套接字只由「二元组」标识:目的 IP + 目的端口号。
接收方的动作:
- 检查收到的 UDP 段中的目的端口号
- 把段导向拥有该端口号的套接字
发送方的动作:创建要发进 UDP 套接字的数据报时,必须指定目的 IP 地址和目的端口号:
clientSocket.sendto(message, (destIP, destPort)) |
讲义 p12 最关键的一句: 「目的 IP、目的端口相同,但源 IP 和/或源端口不同的分组,会被导向目的地的同一个套接字。」
换句话说:UDP 不区分发送方。 一个 UDP 服务器套接字会收到所有发往该端口的数据报, 想知道是谁发来的,只能靠
recvfrom()返回的地址自己判断。
讲义 p13 的例子(要能读懂)
| 主机 | 套接字端口 |
|---|---|
| 服务器 | DatagramSocket(6428) |
| 客户 1 | DatagramSocket(9157) |
| 客户 2 | DatagramSocket(5775) |
段的流向:
| 方向 | 源端口 | 目的端口 |
|---|---|---|
| 客户 1 → 服务器 | 9157 | 6428 |
| 客户 2 → 服务器 | 5775 | 6428 |
| 服务器 → 客户 1 | 6428 | 9157 |
| 服务器 → 客户 2 | 6428 | 5775 |
注意:两个客户发来的段目的端口都是 6428,所以都进同一个服务器套接字。 服务器回复时,把收到的源端口当作目的端口填回去 —— 这就是「源端口号提供了返回地址」的含义。
六、面向连接分解(TCP)
TCP 套接字由「四元组(4-tuple)」标识: 源 IP + 源端口 + 目的 IP + 目的端口
接收方用全部四个值把段导向正确的套接字。
三个重要推论
| 推论 | 说明 |
|---|---|
| 服务器可同时支持很多 TCP 套接字 | 每个由自己的四元组标识 |
| Web 服务器为每个连接的客户建立不同的套接字 | — |
| 非持久 HTTP 为每个请求都建立不同的套接字 | 这也是非持久 HTTP 开销大的原因之一 |
讲义 p15 的例子(必须能读懂)
三个段全都发往
IP=B, 目的端口=80,却被分解到不同的套接字:
| 段 | 源 IP, 源端口 | 目的 IP, 目的端口 | 进入哪个套接字 |
|---|---|---|---|
| ① | A, 9157 | B, 80 | 套接字 1 |
| ② | C, 5775 | B, 80 | 套接字 2 |
| ③ | C, 9157 | B, 80 | 套接字 3 |
②和③的对比是本例的精华:两个段的源 IP 相同(都是 C)、目的完全相同,只有源端口不同(5775 vs 9157),就被分到了不同的套接字。 这正说明 TCP 用的是四元组而非二元组。
💡 讲义 p16 补充:若是多线程服务器,这些套接字会分给不同的线程处理。
七、UDP 二元组 vs TCP 四元组(本讲最经典的 MCQ)
| UDP | TCP | |
|---|---|---|
| 套接字标识 | 二元组:目的 IP + 目的端口 | 四元组:源 IP + 源端口 + 目的 IP + 目的端口 |
| 不同源发来的段 | 进同一个套接字 | 进不同的套接字 |
| 服务器区分客户靠什么 | 应用自己从 recvfrom()
拿地址 |
传输层自动按四元组区分 |
第三部分:UDP
八、UDP 的特性 [RFC 768]
| 特性 | 说明 |
|---|---|
| 定位 | 「无修饰(no frills)」的互联网传输协议 |
| 服务 | 「尽力而为」 —— 段可能丢失、乱序交付给应用 |
| 无连接 | 发送方与接收方之间无握手;每个 UDP 段独立处理 |
九、为什么要有 UDP(必考四点)
讲义 p19 直接抛出了这个问题:"why is there a UDP?"
| 理由 | 说明 |
|---|---|
| 无连接建立 | 连接建立会增加时延(TCP 要三次握手) |
| 简单 | 发送方、接收方都不维护连接状态 |
| 首部小 | 只有 8 字节(TCP 首部 20 字节起) |
| 无拥塞控制 | UDP 想多快发多快(can blast away as fast as desired) |
第四点是双刃剑:对实时音视频是优点(不会因为网络拥塞而被迫降速导致卡顿), 但也意味着 UDP 流量不「谦让」,可能挤垮 TCP 流量 —— 这是网络公平性的经典议题。
UDP 的典型用途
- 流媒体应用(容忍丢失、对速率敏感)
- DNS
想在 UDP 上要可靠传输怎么办:在应用层加可靠性 —— 应用特定的差错恢复。 (现代例子:QUIC 就是在 UDP 之上重新实现了可靠传输和拥塞控制。)
十、UDP 段格式
┌────────────────┬────────────────┐ |
| 字段 | 大小 | 说明 |
|---|---|---|
| 源端口号 | 16 位 | 范围 0–65535 |
| 目的端口号 | 16 位 | — |
| length | 16 位 | UDP 段的字节长度,含首部 |
| checksum | 16 位 | 差错检测 |
首部大小 = 4 × 2 = 8 字节,这是必背数字。 length 的最小值是 8(没有数据时,只有首部)。
第四部分:Internet 校验和(会考手算)
十一、算法
目标:检测传输中的「差错」(如比特翻转)。
| 侧 | 步骤 |
|---|---|
| 发送方 | ①
把段内容(含首部字段)视为一串 16
位整数 ② 求和(反码和 one's complement sum) ③ 校验和 = 和的反码(取反) ④ 把校验和放进 UDP 校验和字段 |
| 接收方 | ① 计算收到段的校验和 ② 比较是否等于校验和字段的值 不等 → 检测到差错;相等 → 未检测到差错 |
十二、关键细节:回卷进位(wraparound)
讲义原话:「相加时,来自最高有效位的进位需要加回到结果上。」
这一步最容易漏,也是手算题的主要失分点。
讲义 p21 的完整手算(逐位验算过)
第一步:两个 16 位整数相加
1110011001100110 (0xE666) |
第二步:回卷 —— 把进位加回低 16 位
1011101110111011 ← 低 16 位 |
第三步:取反得校验和
sum = 1011101110111100 |
自检技巧:回卷和 + 校验和应当全为 1。 本例:
1011101110111100 + 0100010001000011 = 1111111111111111✅
十三、接收方验证(讲义 p22)
讲义原话:"Use checksum as addend: Checksum should be 0" —— 把校验和当作一个加数一起求和,最后取反应得全 0。
⚠️ 注意 p22 换了字长:p21 用的是两个 16 位字,p22 用的是四个 8 位字。 讲义没有说明这个切换,容易看串。下面按 p22 实际的 8 位口径推。
左半边:发送方算校验和
10010010 ← 146 |
回卷:把第 9 位的进位加回低 8 位
10010100 ← 低 8 位(148) |
取反得校验和:
SUM = 10010101 |
右半边:接收方验证
把校验和也当成一个加数加进去:
10010010 |
回卷:
11111110 + 1 = 11111111 ← 全 1 ✅ |
取反:
取反 → 00000000 ← 全 0 ⇒ 无差错 |
MCQ 常问「接收方验证时结果应该是什么」 —— 答案是 全 0(把校验和一起加进去,回卷后取反)。 等价说法:所有字(含校验和)的反码和应为全 1。
💡 想多练:Kurose 官方交互题 Internet checksum 可以无限刷随机实例(用的是 16 位口径),每做一次都会重新出题。
校验和的局限(值得知道)
校验和只能「检测」差错,不能「纠正」差错。 而且它检测不出所有差错 —— 例如两个比特同时翻转且相互抵消时,校验和不变。 真正的可靠性靠的是「校验和 + 序号 + ACK + 重传」的组合,这正是下一节 rdt 要讲的。
第五部分:可靠数据传输原理(rdt)
十四、为什么重要
在应用层、传输层、链路层都很重要 —— 网络十大重要话题之一。 底层信道的特性决定了 rdt 协议的复杂度。
讲义 p24–26 用三张递进的图强调:可靠数据传输协议要在不可靠的网络层之上建立可靠的抽象。
十五、四个接口函数(要认得)
| 函数 | 谁调用 | 作用 |
|---|---|---|
rdt_send() |
上层调用(如应用) | 传入要交付给接收方上层的数据 |
udt_send() |
由 rdt 调用 | 把分组通过不可靠信道传给接收方 |
rdt_rcv() |
信道调用 | 分组到达接收侧时被调用 |
deliver_data() |
由 rdt 调用 | 把数据交付给上层 |
命名规律:
rdt_= reliable data transfer(可靠的);udt_= unreliable data transfer(不可靠的)。udt_send()是唯一真正把数据丢进不可靠信道的函数。
方法论:用有限状态机(FSM)描述发送方与接收方;只考虑单向数据传输,但控制信息双向流动。
十六、rdt 的演进(必背这张表)
| 版本 | 信道假设 | 新增机制 |
|---|---|---|
| rdt1.0 | 完全可靠:无比特差错、无丢包 | 无 |
| rdt2.0 | 可能翻转比特 | ① 差错检测(校验和)② 接收方反馈:ACK / NAK |
| rdt2.1 | 同上 | 序号(0/1) —— 处理 ACK/NAK 自身被损坏 |
| rdt2.2 | 同上 | 无 NAK 协议 —— 只用带序号的 ACK |
| rdt3.0 | 还会丢包(数据和 ACK 都可能丢) | 倒计时定时器 + 超时重传 |
记忆线索:每一版只解决前一版暴露的一个新问题。 1.0 什么都不做 → 2.0 处理比特错 → 2.1 处理「反馈本身也会错」→ 2.2 简化掉 NAK → 3.0 处理丢包。
十七、rdt1.0:完全可靠的信道
假设:无比特差错、无丢包。
发送方 FSM(只有一个状态):
[Wait for call from above] |
接收方 FSM(只有一个状态):
[Wait for call from below] |
这一版没有任何反馈 —— 发送方发完就不管了,因为假设信道完美。
十八、rdt2.0:有比特差错的信道
新假设:底层信道可能翻转分组中的比特。
解决思路:
| 机制 | 作用 |
|---|---|
| 校验和 | 检测比特差错 |
| ACK(肯定确认) | 接收方显式告诉发送方「分组收到了,没问题」 |
| NAK(否定确认) | 接收方显式告诉发送方「分组有错」 |
| 重传 | 发送方收到 NAK 后重传该分组 |
讲义的类比很好:「人类在对话中是怎么从『错误』中恢复的?」 —— 听不清就说「你再说一遍」,这就是 NAK。
rdt2.0 新增的两个机制(相对 rdt1.0):
- 差错检测
- 接收方反馈:控制消息(ACK/NAK)从接收方到发送方
发送方 FSM(两个状态):
[Wait for call from above] |
停等(stop-and-wait)的定义:发送方发一个分组,然后等待接收方的响应。 rdt2.0 及之后的所有版本都是停等协议,直到引入流水线。
十九、rdt2.0 的致命缺陷与 rdt2.1
缺陷:如果 ACK/NAK 本身被损坏了怎么办?
| 问题 | 说明 |
|---|---|
| 发送方不知道接收方发生了什么 | 收到的反馈是乱码,无法判断是 ACK 还是 NAK |
| 不能简单重传 | 可能造成重复 —— 万一原本是 ACK,重传就多发了一份 |
rdt2.1 的解决办法
| 措施 | 说明 |
|---|---|
| ACK/NAK 损坏时发送方重传当前分组 | 宁可重复也不能漏 |
| 发送方给每个分组加序号 | 两个序号(0, 1)就够了 |
| 接收方丢弃重复分组 | 不向上交付,但仍然要回 ACK |
为什么两个序号就够(经典 MCQ)
因为是停等协议,任何时刻只有一个分组「在飞」。 接收方只需区分「这个分组是我在等的那个,还是上一个的重复」 —— 一个比特就能表达。
rdt2.1 的状态数
| 侧 | 说明 |
|---|---|
| 发送方 | 状态数翻倍(4 个);状态必须「记住」当前该用序号 0 还是 1 |
| 接收方 | 必须检查收到的分组是不是重复;状态指示期望的是 0 还是 1 |
发送方的四个状态:
[Wait for call 0 from above] → [Wait for ACK/NAK 0] |
接收方对损坏分组的处理(讲义 p37):发 NAK。 对重复分组的处理:重发 ACK(不是 NAK!)—— 因为分组本身是好的,只是重复了。
二十、rdt2.2:无 NAK 协议
与 rdt2.1 功能完全相同,但只用 ACK。
| 改动 | 说明 |
|---|---|
| 接收方不发 NAK | 改为对「最后一个正确收到的分组」发 ACK |
| ACK 必须带序号 | 接收方必须在 ACK 中显式包含被确认分组的序号 |
| 发送方的判断 | 收到「非预期序号的 ACK」时,动作与收到 NAK 相同:重传当前分组 |
举例:发送方正在等 ACK0,却收到了
ACK1 —— 说明接收方还停在上一个分组,等同于
NAK,于是重传 pkt0。
💡 这就是「重复 ACK(duplicate ACK)」概念的起源。 TCP 的快速重传(fast retransmit)正是基于「收到 3 个重复 ACK 就立刻重传」 —— Week 6 会讲。
二十一、rdt3.0:处理丢包
新假设:底层信道还会丢分组(数据、ACK 都会丢)。 校验和、序号、ACK、重传都有帮助……但还不够。
为什么不够:如果分组彻底丢了,接收方根本不知道该发什么反馈,发送方就会永远等下去。
方法:发送方等待「合理」的时间;若超时未收到 ACK 则重传。
| 要点 | 说明 |
|---|---|
| 需要倒计时定时器 | countdown timer —— 这是 rdt3.0 唯一的新机制 |
| 分组只是延迟而非丢失时 | 重传会产生重复,但序号机制已能处理 |
| 接收方必须指明 ACK 的序号 | 沿用 rdt2.2 的做法 |
发送方 FSM 的关键动作:
rdt_send(data) → make_pkt(seq, data, checksum); udt_send; start_timer |
讲义 p43–44 的四个场景(要能画出时序图)
| 场景 | 发生了什么 | 结果 |
|---|---|---|
| (a) 无丢失 | 正常一来一回 | 无重传 |
| (b) 分组丢失 | pkt1 在途中丢失 | 超时 → 重传 pkt1 |
| (c) ACK 丢失 | pkt1 到了,但 ack1 丢失 | 超时 → 重传 pkt1 → 接收方检测到重复,重发 ack1 |
| (d) 过早超时 / ACK 延迟 | ack1 只是慢了,不是丢了 | 重传后收到两个 ack1,第二个「do nothing」 |
场景 (c) 和 (d) 的区别值得琢磨:
- (c) 中接收方收到了重复分组 —— 靠序号识别,丢弃但仍回 ACK
- (d) 中发送方收到了重复 ACK —— 直接忽略
两种重复分别由「接收方的序号检查」和「发送方的状态机」处理,这是 rdt 设计的对称之美。
第六部分:性能与流水线
二十二、停等协议的利用率(必考计算)
公式
| 符号 | 含义 |
|---|---|
| 分组长度(比特) | |
| 链路速率(bps) | |
| 往返时间 | |
| 传输时延 |
物理含义:发送方「忙于发送」的时间占整个周期的比例。
时间轴拆解(讲义 p46)
t = 0 第一个比特送出 |
讲义 p45 的算例(逐步验算过)
参数:1 Gbps 链路,15 ms 传播时延(RTT = 30 ms),8000 比特分组
讲义的结论(要会说): 在 1 Gbps 的链路上,每 30 ms 才发 1 KB,只跑出 33 kB/s —— 「网络协议限制了物理资源的使用!」
不同参数下的利用率对照(自己算的,帮助理解规律)
| 场景 | ||
|---|---|---|
| 1 Gbps, 8000 bit, RTT 30 ms | 0.008 ms | 0.027% |
| 100 Mbps, 8000 bit, RTT 10 ms | 0.080 ms | 0.79% |
| 10 Mbps, 1000 bit, RTT 20 ms | 0.100 ms | 0.50% |
| 1 Mbps, 1000 bit, RTT 20 ms | 1.000 ms | 4.76% |
规律:
相对 越小,利用率越低。 链路越快、RTT 越大,停等协议就越浪费 —— 这正是「高带宽-高时延(long fat pipe)」网络的经典问题。
延伸:要多大的窗口才能占满链路
代入讲义的例子:
也就是说,那条 1 Gbps 链路要有近 3751 个分组同时「在飞」才能被占满。 这个数就是所谓的带宽时延积(bandwidth-delay product)除以分组大小。
二十三、流水线(Pipelining)
定义:发送方允许多个「在飞的」、尚未确认的分组。
需要什么:
| 要求 | 原因 |
|---|---|
| 序号范围必须扩大 | 停等只要 2 个序号,流水线要能区分窗口内的所有分组 |
| 发送方和/或接收方要缓存 | 发送方要留着未确认的分组以备重传;接收方可能要缓存乱序分组 |
两种通用的流水线协议:Go-Back-N 和 Selective Repeat。
流水线的利用率公式
讲义 p48 的 3 分组例子:
恰好是停等的 3 倍 —— 「流水线 N 个分组,利用率提升 N 倍」。
⚠️ 讲义 p48 有两处印刷问题,自己算时会撞上:
- 讲义写
.0024,应为.024(,小数点错位) - 讲义得数写 0.00081,精确值是 0.0008 —— 差异来自讲义拿「已四舍五入的 0.00027」乘 3(
) 考试按
直接算即可,结论「提升 N 倍」不变。
第七部分:Go-Back-N vs Selective Repeat
二十四、六维对比表(最高频 MCQ)
| 维度 | Go-Back-N(GBN) | Selective Repeat(SR) |
|---|---|---|
| 发送方窗口 | 最多 N 个未确认分组 | 最多 N 个未确认分组 |
| ACK 类型 | 累积 ACK(cumulative) | 对每个分组单独 ACK |
| 定时器 | 只为「最老的未确认分组」设 1 个 | 每个未确认分组各 1 个 |
| 超时重传 | 重传该分组及窗口内所有更高序号的分组 | 只重传那一个分组 |
| 接收方缓存 | 不缓存!乱序分组直接丢弃 | 缓存乱序分组 |
| 接收方对乱序分组 | 重发「最高按序序号」的 ACK | 发该分组自己的 ACK |
两个最容易被考的差异:
- GBN 接收方「不缓存」 —— 讲义 p53 明写 "discard (don't buffer): no receiver buffering!"
- GBN 只有一个定时器(给最老的未确认分组),SR 每个分组一个
设计哲学的对比
| GBN | SR | |
|---|---|---|
| 接收方复杂度 | 极简(只记
expectedseqnum) |
复杂(要维护接收窗口和缓存) |
| 带宽效率 | 差(丢一个要重传一批) | 好(只重传丢的那个) |
| 适用场景 | 丢包率低、带宽充裕 | 丢包率高、带宽宝贵 |
一句话概括:GBN 把复杂度留给发送方(重传更多),SR 把复杂度留给接收方(缓存更多)。
二十五、Go-Back-N 详解
发送方
- 窗口 = 最多 N 个连续的未确认分组
ACK(n)确认「直到并包含序号 n」的所有分组 —— 累积 ACK- 可能收到重复 ACK
- 只为「最老的在飞分组」设定时器
timeout(n):重传分组 n 及窗口内所有更高序号的分组
发送方的三个变量:
| 变量 | 含义 |
|---|---|
base |
窗口最左端(最老的未确认分组序号) |
nextseqnum |
下一个要发送的序号 |
N |
窗口大小 |
发送逻辑(讲义 p52 的 FSM):
rdt_send(data): |
接收方
ACK-only:总是对「已正确按序收到的最高序号分组」发 ACK。
| 特性 | 说明 |
|---|---|
| 可能产生重复 ACK | 收到乱序分组时会重复确认同一个序号 |
只需记住
expectedseqnum |
接收方状态极简 |
| 乱序分组 | 丢弃(不缓存),然后重发最高按序序号的 ACK |
讲义 p54 的完整时序推演(N = 4,pkt2 丢失)
| 时刻 | 发送方 | 接收方 |
|---|---|---|
| 1 | 发 pkt0, pkt1, pkt2, pkt3(窗口满) | — |
| 2 | — | 收 pkt0 → 发 ack0 |
| 3 | — | 收 pkt1 → 发 ack1 |
| 4 | — | pkt2 丢失 |
| 5 | 收 ack0 → 发 pkt4 | — |
| 6 | 收 ack1 → 发 pkt5 | — |
| 7 | — | 收 pkt3 → 丢弃!重发 ack1 |
| 8 | — | 收 pkt4 → 丢弃!重发 ack1 |
| 9 | — | 收 pkt5 → 丢弃!重发 ack1 |
| 10 | pkt2 超时 | — |
| 11 | 重发 pkt2, pkt3, pkt4, pkt5(4 个!) | — |
| 12 | — | 依次收下并交付,发 ack2, ack3, ack4, ack5 |
发送方对重复的 ack1 是「忽略」 —— GBN 没有快速重传机制,只能等超时。 注意 pkt3/4/5 白白传了一遍又被丢弃 —— 这就是 GBN 浪费带宽的地方。
二十六、Selective Repeat 详解
核心特性
- 接收方对每个正确收到的分组单独确认
- 按需缓存分组,以便最终按序交付给上层
- 发送方只重传未收到 ACK 的分组
- 发送方为每个未确认分组设定时器
- 有发送方窗口和接收方窗口
发送方的三种事件(讲义 p57)
| 事件 | 动作 |
|---|---|
| 上层来数据 | 若窗口内有可用序号,就发送 |
timeout(n) |
只重发 pkt n,重启该分组的定时器 |
收到
ACK(n),且 n 在
[sendbase, sendbase+N-1] 内 |
标记 pkt n 已收到;若 n 是最小的未确认分组,窗口前移到下一个未确认序号 |
接收方的三种情况(考点)
| 收到的分组 n | 动作 |
|---|---|
在
[rcvbase, rcvbase+N-1] 内 |
发 ACK(n);乱序 → 缓存;按序 → 交付(连同已缓存的按序分组),窗口前移 |
在
[rcvbase-N, rcvbase-1] 内 |
仍然要发 ACK(n)! |
| 其他 | 忽略 |
第二行是最容易忽略的一条:分组明明已经交付过了,为什么还要 ACK? 因为这说明发送方没收到之前的 ACK(ACK 丢了),如果不再 ACK 一次,发送方的窗口就永远前移不了,会死锁。
讲义 p59 的完整时序推演(同样 N = 4,pkt2 丢失)
| 时刻 | 发送方 | 接收方 | 与 GBN 的差异 |
|---|---|---|---|
| 1 | 发 pkt0–pkt3 | — | 同 |
| 2–3 | — | 收 pkt0, pkt1 → ack0, ack1 | 同 |
| 4 | — | pkt2 丢失 | 同 |
| 5–6 | 收 ack0/ack1 → 发 pkt4, pkt5 | — | 同 |
| 7 | — | 收 pkt3 → 缓存,发 ack3 | GBN 会丢弃并重发 ack1 |
| 8 | — | 收 pkt4 → 缓存,发 ack4 | GBN 会丢弃 |
| 9 | — | 收 pkt5 → 缓存,发 ack5 | GBN 会丢弃 |
| 10 | 记录 ack3/ack4/ack5 已到 | — | GBN 会忽略这些 |
| 11 | pkt2 超时 → 只重发 pkt2(1 个) | — | GBN 重发 4 个 |
| 12 | — | 收 pkt2 → 一次性交付 pkt2, pkt3, pkt4, pkt5,发 ack2 | GBN 要逐个重收 |
这一对例子(p54 vs p59)几乎必考 —— 记住「同样丢 pkt2,GBN 重传 4 个,SR 只重传 1 个」。
重传量的一般规律
| 窗口 N | 丢失位置 | GBN 重传数 | SR 重传数 |
|---|---|---|---|
| 4 | 第 1 个 | 3 | 1 |
| 6 | 第 1 个 | 5 | 1 |
| 8 | 第 3 个 | 5 | 1 |
GBN 重传数 = 从丢失的那个起,窗口内已发出的分组个数。
二十七、序号空间的最小要求(讲义未展开但常考)
| 协议 | 序号空间下限 | 理由 |
|---|---|---|
| 停等(rdt2.1/3.0) | 2 | 只需区分「这个」和「下一个」 |
| GBN | N + 1 | 接收窗口大小为 1 |
| SR | 2N | 收发窗口可能完全不重叠 |
SR 为什么要 2N:如果序号空间只有 N,接收方无法区分「新分组」和「上一轮的重传」, 会把旧数据当新数据交付给上层。这是 SR 的经典陷阱题。
第八部分:配套 Tutorial —— DNS
⚠️ tutorial 比 lecture 慢一周 —— Week 5 模块里这份 tutorial 练的是 Week 04 的 DNS,不是本讲的传输层。
✅ 官方答案(Tutor's version)已拿到并逐条核对完毕, 完整解答独立成篇 → Week 05 Tutorial:DNS 与 HTTP 时延
二十八、要点速览
| Exercise | 内容 | 关键结论 |
|---|---|---|
| 1 | 13 道 DNS 概念题 | 协议 Both、端口 53、先问 Local DNS、记录存在 Authoritative、TLD 转介返回 NS + A 两条 |
| 2 | HTTP 与 DNS 时延 | 148 / 508 / 268 / 208 ms,持久并行最快 |
Exercise 2 的四个答案(
| 情形 | 计算 | 结果 |
|---|---|---|
| 单对象 | 148 ms | |
| 非持久·串行,n=3 | 508 ms | |
| 非持久·并行(上限 5) | 268 ms | |
| 持久·并行 | 208 ms |
二十九、通用公式(背下来,考试直接套)
| 情形 | 公式 |
|---|---|
| 非持久·串行(n 个引用对象) | |
| 非持久·并行(上限 k 条) | |
| 持久 + 并行(上限 k 条) |
记忆逻辑:
- 基础对象永远要 2 个 RTT(建连 + 请求)
- 非持久串行:每个引用对象再各要 2 个 RTT
- 非持久并行:每一轮 2 个 RTT,轮数 =
- 持久并行:每一轮只要 1 个 RTT(不用重建连接),轮数仍是
⚠️ 持久那条别写成恒定的
—— 本题 只有一轮才凑巧等于 3。 时要分轮,例如 、 是 。 只有「持久 + 流水线、不限并行数」才是所有对象共 1 个 RTT。 ⚠️ 官方答案 PDF 另有两处与题面不一致(Q3 的类型数、Q9 的 IP), 详见 Tutorial 笔记。
第九部分:MCQ 速查表
分层与分解
| 项 | 答案 |
|---|---|
| 网络层 vs 传输层 | 主机到主机 vs 进程到进程 |
| UDP 套接字标识 | 二元组:目的 IP + 目的端口 |
| TCP 套接字标识 | 四元组:源 IP + 源端口 + 目的 IP + 目的端口 |
| 传输层数据单元 | segment(段) |
| UDP 首部大小 | 8 字节(4 个字段各 2 字节) |
| TCP/UDP 都不提供 | 时延保证、带宽保证 |
UDP
| 项 | 答案 |
|---|---|
| 为什么要 UDP | 无连接建立时延、无连接状态、首部小、无拥塞控制 |
| 典型用途 | 流媒体、DNS |
| 想要可靠怎么办 | 在应用层加 |
| length 字段 | 含首部的字节数,最小 8 |
| 校验和验证结果 | 把校验和一起加进去 → 全 1 → 取反全 0 |
| 校验和能力 | 只能检测差错,不能纠正;也检测不出所有差错 |
rdt 演进
| 版本 | 信道假设 | 新机制 |
|---|---|---|
| rdt1.0 | 完美 | 无 |
| rdt2.0 | 有比特错 | 校验和 + ACK/NAK |
| rdt2.1 | 有比特错 | 序号(0/1) |
| rdt2.2 | 有比特错 | 无 NAK,ACK 带序号 |
| rdt3.0 | 还会丢包 | 定时器 + 超时重传 |
性能
| 项 | 公式 |
|---|---|
| 停等利用率 | |
| 流水线利用率 | |
| 占满链路所需窗口 | |
| 讲义算例 | 1 Gbps、RTT 30 ms、8000 bit → U = 0.00027,吞吐 33 kB/s |
GBN vs SR
| 项 | GBN | SR |
|---|---|---|
| ACK | 累积 | 单独 |
| 定时器 | 1 个 | 每包 1 个 |
| 重传 | N 个 | 1 个 |
| 接收方缓存 | 不缓存 | 缓存 |
| 接收窗口 | 1 | N |
| 序号空间下限 | N + 1 | 2N |
最容易错的 6 个点
- UDP 是二元组,TCP 是四元组 —— 别记反
- 校验和要回卷进位,漏掉就全错
- 接收方验证的结果是全 0(不是全 1 —— 全 1 是取反之前)
- GBN 接收方不缓存 —— 这是它要重传 N 个的根本原因
- SR 对「已交付过的旧分组」仍要 ACK,否则发送方窗口卡死
- 停等利用率公式的分母是
,不是只有
第十部分:本讲在考纲中的位置
| Week | 主题 | 笔记 |
|---|---|---|
| 1 | Introduction | Week 01 |
| 2 | Performance | Week 02 |
| 3 | Applications | Week 03 |
| 4 | DNS and P2P | Week 04 |
| 5 | Transport | 本文 |
| 6 | TCP | ⚠️ Canvas 未发布 |
本讲是 Week 6 TCP 的直接铺垫。 把本讲的机制拼起来,基本就是 TCP 的可靠传输部分:
本讲的机制 在 TCP 里的对应 校验和 TCP 首部的 checksum 字段 序号 TCP 的序号是「字节编号」而非「分组编号」 累积 ACK(GBN) TCP 用的正是累积 ACK 单个定时器(GBN) TCP 也只为最老的未确认段设一个定时器 缓存乱序分组(SR) TCP 接收方通常会缓存 —— 所以 TCP 是 GBN 与 SR 的混合体 重复 ACK(rdt2.2) 快速重传:收到 3 个重复 ACK 立即重传 Week 6 会在此之上补充:三次握手、流量控制(接收窗口 rwnd)、拥塞控制(慢启动、拥塞避免、快速恢复)。
配套练习:20 题期中模拟卷 的板块四(Q17–Q20)专测本讲内容。