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

16 tcpip udp mtu network-protocols

我使用的是 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);
}
Run Code Online (Sandbox Code Playgroud)

接收器代码为

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

我观察到send returns -1i > 65507recv打印或接收的分组maximum of length 65507

小智 14

UDP 数据报与 MTU 大小几乎没有关系,您可以将它们设置为您喜欢的大小,最大为 64K 是上面提到的最大值。您甚至可以在整个数据包中发送其中一个,只要您使用的巨型帧的大小大于大型数据报。

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

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

所以不要创建大于 MTU 大小碎片的 UDP 数据报,除非您必须并且如果您必须指定正在通信的基础设施是关闭的(同一子网关闭),此时巨型帧可能是一个不错的选择。

  • 注意:如果数据分段,某些操作系统将不会传输 UDP。IE Linux 文档,“默认情况下,Linux UDP 进行路径 MTU(最大传输单元)发现。这意味着内核将跟踪特定目标 IP 地址的 MTU,并在 UDP 数据包写入超过它时返回 EMSGSIZE。 (2认同)

Mec*_*cki 5

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 字节。如果您的协议实现不能接受至少该大小的数据包,则它不会符合标准。但是请注意,标准并没有说 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 字节倍数的有效载荷。


gee*_*aur 1

事实证明,允许 TCP/IP 堆栈根据需要对数据包进行分段比发送单个数据包的开销要低得多。