Appearance
题目
如图,主机 H 登录到 FTP 服务器后,向服务器上传一个大小为 18000 B 的文件 F,假设 H 传输 F 建立数据连接时,选择的初始序号为 100,MSS = 1000 B,拥塞控制的初始阈值 ssthresh = 4 MSS,RTT = 10 ms,忽略 TCP 的传输时延,在 F 的传送过程中,H 以 MSS 段向服务器发送数据,且始终没有错误、丢包和乱序。
(1) FTP 的控制连接是持久的还是非持久的?FTP 的数据连接是持久的还是非持久的?H 登录服务器时,建立的 FTP 连接是数据连接还是控制连接?
(2) H 通过数据连接发送 F 时,F 的第一个字节序号是多少?在断开数据连接的过程中,FTP 发送的第二次挥手的 ACK 序号是多少?
(3) F 发送过程中,当 H 收到确认序号为 2101 的确认段时,H 的拥塞窗口调整为多少?收到确认序号为 7101 的确认段时,H 的拥塞窗口调整为多少?
(4) H 从请求建立数据连接开始,到确认 F 已被服务器全部接收为止,至少要多长时间?期间应用层数据平均发送速率是多少?
解析
(1) FTP 控制连接 vs 数据连接
FTP 用了两条独立的 TCP 连接:
| 连接类型 | 端口 | 何时建立 | 持久性 |
|---|---|---|---|
| 控制连接 | 21 | 客户端登录时建立 | 持久(整个会话期间一直保持,用来传 USER / PASS / RETR / STOR 等命令) |
| 数据连接 | 20(主动模式) | 每次 list / get / put 文件时临时新建 | 非持久(每传一个文件新建一次,传完即关) |
逐条回答:
- FTP 控制连接是 持久的
- FTP 数据连接是 非持久的
- H 登录服务器时建立的是 控制连接(用于发送用户名、密码命令)
为什么 FTP 要双连接:分离控制流和数据流的好处是命令永远能立刻送达——即使数据连接被某个大文件占满,用户依然能在控制连接上发送 ABOR(中止)这种紧急命令。代价是 NAT 穿透很麻烦(FTP 是公认对 NAT 不友好的协议)。
(2) 序号计算
F 的第一个字节序号:
H 选择的初始序号为 100。三次握手中 SYN 段消耗 1 个序号(虽然不携带数据),所以数据连接建立后第一个字节的序号 = 100 + 1 = 101。
第二次挥手的 ACK 序号:
四次挥手是这样发生的:
第 1 次:H → S FIN(seq = X) ← H 主动断开
第 2 次:S → H ACK(ack = X + 1) ← S 确认收到 FIN
第 3 次:S → H FIN(seq = Y) ← S 也想断开
第 4 次:H → S ACK(ack = Y + 1)要算 X:H 在断开前发出的最后一个字节序号 = 101 + 18000 - 1 = 18100。所以 H 的 FIN 段序号 = 18101(FIN 段不携带数据,但消耗 1 个序号;它紧接最后一字节之后)。
第二次挥手的 ACK 序号 = 18101 + 1 = 18102。
(3) 拥塞窗口变化
先理清"确认序号 N"的含义:N 表示"对方已经收到字节 [101, N-1]",期望下一个收到字节 N。所以确认序号 N 意味着已经收到 (N - 101) 字节 = (N - 101) / 1000 个 MSS 段的 ACK。
| 收到的确认序号 | 已收到字节数 | 已收到的 ACK 数 | 状态 |
|---|---|---|---|
| 2101 | 2000 B = 2 MSS | 2 个 ACK | 慢启动(cwnd 还没达到 ssthresh) |
| 7101 | 7000 B = 7 MSS | 7 个 ACK | 已经过了 ssthresh,处在拥塞避免阶段 |
拥塞窗口规则:
- 慢启动:每收到一个新段的 ACK,cwnd += 1 MSS(指数增长)
- 拥塞避免(cwnd ≥ ssthresh 后):每过一个 RTT,cwnd += 1 MSS(线性增长),等价于每收 cwnd 个 ACK 加 1 MSS、即每个 ACK 加 (1/cwnd) MSS
追踪 cwnd(从 1 MSS 起步):
| 状态 | cwnd 起始 | 收到 ACK 后 cwnd |
|---|---|---|
| 收到 ACK #1 | 1 | 2(慢启动 +1) |
| 收到 ACK #2 | 2 | 3(慢启动 +1)← 此时确认号 2101 |
| 收到 ACK #3 | 3 | 4(慢启动 +1,达到 ssthresh) |
| 收到 ACK #4~7(拥塞避免) | 4 | 4 + 4×(1/4) = 5 ← 此时确认号 7101 |
细化拥塞避免的"+1/cwnd"算法:cwnd=4 时收 4 个 ACK 才加满 1 MSS。从 ACK #4 起每个 ACK 加 0.25 MSS:4 → 4.25 → 4.5 → 4.75 → 5。教科书简写为"每个 RTT 加 1 MSS"。
最终答案:
- 收到 ack = 2101 时:cwnd = 3 MSS = 3000 B
- 收到 ack = 7101 时:cwnd = 5 MSS = 5000 B
(4) 总时间 + 平均速率
先按 RTT 切片,跟踪每轮发送的段数:
| RTT 轮次 | 本轮 cwnd | 本轮发送段数 | 累计已发段数 | 状态切换 |
|---|---|---|---|---|
| 1 | 1 MSS | 1 | 1 | 慢启动 |
| 2 | 2 MSS | 2 | 3 | 慢启动 |
| 3 | 4 MSS | 4 | 7 | 慢启动→(轮末达到 ssthresh)切换到拥塞避免 |
| 4 | 5 MSS | 5 | 12 | 拥塞避免 |
| 5 | 6 MSS | 6 | 18 ✓ | 拥塞避免,文件刚好发完 |
总时间(题目说"忽略 TCP 传输时延"——意为段在链路上单程时延 = 0,但 RTT 仍 10 ms):
- 三次握手建立连接:1 RTT(H 发 SYN → 收 SYN+ACK → 发 ACK,最后这个 ACK 可携带第一段数据,所以建立连接的"代价"取 1 RTT)
- 数据传输 + 等最后 ACK:5 RTT(最后一段在 RTT 5 中发出,其 ACK 在 RTT 5 末尾返回——题目"忽略传输时延"等价于把 ACK 看作即时返回,所以 5 RTT 含 5 次往返)
总时间 = 1 + 5 = 6 RTT = 60 ms。
平均应用层速率:
为什么连接建立只算 1 RTT 而不是 1.5 RTT:在三次握手中,客户端发出第三次握手的 ACK 后立刻可以发数据(甚至和 ACK 合并发出);服务器在收到 SYN+ACK 后已经在等数据。所以"从客户端首发 SYN 到客户端可以发送数据"耗时 1 RTT。这是标准简化算法。
慢启动→拥塞避免切换点的两种算法约定:教材里有两种写法:
- 写法 A(本题用的):cwnd 在涨到 ssthresh 当下那一刻进入拥塞避免,下一轮起每 RTT +1
- 写法 B:cwnd 翻倍超过 ssthresh 时才切换,于是 1→2→4→8(超过 4) 才切
408 真题倾向写法 A——只要 cwnd 达到 ssthresh 就切换。本题严格按写法 A 算。