Appearance
题目
设主机甲通过 TCP 向主机乙发送数据,部分过程如下图所示。甲在 t0 时刻发送一个序号 seq=501、封装 200 B 数据的段,在 t1 时刻收到乙发送的序号 seq=601、确认序号 ack_seq=501、接收窗口 rcvwnd=500 B 的段,则甲在未收到新的确认段之前,可以继续向乙发送的数据序号范围是( )。
错因
A
把整个发送窗口当成"可继续发"——窗口左端 501、右端 501 + 500 − 1 = 1000,但 [501, 700] 这 200 B 已经在 t0 发出去了(且尚未被确认)。"可继续发"指的是窗口内还没发过的部分,应从 701 开始。错的根源:混淆"发送窗口"和"还能继续发的窗口"。
B
把乙发送段中的 seq=601 当成自己(甲)下一个待发字节序号——但 seq=601 是乙发出本段的起始序号,不是甲的发送游标。甲的发送游标由 ack_seq + rcvwnd 决定,与乙自己的 seq 无关。错的根源:混淆双向 TCP 中的两个独立序号空间——甲发乙收一套(甲的 seq)、乙发甲收一套(乙的 seq),互不影响对方的发送窗口。
D
可能误以为甲的发送窗口起点是"已发数据的下一个" = 701,再加 100 字节偏移凑出 801;或者把 ack_seq 算成 601 后再加偏移。错的根源:发送窗口左端永远 = ack_seq(最早未确认字节序号),不是任何"已发的下一个"或更后位置。
总解析
第一步:解析 t1 时收到的 ACK 段
乙发的段携带:
- seq = 601 → 这是乙自己的发送序号(乙在双向 TCP 中向甲发的字节起始序号),与甲的发送窗口无关
- ack_seq = 501 → 乙告诉甲:"我期待你下一个发的字节序号是 501"。等价于"我已确认到字节 500"
- rcvwnd = 500 B → 乙告诉甲:"你最多再发 500 字节我就接不下了"
第二步:算甲的发送窗口
发送窗口 = 从 ack_seq 起、长度 rcvwnd:
发送窗口含 500 B(501 ~ 1000)。
第三步:减掉已发但未确认的部分
甲在 t0 已经发了 seq=501 起的 200 B 数据,覆盖序号 [501, 700]。这 200 B 落在窗口内但已发出——还在等 ACK(目前 ack_seq 仍是 501,说明乙还没确认这段,可能传播时延让 t1 ACK 早于 t0 的 200 B 数据到达)。
仍未发出的窗口部分 = 窗口范围 减去 已发出部分:
共 300 B,对应序号 701 ~ 1000。
t1 时刻甲的字节状态全景(一表收全):
| 状态 | 序号范围 | 字节数 | 说明 |
|---|---|---|---|
| 已发已确认 | ≤ 500 | — | 在 t0 之前累计被对方 ack 过;ack_seq=501 说明乙累计确认到 500 |
| 已发未确认 | 501 ~ 700 | 200 B | 甲在 t0 发出的那段(含在发送窗口内) |
| 窗口内可发 | 701 ~ 1000 | 300 B | 答案 |
| 窗口外(待 ack 推动) | ≥ 1001 | — | 超出当前发送窗口右沿,必须等 ack_seq 推进或 rcvwnd 增大才能发 |
第四步:核对
| 选项 | 序号范围 | 与正确答案比较 |
|---|---|---|
| A | 501 ~ 1000 | 包含已发 [501, 700],错 |
| B | 601 ~ 1100 | 起点搞混(用了乙的 seq 601),右端超出窗口 |
| C | 701 ~ 1000 | 正确 |
| D | 801 ~ 1100 | 起点偏移 100,右端超出窗口 |
最终答案是 C(701 ~ 1000)。
编者注(生僻术语):TCP 是全双工协议——甲发乙收用甲的序号空间,乙发甲收用乙的序号空间,两套序号互不影响对方的发送窗口。本题中甲的发送窗口完全由 (ack_seq, rcvwnd) 决定,乙自己发的 seq=601 只是描述乙在反向通道里的起点,与甲下一步该发什么字节序号没关系。这是 TCP 序号题的常见陷阱,记牢"我发我的、你发你的"。