本书 《网络协议栈入门》 采用的代码是 基于 linux 内核 4.4.4 版本的。linux 内核源码下载地址: mirrors.edge.kernel.org
在 《IP分片分析》 一文中,TCP 使用的 MTU 发现,是通过 3次握手,发送双方的MSS来实现。发送的MSS是结合自己网卡的MTU跟ARP路由表的信息计算出来的。
本文主要讲解,现有网络技术中,还有哪些MTU 发现技术。
为什么要找出路径中的最小MTU,是因为 MTU 是MAC 层的最小传输单元,如果超过 MTU,IP层就会分片。分片有什么坏处?会降低传输效率。
举个例子.,假设最小MTU是 1500 字节,一个原始 IP 数据包 长度 1800 字节(不包含IP header)。发送的时候IP层能容纳的body长度是 1500-20 = 1480,减去IP 头部,IP 包的body只能放 1480 个字节,所以原始数据包会拆成两个 IP 包发送,IP包 A是的body长度是 1480 字节,IP包B 的body长度是 320 字节。
这样,原始数据包会拆成两个 IP 包,只要其中一个IP包传输过程中丢失了,就要全部重传。
所以 IP 层分片有两个缺点。
1,一个分片丢失,导致所有分片重传。
2,没有最大限度的利用 MAC 层的传输能力。
第二点比较容易忽略,为什么说 分片了 就没有最大限度的利用 MAC 层的传输能力。
这里需要普及一个知识点,MTU 是MAC 层限制的,是最小传输单元。MAC 层的body最多能放 1500 字节,但是 第二个包只放了 320 字节就发出去了,所以浪费了 1180 字节的空间。
第二点在少数据传输的时候不会有太大影响。这个是这样的,网络最小传输单元,一个单元 从 北京到上海,路径不变,RTT 的时间是固定的,比特流的传输速度只能那么快,这是物理限制。但是网络设备他不是只能发一个网络单元再发另一个单元,MAC 层没有ACK确认机制,也没有 TCP 类似的滑动窗口,MAC 层的设备能同时发多个网络单元。虽然RTT 时间有物理限制,但是通道是很大的,可以容纳多个 网络单元同时发送。
也就是说,上面的 IP包A 跟B,可能是同时发出去的,也会同时到达。但是由于浪费了空间,没满1500字节执行了一次发送,没有充分利用,在传输大量数据时候比较明显。
Path MTU Discover (路径MTU发现机制),可以使用 ICMP PTB 消息来实现。
我用在 windows + qt + msvc 环境用 udp 写了一个程序,模拟 发送 1800 字节的包,同时设置 IP层 的DF 位。
linux udp 设置 IP header 的选项可以看这篇文章《Set Don't Fragment flag using raw sockets for UDP》
UDP 原始socket 设置 IP层 的DF 位代码如下:
SOCKET sock = socket(PF_INET, SOCK_DGRAM, 0);
int dontfragment = 1;
if (setsockopt(sock,IPPROTO_IP,IP_DONTFRAGMENT,&dontfragment,sizeof(dontfragment)) == -1) {
printf("set failed \r\n");
}
完整 qt 项目下载,百度网盘,提取码:lyop
由于 windows 的 UDP 的实现内部自己做了限制,如果我设置了 IP_DONTFRAGMENT
,又发送 1800 字节的数据,sendto()
会直接返回 -1 ,没发送到网络,所以不太好演示 路由器返回的 ICMP 包。
这个路径MTU发现逻辑是这样的,如果路由器发现 IP包 的DF 位设置为1,就不能分片,会返回一个 ICMP PTB 包给发送端,这个 ICMP PTB 有那个路由器的MTU。
UDP scoket怎么拿到这个 ICMP PTB 数据,具体我还要找找接口,应该是有的。
TCP 是内部自己处理了 ICMP PTB 数据,在内核处理。
补充:ICMP PTB 的MTU应该会更新到 本机的路由表,TCP 读路由表数据实现动态调整 MSS。这样实现了不同协议层的功能解耦
TCP 内部,设置了 IP 层的 DF 位置,根据返回的 ICMP PTB消息来动态调整MTU。这个功能由各个厂家自己实现的,有些厂家可能没这么做。
相关阅读:
- 《TCP/IP详解卷一》第8章
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。QQ:2338195090。