Skip to content

3.7. 分段

“分段”是指 TCP 接收来自发送应用的字节流,并将其打包为 TCP 报文段的过程。单个 TCP 报文段通常并不对应应用的一次 send(或套接字写入)调用。应用可以按上层协议的消息粒度写入数据,但 TCP 不保证收发两端 TCP 报文段的边界与用户应用读写数据的缓冲区边界之间存在任何对应关系。对于某些特定协议——例如在远程直接内存访问(RDMA)中使用直接数据放置(DDP)和标记 PDU 对齐成帧(MPA)(参见 RFC 5044)——若能控制 TCP 报文段与应用数据单元之间的对应关系,便可借此优化性能;MPA 本身也包含检测并验证 TCP 报文段与应用消息数据结构之间关系的专门机制。但这仅适用于 RDMA 一类的应用。总体而言,TCP 实现在确定报文段大小时需要权衡多个目标。

促使发送较大报文段的考虑因素包括:

  • 减少网络中在途分组的数量。
  • 减少中断次数和层间交互次数,从而提高处理效率,并可能带来性能提升。
  • 限制 TCP 首部开销。

发送较大报文段的性能收益可能随报文段增大而递减,甚至可能出现适得其反的转折点。例如,在某些实现架构中,仅仅由于数据复制操作中的对齐问题,1025 字节的报文段反而可能比 1024 字节的性能更差。

促使发送较小报文段的考虑因素包括:

  • 避免所发送的 TCP 报文段使 IP 数据报超过 IP 路径上的最小 MTU,否则会导致分组丢失或分片。更麻烦的是,某些防火墙或中间盒会丢弃分片分组,或丢弃与分片相关的 ICMP 消息。
  • 避免应用数据流出现延迟——尤其是在 TCP 需要等待应用生成更多数据,或应用需要等待对端的事件/输入之后才能生成更多数据的情况下。
  • 允许 TCP 报文段与下层数据单元“命运共享”,例如当 IP 层之下存在小于 IP MTU 的链路单元或帧时。

为了在这些相互矛盾的目标之间取得平衡,TCP 提供了多种机制,包括最大报文段大小选项、路径 MTU 发现、Nagle 算法以及 IPv6 超大报文支持,下面分别介绍。

3.7.1. 最大报文段大小选项

TCP 端点必须同时实现 MSS 选项的发送和接收(MUST-14)。

当接收 MSS 与 IPv4 默认值 536 或 IPv6 默认值 1220 不同时,TCP 实现应在每个 SYN 报文段中发送 MSS 选项(SHLD-5);也可以无条件地始终发送(MAY-3)。

如果建立连接时未收到 MSS 选项,TCP 实现必须假定发送 MSS 为 536(576 - 40,IPv4)或 1220(1280 - 60,IPv6)(MUST-15)。

TCP 端点实际发送的最大报文段大小称为“有效发送 MSS”,它必须取以下两者中的较小值:发送 MSS(反映远端主机可用的重组缓冲区大小 EMTU_R),以及 IP 层允许的最大传输大小 EMTU_SMUST-16):

text
Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize

其中:

  • SendMSS 是从远端主机收到的 MSS 值;若未收到 MSS 选项,则取 IPv4 默认值 536 或 IPv6 默认值 1220。
  • MMS_S 是 TCP 可以发送的传输层消息最大尺寸。
  • TCPhdrsize 是固定 TCP 首部和所有选项的大小。没有选项时(这种情况很少见)为 20;发送 TCP 选项时可能更大。某些选项未必出现在每个报文段中,但发送方在发送每个报文段时,都应在 Eff.snd.MSS 的限制之内相应调整数据长度。
  • IPoptionsize 是与 TCP 连接相关的 IPv4 选项或 IPv6 扩展首部的大小。某些选项或扩展首部未必出现在每个分组中,但发送方在发送每个报文段时,都应在 Eff.snd.MSS 的限制之内相应调整数据长度。

MSS 选项中通告的值应等于有效 MTU 减去固定长度的 IP 首部和 TCP 首部。若计算 MSS 选项值时忽略了 IP 和 TCP 选项,而实际分组中又包含这些选项,发送方必须相应缩减 TCP 数据的长度。RFC 6691 对此有更详细的讨论。

MSS 选项中通告的值必须小于或等于 MMS_R - 20,其中 MMS_R 是能够接收(并在 IP 层重组)的传输层消息的最大尺寸(MUST-67)。TCP 从 IP 层获取 MMS_RMMS_S;参见 RFC 1122 第 3.4 节中的通用调用 GET_MAXSIZES。这两个值分别定义为与 IP MTU 等价的量 EMTU_REMTU_S

