📚 覆盖:第 10–16 讲 · ⚠️ 这是本课程最难的一份习题


第一部分:传输层服务与 UDP(第 10 讲,14 分)

题 1(4 分)

某服务器 IP 198.51.100.7,同时提供 TCP 443 与 UDP 53 服务。以下报文段先后到达:

① TCP  源(203.0.113.5, 40001)  目的(198.51.100.7, 443)
② TCP  源(203.0.113.5, 40002)  目的(198.51.100.7, 443)
③ TCP  源(198.18.0.9,  40001)  目的(198.51.100.7, 443)
④ UDP  源(203.0.113.5, 33333)  目的(198.51.100.7, 53)
⑤ UDP  源(198.18.0.9,  55555)  目的(198.51.100.7, 53)

(a) 这些报文被交给几个不同的 socket?分别写出标识元组。 (b) 为什么 ④⑤ 共享一个 socket 而 ①②③ 不能? (c) 服务器一共需要多少个 socket(含欢迎套接字)?

题 2(4 分)⭐

计算以下三个 16 位字的反码校验和:

0110 1010 0011 1100
1111 0000 1010 0101
0000 1111 0101 1010

(a) 给出反码和与最终的校验和。 (b) 验证接收端的计算(结果应为全 1)。 (c) 构造一个两比特翻转但校验和不变的例子。 (d) UDP 校验和的漏检概率量级是多少?这在 10 Gbps 链路上意味着什么?

题 3(3 分)

(a) 说出使用 UDP 的四个理由。 (b) 为什么 DNS 用 UDP 而不是 TCP? (c) ⚠️ 一位工程师说「我们用 UDP 自己实现重传,这样比 TCP 快」。指出这个想法的风险。

题 4(3 分)

(a) 什么是伪首部?它包含哪些字段? (b) ⭐ 为什么说伪首部破坏了分层? (c) 它给 NAT 带来了什么实际负担?


第二部分:可靠数据传输原理(第 11–12 讲,34 分)

题 5(4 分)

按顺序说出 rdt1.0 → rdt3.0 每一步引入的信道缺陷和被逼出的机制,填表:

版本 新增的信道缺陷 新增的机制

题 6(4 分)⭐

(a) rdt2.0 的致命缺陷是什么? (b) rdt2.1 如何解决?为什么 1 位序号就够? (c) 接收方收到重复分组时,为什么必须仍然发 ACK? (d) rdt2.2 用什么代替 NAK?这个思想在 TCP 中变成了什么机制?

题 7(4 分)

一条链路 R = 500 Mbps,RTT = 80 ms,分组 1500 字节。

(a) 停等协议的利用率是多少?有效吞吐量是多少? (b) 带宽时延积是多少? (c) 要达到 100% 利用率,窗口需要多少个分组?

题 8(4 分)

⭐ 定时器设置得过短会导致什么?会导致协议错误吗?请从"序号机制的作用"角度回答。

题 9(5 分)⭐

窗口 N = 4,序号空间充足。发送方连续发 pkt0–pkt7。pkt3 丢失,其余全部到达,所有 ACK 均正常。超时发生在 pkt6 发出之后。

(a) 用 GBN,写出接收方对每个到达分组的动作与 ACK 值。 (b) 发送方一共发送了多少个分组(含重传)? (c) 用 SR 重做 (a)(b)。 (d) 两者的发包数差多少?浪费率是多少?

题 10(4 分)⭐⭐

(a) 证明 GBN 的序号空间必须 ≥ N+1(给出一个 N 时出错的具体场景)。 (b) 证明 SR 的序号空间必须 ≥ 2N(给出 2N−1 时出错的具体场景)。 (c) 窗口 N = 6、序号用 4 位(空间 16)。GBN 可以吗?SR 可以吗?

题 11(3 分)

(a) SR 接收方收到一个落在接收窗口左侧的分组,应该怎么办?为什么? (b) 若不这么做会发生什么?

题 12(3 分)

累积确认对 ACK 丢失有什么好处?逐分组确认(SR)在这方面有什么劣势?

题 13(3 分)

⭐ TCP 在哪些方面像 GBN,哪些方面像 SR?列表回答。


