文章目录
前言
开新篇,传输层结束了,那是不是就到了网络层了?这里是IP协议的主场!
其实还有往下的,叫做数据链路层,不过不急,我们一层一层往下讲解!
一、基本概念
TCP 作为传输层控制协议,其保证的是数据传输的可靠性和传输效率,但 TCP 提供的仅仅是数据传输的策略,而真正负责数据在网络中传输的则传输层之下的网络层和链路层。

我们再来回顾一下数据在网络中传输的过程,上图可以很形象的说明这个知识点,两个人要进行数据交互,必须从四楼往下走,在走路到对面那一栋楼,再往上传递到那一层楼,再给那个人
其中,送数据的这个人从四楼下来的过程就是数据封装的过程,这个人在路上经过路径选择到达对方楼下的过程就是数据路由的过程,而这个人再上到四楼将数据交给对方的过程就是数据解包的过程
如果说之前的传输层主要提供的是可靠性保证,那么网络层主要负责的就是跨网络发送的能力,也就是 端 到 端 的通信,主机 与 主机 的通信
路径选择
还是先对几个网络层的常见概念做一个了解掌握吧:
- 主机:配有 IP 地址,但是不进行路由控制的设备。但实际现在几乎不存在不进行路由控制的设备了,就连你的笔记本也会进行路由控制
- 路由器:既配有 IP 地址,又能进行路由控制。实际现在主流的路由器已经不仅仅具有路由的功能了,它甚至具备某些应用层的功能
- 节点:主机和路由器的统称
数据进行的网络传输一般都是跨网络的,而路由器就是连接多个网络的硬件设备,因此数据在进行跨网络传输时一定需要经过多个路由器。

确定数据路由的目的地后,数据就可以在网络中进行路由了,但数据在路由时无法自行进行路径选择,因为这个数据本身是"不认识路"的,因此数据在路由的过程中需要不断"找路人问路",而这里所谓的"路人"就是网络当中的一台台路由器。
网络当中的路由器是"认识路的",它们将自己的"认路经验"都记录到路由表当中,因此路由器可以通过查路由表找到去特定点的最短路径。因此数据在路由时,会不断通过路由器来进行路径选择,以此来一步步靠近目标网络或目标主机。

二、IP协议格式

