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

TCP 是「可靠的字节流」,UDP 是「不可靠的报文」:一个像打电话,一个像寄明信片。 两者的头差 12 字节,差的这 12 字节全是「可靠性」那套机制要用的字段。

📝 本节和前面几节的关系

6.1.1 讲了传输层要补三件事,这一节看两个协议各补了哪些。 6.1.4 会讲「怎么选」,这一节先把两者的性格说清。

一、TCP 的五个特征

TCP 的性格可以拆成五条:

特征 含义 代价
面向连接 先握手再传 多一个往返
可靠 丢了重传、错了重传 要等、要缓冲
按序 乱序会重排 队头阻塞
字节流 没有消息边界 应用自己划边界
全双工 两个方向独立 要维护两套序号

第四行最容易被忽略,也最容易出错: 「发两次 100 字节」和「发一次 200 字节」,对 TCP 是一回事, 接收方可能一次读到 200,也可能分三次读到 50、80、70。 所以「一次 send 对应一次 recv」这个假设是错的,必须自己定「怎么算一条消息」(长度前缀、 分隔符、定长)。

二、UDP 的三个特征

UDP 只有三条,而且都很朴素:

特征 含义 好处
无连接 不发握手,直接发 没有建连延迟
不可靠 丢了不管,错了丢弃 不用等确认
保留边界 一次发就是一个报文 收的时候是整块

第三行是 UDP 相对 TCP 的一个「隐性优势」:它把「消息边界」保住了, 所以「一次发一条」就等于「一次收一条」——不用自己划边界。 DNS 就是靠这个:一个查询一个响应,边界天然清楚。 第一、二行是一体的:因为不建连接,所以也没有「连接状态」可以维护, 于是「重传」这件事连放的地方都没有。 所以「UDP 不可靠」不是「实现得差」,是「设计上就没打算管」。

三、头部差在哪

TCP 头 20 字节,UDP 头 8 字节,差的 12 字节对应四个字段: 序列号、确认号、标志位、窗口。 正好是「可靠性」要用的那四样。 UDP 的 8 字节只有四样:源端口、目的端口、长度、校验和。 它连「长度」都要自己记,因为不记就不知道报文到哪儿结束。 而 TCP 不需要「长度」字段——它是字节流,靠 IP 的总长度就能算。 这个小差别能看出两种设计哲学:UDP 把「边界」当成自己的责任,所以带长度; TCP 把「边界」交给上层,所以不带。 两者都没有错,只是选择不同。

四、不是好坏,是取舍

很多人第一次学的时候会问「为什么还用 UDP」, 这个问题本身就问错了——UDP 不是「简陋版的 TCP」,它是「另一个方向的取舍」。 举一个最实在的例子:语音通话。 丢一个 20 毫秒的包,用 TCP 重传要等一个往返(可能 100 毫秒),等回来这段声音早就过去了, 所以「重传」在这里不但没用,还会把后面的声音也拖住。 而 UDP 丢掉就丢掉,解码器糊一下,听起来只是「咔」一声。 所以判断标准只有一句:「晚到的数据还有没有价值」——有,就选 TCP;没有,就该考虑 UDP。

五、动手 Demo:TCP 和 UDP 的头差多少

算四种载荷下,头部占整包的比例。 重点看「1 字节」那一行。

for payload in (1, 100, 1000, 1400):
    print(payload, payload + 20, '%.1f%%' % (100.0 * 20 / (payload + 20)),
          payload + 8, '%.1f%%' % (100.0 * 8 / (payload + 8)))

实跑输出:

--------------------------------------------------------------------
TCP 和 UDP 的头差多少
--------------------------------------------------------------------
**TCP 固定 20 字节、UDP 固定 8 字节**,**差的 12 字节换来的是「可靠性」那套机制。**

  载荷     TCP 总长     头占 TCP     UDP 总长     头占 UDP
  ---------------------------------------------------------
  1        21           95.2%        9            88.9%
  100      120          16.7%        108          7.4%
  1000     1020         2.0%         1008         0.8%
  1400     1420         1.4%         1408         0.6%

**第一行最刺眼**:**1 字节数据装进 TCP 要 21 字节,头占 95%。**
**所以「一个字节一个包」这种用法,效率是灾难性的**——**Nagle 算法就是专门治它的。**

**最后一行两边差距最小**:**载荷越大,头的相对开销越小。**
**所以「大批量传输」时 TCP 的开销可以忽略**,
**而「实时语音」这种小包业务,UDP 的 8 字节就有意义了。**

1 字节那一行是「极端情况」:TCP 的头占 95%,等于「几乎全在传头」。 这种用法在「远程登录」时代很常见,所以 Nagle 算法才会被发明出来。 最后一行两边差距最小:1400 字节时,头的占比都不到 3%。 所以「UDP 比 TCP 省」这个优势,只在小包上成立。 大批量传输时,20 字节和 8 字节的差别可以忽略。 换个角度看这张表:「头占比高」不是「协议不好」,是「载荷太小」。 同样一个包,装 1 字节和装 1400 字节,头的绝对值都是 20。 所以优化方向应该是「让包大一点」,而不是「换一个头小的协议」。

30 秒实验

  1. 算一下:1 字节数据装进 TCP,头占多少比例?
  2. 想一下:为什么 UDP 有「长度」字段,TCP 却没有?
  3. 想一下:语音通话为什么宁可丢包也不用重传?

常见误区

  1. 以为「TCP 比 UDP 好」。 是取舍:TCP 换可靠要付延迟,UDP 换低延迟要自己兜底。
  2. 以为「UDP 也会重传」。 它连连接状态都没有,没有地方放重传的逻辑。
  3. 以为「一次 send 就是一次 recv」。 TCP 是字节流,接收方怎么切都合法。
自测三问(点开对答案) 1. TCP 的五个特征是什么? —— **面向连接、可靠、按序、字节流、全双工**,每一条都对应一种代价。 2. UDP 的「保留边界」有什么好处? —— **一次发就是一条消息**,收的时候是整块,应用不用自己划边界。 3. 选 TCP 还是 UDP,一句话标准是什么? —— **「晚到的数据还有没有价值」**:有就 TCP,没有就该考虑 UDP。

下一节把「怎么选」落到具体应用上。

相关笔记