1. IP分片
IP头的一部分为
++++++++++++++++++++++++++++++++++++++++++++++++
| 16 bit 标识 | 3 bit 标志 | 13 bit 片偏移 |
++++++++++++++++++++++++++++++++++++++++++++++++
对于IP层来说,收到上层(UDP/TCP)发过来的数据,会先检查链路层的MTU: 1)如果 “上层数据+IP头” 的总长度没有超过MTU,则直接进行发送。 2)如果长度超过了MTU,则要进行分片,根据IP头部的格式,有一个16位的标识,分片的时候会把这个标识复制到所有的分片中去,也就是说同一个IP的分片,他们的标识是一样的;然后3bit标志中有一位是表示是否最后一片,除了最后一片为0,其它片这个标识位都为1;修改IP的长度为分片中实际数据的长度;片偏移量是该片偏移原始数据的位置。 接收端根据 标识,是否为最后一片,各个片的长度,偏移量就可以把分片的包重新组装起来。
在3位标识中还有一位是表示是否允许分片,如果该位是1,而且长度又大于MTU,那么,IP层不对数据进行分片,而是把包丢弃,然后发送一个ICMP消息给起始端。
note:
1) 经过每一个路由或网络都有可能对原来的IP包进行分片,也就是说起始端并不清楚具体的分片情况,因为中间的过程中可能又产生了很多分片。
2) 因为分片可以选择不同的路由,所以分片的到达顺序是乱的,最后一片有可能最先到达。
3) IP分片对于传输层是透明的,但是如果IP的接收端收到的分片少了一片,那么整个IP包都会被丢弃,如果上层有超时重传机制则发重发整个包,而不是一个分片,因为IP层本身是没有超时重传机制的。
4) 任何传输层的部首只会在第一个分片中出现,后续分片中是没有的。
2. UDP分片
对于UDP层来说,其实是没有分片这个概念的,它都是整包发送,接收端整包接收的,如果发送的长度超过了限制就不能发送。
从理论上来说,UDP数据的总长度为 65535(IP最大长度)-20(IP头)-8(UDP头) = 65507个字节,但大多数系统都达不到这个长度。这一般是受到两个方面的因素限制:
1) 应用程序编程接口限制。一般socket的缓冲区大小是8K,但都提供API来设置缓冲区的大小(SetSockOpt)。一般发送UDP最好不要超过512字节,这样基本可以保证不丢包(因为大部分网络和主机的MTU都大于512).
2) TCP/IP内核的限制。可能存在一些实现特性使得IP长度不能达到65535。
由于IP能够发送或接收特定长度的数据报并不意味着接收应用程序可以读取该长度的数据。因此,UDP编程接口允许应用程序指定每次返回的最大字节数。如果接收到的数据报长度大于应用程序所能处理的长度,那么会发生什么情况呢?典型的Berkeley版socket API对数据报进行截断,并丢弃任何多余的数据;SVR4下的socket API(包括Solaris 2.x) 并不截断数据报。超出部分数据在后面的读取中返回。它也不通知应用程序从单个UDP数据报中多次进行读取操作;TLI API不丢弃数据。相反,它返回一个标志表明可以获得更多的数据,而应用程序后面的读操作将返回数据报的其余部分。
3. TCP分片
应用数据被分割成TCP认为最适合发送的数据块。这和 UDP完全不同,应用程序产生的数据报长度将保持不变。由 TCP传递给IP的信息单位称为报文段或段(segment)。如果一方的应用程序先传10字节,又传20字节,再传50字节,连接的另一方将无法了解发方每次发送了多少字节。收方可以分4次接收这80个字节,每次接收 20字节,也可以一次性收全80个字节。而且TCP不对内容进行解释,对它来说都是二进制流,解释是由应用层进行的。