0%

为什么TCP/IP要拆分我的数据

TCP/IP 作为目前互联网举足轻重的网络通信协议,不是没有它的道理。TCP/IP 提供供了点对点的链接机制,以让源主机中的源进程发出的数据能够送达目标主机的目标进程里去,这其中的资料封装、寻址、传输、路由、接收都会以标准化的操作完成,并且它们还能保证数据在传输过程中有序不重不漏

但是你不知道的是,当应用层协议使用传输层的 TCP 协议进行数据传输时,传输层 TCP 协议可能会将应用层所发送的消息分成多个数据段,我们一般称呼它叫:TCP 分段。而在其下层的网络层 IP 协议也有可能会对传输层 TCP 协议的数据段分成多个数据包,我们一般称呼它叫:IP 分片。但是由于 TCP 协议会自行先分段,所以正常情况下都轮不到 IP 协议进行分片。那为什么它们要拆分我们的数据呢?又为什么 TCP 协议自行分段后,就没 IP 协议什么事了呢?我们带着这些疑问继续探索。

TCP分段IP分包

在开始之前,我们先达成共识:

  1. 协议层中传输数据的基本单位统称为:Data Unit(数据单元)
  2. 网络层 IP 协议传输的数据单元称为:Packet(包)
  3. 传输层 TCP 协议传输的数据单元称为:Segment(段)
  4. 传输层 UDP 协议传输的数据单元称为:Datagram(报)
  5. 应用层传输的数据单元称为:Message(消息)

并且,我们把前提摘要先提前放出来:

  1. IP 协议会分片传输过大的数据包避免物理设备的限制
  2. TCP 协议会分段传输过大的数据段保证整体的传输性能,同时避免遭到 IP 协议分片

最大传输单元和 IP 协议分片

IP 协议适用于传输数据包的协议,作为网络层协议,它能提供数据的路由寻址功能,让数据能通过网络到达目的地。但是由于现实中物理设备的限制,导致传输于网络中的数据包不能太大,所以在不同设备进行数据传输前,需要先确定一个 IP 数据包的大小上限,既最大传输单元(MTU),MTU 是 IP 数据包能够传输的数据大小上限。

MTU 的值不是越大越好,更大的 MTU 意味着更低的额外开销,但会增加丢包所带来的风险。更小的 MTU 意味着更低的网络延迟,但太小又会让额外开销加大。所以有一个合适 MTU 值对于网络传输来说是非常重要的,每一个物理设备都自己的 MTU,两个主机之间的 MTU 依赖其底层的网络能力,它由整个链路上 MTU 最小的那台物理设备所决定。如下图所示:

链路MTU发现

路径最大传输单元发现(Path MTU Discovery,PMTUD)是用来确定两个主机数据传输 MTU 的机制,它的工作原理如下:

  1. 向目标主机发送数据包头 DF 控制位设置为 1 的 IP 数据包 ,DF 是不分片(Don’t Fragment)的缩写
  2. 路径上的物理设备会根据数据包的大小和自己的 MTU 比较,做出不同的决定:
    1. 如果数据包的大小大于设备的 MTU,就会丢弃数据包并发回一个包含该设备 MTU 的 ICMP 的消息
    2. 如果数据包小于设备的 MTU,就会继续向目标主机传递数据包
  3. 源主机收到 ICMP 消息后,会不断使用新的 MTU 发送 IP 数据包,直到 IP 数据包到达目标主机

ICMP 是互联网控制协议(Internet Control Message Protocol),它能在 IP 主机之间传递控制消息

位于第二层的以太网协议对其数据帧的数据栏位限制一般都是 1500 字节,所以在一般情况下,IP 主机的路径 MTU 都是 1500,扣除 IP 协议数据包头部占用的 20 字节,则如果数据包内的数据大于 1480 字节,那么 IP 协议就开始拆分我们的数据,把数据分到多个数据包中分片传输。

IP 协议的数据包分片对于传输层协议是透明的,假设我们使用 UDP 协议传输 2000 字节的数据,加上 UDP 协议报头占用的 8 字节,则 IP 协议需要传输 2008 字节的数据。但是当 IP 协议发现自己的路径 MTU 是 1480 字节时,它察觉到其要传输的数据 2008 字节大于 MTU 的 1480 字节,就会手起刀落对 UDP 的数据报进行拆分。如下图所示:

IP协议对UDP数据报进行分片

具体分片后的情况如下:

  1. 20 字节 IP 协议数据包头 + 8 字节 UDP 协议数据报头 + 1472 字节数据
  2. 20 字节 IP 协议数据包头 + 528 字节数据