- 4位版本号(version):指定IP协议的版本(IPv4/IPv6),对于IPv4来说,就是4。
- 4位首部长度(header length):表示IP报头的长度,以4字节为单位。
- 8位服务类型(Type Of Service):3位优先权字段(已经弃用),4位TOS字段,和1位保留字段(必须置为0)。4位TOS分别表示:最小延时,最大吞吐量,最高可靠性,最小成本。这四者相互冲突,只能选择一个。比如对于ssh/telnet这样的应用程序,最小延时比较重要,而对于ftp这样的程序,最大吞吐量比较重要。
- 16位总长度(total length):IP报文(IP报头+有效载荷)的总长度,用于将各个IP报文进行分离。
- 16位标识(id):唯一的标识主机发送的报文,如果数据在IP层进行了分片,那么每一个分片对应的 id 都是相同的。
- 3位标志字段:第一位保留,表示暂时没有规定该字段的意义。第二位表示禁止分片,表示如果报文长度超过 MTU , IP模块 就会丢弃该报文。第三位表示 "更多分片" ,如果报文没有进行分片,则该字段设置为0,如果报文进行了分片,则除了最后一个分片报文设置为0以外,其余分片报文均设置为1。
- 13位片偏移(framegament offset):分片相对于原始数据开始处的偏移,表示当前分片在原数据中的偏移位置,实际偏移的字节数是这个值 ×8 得到的。因此除了最后一个报文之外,其他报文的长度必须是8的整数倍,否则报文就不连续了。
- 8位生存时间(Time To Live,TTL):数据报到达目的地的最大报文跳数,一般是64,每经过一个路由,TTL -= 1,一直减到0还没到达,那么就丢弃了,这个字段主要是用来防止出现路由循环。
- 8位协议:表示上层协议的类型。
- 16位首部检验和:使用CRC进行校验,来鉴别数据报的首部是否损坏,但不检验数据部分。
- 32位源IP地址和32位目的IP地址:表示发送端和接收端所对应的IP地址。
选项字段:不定长,最多40字节。
IP如何将报头与有效载荷进行分离?
IP分离报头 与有效载荷的方法与 TCP 是一模一样的 ,当IP从底层获取到一个报文后,虽然 IP 不知道报头的具体长度,但 IP报文 的前 20 个字节是 IP 的基本报头,并且这 20字节 当中涵盖 4位首部长度 。
因此 IP 是这样分离报头与有效载荷的:
当 IP 从底层获取到一个报文后,首先读取报文的前 20 个字节,并从中提取出 4位 的首部长度,此时便获得了 IP报头 的大小 size 。
如果 size 的值大于 20字节 ,则需要继续从报文当中读取 size − 20 字节的数据,这部分数据就是 IP报头 当中的选项字段。
读取完 IP 的基本报头和选项字段后,剩下的就是有效载荷了。
IP 就是通过这种 定长报头+自描述字段 的方式进行报头和有效载荷的分离的。但需要注意的是, IP报头 当中的 4位首部长度 描述的基本单位与 TCP报头 当中的 4位首部长度 一样,都是以 4字节 为单位进行描述的,这也恰好是报文的宽度。
4位二进制的取值范围是 0000 ~ 1111 ,因此IP报头的最大长度为 15 × 4 = 60 字节,因为基本报头的长度是 20字节 ,所以 IP报头 中选项字段的长度最多是 40字节 。如果 IP报头 当中不携带选项字段,那么 IP报头 的长度就是 20字节 ,此时报头当中的 4位首部长度 字段所填的值就是 20 ÷ 4 = 5 ,即 0101 。
就跟TCP是一摸一样的
IP如何决定将有效载荷交付给上层的哪一个协议?
基于 IP协议 的传输层协议不止一种,因此当 IP 从底层获取到一个报文并对其进行解包后, IP 需要知道应该将分离后得到的有效载荷交付给上层的哪一个协议
在 IP报头 当中有一个字段叫做 8位协议 ,该字段表示的就是上层协议的类型, IP 就是根据该字段判定应该将分离出来的有效载荷交付给上层的哪一个协议的。该字段是发送方的 IP层 从上层传输层获取到数据后填充的,比如是 上层TCP 交给 IP层 的数据,那么该数据在封装 IP报头 时的 8位协议 填充的就是 TCP对应的编号
32位源IP地址和32位目的IP地址
IP报头 当中的 32位源IP地址 和 32位目的IP地址 ,分别代表的就是该报文的发送端和接收端对应的 IP地址
数据在网络传输过程中会遇到一个个的路由器,这些路由器会帮助网络当中的数据进行路由转发,使得网络中的数据慢慢趋近于目标主机。路由器在帮助数据进行路由转发时,会提取出该数据的IP报头当中的目的IP地址,并以此作为数据路由转发的重要依据
当接收端收到了发送端发来的数据后,接收端可能也想要给发送端发送数据,因此发送端在发送数据时除了需要指明该数据的 目的IP地址 ,还需要指明该数据的 源IP地址 ,也就是 发送端的IP地址 。即便接收端收到数据后没有数据想要发送给发送端,但至少接收端需要向发送端发送一个响应报文,表明发送端发送的数据已经被接收端可靠的收到了,因此发送出去的数据除了需要指明该数据的 目的IP地址 ,还需要指明该数据的 源IP地址
所以现在你应该要有个了解:
- 在进行 socket编程 的时候,当一端想要发送数据给另一端时,必须要指明对端的 IP地址 和 端口号 ,也就是发送数据的 目的IP地址 和 目的端口号
- 其中这里的 IP地址 就是给 网络层的IP 用的,用于数据在网络传输过程中的路由转发,而这里的端口号就是给传输层的 TCP 或 UDP 用的,用于指明该数据应该交给上层的哪一个进程
- 发送数据时我们不需要指明发送数据的 源IP地址 和 源端口号 ,因为传输层和网络层都是在操作系统内核当中实现的,数据在进行封装时操作系统会自行填充上对应的 源IP地址 和 源端口号
8位生存时间
报文在网络传输过程中,可能因为某些原因导致报文无法到达目标主机,比如报文在路由时出现了环路路由的情况,或者目标主机已经异常离线了,此时这个报文就成了一个废弃的游离报文。
为了避免网络当中出现大量的游离报文,于是在 IP的报头 当中就出现了一个字段,叫做 8位生存时间(Time To Live,TTL)。 8位生存时间 代表的是报文到达目的地的最大报文跳数,每当报文经过一次路由,这里的生存时间就会减一,当生存时间减为 0 时该报文就会被自动丢弃,此时这个报文就会在网络中消散。
分片与组装
IP层之下是数据链路层,其中数据链路层最典型的代表协议就是MAC帧,解决的是将数据从一个节点传送到和自己相连的下一个节点,也就是局域网通信的问题,这就会牵扯到一个 MTU 的概念
最大传输单元 MTU
MAC帧 作为数据链路层的协议,它会将 IP 传下来的数据封装成数据帧,然后发送到网络当中。但 MAC帧 携带的有效载荷的最大长度是有限制的,也就是说 IP 交给 MAC帧 的报文不能超过某个值,这个值就叫做最大传输单元(Maximum Transmission Unit,MTU),这个值的大小一般是 1500字节
至于说为什么要有这样的 MTU 存在,可以参考下面 DS 给出的解答,就跟高速公路上面的货车为什么有一个最大载货量限制是一样的道理