当 IP 首部或 TCP 首部并非固定大小时,发送方必须按 IP 和 TCP 选项所占的八位组数,在每个分组中相应减少 TCP 数据量。该问题历来容易引起混淆,RFC 6691 第 3.1 节对此有专门说明。

3.7.2. 路径 MTU 发现

TCP 实现可以获知直连链路上的 MTU,但通常无从得知整条网络路径的 MTU。对于 IPv4,RFC 1122 建议:对非直连目的地,IP 层默认的有效 MTU 不超过 576;IPv6 的对应值为 1280。采用固定值会制约 TCP 连接的性能与效率,因此强烈建议实现路径 MTU 发现(PMTUD)和分组化层路径 MTU 发现(PLPMTUD),以便 TCP 作出更优的分段决策。PMTUD 与 PLPMTUD 均可帮助 TCP 选择合适的报文段大小,从而避免路径中的分片(IPv4)以及源端分片(IPv4 和 IPv6)。

IPv4 的 PMTUD(RFC 1191)和 IPv6 的 PMTUD(RFC 8201)需要 TCP、IP 与 ICMP 协同实现。其前提是避免源端分片,并通过设置 IPv4 的 DF(不要分片)标志来抑制路径中的分片;当报文段过大而无法通过某条链路时,还要依靠路径上的路由器回送 ICMP 差错报文。RFC 2923 针对实践中遇到的问题,给出了若干 TCP 实现层面的调整。PLPMTUD(RFC 4821)则是对 PMTUD 的标准化轨道改进:它放宽了路径必须支持 ICMP 的前提,在 ICMP 传递不稳定时也能改善性能,同时仍尽量规避源端分片。上述四个 RFC 中的机制均建议纳入 TCP 实现。

TCP MSS 选项规定的是可接收分组大小的上界。因此,若 MSS 选项值设置得过小,就可能妨碍 PMTUD 或 PLPMTUD 探测更大的路径 MTU。RFC 1191 讨论过这一问题:许多早期的 TCP 实现对非本地目的地一律将 TCP MSS 设为 536(对应 IPv4 默认 MTU 576),而未按建议根据已连接接口的 MTU 推导 MSS。

3.7.3. 有可变 MTU 值的接口

有效 MTU 有时会发生变化——例如采用可变压缩时,稳健首部压缩(ROHC)即为一例(参见 RFC 5795)。为了高效利用压缩后的有效载荷,TCP 实现或许希望通告尽可能大的 MSS。然而,某些压缩方案偶尔需要发送完整首部,以便端点的压缩器/解压缩器重新同步状态,这就要求有效载荷更小。若按最大 MTU 计算并通告 MSS,TCP 重传便可能干扰压缩器的重新同步。

因此,当接口的有效 MTU 在不同分组之间变化时,TCP 实现应使用接口的最小有效 MTU 计算要在 MSS 选项中通告的值(SHLD-6)。

3.7.4. Nagle 算法

Nagle 算法由 RFC 896 提出,RFC 1122 推荐采用,用以缓解早期出现的小分组过多的问题。如今它已在绝大多数 TCP 代码库中实现,且时有细微变体(参见附录 A.3)。

只要存在未确认的数据(即 SND.NXT > SND.UNA),发送端 TCP 就必须缓存所有用户数据(无论 PSH 位是否置位),直至在途数据得到确认,或者直至能够发出一个满尺寸的报文段(Eff.snd.MSS 字节)。

TCP 实现应实现 Nagle 算法,以便将短报文段合并发送(SHLD-7);但应用必须能够针对单个连接禁用 Nagle 算法(MUST-17)。无论在何种情况下,数据发送都还要受到慢启动算法的约束(参见 RFC 5681)。

由于 Nagle 算法与延迟确认之间可能产生不利的交互,一些实现采用了 Nagle 算法的细微变体,附录 A.3中描述的变体即为一例。

3.7.5. IPv6 超大报文

要支持 TCP over IPv6 超大报文,实现需要能够发送超过 64 KB 限制的 TCP 报文段,因为 MSS 选项无法表示更大的数值。RFC 2675 规定:MSS 值 65,535 字节应视为无穷大,实际 MSS 则通过路径 MTU 发现来确定。

不连接 MTU 大于 65,575 之链路的 IPv6 节点,无需实现或理解 Jumbo Payload 选项;现行 IPv6 节点要求中也未要求支持超大报文。

Licensed under CC BY-NC-SA 4.0.