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 字节数据装进 TCP,头占多少比例?
- 想一下:为什么 UDP 有「长度」字段,TCP 却没有?
- 想一下:语音通话为什么宁可丢包也不用重传?
常见误区
- 以为「TCP 比 UDP 好」。 是取舍:TCP 换可靠要付延迟,UDP 换低延迟要自己兜底。
- 以为「UDP 也会重传」。 它连连接状态都没有,没有地方放重传的逻辑。
- 以为「一次 send 就是一次 recv」。 TCP 是字节流,接收方怎么切都合法。
自测三问(点开对答案)
1. TCP 的五个特征是什么? —— **面向连接、可靠、按序、字节流、全双工**,每一条都对应一种代价。 2. UDP 的「保留边界」有什么好处? —— **一次发就是一条消息**,收的时候是整块,应用不用自己划边界。 3. 选 TCP 还是 UDP,一句话标准是什么? —— **「晚到的数据还有没有价值」**:有就 TCP,没有就该考虑 UDP。下一节把「怎么选」落到具体应用上。
- ☐ (本节完,下一节 6.1.4 TCP与UDP区分)