哈喽大家好,我是咸鱼
今天我们来聊聊计算机网络中的 MTU (Maximum Transmission Unit)
什么是 MTU ?
MTU(Maximum Transmission Unit)是指数据链路层中的最大传输单元
通俗点来讲,MTU 是指数据链路层能够传输的最大数据帧的大小(以字节为单位)
以 CentOS 7 为例,可以通过 ifconfig
命令来查看 MTU 值
为什么需要 MTU ?
我们知道,数据在数据链路层中通常是以数据帧(Frame)的形式来传输
因为传输的 Frame 不可能无限大(传小的可以),那么一次传多大的 Frame 最合适、最高效就成了一个需要考虑的问题
所以说我们要设定一个值(也就是 MTU),这个值用来限制 Frame 的大小
维基百科有这么一段话
Larger MTU is associated with reduced overhead. Smaller MTU values can reduce network delay.
由上面的话可以得出:
- MTU越大,开销越小
这句话是很容易理解的,更大的 Frame 意味着包含的有效数据也就更多
我一次能传更多的数据了,那么传输次数就少了,在网络上的开销就变小了
- 较小的MTU值可以减少网络延迟
这句话容易让人引起困惑,为什么 MTU 值小能够减少网络延迟呢?
如果 MTU 设置的很大,意味着一次能传更大的数据了,那占据链路的时间就会更长,所以总体上网络延迟就会变大
而且如果一大段数据里面有一个 bit 出错了,这一大段就会整个重传,重传的代价是很大的
那么 MTU 设置成多少合适呢?
为什么 MTU 的值是 1500?
RFC 标准定义以太网的默认 MTU 值为 1500,为什么是 1500 ?
这是一个历史遗留问题
早期的以太网使用共享链路的工作方式,为了保证 CSMA/CD(载波多路复用/冲突检测)机制,规定了以太帧长度最小为 64 字节,最大为 1518 字节
- 最小 64 字节是为了保证最极端的冲突能被检测到,64 字节是能被检测到的最小值
- 最大不超过 1518 字节是为了防止过长的帧传输时间过长而占用共享链路太长时间导致其他业务阻塞
所以说数据帧的最大长度被限制为 1518 字节(包括帧头、帧尾和CRC校验)
18 字节用于帧头和帧尾,其中 CRC 校验占据 4 字节,剩下的 1500 字节就是最大数据载荷(Payload)
因此,1500 字节被定义为以太网数据帧中数据部分的最大长度(MTU)
虽然技术不断发展,但这个规定一直没有更改
以太网经过几十年的发展,速度已经从最初的10M被提升到了上百G,速度提高了上万倍
在这样高速度的传输数据中,如果还是延续经典以太网的最大帧长不超过1518 字节的限制,那么在每秒中传输的数据包的个数将很大
于是一些厂商提出了巨型帧(Jumbo Frame)的概念,把以太网的最大帧长扩展到了9K
目前还没有获得 IEEE 标准委员会的认可,但是大多数的设备厂商都已经开始支持
发送数据大小超过 MTU 怎么办?
以太网的 MTU 默认值是 1500,如果发送 Frame 小于 MTU(例如 1000 字节),直接传输就行
如果大于 MTU(4000 字节),那就需要进行分片(Fragment)
即第一次发送 1500 字节(IP header 20 字节 + Payload 1480 字节)
第二次发送 1500 字节(IP header 20 字节 + Payload 1480 字节)
第三次 1000 字节(IP header 20 字节 + Payload 980 字节)
可能有小伙伴问,那我传的数据大小不够数据帧规定的最小 64 字节怎么办?
答案是:在实际数据内容后面添加填充数据,使得数据包总长度达到最小长度要求。填充数据可以是任意无意义的字节,例如全 0 或全 1 的数据
如何保证发送的数据不超过 MTU ?
前面我们知道,如果发送的数据大于 MTU,则就会进行分片操作
要让最终传给数据链路层的 Frame 数据大小不超过 1500 bytes,就要保证上层中的每一层的数据都没有超过这个大小
如果 MTU 是 1500,那么 IP 层就要保证 IP 层的 Packet 数据不超过 1480 bytes (1500 bytes – 20 bytes IP header),
对于 TCP 来说,它要保证每一个 Segment 数据大小不超过 1460 bytes (1460 bytes – 20 TCP header)
以 TCP 层(传输层)为例子, TCP 层是怎么知道发送的数据不会超过 MTU 呢?
- IP 层(网络层)问数据链路层数据最大传输单元( MTU )是多少
- TCP 层会问 IP 层数据最大传输大小(Maximum packet size)是多少
这样 TCP 层就会知道自己的最大传输数据大小,叫做 MSS(Maximum segment size)
在 TCP 的握手阶段,MSS(Maximum Segment Size)是用于指定TCP报文段中数据部分的最大长度
对于 TCP 来说,知道自己的 MSS 没有什么用
例如作为接收端来说,收到的包大小取决于发送端,得让发送端知道自己的 MSS 才行
所以在建立 TCP 连接时,双方需要协商一个合适的 MSS 值,以便在数据传输过程中进行分片和重组
什么是 TSO
前面我们介绍了什么是 MTU、MSS
如果你去抓一下包看看,你可能会遇到下面这种情况
明明协商了 MSS 为 1460 字节,为什么数据居然有一万多字节?
在建立 TCP 连接时,双方需要协商一个合适的 MSS 值,以便在数据传输过程中进行分片
但是这个分片过程往往是简单重复而且计算量比较大的,需要占用较多的 CPU 资源
所以网卡就会对内核说:哥这种小事我来干就好了,不需要麻烦您
然后内核就会把大包发给网卡,网卡来负责分片
这个叫做 TSO(TCP Segment Offload)
TSO(TCP Segmentation Offload)是一种网络传输卸载技术,用于减轻主机的网络数据包分段负担,提高网络传输性能
我们在抓包的时候看到是还在内核里的包,后面网卡拆包的过程是看不到的
网卡不但能对发送的包进行 offload,也可以对接收的包进行 onload,简单来说就是网卡会先把一些小的包积攒下来,然后合起来发送给内核
# 查看网卡上面是否开启 TSO 功能
# on 表示开启
[root@root~]# ethtool -k <网卡名称>
tcp-segmentation-offload: on
tx-tcp-segmentation: on
tx-tcp-ecn-segmentation: on
tx-tcp6-segmentation: on
tx-tcp-mangleid-segmentation: off
最后总结一下:
- MTU 是指数据链路层能够传输的最大数据帧的大小(以字节为单位)。由于历史原因,MTU 的值最小为 64 字节,最大为 1518 字节(Payload 则是 46~1500 字节)
- 如果发送的数据大于 MTU,则就会进行分片操作;如果小于 MTU,就会在实际数据内容后面添加填充数据,使得数据包总长度达到最小长度要求
- MSS(Maximum Segment Size)是用于指定 TCP 报文段中数据部分的最大长度。在建立 TCP 连接时,双方需要协商一个合适的 MSS 值以便在数据传输过程中进行分片和重组
- TSO(TCP Segmentation Offload)是一种网络传输卸载技术。TSO 技术将分片过程从 CPU 转移到网卡上的专用硬件,网卡负责将应用程序的大块数据分段为适当大小的数据包,并添加 TCP 首部,然后直接发送到网络上