【TCP/IP 笔记】IPv4-03 | 数据报分片 (Fragmentation)和重组 (Reassembly)

参考 <The TCP/IP Guide> http://tcpipguide.com

概述

MTU (Maximum Transmission Unit) 和数据报碎片

在提及将数据报分片前不得不提一下 MTU:物理层所能传输的最大数据报大小。当一个数据报从一个 MTU 较高的网络传向 MTU 较低的网络时数据报就会被分片成一个个大小小于或等于要通过的网络的 MTU 的碎片。就像石头君在管道中旅行,他要进入一个口径小的管道,可是他比管道大进不去,那他就要分裂成小石头君然后才能进入小管道。

【TCP/IP 笔记】IPv4-03 | 数据报分片 (Fragmentation)和重组 (Reassembly)

多次分片

数据报在传输过程中往往都要经过数个网络,每个网络的 MTU 或许都不同,如果数据报大小比网络的 MTU 大时进行分片,如果比网络的 MTU 小时不做操作,也就是说传输过程中数据报可能被多次分片,但不进行重组,重组操作由数据报的最终接收方执行。互联网的最小 MTU 为 576 Bytes。

【TCP/IP 笔记】IPv4-03 | 数据报分片 (Fragmentation)和重组 (Reassembly)

分片过程

以上面的分片实例为背景,将一个大小为 12,000 Bytes 的数据报分别通过 MTU 为 3,300 1,300 3,300 的网络传送到目的地,其中进行了两次分片操作。

  1. 第一次分片:大小为 12,000 的数据报传入 MTU 为 3,300 的网络,每个碎片都有自己的 20 Bytes报头,所以分片的结果是 3 个携带 3,280 Bytes 数据的数据报碎片和 1 个携带 2,140 Bytes 数据的数据报碎片。偏移量都比前一个碎片多 410,因为偏移量以 8 Bytes 为一单位,410 * 8 = 3280 Bytes 刚好是前一个碎片携带的数据大小。
  2. 第二次分片:第二次和第一次大体相同,要注意的是这次分片是对碎片进行分片,不是将原数据报分片。这也很显而易见,因为数据报不在途中重组,每个碎片去往终点的路由也不同,所以路由器是不一定知道原数据报完整内容的,所以路由器只能对碎片进行再分片,这样分片最终得到的结果不是最优的,我们可以看到第二次分片后,多了很多小碎片,最优的分片应该是像第一次那样的,只有一个小碎片,其他碎片大小一样且都是允许通过的最大值,如果想达到最优的分片效果,可以先测试路由最小 MTU 然后由发件者按照此 MTU 分片后再发送。

【TCP/IP 笔记】IPv4-03 | 数据报分片 (Fragmentation)和重组 (Reassembly)

有关报头字段

Total Length

分片后,这个字段指的是碎片的长度,而不是原数据报的长度。

Identification

为属于同一个数据报的碎片指定相同的唯一标识。

More Flagments

除了最后一个碎片此值为 0 其余都为 1。

Fragment Offset

这个字段指示了每个碎片的位置,帮助接收方进行重组。该字段有 13 bits,所以偏移量最大为 8,191,8191 * 8 = 65,528 Bytes,还记得数据报的最大值吗:65,535 Bytes,这就是为什么偏移量以 8 Bytes 为单位。

Copied Flag

如果报头有需要在分片时复制给碎片的选项,需要将该字段的值设为 1。每个选项都含有这个字段。

DF Flag

Flags 字段中的 DF 子字段,如果设为 1 该数据报将不会被进行分片处理,如果路由器遇到了一个数据报太大不能进入下一个网络,有设置了 DF Flag ,不能分片又传不下去,路由器有将这个数据报丢弃,并返回一个特定的 ICMP,这个特性常用于 MTU Path Discovery。

重组过程

分片操作可以由源和目的之间的路由器进行,但重组操作只能由目的设备进行。

  • 碎片识别:接收者通过源和目标 IP 地址、报头指定的协议和发件人设置的 identification 字段来区分不同原数据报的碎片。
  • 初始化缓冲区:接收者初始化缓冲区,用于存放收到的碎片,并跟踪这个缓冲区来得知哪部分已经填充以及何时填满。
  • 初始化计时器:接收者为重组操作设置一个计时器,当有些碎片已经丢失不再出现时,计时器保证接收者不会永远的等下去,没有结果的等待注定是悲剧收尾。如果计时器超时而还有碎片未到达,将会发送一个超时 ICMP。
  • 碎片接收和处理:一个片段送达时,通过上面提到的碎片识别,放入对应的缓冲区,至于放入缓冲区的哪个位置,要根据 fragment offset 的值来决定。

这一过程就像你获得一块块拼图,拼图的背面写着它所属哪幅图(碎片识别),还有它在图中的位置 (fragment offset),然后你会找个地方去放属于这幅图的碎片(初始化缓冲区),然后你会定个时间去完成拼图然后去吃饭碎片不够也不再等别人给你碎片(初始化计时器)。