Appearance
题目
主机甲通过 TCP 向主机乙发送数据的部分过程如下图,seq 为序号,ack_seq 为确认序号,rcwnd 为接收窗口。甲在 t0 时刻的拥塞窗口和发送窗口均为 2000 B,拥塞控制阈值为 8000 B,MSS = 1000 B。甲始终以 MSS 发送 TCP 段。若甲在 t1 时刻收到如图所示的确认段,则甲在未收到新的确认段之前,还可以继续向乙发送的 TCP 段数是( )。
t0 是甲发出第一个 TCP 段(seq = 2001)的时刻,此时 cwnd = 发送窗口 = 2000 B;t1 是甲收到乙的确认段那一刻。
错因
B
把"ack_seq = 3001 已确认 seq = 3001 段"了——以为 ack 把所有已发段都确认了,已发未确认 = 0,于是 3000B / 1000B = 3 段。但 TCP ack 语义是"期待":ack_seq = 3001 意思"已收到 < 3001 的所有字节、期待从 3001 开始",所以 seq = 3001 段(占 3001 ~ 4000)还未被确认,仍然计为 1000B 已发未确认。
C
误用 rcwnd 当作发送窗口——以为发送窗口 = min(cwnd, rwnd) 中漏了 cwnd 限制:直接用 rcwnd = 4000B 作为发送窗口、且把 ack_seq = 3001 当成"全部已确认"(同 B 错因),4000 - 0 = 4000B = 4 段。两处错合并:①漏了 cwnd 限制(cwnd 在慢启动期 +1 MSS 后是 3000B、比 rwnd 小)②误读 ack 语义。
D
直接用 rcwnd 当发送窗口、并误算 cwnd 在慢启动下涨更多——可能算 cwnd(t1) = cwnd(t0) × 2 = 4000B(以为慢启动是"翻倍")、然后取 min(cwnd, rwnd) = 4000B、再加上漏算已发未确认的 1000B → 5000B / 1000B = 5 段。两个误区叠加:①cwnd 慢启动是"每 ack +1 MSS",不是翻倍 ②没扣已发未确认的 1000B。
总解析
第一步:t1 时刻 cwnd 涨多少?
慢启动判定:cwnd(t0) = 2000 B < ssthresh = 8000 B → t0 仍在慢启动阶段。慢启动规则:每收到一个 ack,cwnd += MSS。t1 时甲收到 1 个 ack(ack_seq = 3001)→
慢启动不是"翻倍"——是"每 ack +1 MSS"。当所有发出去的段都被同时 ack 时(一个 RTT 内),效果上等于"翻倍",所以容易记成翻倍但本质是逐 ack 增长。本题只收到 1 个 ack,所以只 +1 MSS。
第二步:t1 时刻发送窗口 = min(cwnd, rwnd)
- cwnd(t1) = 3000 B
- rwnd(t1) = rcwnd = 4000 B(题面给定)
- 发送窗口 = min(3000, 4000) = 3000 B
第三步:t1 时刻已发未确认是多少?
甲在 t0 ~ t1 之间发了 2 个段:
- seq = 2001 段(占字节 2001 ~ 3000):ack_seq = 3001 表示已确认 ✓
- seq = 3001 段(占字节 3001 ~ 4000):ack_seq = 3001 表示期待 3001 起、还没确认它 ✗
所以 t1 时已发未确认 = 1000 B(仅 seq = 3001 那段)。
第四步:算还能发多少
关键陷阱:很多同学想当然认为"ack_seq = 3001 → seq = 3001 段已确认",于是把已发未确认算成 0、得 3 段或更多。TCP 的 ack 是 cumulative + 期待型——ack_seq = N 永远表示"期待 N",意味着 < N 已确认、N 及以后还没确认。
t1 时刻甲的发送窗口全景(窗口左沿 = ack_seq = 3001,长度 = min(cwnd, rwnd) = 3000,右沿 = 6000):
| 段 | 序号范围 | 状态 |
|---|---|---|
| seq = 2001(t0 第 1 段) | 2001 ~ 3000 | ✅ 已确认(落在窗口左沿之外) |
| seq = 3001(t0 第 2 段) | 3001 ~ 4000 | ⚠️ 已发未确认(占用 1 个 MSS 的窗口配额) |
| 可继续发 ① | 4001 ~ 5000 | ⏳ 窗口内未发,下一步可发 |
| 可继续发 ② | 5001 ~ 6000 | ⏳ 窗口内未发,下一步可发 |
| 暂不能发 | ≥ 6001 | ❌ 超出窗口右沿(cwnd 限制) |
→ 还能发 2 段。
最终答案是 A(2)。
编者注(知识延伸):题面里
seq = 4001是乙作为发送方时的序号(乙也在向甲发数据,所以也有自己的 seq)。这跟ack_seq = 3001是分开两个字段——一个是"我(乙)发的字节起始号",一个是"我(乙)确认收到对方的字节到哪"。TCP 是全双工的,每一方都同时维护这两组序号。