Appearance
题目
(本题满分 9 分)
假设客户端 C 建立一条 TCP 连接,向服务器 Si 上传一个总长度为 2000 B 的计算任务描述文件。已知 C 的拥塞窗口初始阈值为 8 MSS,MSS = 500 B,Si 对收到的每个 TCP 段进行确认,且确认段不封装数据。接收窗口始终为 1000 B,RTT = 5 ms,C 建立连接时选择的初始序号为 1000,Si 选择的初始序号为 2000,SYN、ACK、FIN 为标志位,seq 为序号,ack_seq 为确认序号。在整个文件传输过程中未出现任何重传或报文丢失。
(1)C 与 Si 建立 TCP 连接过程需要几次握手?C 收到的 SYN = 1、ACK = 1 的 TCP 段的确认序号是多少?
(2)当 C 接收 Si 发送的 ACK = 1,seq = 2001,ack_seq = 2001,rwnd = 1000 确认段后,C 的拥塞窗口增加到多少?C 的发送窗口设置为多少?
(3)C 与 Si 释放 TCP 连接过程需要几次挥手?C 收到最后一个 TCP 报文段的序号(seq)、确认序号(ack_seq)、FIN 的值分别是多少?
(4)忽略报文段传输时延,且时间从 C 请求建立 TCP 连接时刻算起,则 C 确定 Si 已成功接收到文件的时间是多少?
解析
(1) 三次握手 + 第二次握手的确认序号
TCP 建立连接需要 3 次握手:
1️⃣ C → Si SYN=1,seq=1000
2️⃣ Si → C SYN=1,ACK=1,seq=2000,ack_seq=1001
3️⃣ C → Si ACK=1,seq=1001,ack_seq=2001C 收到的"SYN=1、ACK=1"段就是第二次握手的报文。它的确认序号是对 C 第一次握手 SYN 的确认 = 。
SYN 段不携带数据但消耗 1 个序号,所以 ack_seq 是 SYN 段 seq + 1 而不是直接等于。FIN 段同理也消耗 1 个序号。
(2) 收到 ack_seq = 2001 后的窗口
先解读 ack_seq = 2001 是哪个 ACK:
- 题面说"Si 发送的 seq = 2001、ack_seq = 2001"——Si 的 seq 从 2000 起步加 1(SYN 消耗),所以 seq = 2001 是 Si 第二次握手后的下一段,即 数据传输阶段的第一个 ACK
- 这个 ACK 是 Si 收到 C 第一段数据(500 B)后回的——它说"我已收到 [1001, 1500],下一字节期待 1501"——但题面给的 ack_seq = 2001 这个数让我们推算 Si 已收的字节数
仔细理解 ack_seq = 2001:
实际上,如果是 C 发的第 1 个数据段(seq=1001, len=500)的 ACK,应该是 ack_seq=1501;ack_seq=2001 意味着 Si 已收到 [1001, 2000],即 2 个 MSS = 1000 B = 2 个段的数据。所以这是 C 已收到的第 2 个 ACK。
题面文字描述的"该确认段"实际上是从 C 的视角看到的"某个收到的"确认段,编号是相对的。从内容看 ack_seq=2001,明确说明是 Si 累计确认到 2000 之时——对应 C 已发送 2 段后收到的第 2 个 ACK。
慢启动阶段(cwnd < ssthresh = 8 MSS 期间),每收一个 ACK,cwnd += 1 MSS:
| 时刻 | cwnd 起始 | 收 ACK 后 cwnd |
|---|---|---|
| 收第 1 个 ACK | 1 MSS | 2 MSS |
| 收第 2 个 ACK(即 ack_seq=2001 这次) | 2 MSS | 3 MSS = 1500 B |
发送窗口 = min(cwnd, rwnd) = min(1500 B, 1000 B) = 1000 B
rwnd 经常成为瓶颈:本题 rwnd 固定 1000 B(接收方告诉发送方"最多再给我 1000 B"),cwnd 即便涨到 8 MSS 也用不上——发送窗口被 rwnd 死死卡在 1000 B。这种情况下慢启动/拥塞避免的算法效果失效,要靠应用层主动取走数据让 rwnd 抬升。
(3) 四次挥手 + 最后一段的字段
TCP 释放连接需要 4 次挥手:
1️⃣ C → Si FIN=1,seq=3001
2️⃣ Si → C ACK=1,seq=2001,ack_seq=3002
3️⃣ Si → C FIN=1,ACK=1,seq=2001,ack_seq=3002 ← C 最后收到的报文段
4️⃣ C → Si ACK=1,seq=3002,ack_seq=2002算 C 的 FIN seq = 3001:
C 的初始 seq=1000,SYN 占 1 → 数据从 1001 开始,发了 2000 B → 最后一字节 seq=3000。FIN 段紧接其后 → seq=3001。
Si 发的 FIN seq = 2001:
Si 没发数据,自己的序号始终在 2001(SYN+1)原地未动。
ack_seq = 3002:
Si 第二次挥手的 ACK 已确认了 C 的 FIN(ack_seq = 3001 + 1 = 3002)。第三次挥手时同样填这个值。
C 收到的最后一个报文段(即第三次挥手):
- seq = 2001
- ack_seq = 3002
- FIN = 1
为什么挥手是 4 次而握手是 3 次:握手时服务器把"对客户端 SYN 的 ACK"和"自己的 SYN"合并成一段(SYN+ACK),所以 3 次。挥手时服务器收到 FIN 后可能还有数据要发——必须先 ACK 让客户端知道"FIN 收到了",等数据发完才发自己的 FIN,两个段不能合并。本题 Si 没数据,但 4 次挥手是 TCP 标准定义的,不会因为没数据就压缩成 3 次。
(4) C 确认 Si 接收完文件的时间
先列时间线(题面"忽略报文段传输时延"意味着段在链路上花 0 时间,但 RTT 仍是 5 ms 表示"信号往返一趟"):
| 时刻 | 事件 |
|---|---|
| t = 0 | C 发 SYN(请求建立连接) |
| t = 1 RTT = 5 ms | C 收到 SYN+ACK,立即发 ACK(建立完成),同时发 第 1 段数据 |
| t = 2 RTT = 10 ms | C 收到第 1 段的 ACK(ack_seq=1501),cwnd 变 2 MSS;同时 rwnd 给的发送窗口允许发送 2 段 — 但本轮 rwnd=1000 限制发 2 段(第 2、3 段) |
| t = 3 RTT = 15 ms | C 收到第 2、3 段的 ACK,cwnd 变 4 MSS;rwnd 仍 1000 限制 → 发 第 4 段(最后一段) |
| t = 4 RTT = 20 ms | C 收到第 4 段的 ACK → C 确定 Si 已成功接收完所有 4 段 = 整个文件 |
总耗时 = 4 RTT = 4 × 5 ms = 20 ms。
"忽略传输时延" 在时间线上的等价说法:把每个报文段在链路上看作"瞬时到达",所以 C 发完一段 / 一组到下一组到达的间隔正好是 RTT。RTT 含两个方向的传播——"发出去 0.5 RTT 到对端 + 对端 ACK 0.5 RTT 回来"。
本题的窗口动态:表面看是慢启动(cwnd 每 RTT 翻倍:1→2→4→8),但 rwnd=1000 B 把发送窗口卡在 2 MSS 后就涨不动了——所以从第 2 轮 RTT 起每轮都是 2 段(直到剩余字节数小于 2 段为止)。rwnd 才是真正的瓶颈,不是 cwnd。