UDP 中的 MTU 为何为 65535,但以太网不允许帧大小超过 1500 字节

UDP 中的 MTU 为何为 65535,但以太网不允许帧大小超过 1500 字节

我使用的是 100 Mbps 的快速以太网,其帧大小小于 1500 字节(根据我的教科书,有效载荷为 1472 字节)。这样,我能够发送和接收消息大小为 65507 字节的 UDP 数据包,这意味着数据包大小为 65507 + 20(IP 标头)+ 8(UDP 标头)= 65535。

如果帧的有效负载大小本身最大为 1472 字节(根据我的教科书),那么 IP 的数据包大小怎么会大于这里的 65535?

我使用发件人代码作为

char buffer[100000];
for (int i = 1; i < 100000; i++)
{
    int len = send (socket_id, buffer, i);
    printf("%d\n", len);
}

接收方代码为

while (len = recv (socket_id, buffer, 100000))
{
     printf("%d\n". len);
}

我观察到,send returns -1在和i > 65507recv打印或接收一包maximum of length 65507

答案1

UDP 数据报与 MTU 大小关系不大,您可以根据需要将其设置为任意大,但最高可达上述的 64K。您甚至可以在整个数据包中发送其中一个数据报,只要您使用的巨型帧比大型数据报更大即可。

但是,所有帧将经过的设备都必须支持巨型帧,而这正是问题所在。从实际目的来看,以太网帧是最常见的传输大小,其 MTU 大约为 1500 字节,我会说以后都是 1500 字节,但并非总是如此。当您创建大于底层 MTU(如图所示,通常是以太网)的 UDP 数据报时,它将被悄悄分解为多个 1500 字节的帧。如果您对此流量进行 tcpdump,您将看到多个在 MTU 边界处断开的数据包,这些数据包将设置更多片段标志以及片段编号。第一个数据包的片段编号为 0,并设置了更多片段,而最后一个数据包的片段编号非零,并且未设置更多片段。

那么为什么要关心呢?实施细节实际上很重要。碎片化可能会损害网络性能,这不再是一个大问题,但需要注意。如果使用的数据报很大,那么任何碎片丢失,整个数据报都需要重新发送。同样,在高容量下(今天这些是完全可以实现的容量),在重组时帧的关联错误是可能的。碎片化的 UDP 数据包在企业防火墙配置中穿越时也会出现问题,其中负载平衡器会将数据包分散开来,如果一个碎片在一个防火墙上,另一个在另一个防火墙上,那么流量将被视为不完整而被丢弃。

因此,不要创建大于 MTU 大小碎片的 UDP 数据报,除非您必须这样做,并且如果您必须指定其间通信的基础设施很接近(同一子网接近),此时巨型帧可能是一个不错的选择。

答案2

UDP 不知道 MTU。UDP 数据包的大小可以是 8 到 65535 字节之间的任意大小。UDP 下面的协议层可以发送特定大小的数据包,或者如果数据包太大,则会拒绝发送该数据包并显示错误。

UDP 下面的层通常是 IP,可以是 IPv4 或 IPv6。IP 数据包的大小可以是 20(IPv4)/40(IPv6)到 65535 字节,与 UDP 的最大大小相同。但是,IP 支持一种称为碎片化如果 IP 数据包的大小大于下一层可以传输的大小,则 IP 可以将单个数据包拆分为多个数据包(称为片段)。事实上,每个片段都是一个独立的 IP 数据包(具有自己的 IP 标头),并且也会单独发送到目的地;目的地的任务是收集所有片段,并从中重建完整的数据包,然后再将接收到的数据传递到下一个更高层(例如 UDP)。

以太网协议只能传输有效载荷在 46 到 1500 字节之间的帧(有例外,但这超出了此回复的范围)。如果有效载荷数据小于 46 字节,则将其填充为正好 46 字节。如果有效载荷数据超过 1500 字节,接口将拒绝接受它。如果发生这种情况,现在由 IP 层决定是将数据包分段,以使任何分段都不大于 1500 字节,还是向下一层报告错误(如果已为此特定连接禁用或禁止分段)。

碎片化通常是应该避免的,因为

  • 浪费了发送方的资源。
  • 这浪费了接收端的资源。
  • 它会增加相同数量有效载荷数据的协议开销。
  • 如果单个片段丢失,整个数据包就会丢失。
  • 如果单个片段损坏,整个数据包都会损坏。
  • 如果需要重新发送,则必须重新发送所有片段。

这就是为什么 TCP 会智能地调整其帧大小,这样数据包就永远不需要 IP 对其进行分段。这可以通过禁止 IP 对数据包进行分段来实现,如果 IP 报告数据包太大而无法发送,则 TCP 会减小帧大小并重试,直到不再报告错误为止。

但是对于 UDP 来说,这将是应用程序本身的任务,因为 UDP 是一种“哑”协议,它没有自己的管理逻辑,这使得它非常灵活、快速和简单。

唯一可以依赖的始终可传输的 UDP 大小是 576 减去 8 个字节的 UDP 报头和减去 20(v4)/40(v6) 个字节的 IP 报头,因为 IP 标准要求每个 IP 主机都能够接收总大小为 576 字节的 IP 数据包。如果您的协议实现不能接受至少该大小的数据包,则它不符合标准。但请注意,标准并没有说 576 不带碎片,因此即使是 576 字节的 IP 数据包也可能会在两个主机之间碎片化。

唯一可以保证不分段传输的数据包大小是 IPv4 的 24 字节和 IPv6 的 56 字节,因为分段的最小 IP 标头是 20/48 字节 (v4/v6),并且分段必须至少包含 4/8 字节 (v4/v6) 有效载荷数据。因此,IP 层以下的传输系统如果不能传输至少这些大小的数据包,就不能用于传输 IP 流量。

在有人评论 IPv6 报头只有 40 个字节之前:这是正确的,但与 IPv4 报头不同,标准 IPv6 报头没有用于分段的报头字段。如果必须对数据包进行分段,则必须在 IPv6 基本报头下方添加分段扩展报头,并且此扩展报头长度为 8 个字节。此外,与 IPv4 不同的是,IPv6 中的分段偏移以 8 个字节为单位计算,而不是 4 个字节,因此在 IPv6 的情况下,分段只能承载 8 个字节倍数的有效载荷。

答案3

IP 层会在发送端对数据包进行分段,然后在接收端重新组装,然后再将其传递给 UDP。从 UDP 层,你无法真正判断数据包是否已被分段。如果你使用数据包捕获工具,例如Wireshark,您应该能够看到您的计算机正在接收限制在 MTU 的 IP 数据包。

答案4

回答您的问题,“如果帧的有效负载大小本身最大为 1472 字节(根据我的教科书),那么 IP 的数据包大小怎么会大于这里的 65535?”

这是由于名为 UFO(UDP 碎片卸载)的卸载功能所致。请参阅关联。

您可以分别通过 ethtool -k ethX 和 ethtool -K ethX 来验证和切换卸载功能。

相关内容