第三部分:TCP(第 13–14 讲,30 分)

题 14(4 分)

主机 A 向 B 发送数据,ISN = 8000,MSS = 500。A 连续发出 6 个满载报文段,第 3 个(Seq=9000)丢失,其余全部到达。B 缓存乱序报文段并启用 SACK。

(a) 写出 6 个报文段各自的序号。 (b) 写出 B 对每个到达报文段的确认号。 (c) A 何时触发快速重传? (d) B 的第 4 个 ACK 携带的 SACK 块是什么?

题 15(4 分)⭐

某 TCP 连接测得 SampleRTT = 180 ms,此前 EstimatedRTT = 150 ms,DevRTT = 20 ms。

(a) 计算更新后的 EstimatedRTT、DevRTT 与 TimeoutInterval。 (b) 若下一次超时,TimeoutInterval 变成多少? (c) ⭐ Karn 算法要解决什么问题?Timestamps 选项如何绕过它?

题 16(4 分)

(a) 完整说明为什么 TCP 握手需要三次而不是两次,给出一个两次会出错的具体场景。 (b) 为什么关闭需要四次而握手只要三次? (c) SYN 和 FIN 各消耗几个序号?

题 17(5 分)⭐

(a) TIME_WAIT 存在的两个理由是什么? (b) 为什么等待时间是 2×MSL 而不是 1×MSL? (c) 某反向代理服务器出现数万个 TIME_WAIT,说明了什么?根本解法是什么? (d) tcp_tw_recycle 为什么被从 Linux 中移除?

题 18(4 分)

A 与 B 建立 TCP 连接,B 的接收缓冲区 10,000 字节,MSS = 1000。B 的应用一直不读取数据。

(a) A 最多能连续发送多少个报文段? (b) 之后 A 应该怎么办? (c) ⭐ 若 B 的应用随后读走 4000 字节但 B 没有数据要发给 A,会发生什么?如何打破? (d) 为什么解决死锁的责任在发送方?

题 19(4 分)

(a) 流量控制和拥塞控制的三点区别是什么? (b) 一条 40 Gbps、RTT 60 ms 的链路,不启用窗口缩放时最大吞吐量是多少?利用率多少? (c) 窗口缩放为什么只能在握手时协商?

题 20(5 分)⭐

某工程师发现自己的 RPC 服务在小请求上有稳定的 40 ms 延迟。

(a) 最可能的原因是什么? (b) 详细解释这个现象的产生机制。 (c) 给出三种解决方案,并说明推荐哪一种。 (d) 从这个案例能得出什么一般性的工程教训?


第四部分:拥塞控制(第 15–16 讲,26 分)

题 21(4 分)

(a) 说出拥塞的四种代价。 (b) 哪一种最严重?为什么它会导致拥塞崩溃? (c) 1986 年发生了什么?它确立了什么原则?

题 22(5 分)⭐⭐

两条 TCP 连接共享 20 Mbps 瓶颈链路。初始时连接 1 得 16 Mbps,连接 2 得 4 Mbps。每轮无拥塞时各加 1 Mbps,拥塞时各乘 0.5。

(a) 写出前三个完整循环的状态与差距。 (b) 差距的变化规律是什么? (c) 若改用 AIAD(拥塞时各减 2 Mbps),差距如何变化? (d) ⭐ 用相图论证为什么只有 AIMD 能同时收敛到公平与高效。

题 23(4 分)

(a) 写出 TCP 吞吐量与丢包率的关系式。 (b) 一条 100 Gbps、RTT 80 ms、MSS 1500 字节的链路,Reno 要跑满需要多低的丢包率? (c) 从 (b) 说明为什么需要 CUBIC 或 BBR。

题 24(5 分)⭐

某 TCP Reno 连接的 cwnd 演化(单位 MSS),初始 ssthresh = 16:

轮次:  1   2   3   4   5   6   7   8   9  10  11  12  13  14
cwnd:  1   2   4   8  16  17  18  19  20  10  11  12  13   1

(a) 第 1–5 轮处于什么阶段? (b) 第 6 轮为什么是 17 而不是 32? (c) 第 9 轮到第 10 轮发生了什么事件?判断依据是什么?此时 ssthresh 变成多少? (d) 第 13 轮到第 14 轮发生了什么?ssthresh 变成多少? (e) 第 15 轮的 cwnd 会是多少?

