问题
一开始了解的是 DNS 服务使用的是 UDP 协议,后面看到 DNS 服务主要使用 UDP 协议,在少数情况(传输的数据超过 512 个字节)下也会使用 TCP 协议,因为 UDP 数据包不能超过 512 个字节。
那问题来了,为什么 UDP 数据包不能超过 512 个字节呢?
探索
首先,对于传输层,即 UDP 数据包本身来说,Length 字段为 16 位,理论限制为 65535 字节(2^16 - 1),那么能传输的数据为 65535 - IPHeader(20) - UDPHeader(8) = 65507 字节。
其次,对于网络层,以太网规定 MTU 上限为 1500 字节(综合权衡的结果),如果按照 MTU = 1500 计算,那么 UDP 能传输的数据包上限为 MTU(1500) - IPHeader(20) - UDPHeader(8) = 1472 字节。
- 如果 UDP 数据包小于等于1472 个字节,那么正常发送不用分片
- 如果 UDP 数据包超过 1472 个字节,那么移交网络层进行分片并在接收方进行重组
这里补充一下 UDP 超过 1472 字节时的切片处理:
网络层并不会在每个分片里复制一次 UDP 头,它是把完整的 UDP 包切开,加上 IP 头发送出去,除了第一个分片有 UDP 头,后面的分片都不包含 UDP 头。
目的主机的网络层接收到多个 UDP 分片包后,网络层必须重组才能交给上层。因为多个分片包只有第一个是有 UDP 头的,它可以根据 UDP 头里的端口号通知相应的应用取走,但是后面的分片包由于没有 UDP 头,传输层无法把分片包交给正确的应用程序。所以 UDP 分片包必须在网络层重组成一个完整的 UDP 包,再交给传输层处理,耗费资源。
但如果某些分片包没有被目的主机的网络层接收到,造成 UDP 包重组失败,接收方会丢弃整个数据包,这是 UDP 不可靠传输的一个表现。而 TCP 发 生组包错误时,该包会被重传,保证可靠传输。
因此一般建议将 UDP 数据包限制在 1472 字节以下。但是呢,这个限制是在普通局域网(Ethernet v2)环境下的,在非局域网(X.25)环境下则有所不同。
因为 Internet 上的路由器可能会将 MTU 设为不同的值。如果我们假定 MTU 为 1500 字节来发送数据的,而途经的某个网络的 MTU 值小于 1500 字节,那么系统将会使用一系列的机制来调整 MTU 值,使数据报能够顺利到达目的地,这样就会做许多不必要的操作。
鉴于 Internet 上的标准 MTU 值为 576 字节(IPv4 标准规定,每个主机必须能够重新组装 576 字节或更少的数据包),所以建议在进行 Internet 的 UDP 编程时,最好将 UDP 的数据长度控件在 548 字节(MTU(576) - IPHeader(20) - UDPHeader(8))以内。
但这也还是 548 字节,并不是 512 字节。于是又搜索了一番,发现 StackOverflow 上有一个回答提到:
典型的 IPv4 头部是 20 字节,而 UDP 头部是 8 字节。然而,可以包括 IP 选项,该选项可以将 IP头部的大小增加到多达 60 字节(如图 1 所示)。
此外,有时中间节点需要将数据报封装在另一种协议(如IPsec(用于VPN等))中,以便将数据包路由到其目的地。
因此,如果不知道特定网络路径上的 MTU,最好为可能没有预料到的其他头部信息留出合理的余量。512字节的 UDP 有效载荷通常被认为可以做到这一点,尽管即使这样也没有为最大尺寸的 IP报头留下足够的空间。
图1,IPV4 头部字段,来源:Chapter 5. The Internet Protocol (IP) - Shichao's Notes。
通过这个回答我们可以知道,UDP 数据包的最大安全负载应该是 508 字节(MTU(576) - IPHeader(60) - UDPHeader(8)),因为 IP 头部最大时为 60 字节。512 也是一个综合考虑的结果。
总结
总的来说,这些数值的限制就是各层之间综合权衡的结果,以在整体上达到最优传输效率。当然,还得提一下,上述讨论针对的是 IPV4,而非 IPV6。