📚 第六章 TCP与UDP
📄 一句话概括

序列号记的是「字节偏移」,不是「第几个包」:这是最容易记错的一点。 确认应答是「累积」的:ack=301 意味着 1 到 300 都到了。

📝 本节和前面几节的关系

6.4.1 把四件工具列了一遍,这一节把前两件讲透。 6.4.3 讲第三件(重传定时器),这一节先讲「怎么发现要重传」。

一、序列号记的是字节

TCP 的序列号不是「包号」,是「字节偏移」:第一个字节编号 1,第二个编号 2,一直到无穷。 所以「发 100 字节」的段,它的 seq 是起点,占用的范围是「起点到起点加 99」。 这个设计的好处是「可以任意切分」: 同样的 300 字节,切成三个 100 也可以,切成 150 加 150 也可以。 接收方只看「字节到齐没」,不看「包长什么样」。 这就是「字节流」的具体含义。 坏处是「边界没了」:应用层的「一条消息」和 TCP 的「一个段」没有对应关系, 所以必须自己定分隔规则(长度前缀、分隔符、定长)。 这一点在 6.1.3 提过,这里补上「为什么」。

二、累积确认的好处与代价

ack 报的是「我连续收到了多少」:所以一个 ack 可以确认很多段, 这省了「一段一个确认」的报文量。 好处在大窗口时最明显:窗口里 100 个段,如果逐个确认要 100 个 ack, 累积确认只要「收到最后一个时报一次」。 代价是「丢一段就全卡住」:后面到的段虽然收下了,但 ack 不能跳过缺口, 所以发送方看到的是「反复的同一个 ack」。 这个「反复」反而成了信号——看到三次重复,就知道「缺口之后的东西都到了,只缺那一个」。

三、快速重传

发现丢包有两条路:等超时,或者看到三次重复确认。 前者慢(要等一个 RTO,通常几百毫秒),后者快(一个往返就够)。 所以「三次重复 ack」是个很有价值的信号:它说明「后面的包都到了,只有那一个没到」, 而不是「网络彻底断了」。 这两种情况的正确反应完全不同:前者该「立刻重传那一个」,后者该「把窗口降下来慢慢来」。 这也是「丢一个包」和「网络拥塞」的区别:一个包丢了,通常只是偶发; 连续丢、大面积丢,才是拥塞的信号。 所以「快速重传」之后只把窗口减半(快速恢复), 而不是回到 1(慢启动),原因就在这里。

四、动手 Demo:序列号和确认应答怎么走

模拟发三个 100 字节的段,看 seq 和 ack 怎么演进。 注意 ack 总是「下一段的起点」。

seg, seq = 100, 1
for i in range(1, 4):
    print('段%d seq=%d 覆盖 %d~%d ack=%d'
          % (i, seq, seq, seq + seg - 1, seq + seg))
    seq += seg

实跑输出:

--------------------------------------------------------------------
序列号和确认应答怎么走
--------------------------------------------------------------------
**序列号记的是「字节偏移」,不是「第几个包」**。**这一点最容易记错。**

  段号     seq     这段覆盖的字节     回过来的 ack
  -------------------------------------------------
  1        1       100                101
  2        101     200                201
  3        201     300                301

**第四列等于「下一段该从哪开始」**:**所以 ack=101 的意思是「100 及以前的我都收到了」。**
**注意它是「累积确认」**:**ack=301 就同时意味着 1~300 全都到了**,**不需要一段一段确认。**
**这个设计的代价是「丢一段就全卡住」**——**后面到的包只能先存着,等缺口补上。**

**如果第二段丢了会怎样**:
**第 1 段到,回 ack=101;第 3 段到,还是回 ack=101**——
**发送方收到三个重复的 ack=101,就知道「101 之后的东西没到」**,**立刻重传第二段,不用等超时。**
**这就是「快速重传」,条件是「三个重复确认」。**

第四列等于「下一段该从哪开始」:所以 ack=101 的意思是「100 及以前的我都收到了」。 注意它是「累积确认」:ack=301 就同时意味着 1 到 300 全都到了。 如果第二段丢了会怎样:第 1 段到,回 ack=101;第 3 段到,还是回 ack=101。 发送方收到三个重复的 ack=101,就知道「101 之后的东西没到」,立刻重传第二段,不用等超时。 这里有个细节值得注意:接收方其实已经把第 3 段存进缓冲区了, 所以重传的第二段一到,就可以直接跳到 ack=401。 这个「一次补齐」的行为叫「累积确认的补偿效应」—— 它让「丢一个包」的代价只是「多一个往返」,而不是「重传后面全部」。

30 秒实验

  1. 算一下:seq=1001、每段 500 字节,第三段的 seq 是多少?
  2. 想一下:为什么 ack 报的是「下一段的起点」而不是「最后一字节」?
  3. 想一下:三次重复确认为什么比等超时快?

常见误区

  1. 以为「序列号是包号」。 是字节偏移,所以同样字节数可以切成不同的段。
  2. 以为「每个段都要单独确认」。 是累积确认,一个 ack 能确认前面全部。
  3. 以为「丢包只能等超时」。 三次重复 ack 就能触发快速重传,快一个数量级。
自测三问(点开对答案) 1. TCP 的序列号记的是什么? —— **字节的偏移量**,从 1 开始,不是包的编号。 2. ack=301 是什么意思? —— **1 到 300 全部收到,下一段该从 301 开始。** 3. 快速重传的触发条件? —— **收到三个重复的确认**,说明缺口之后的数据都到了,只缺那一个。

下一节讲「等多久没确认才算丢」。

相关笔记