在 Linux 下使用 ifconfig 命令可以查看对应的 MTU

由于 MAC帧 无法发送大于 1500字节 的数据,因此 IP层 向下交付的数据的长度不能超过 1500字节 ,这里所说的数据包括 IP的报头和IP的有效载荷
第二个 lo 那个是本地环回,所以 MTU 能达到 65536 个字节,这在实际网络不同主机之间的数据交流不可能的
分片与组装
因为数据链路层存在一个 MTU 的限制,所以自然而然的就会有一个 IP分片和组装 的一个机制,下面就让我来跟大家讲讲这里面的门道
如果发送数据时在 IP层 进行了分片,那么当这些分片数据到达对端主机的 IP层 后就需要先进行组装,然后再将组装好的数据交付给上层传输层

在我正式的讲一个具体的分片与组装的过程前,我觉得你先需要注意以下几点:
- 数据的分片不是经常需要做的,实际在网络通信过程中不分片才是常态,因为数据分片会存在一些潜在的问题,比如分片可能会增加丢包的概率
- 数据的分片和组装发生在 IP层 ,不仅源端主机可能会对数据进行分片,数据在路由过程中的路由器也可能对数据进行分片。因为不同网络的 MTU 是不一样的,如果传输路径上的某个网络的 MTU 比源端网络的 MTU 小,那么路由器就可能对 IP数据报 再次进行分片
- 分片数据的组装只会发生在目的端的 IP层
- 在分片的数据中,每一个分片在 IP层 都会被添加上对应的 IP报头 ,而传输层添加的报头只会出现在第一个分片中,因此网络中传输的数据包可能没有传输层的报头
其实你也可以看出,分片与组装完完全全的就是 网络层IP 干的事情,与 传输层 和 数据链路层 完全没有一点关系
传输层只负责为数据传送提供可靠性保证 ,比如当数据传送失败后,传输层的 TCP协议 可以组织进行数据重传
而链路层的 MAC帧 只负责,将数据从一个节点传送到和自己相连的下一个节点
分片的过程
理论知识具备,现在我们来看看一个真实的分片的过程,让我来详细解释 假设IP层要发送 4500字节 的数据,由于该数据超过了 MAC帧 规定的 MTU ,因此 IP 需要先将该数据进行分片,然后再将一个个的分片交给 MAC帧 进行发送 的这个过程
IP报头 如果不携带选项字段,那么其大小就是 20字节 ,假设 IP层 添加的 IP报头 的长度就是 20字节 ,并按下列方式将数据分片后形成了四个分片报文
| 分片报文 | 总字节数 | IP报头字节数 | 数据字节数 |
|---|---|---|---|
| 1 | 1500 | 20 | 1480 |
| 2 | 1500 | 20 | 1480 |
| 3 | 1500 | 20 | 1480 |
| 4 | 80 | 20 | 60 |
需要注意的是,分片后的每一个分片数据都需要封装上对应的 IP报头 ,因此 4500字节 的数据至少需要分为 四个分片报文 进行发送
分片报文到达对方的 IP层 后需要被重新组装起来,因此 IP层 在对数据进行分片时需要记录分片的信息,而 IP报头 当中的 16位标识、3位标志和13位片偏移 实际就是与 数据分片 相关的字段
- 16位标识:唯一标识主机发送的报文,如果数据在 IP层 进行了分片,那么每一个分片报文的 16位标识 是相同的
- 3位标志:第一位保留,表示暂时没有规定该字段的意义。第二位表示禁止分片,表示如果报文长度超过 MTU , IP模块 就会丢弃该报文。第三位表示"更多分片",如果报文没有进行分片,则该字段设置为 0 ,如果报文进行了分片,则除了最后一个分片报文设置为 0 以外,其余分片报文均设置为 1
- 13位片偏移:分片相对于原始数据开始处的偏移,表示当前分片在原数据中的偏移位置,实际偏移的字节数是这个值 ×8 得到的。因此除了最后一个报文之外,其他报文的长度必须是 8的整数倍 ,否则报文就不连续了
| 分片报文 | 总字节数 | IP报头字节数 | 数据字节数 | 16位标识 | "更多分片" | 13位片偏移 |
|---|---|---|---|---|---|---|
| 1 | 1500 | 20 | 1480 | 123 | 1 | 0 |
| 2 | 1500 | 20 | 1480 | 123 | 1 | 185 |
| 3 | 1500 | 20 | 1480 | 123 | 1 | 370 |
| 4 | 80 | 20 | 60 | 123 | 0 | 555 |
需要注意的是, 13位片偏移 当中记录的字节数是当前分片在原数据开始处的偏移字节数的值 ÷8 得到的,比如 分片报文2 在原始数据开始处的偏移字节数是 1480 ,其对应的 13位片偏移 的值就是 1480 ÷ 8 = 185 。