目标主机在接收到数据包时会对分片的数据进行重组,不过因为第二个数据包中不包含 UDP 协议的相关信息,一旦发生丢包,那整个 UDP 数据报就无法重新组合。如果 UDP 数据报需要传输的数据过多,那么 IP 协议就会抄起家伙大量分片,增加了数据传输的不稳定性。如果 IP 协议没有数据包大小的限制,那么上层可以以消息为单位传输数据,自然就不存在分片和组合的需求,不过因为物理设备 MTU 的限制,想要保证数据传输的可靠性和稳定性还是需要传输层的配合。

IP 协议分片与 TCP 最大分段大小

TCP 协议是面向字节流的协议,应用层交给 TCP 协议的数据并不会以消息为单位往目标主机发送,并且 TCP 协议引入了最大分段大小(Maximum segment size,MSS)这一概念,它是 TCP 数据段能够携带的数据上限。在正常情况下,TCP 链接的 MSS 是 MTU - 40 字节(IP 和 TCP 协议头部各占用 20 字节),既 1460 字节;不过如果通信双方没有指定 MSS 的话,在默认情况下 MSS 的大小是 536 字节。所以应用层交给 TCP 协议发送的数据大小如果大于 MSS 就可能会被拆分到多个数据段里。

IP 协议的 MTU 是物理设备上的限制,它限制了传输路径上能够发送数据包大小的上限。而 TCP 协议的 MSS 是操作系统内核层面的限制,通信双方会在三次握手时确定这次所建立连接的 MSS 值。一旦确定了 MSS,TCP 协议就会对应用层交给 TCP 协议发送的数据按 MSS 的大小进行拆分,构成多个数据段。而需要注意的是,IP 协议和 TCP 协议虽然都会对数据进行拆分,但是 IP 协议以数据包为单位组织数据,而 TCP 协议以数据段为单位组织数据。这一点在文章开始的达成共识时有提到过。

如下图所示,如果 TCP 连接 MSS 是 1460 字节,应用层想通过 TCP 协议传输 2000 字节的消息数据,那么 TCP 协议会根据 MSS 将 2000 字节的数据拆分到两个数据段中:

TCP协议数据段进行分段

最终分段后的情况如下:

  1. 20 字节 IP 协议数据包头 + 20 字节 TCP 协议数据段头 + 1460 字节消息数据
  2. 20 字节 IP 协议数据包头 + 20 字节 TCP 协议数据段头 + 540 字节消息数据

从应用层的角度来看,两个数据段中的 2000 字节数据构成了源主机想要发送的消息,但是 TCP 协议是面向字节流的,向协议写入的数据会以流的形式传递到对端。TCP 协议为了保证可靠性,会通过 IP 协议的 MTU 计算出 MSS 并根据 MSS 大小进行分段,以至于能够避免 IP 协议对装有数据段的数据包进行分片。因为 IP 协议对数据包的分片对于上层来说是透明的,如果上层协议不针对 MTU 做一些大小限制,那么 IP 协议进行分片操作时,将导致部分数据包里的部分数据与包含传输层的协议头的数据包分离,一旦数据包发生丢失就只能丢弃全部数据。损失惨重!

我们可以通过一个例子分析 MSS 存在的必要性。如下图所示,假设 TCP 协议中不存在 MSS 的概念,因为每个数据段的大小没有上限,当 TCP 协议交给 IP 协议各发送两个共 1600 字节的 IP 协议数据包时,由于物理设备的限制,IP 协议的路径 MTU 为 1500,所以 IP 协议会对数据包进行分片:

IP协议第TCP协议数据段进行分片

所以就会造成不存在 TCP 协议头的数据包,那么当 IP 协议进行数据传输时出现了丢包,TCP 协议的接收方就没办法对数据段进行重组,最终导致整个 TCP 数据段都需要重传,带来了更多的额外开销。

总结

TCP/IP 拆分数据究根结底就是因为物理设备的限制:

  1. IP 协议则是因为数据包大于 MTU 后会被链路中的设备丢弃,为了避免被丢弃,IP 协议就需要通过路径 MTU 发现获取链路上的 MTU 值后,再抄起数据包分片大刀,把数据包大小大于 MTU 的一节一节砍断并把砍断的数据包重新封包发送。
  2. 而 TCP 协议则是为了避免自个的数据段被 IP 协议的分片大刀无情砍中,因为一旦砍中,那么就会首尾分离,就需要目标主机进行重组,产生不必要的性能消耗,万一传输途中还丢包了,就会前功尽弃需要全部重传。所以根据这个情况 TCP 协议自然的也就自觉的按造 MTU 算出自己的 MSS,然后再根据 MSS 学起 IP 协议抄起了分段大刀,利落的砍向了应用层传过来的待发送消息数据,并重新封段,拿给 IP 协议发送。

延伸阅读