题 25(4 分)

(a) CUBIC 相对 Reno 的两个关键改进是什么? (b) 什么是缓冲区膨胀?为什么基于丢包的算法必然导致它? (c) BBR 测量哪两个量?它的最优工作点是什么?

题 26(4 分)

(a) ECN 相比丢包信号的优势是什么? (b) 为什么 ECN 在公网上部署缓慢? (c) CoDel 与 RED 的根本区别是什么?为什么 CoDel 的指标更好?


参考解答

题 1

(a) 四个 socket

① TCP socket A:(203.0.113.5, 40001, 198.51.100.7, 443)
② TCP socket B:(203.0.113.5, 40002, 198.51.100.7, 443)
③ TCP socket C:(198.18.0.9,  40001, 198.51.100.7, 443)
④⑤ 同一个 UDP socket:(198.51.100.7, 53)

(b)UDP socket 由二元组(目的 IP, 目的端口)标识——因为 UDP 无连接,内核不维护任何连接状态,只能按目的地分发。TCP socket 由四元组标识——每条连接有独立的序号、窗口、缓冲区状态,必须能区分开。

(c) 3 个 TCP 连接套接字 + 1 个 TCP 欢迎套接字 + 1 个 UDP socket = 5 个

题 2

(a)

  0110101000111100
+ 1111000010100101
 ─────────────────
 10101101011100001    ← 17 位,溢出
回卷: 0101101011100001 + 1 = 0101101011100010

  0101101011100010
+ 0000111101011010
 ─────────────────
  0110100111000000   ← 无溢出,即反码和

⭐ 校验和(取反)= 1001011000111111

(b)

  0110100111000000
+ 1001011000111111
 ─────────────────
  1111111111111111   ✅ 全 1

(c) 取第一个字的最高位 0→1,第二个字的最高位 1→0:

1110101000111100
0111000010100101

两字之和不变(一个 +2¹⁵,一个 −2¹⁵),因此校验和不变,两处翻转都检不出

(d) 漏检概率约 2⁻¹⁶ ≈ 1.5×10⁻⁵。在 10 Gbps 链路上,⭐ 每传输几十 GB 就可能有一个未被检出的错误。因此对完整性要求高的应用(文件同步、数据库复制、软件分发)必须在应用层再做一次强校验(SHA-256)——这是端到端原则。

题 3

(a) ① 对何时发什么数据有精确的应用级控制(不被拥塞控制限速、不被重传拖延);② 无需建立连接(省一个 RTT);③ 无连接状态(服务器能支持更多客户端);④ 头部开销小(8 字节 vs 20 字节)。

(b) DNS 一次查询本来就只有一来一回,为它做三次握手(1 个 RTT)是纯粹的浪费;丢了直接重问即可;而且 DNS 服务器要处理海量查询,无连接状态使它能支持的并发量高得多。

(c) ⚠️ 风险有二:① ⭐ 没有拥塞控制——若这类应用大规模部署且不实现速率控制,会导致拥塞崩溃(第 15 讲的 1986 年事件);② 自己实现的可靠传输极难做对——TCP 的重传、RTT 估计、快速重传、拥塞控制是三十多年调优的结果。正确的做法是:要么用 TCP,要么用 QUIC 这种已经实现了完整拥塞控制的方案,而不是自己造一个半成品。

题 4

(a) 伪首部包含源 IP 地址、目的 IP 地址、保留字节、协议号、传输层报文段长度。它不真正发送,只参与校验和计算。

(b) 因为它让传输层的校验和依赖了网络层的地址字段,违反了"每层只处理自己的头部"的分层原则。设计目的是检测"报文段被送错了主机"这类错误。

(c)任何改写 IP 地址或端口的设备(NAT、负载均衡器)都必须重新计算 TCP/UDP 校验和,而不能只改 IP 头。这显著增加了 NAT 的实现复杂度,也是 NAT 无法对某些协议(如 IPsec AH)透明工作的原因之一。

题 5

