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

小包的问题不是「慢」,是「头比数据还多」:1 个字节要配 40 字节头。 Nagle 和延迟确认能省包,但两者同时开会互相等,这是一个经典的坑。

📝 本节和前面几节的关系

6.4.9 讲的是「别把网络淹了」,这一节讲「别把网络浪费了」。 6.1.2 算过封装开销,这一节讲「怎么把开销摊薄」。

一、小包的浪费有多严重

1 字节数据加 40 字节头,有效占比只有 2.4%:如果每个字节都单独发一个包,网络基本在传头。 这种用法在「远程登录」时代非常常见——敲一个键发一个包,每个包 41 字节。 问题不只是浪费带宽,还有「包数」:路由器处理的是「包」,不是「字节」, 所以一万个小包比一个大包消耗更多处理能力。 这就是「小包攻击」能拖垮设备的原理——它不需要大流量,只需要大量小包。

二、三个省包的技巧

技巧 做什么 代价
Nagle 算法 小包攒着一起发 最后一个可能多等
延迟确认 确认攒着一起回 发送方等确认更久
捎带确认 确认搭在数据包上 没数据可搭就白等

第一行的规则很具体:「已经发出去但还没被确认的小包,先攒着」。 等确认回来,或者攒够一个 MSS,再一起发。 它把「很多小包」变成「一个大包」,代价是「最后一个包要多等一个往返」。 第三行是最理想的:「我正好有数据要发,就顺手把确认带上」,这样确认不占额外的包。 但「正好有数据」是可遇不可求的,所以它只是「省一点」,不能当主力。

三、那个经典的坑

Nagle 和延迟确认同时开,会互相等: 发送方等确认才发下一个小包,接收方等「有数据或超时」才回确认。 结果谁都不动,卡到超时。 所以「一问一答」的协议里,这两个至少要关一个: 要么关 Nagle(TCP_NODELAY),要么关延迟确认。 实际做法通常是前者——因为 Nagle 在「请求响应」模式里本来就没什么收益。 这也是「游戏和交易系统都要关 Nagle」的原因:它们宁可多传几个头,也不愿多等那几十毫秒。 所以在这些场景里,「省包」和「低延迟」是直接冲突的——没有两全的方案,只能按需求取舍。

四、动手 Demo:一个字节也要 40 个字节的头

算四种载荷下的有效占比。 第一行只有 2.4%,这就是 Nagle 想解决的问题。

for payload in (1, 2, 10, 100):
    total = payload + 40
    print('%4d 字节 -> 线路 %3d 字节,有效 %5.1f%%'
          % (payload, total, 100.0 * payload / total))

实跑输出:

--------------------------------------------------------------------
一个字节也要 40 个字节的头
--------------------------------------------------------------------
**「小包」的效率问题是 TCP 的老毛病**:**头和载荷的比例太悬殊。**

  应用数据     线路上合计     有效占比
  -------------------------------------
  1            41             2.4%
  2            42             4.8%
  10           50             20.0%
  100          140            71.4%

**第一行只有 2.4% 是「有用的」**:**如果每个字节都单独发一个包,网络基本是在传头。**
**这种用法在「远程登录」时代很常见**——**敲一个键发一个包,每个包 41 字节。**

**Nagle 算法的做法很直接**:**「发出去但还没被确认的小包,先攒着」**,
**等确认回来或者攒够一个 MSS 再一起发。**
**它把「很多小包」变成「一个大包」**,**代价是「最后一个包可能要多等一会儿」。**
**所以游戏和交易系统会关掉它**(TCP_NODELAY),**宁可多传头,也要少等那几十毫秒。**

**这里有个经典的坑**:
**Nagle 和「延迟确认」同时开,会互相等**——**发送方等确认才发小包,接收方等数据才回确认**,
**结果谁都不动,卡到超时。**
**所以在「一问一答」的协议里,这两个至少要关一个。**

「有效占比」这个指标比「带宽」更贴近真实体验: 因为「有效数据少」意味着「同样的带宽传不了多少东西」。 所以「小包业务占满带宽但没用」是真实现象。 注意 100 字节那一行也只有 71%:所以「包大一点」的收益是连续的, 不是「小包很差、大包就好」。 这也是「MTU 越大越好」的直觉来源,只是实际受路径限制。 最后一个提醒:「关掉 Nagle」不是「永远更好的选择」。批量传输时开着它,能显著减少包数; 只有「一问一答」和「实时交互」才该关。 所以这个开关要按业务类型决定,不能一刀切。

30 秒实验

  1. 算一下:10 字节数据走 TCP,有效占比是多少?
  2. 想一下:Nagle 和延迟确认为什么会互相等?
  3. 想一下:什么业务应该关掉 Nagle?

常见误区

  1. 以为「Nagle 总是好的」。 它增加延迟,实时业务要关掉。
  2. 以为「关掉 Nagle 就没有延迟问题」。 延迟确认还会在,两个要一起看。
  3. 以为「小包只是浪费带宽」。 还浪费路由器的处理能力,包数比字节数更影响设备。
自测三问(点开对答案) 1. Nagle 算法的规则是什么? —— **已发出但未确认的小包先攒着**,等确认回来或攒够一个 MSS 再发。 2. Nagle 和延迟确认为什么会死等? —— **一个等确认才发,一个等数据才回确认**,双方互相等,卡到超时。 3. 什么时候该关掉 Nagle? —— **实时交互的业务**:游戏、交易、远程操作,宁可多传头也不愿多等。

下一节看哪些应用跑在 TCP 上。

相关笔记