组装的过程
MAC帧 交给 IP层 的数据可能来自世界各地,这些数据可能是经过分片后发送的,也可能是没有经过分片直接发送的,因此 IP 必须要通过某种方式来区分收到的各个数据
- IP报头 当中有 32位源IP地址 , 源IP地址 记录了发送端所对应的 IP地址 ,因此通过 IP报头 当中的 32位源IP地址 就可以区分来自不同主机的数据
- IP报头 当中有 16位标识 ,未分片的数据各自的 16位标识 都是不同的,而由同一个数据分片得到的各个分片报文所对应的 16位标识 都是相同的,因此通过IP报头当中 16位标识 就可以判断哪些报文是没有经过分片的独立报文,哪些报文是经过分片后的分片报文
因此 IP 可以通过 IP报头 当中的 32位源IP地址 和 16位标识 ,将经过分片的数据各自聚合在一起,聚合在一起后就可以开始进行组装了
对于各个分片报文来说
- 第一个分片报文中的13位片偏移的值一定为0
- 最后一个分片报文中的"更多分片"标志位一定为0
- 对于每一个分片报文来说,当前报文的13位片偏移加上当前报文的数据字节数 ÷ 8 所得到的值,就是下一个分片报文的所对应的13位片偏移
根据分片报文的这三个特点就能够将分片报文合理的组装起来
- **先找到分片报文中 13位片偏移为0 的分片报文,然后提取出其 IP报头 当中的 16位总长度字段 ,通过计算即可得出下一个分片报文所对应的 13位片偏移 ,按照此方式依次将各个分片报文拼接起来。
- 直到拼接到一个更多分片 标志位为 0 的分片报文,此时表明分片报文组装完毕。
分片报文丢包的问题
分片后的报文在网络传输过程中也可能会出现丢包问题,但接收端有能力判断是否收到了全部分片报文,比如假设某组分片报文对应的 16位标识值为x
- 如果分片报文中的第一个分片报文丢包了,那么接收端收到的分片报文中就找不到对应 16位标识为x ,并且 13位片偏移为0 的分片报文
- 如果分片报文中的最后一个分片报文丢包了,那么接收端收到的分片报文中就找不到对应 16位标识为x ,并且 "更多分片" 标志位为 0 的分片报文
- 如果分片报文中的其它分片报文丢包了,那么接收端在进行分片报文的组装时就会找不到对应 13位片偏移 为特定值的分片报文
需要注意的是,未分片报文的 "更多分片" 标志位为0 ,最后一个分片报文的 "更多分片"标志位 也为0,但当接收端只收到分片报文中的最后一个分片报文时,接收端不会将其识别成一个未分片的报文,因为未分片的报文所对应的 13位片偏移的值 也应该是0,而最后一个分片报文所对应的 13位片偏移 的值不为0
因此只有当一个报文的 13位片偏移 为0,并且该报文的 "更多分片" 标志位也为 0 时,该报文才会被识别成一个没有被分片的独立报文,否则该报文就会被识别成一个分片报文
为什么不建议进行分片?
虽然传输层并不关心 IP层 的分片问题,但分片对传输层也是有影响的
- 如果一个数据在网络传输过程中没有经过分片,那么只要接收端收到了这一个报文,我们就可以认为该数据被对方可靠的收到了
- 而如果一个数据在网络传输过程中进行了分片,那么只有当接收端收到了全部的分片报文并将其成功组装起来,这时我们才认为该数据被对方可靠的收到了。但如果众多的分片报文当中有一个报文出现了丢包,就会导致接收端就无法将报文成功组装起来,这时接收端会将收到的分片报文全部丢弃,此时 传输层TCP 会因为收不到对方应答而进行超时重传
- 假设在网络传输时丢包的概率是万分之一,如果将数据拆分为一百份进行发送的话,我们可以算一下概率,就是一次成功概率是 99.99% 那么100次的话就是(99.99)^100。因为只要有一个分片报文丢包了也就等同于这个报文整体丢失了,因此分片会增加传输层重传数据的概率
需要注意的是,只要分片报文当中的某一个出现了丢包,此时传输层都需要将数据整体进行重传 ,因为传输层并不知道 底层IP 对数据进行了分片,当传输层发送出去的数据得不到应答时传输层就只能将数据整体进行重传,因此数据在发送时不建议进行分片
如何尽可能避免分片?
实际数据分片的根本原因在于传输层一次向下交付的数据太多了,导致 IP 无法直接将数据向下交给 MAC帧 ,如果传输层控制好一次交给 IP 的数据量不要太大,那么数据在 IP层 自然也就不需要进行分片
因此 TCP 作为传输控制协议,它需要控制一次向下交付数据不能超过某一阈值,这个阈值就叫做MSS(Maximum Segment Size,最大报文段长度)
通信双方在建立 TCP 连接时,除了需要协商 自身窗口大小 等概念之外,还会协商后续通信时每一个报文段所能承载的最大报文段长度 MSS
MAC帧 的有效载荷最大为 MTU , TCP 的有效载荷最大为 MSS ,由于 TCP 和 IP 常规情况下报头的长度都是 20字节 ,因此一般情况下 MSS = MTU - 20 - 20,而 MTU的值 一般是 1500字节 ,因此 MSS 的值一般就是 1460 字节
所以一般建议 TCP 将发送的数据控制在 1460字节 以内,此时就能够降低数据分片的可能性 。之所以说是降低数据分片的可能性,是因为每个网络的链路层对应的 MTU 可能是不同的,如果数据在传输过程中进入到了一个 MTU 较小的网络,那么该数据仍然可能需要在路由器中进行分片。
MSS = MTU - IP头(20) - TCP头(20) = 1460字节 ,这个公司其实也说明了传输层对下层网络层约束的适应,所以我们虽然说分片是完完全全发生在网络层的行为 ,但是其实也影响着传输层,也深深地受着传输层的影响

总结
有点太多了,那我就单独先写个上篇,来个"分片"~~
hhhh,下篇我们再见!!!