版本 新增的信道缺陷 新增的机制
rdt1.0
rdt2.0 比特差错 校验和、ACK、NAK
rdt2.1 ACK/NAK 自身也会损坏 序号(1 位)
rdt2.2 同上 去掉 NAK,改用重复 ACK
rdt3.0 分组丢失 定时器 + 超时重传

题 6

(a) 若 ACK/NAK 本身被损坏,发送方无法判断接收方是否收到了数据。重传会产生接收方无法识别的重复分组

(b) 在分组中加入序号,接收方看到序号与上次相同就知道是重复,丢弃数据但仍回 ACK。1 位够用,是因为⭐ 停等协议下信道中最多只有一个未确认分组,接收方只需区分"新的"和"上一个的重复"两种情况。

(c) 因为对方重传的原因正是它没收到(或没看懂)上一次的 ACK。若接收方沉默,发送方会一直超时重传,形成无限重传的死锁

(d) 用 ⭐ 对最后一个正确接收的分组重复发送 ACK(带序号) 代替 NAK。在 TCP 中它演化为⭐ 累积确认 + 三次重复 ACK 触发快速重传

题 7

(a)

L/R = 12000 bit / 5×10⁸ = 24 μs = 0.024 ms
U = 0.024 / (80 + 0.024) ≈ 3×10⁻⁴ = 0.03%
吞吐量 = 12000 / 0.080024 s ≈ 150 kbps

(b) BDP = 5×10⁸ × 0.08 = 4×10⁷ bit = 5 MB

(c) W = 4×10⁷ / 12000 ≈ 3334 个分组

题 8

会导致不必要的重传,浪费带宽;在拥塞时还会加剧拥塞(第 15 讲的代价 3)。

但不会导致协议错误。因为序号机制保证了重复分组会被接收方正确识别并丢弃、且不会重复交付给应用。

📌 这体现了一个重要的设计思想:⭐ **与其精确判断,不如让错误判断的后果可容忍。**正是因为过早超时是无害的,TCP 才可以采用「宁可保守也不激进」的超时估计策略(Est + 4·Dev),并在超时后大胆地指数退避。

题 9

(a) GBN

pkt0 ✓ 交付, ACK0        pkt1 ✓ 交付, ACK1        pkt2 ✓ 交付, ACK2
pkt3 ╳ 丢失
pkt4 → 乱序,丢弃,重发 ACK2
pkt5 → 乱序,丢弃,重发 ACK2
pkt6 → 乱序,丢弃,重发 ACK2
     (窗口 N=4,send_base=3 时窗口为 [3,4,5,6],恰好发完)
[pkt3 超时] → 重传 pkt3, 4, 5, 6
pkt3 ✓, pkt4 ✓, pkt5 ✓, pkt6 ✓
pkt7 ✓

(b) 8 个原始 + 3 个重复重传(pkt4,5,6)+ pkt3 重传 = 12 个分组。 (pkt0–pkt7 各发一次 = 8,加上 pkt3,4,5,6 的重传 = 4,合计 12。)

(c) SR

pkt0,1,2 ✓ 交付
pkt3 ╳ 丢失
pkt4 → ⭐ 缓存, ACK4
pkt5 → ⭐ 缓存, ACK5
pkt6 → ⭐ 缓存, ACK6
[仅 pkt3 超时] → 只重传 pkt3
pkt3 ✓ → 一次性交付 3,4,5,6
pkt7 ✓

发送分组数 = 8 + 1 = 9 个

(d) GBN 比 SR 多发 3 个分组。浪费率 = 3/9 ≈ 33%(相对 SR)或 3/12 = 25%(占 GBN 总发送量)。

题 10

(a) GBN 序号空间 = N 时出错的场景

设序号空间 4(0,1,2,3),窗口 N = 4
① 发送方发出 pkt0,1,2,3
② 接收方全部收到并交付,发出 ACK0–ACK3
③ ⭐ 所有 ACK 全部丢失
④ 发送方超时,重传 pkt0
⑤ 接收方此时 expectedseqnum = 0(序号回绕)
   ⭐ 它把重传的 pkt0 当作【新的 pkt0】接受 → 数据重复交付 ❌

因此空间必须 ≥ N+1,让接收方能区分"新一轮的 0"和"上一轮的 0"。

(b) SR 序号空间 = 2N−1 时出错的场景

设 N = 3,序号空间 5(0–4)
① 发送方发 pkt0,1,2
② 接收方全收到、交付、发 ACK0,1,2,接收窗口滑到 [3,4,0]
③ ⭐ 三个 ACK 全部丢失
④ 发送方超时,重传 pkt0
⑤ ⭐ 接收方看到序号 0,而 0 【在当前接收窗口 [3,4,0] 内】
   → 误当作新分组接受 ❌

从接收方角度,这个重传的 pkt0 与"下一轮全新的 pkt0"完全无法区分。因此必须 ≥ 2N。

(c) 序号空间 16,N = 6:

GBN: 需要 ≥ N+1 = 7。 16 ≥ 7  ✅ 可以
SR:  需要 ≥ 2N = 12。 16 ≥ 12 ✅ 可以

(SR 在 4 位序号下窗口最大为 8。)

题 11

(a)必须发送 ACK(n),但丢弃数据(不重复交付)。因为该分组落在窗口左侧,说明接收方之前已经收过并确认过它,发送方之所以重传,是因为那次的 ACK 丢失了。

(b) 若接收方沉默,⭐ 发送方的 send_base 将永远无法推进,它会不停地重传同一个分组直到放弃——连接死锁。这是 Lab 3 Part 3 最常见的 bug。

题 12

好处:⭐ ACK(n) 隐含确认了 n 之前的所有分组,因此中间的 ACK 丢失是无害的——只要后面有任何一个更大的 ACK 到达,发送方就能一次性推进窗口。这使 GBN 对反向路径的丢包天然鲁棒。

SR 的劣势:⭐ 每个 ACK 都是独立信息,丢一个就必须等对应分组的定时器超时才能恢复,恢复代价高得多。

题 13

TCP 的特征 更像
累积确认 GBN
只维护一个重传定时器 GBN
超时时只重传最老的一个报文段 ⭐ 二者皆非(比 GBN 保守)
接收方缓存乱序报文段 SR
快速重传只重传一个 SR
SACK 选项明确指出所有空洞 SR

TCP 的哲学:默认走 GBN 的简单确认框架,但用 SR 的机制处理常见情况(单个丢包)。

题 14

(a)

seg1: 8000  seg2: 8500  seg3: 9000(丢失)
seg4: 9500  seg5: 10000 seg6: 10500

(b)

收到 seg1 → ACK=8500
收到 seg2 → ACK=9000
seg3 丢失
收到 seg4 → ACK=9000  重复 #1
收到 seg5 → ACK=9000  重复 #2
收到 seg6 → ACK=9000  重复 #3  ⭐

(c) 收到第 3 个重复 ACK(由 seg6 引发,总共第 4 个 ACK=9000)时触发快速重传,立即重发 Seq=9000。

(d) B 的第 4 个 ACK(即第 1 个重复 ACK,由 seg4 引发):

ACK = 9000
SACK: [9500, 10000)

若指的是最后一个 ACK(由 seg6 引发):

ACK = 9000
SACK: [9500, 11000)      ← 三块连续,可合并

题 15

(a)

EstimatedRTT = 0.875 × 150 + 0.125 × 180 = 131.25 + 22.5 = 153.75 ms
DevRTT = 0.75 × 20 + 0.25 × |180 − 153.75|
       = 15 + 0.25 × 26.25 = 15 + 6.5625 = 21.5625 ms
TimeoutInterval = 153.75 + 4 × 21.5625 = 153.75 + 86.25 = 240 ms

(b)指数退避240 × 2 = 480 ms。(上限通常 60 s。)

(c)重传歧义:一个报文段被重传后收到 ACK,无法判断它对应的是原始发送还是重传,因而任何 RTT 测量都有系统性误差。Karn 算法的解法是不对重传过的报文段采样 RTT,并在超时时用指数退避保守放大。⭐ Timestamps 选项在每个报文段中携带发送时刻并由接收方原样回显,ACK 中的时间戳直接指明它对应哪一次发送,歧义消失,重传段的 RTT 也能精确测量。

题 16

(a) 因为在允许分组延迟、重复、乱序的网络上,两次消息不足以让双方都确认连接的建立。具体场景:客户端的一个旧 SYN 在网络中长期滞留;客户端超时重连、完成传输并关闭;随后滞留的 SYN 到达服务器——两次握手下服务器会立即认为连接建立、分配资源、进入 ESTABLISHED,并可能接受随后到达的旧数据分组。第三次握手让服务器确认"客户端现在确实还想连"。

(b) 握手时服务器的"确认对方 SYN"与"发出自己的 SYN"可以合并成一个 SYN-ACK。关闭时服务器收到 FIN 后可能还有数据没发完,必须先只回 ACK,等数据发完再发 FIN——这中间的状态就是半关闭

(c)各消耗 1 个序号(虽然它们不携带数据)。这就是确认号为 ISN+1 的原因。

题 17

(a) ① ⭐ 保证自己发出的最后一个 ACK 能可靠送达——若它丢失,对方会重传 FIN,处于 TIME_WAIT 的一方能收到并重发 ACK;若已 CLOSED 则会回 RST,导致对方异常终止、可能丢失最后的数据。② ⭐ 让本连接残留在网络中的旧报文段全部消亡,避免被四元组相同的新连接误接受。

(b) 最坏情况下,最后的 ACK 走完全程需要 1 个 MSL;对方没收到、重传的 FIN 走回来又需要 1 个 MSL。共 2 个 MSL

(c) 说明该服务器是大量连接的主动关闭方,且在频繁建立/关闭短连接(典型:反向代理到后端用短连接)。⭐ 根本解法是改用长连接/连接池,减少连接建立次数。SO_REUSEADDRtcp_tw_reuse 只是缓解手段。

(d) 因为它依赖 TCP Timestamps 的单调性来判断"这是新连接还是旧连接的残留",⭐ 而在 NAT 之后,多个客户端共享一个源 IP 但各自的时间戳互不相关——服务器会把某些客户端的合法 SYN 误判为旧连接的重复而丢弃,造成随机的连接失败。这类故障极难排查,因此该选项被移除。

题 18

(a) 握手时 B 通告 rwnd = 10000,A 在收到窗口更新前最多发 10000 字节 = ⭐ 10 个报文段

(b) 收到 rwnd = 0 后,A 必须停止发送数据并启动坚持定时器,定期发送 1 字节的窗口探测报文段。

(c) B 的 rwnd 变为 4000,但因为 ⭐ B 没有数据要发,它不会主动发出任何报文段告知这一变化——A 无从得知窗口已打开,形成零窗口死锁。打破方式:A 的窗口探测到达后,B 必须回一个 ACK,其中携带最新的 rwnd,A 随即恢复发送。

(d) 因为 ⭐ 发送方才是有动力解决这个问题的一方(它想把数据发出去)。协议设计上把责任放在有动机的一方更可靠——如果依赖接收方主动通知,接收方的实现出任何疏漏都会造成永久挂起。

题 19

(a)保护对象:流量控制保护接收方缓冲区,拥塞控制保护网络内部的路由器队列;② 信息来源:流量控制有接收方通过 rwnd 的显式告知,拥塞控制只能从丢包/时延间接推断;③ 参与范围:流量控制是两个端点之间的事,拥塞控制涉及所有共享该路径的连接,是多方博弈

(b)

最大在途 = 65535 字节
吞吐量 = 65535 × 8 / 0.06 s ≈ 8.74 Mbps
利用率 = 8.74 Mbps / 40 Gbps ≈ 0.022%

(c) 因为窗口缩放因子决定了如何解释接收窗口字段中的数值——通信双方必须从连接的第一个字节起就对这个解释达成一致。若允许中途启用,双方对同一个 rwnd 数值的理解会出现分歧,导致严重的窗口计算错误。⭐ 因此它只能在双方都能看到、且尚无数据流动的握手阶段协商。

题 20

(a)Nagle 算法与延迟确认的负面交互。

(b)

① 发送方(开启 Nagle)发出第一个小包
② 接收方(开启延迟确认)收到后【压住 ACK 不发】,
   等待最多 200 ms 看是否有反向数据可以捎带
③ ⭐ 发送方因为有未确认数据在途,按 Nagle 规则【攒着第二个小包不发】
④ 双方互相等待,直到接收方的延迟确认定时器超时
⑤ ACK 发出 → 发送方才发出第二个包
⭐ 一个本该几毫秒完成的交互,变成了 40–200 ms

(c)

① ⭐ TCP_NODELAY(关闭 Nagle)—— 【推荐】
② 应用层合并写:把 header 和 body 用一次 writev / sendall 发出
③ TCP_QUICKACK(Linux 特有,临时关闭延迟确认;不持久,需反复设置)

推荐 ① 加 ②:①是标准做法(几乎所有 RPC 框架、数据库客户端默认这么做),②从源头上不产生连续小包,是更彻底的修复。③不推荐,因为它不持久且平台相关。

(d) ⭐ **两个各自合理的优化,组合起来可能产生严重的负面交互。**Nagle 减少小包是对的,延迟确认减少纯 ACK 也是对的,但它们对彼此的假设不成立。局部最优 ≠ 全局最优——这在分层系统中尤其常见,因为每一层都在自己的视野内做优化,而看不到其他层的行为。

题 21

(a) ① 到达速率接近容量时排队时延趋于无穷;② 丢包迫使重传,重传消耗容量却不产生新的有效吞吐量;③ 过早超时导致不必要的重传,路由器为同一分组的多个副本做无用功;④ ⭐ 一个分组在下游被丢弃时,它在所有上游链路上消耗的容量全部白费

(b)第 ④ 种最严重。因为它意味着发送得越多,有效吞吐量反而越低——构成一个正反馈:丢包 → 超时 → 重传 → 更多拥塞 → 更多丢包。这就是拥塞崩溃的直接机制。

(c) 1986 年 10 月,NSFNET 上一段链路的吞吐量从 32 kbps 崩溃到 40 bps(下降 1000 倍),因为当时的 TCP 没有拥塞控制。它确立的原则是:⭐ 在一个共享的、无准入控制的网络中,端系统必须自愿限制自己的发送速率,否则系统会集体崩溃。

题 22

(a)

初始 (16, 4),和 = 20 = 容量 → 拥塞
乘性减 ×0.5:  (8.0, 2.0)   和 6.0   ⭐ 差距 6.0
加性增(各 +1,直到和 = 20,共 7 轮): (15.0, 9.0)  差距 6.0
乘性减:       (7.5, 4.5)   和 12.0  ⭐ 差距 3.0
加性增(4 轮): (11.5, 8.5)  和 20.0  差距 3.0
乘性减:       (5.75, 4.25) 和 10.0  ⭐ 差距 1.5
加性增(5 轮): (10.75, 9.25) 和 20.0 差距 1.5
乘性减:       (5.375, 4.625)       ⭐ 差距 0.75

(b) ⭐ 差距序列:12.0 → 6.0 → 3.0 → 1.5 → 0.75 → …

(初始差距为 16−4 = 12。)**加性增阶段差距不变;每次乘性减差距恰好减半。**因此按 0.5ⁿ 指数收敛到 0

(c) AIAD

初始 (16, 4),差距 12
加性减 −2:  (14, 2)  差距 12   ❌ 不变
加性增 +1×2 轮: (16, 4) 差距 12  ❌ 不变
...
⭐ 差距永远是 12 —— 永不收敛到公平

(d) 在 (x₁, x₂) 相图中:

⭐ 加性增:位移向量 (a, a),方向 45°,【平行于公平线】
   → |x₁ − x₂| 不变(对公平性中性)

⭐ 乘性减:新点为 (bx₁, bx₂),位于连接原点的射线上
   → |bx₁ − bx₂| = b·|x₁ − x₂|,⭐ 差距按比例 b < 1 缩小

因此 AIMD 每完成一次"增-减"循环,差距乘以 b,指数收敛到 0,同时被效率线约束在高效区域。

  • AIAD:增和减都平行于公平线,差距永不改变
  • MIMD:乘性增放大差距、乘性减缩小差距,二者恰好抵消
  • MIAD:乘性增放大、加性减不变,⭐ 越来越不公平

⭐ **结论:乘性减是公平性的唯一来源。**TCP 的公平性不是靠"涨得慢",而是靠"跌得狠"实现的。

题 23

(a) 平均吞吐量 ≈ 1.22 × MSS / (RTT × √L)

(b)

所需 cwnd W = 100×10⁹ × 0.08 / (1500×8) = 8×10⁹ / 12000 ≈ 666,667 个报文段
L ≈ 8 / (3W²) = 8 / (3 × 4.44×10¹¹) ≈ 6.0 × 10⁻¹²

需要低于万亿分之六的丢包率——完全不可实现。

(c) Reno 在这类高带宽长距离链路上根本跑不满,而且从 cwnd = 333,333(减半后)线性涨回 666,667 需要 333,334 个 RTT ≈ 7.4 小时——一次丢包让你损失大半天。⭐ 这就是 CUBIC(三次函数快速回升 + 减小因子 0.7)和 BBR(不依赖丢包信号)存在的全部理由。

题 24

(a) 慢启动。cwnd 每轮翻倍(1→2→4→8→16),且 cwnd ≤ ssthresh = 16。

(b) 因为第 5 轮末 cwnd 达到 ssthresh = 16,退出慢启动,⭐ 转入拥塞避免,改为每 RTT 加 1 MSS

(c) 发生了 ⭐ 3 个重复 ACK(快速重传 + 快速恢复)。判断依据:⭐ cwnd 从 20 掉到 10(约一半),而不是掉到 1

ssthresh = 20 / 2 = 10
(cwnd 应为 ssthresh + 3 = 13,本题按简化模型取 ssthresh = 10)

(d) 发生了 ⭐ 超时。判断依据:⭐ cwnd 直接跌到 1

ssthresh = 13 / 2 = 6.5 → 取 6

(e) 第 14 轮 cwnd = 1,处于慢启动,第 15 轮 ⭐ cwnd = 2(翻倍,因为 1 < ssthresh = 6)。

📌 判读口诀

cwnd 跌到 1        → 超时
cwnd 跌到约一半     → 3 个重复 ACK
cwnd 翻倍          → 慢启动
cwnd 每轮 +1       → 拥塞避免

题 25

(a) ① ⭐ cwnd 是距上次丢包的绝对时间的三次函数,而不是"每个 RTT 加一次",因而消除了 RTT 不公平,并能在丢包后快速回升到接近 W_max、再在其附近平台期谨慎试探;② ⭐ 减小因子从 0.5 改为 0.7,因为高速链路上砍半的损失过大、恢复过慢。

(b) 缓冲区膨胀指路由器/调制解调器配置了过大的缓冲区,导致排队时延达数百毫秒甚至数秒。基于丢包的算法必然导致它,因为它们的⭐ 唯一拥塞信号就是丢包——在缓冲区被完全填满之前不会有任何信号,TCP 会持续增长 cwnd 直到填满。而缓冲区中排队的数据⭐ 不增加任何吞吐量(链路早已饱和),只增加时延。

(c) 测量 ⭐ BtlBw(瓶颈带宽,观测到的最大交付速率)与 ⭐ RTprop(往返传播时延,观测到的最小 RTT)。最优工作点是在途数据量 = BtlBw × RTprop = BDP——恰好填满管道但不在缓冲区中留下排队。

题 26

(a)在不丢弃任何分组的前提下传递拥塞信号:避免了重传消耗的带宽、避免了丢包恢复的时延,且信号可以在队列开始增长时就发出(更早、更准)。

(b) ⭐ **协议僵化。**ECN 使用 IP 头部原先保留的比特,部分老旧中间设备(防火墙、NAT、负载均衡)会丢弃或错误处理带 ECN 标记的分组,导致连接失败。各方因而都不敢默认开启。(数据中心内部因为设备可控,DCTCP 广泛使用 ECN。)

(c)RED 看队列长度(超过阈值按概率丢包),问题是"多长算长"高度依赖链路速率和流量特征,参数极难调对。⭐ CoDel 看分组在队列中的驻留时间,只有当它持续超过目标值(5 ms)时才开始丢弃。CoDel 的指标更好,因为:① 与链路速率无关,无需调参;② ⭐ 能正确区分"健康的瞬时突发队列"(驻留时间短,应当保留以吸收突发)与"有害的持久队列"(驻留时间长,就是 bufferbloat)