【Linux 网络】网络基础(三)(网络层协议:IP 协议)

news2024/11/16 13:53:24

在复杂的网络环境中确定一个合适的路径。


一、TCP 与 IP 的关系

IP 层的核心作用是定位主机,具有将数据从主机 A 发送到主机 B 的能力,但是能力并不能保证一定能够做到,所以这时就需要 TCP 起作用了,TCP 可以通过超时重传、拥塞控制等策略来保证数据能够发送到 B 主机。
所以,TCP 提供的是策略(保证可靠),而 IP 付出的是行动。


二、IP 协议的基本概念

2b2477bd46504263baa9646622a9b2b3.png

网络层要解决的问题是:将数据从一台主机送到另一台主机,也就是数据的路由

  • 主机:配有 IP 地址,但是不进行路由控制的设备。
  • 路由器:即配有 IP 地址,又能进行路由控制。
  • 节点:主机和路由器的统称。

在路径选择中,目的 IP 非常重要,决定了路径该如何走。
数据进行的网络传输一般都是跨网络的,而路由器就是连接多个网络的硬件设备,因此数据在进行跨网络传输时一定需要经过多个路由器。
ip = 目标网络 + 目标主机

举个例子,我们要到北京故宫,北京就像目标网络,故宫就像目标主机,所以我们要先找到目标网络再找到目标主机。
IP 地址是进行路由的根本。


三、IP 协议格式

  • 4 位版本号(version):指定 IP 协议的版本(IPv4/IPv6),对于 IPv4 来说,就是 4。
  • 4 位头部长度(header length):表示 IP 报头的长度,以 4 字节为单位。IP 头部的长度是多少个 32 bit,也就是 length * 4 的字节数。4bit 表示最大的数字是 15,因此 IP 头部最大长度是 60 字节。
  • 8 位服务类型(Type Of Service):找到最优路径。3 位优先权字段(已经弃用),4 位 TOS 字段,和 1 位保留字段(必须置为0)。
  • 4位 TOS 分别表示:最小延时,最大吞吐量,最高可靠性,最小成本。这四者相互冲突只能选择一个,对于 ssh/telnet 这样的应用程序,最小延时比较重要。对于 ftp 这样的程序,最大吞吐量比较重要。
  • 16 位总长度(total length):IP 报文(IP 报头 + 有效载荷)的总长度,IP 数据报整体占多少个字节,用于将各个IP报文进行分离。
  • 16 位标识(id):唯一的标识主机发送的报文。如果 IP 报文在数据链路层被分片了,那么每一个片里面的这个 id 都是相同的。 
  • 3 位标志字段第一位保留(保留的意思是暂时没有规定该字段的意义)。第二位置为 1 表示禁止分片,这时候如果报文长度超过 MTU,IP 模块就会丢弃报文。第三位表示 “更多分片,如果报文没有进行分片,则该字段设置为 0,如果报文进行了分片,则除了最后一个分片报文设置为 0 以外,其余分片报文均设置为 1,类似于一个结束标记。
  • 13 位分片偏移(framegament offset):分片相对于原始 IP 报文开始处的偏移,表示当前分片在原数据中的偏移位置,实际偏移的字节数是这个值 *8 得到的。因此, 除了最后一个报文之外,其他报文的长度必须是 8 的整数倍,否则报文就不连续了。
  • 8 位生存时间(Time To Live,TTL):数据报到达目的地的最大报文跳数,一般是 64。每次经过一个路由,TTL -= 1,一直减到 0 还没到达,那么就丢弃了。这个字段主要是用来防止出现路由循环。
  • 8 位协议:表示上层协议的类型。
  • 16 位头部校验和:使用 CRC 进行校验,来鉴别数据报的首部是否损坏,但不检验数据部分。
  • 32 位源地址和 32 位目标地址:表示发送端和接收端所对应的 IP 地址。
  • 选项字段:不定长,最多 40 字节。

1、IP 将报头与有效载荷进行分离(解包)& 向上交付(分用)

如何将报头与有效载荷分离?

首先要知道报文部分把选项除去一共是 20 字节(跟 TCP 一样)
IP 报头中有一个是 4 位首部长度,基本单位是四字节。
所以大小范围是【0~60】字节。根据计算,如果没有选项,那么就填写 0101(5 的二进制)。

既然报头和选项都能读取到了,总长度也知道了(16 位总长度),那么剩下的就是有效载荷了。

有效载荷 = 16位总长度 - 4位首部长度*4


向上交付给哪个协议?

IP 报头当中有一个字段叫做 8 位协议,该字段表示的就是上层协议的类型,IP 就是根据该字段判定应该将分离出来的有效载荷交付给上层的哪一个协议(TCP/UDP)的。

该字段是发送方的 IP 层从上层传输层获取到数据后填充的,比如是上层 TCP 交给 IP 层的数据,那么该数据在封装 IP 报头时的 8 位协议填充的就是 TCP 对应的编号。


2、8 位生存时间

报文在网络传输过程中,可能因为某些原因导致报文无法到达目标主机,比如报文在路由时出现了环路路由的情况,或者目标主机已经异常离线了,此时这个报文就成了一个废弃的游离报文。

为了避免网络当中出现大量的游离报文,于是在IP的报头当中就出现了一个字段,叫做 8 位生存时间)。8 位生存时间代表的是报文到达目的地的最大报文跳数,每当报文经过一次路由,这里的生存时间就会减一,当生存时间减为 0 时该报文就会被自动丢弃,此时这个报文就会在网络中消散。


3、32 位源 IP 地址和 32 位目的 IP 地址

数据想要发送到对端主机,要在路由器之间一跳一跳的过去,而路由器的转发就需要源 IP 和目的 IP。
当接收端收到了发送端发来的数据后,接收端可能也想要给发送端发送数据,因此发送端在发送数据时除了需要指明该数据的目的 IP 地址,还需要指明该数据的源 IP 地址,也就是发送端的 IP 地址。即便接收端收到数据后没有数据想要发送给发送端,但至少接收端需要向发送端发送一个响应报文,表明发送端发送的数据已经被接收端可靠的收到了,因此发送出去的数据除了需要指明该数据的目的 IP 地址,还需要指明该数据的源 IP 地址。


(1)绑定 socket

端口号的绑定其实是 TCP/UDP 层用来进行从操作系统向进程交付数据。
IP 地址就是绑定在 IP 层,用于数据在网络传输过程中的路由转发。

发送数据时,不需要指明发送数据的源 IP 地址和源端口号,因为传输层和网络层都是在操作系统内核当中实现的,数据在进行封装时操作系统会自行填充上对应的源 IP 地址和源端口号。


四、网段划分(重要)

1、子网划分的原因

用一个场景举例帮助理解:我们的入学学号不是随便给的,它包含了我们的入学年份、学院、班级等信息,假设学号只包含学院号 + 自己的编号。那么我们就可以通过学号知道你是哪个学院的,你是谁。
假设每个学院都有一个负责人,他也有一个自己的学号,每个学院的负责人都在一个大群里。现在张三的学生证丢了, 当张三的学生证被李四捡到了后,李四发现张三不是自己学院的人,但不知道张三到底是哪个学院的,那么李四就只能将这个学生证给学院的负责人:王五。因为王五清楚每一个学院的编号,所以他就可以将学生证交给对应学院的负责人:赵六,然后赵六就可以把学生证归还给张三。

我们将张三和李四叫做源主机目标主机,负责人们就是路由器,院里的群我们叫做局域网,把负责人的群叫做公网

每个同学都以一个学号(IP 地址),而且每个同学都得归类到每个学院。
类比到计算机中:互联网中的每一个主机都隶属于一个子网, 以方便定位到具体主机提高查找效率


2、IP 地址的划分

IP 地址分为两个部分:网络号(表征的是不同的区域)和主机号

  • 网络号:保证相互连接的两个网段具有不同的标识。在不同的查找过程中,不断变大且是收敛的。
  • 主机号:同一网段内,主机之间具有相同的网络号,但是必须有不同的主机号。

路由器连接了两个网段,一般主机标识都是 1。对于网络标识来说,同一网段内主机的网络标识是相同的,不同网段内主机的网络标识是不同的。而对于主机标识而言,同一网段内主机的主机标识是不同的,不同网段内主机的主机标识是可以相同的。

  • 不同的子网其实就是把网络号相同的主机放到一起。
  • 如果在子网中新增一台主机,则这台主机的网络号和这个子网的网络号一致,但是主机号必须不能和子网中的其他主机重复。

通过合理设置主机号和网络号就可以保证在相互连接的网络中,每台主机的 IP 地址都不相同。

可以看到一个子网内有很多主机,这些主机是由路由器管理的。实际上手动管理 IP 地址是一件非常麻烦的事情,当子网中新增主机时需要给其分配一个 IP 地址,当子网当中有主机断开网络时又需要将其 IP 地址进行回收,便于分配给后续新增的主机使用。


(1)DHCP 技术

对于 IP 地址的分配和回收,一般不会选择手动进行,而是采用 DHCP 技术。

DHCP 通常被应用在大型的局域网环境中,其主要作用就是集中地址管理、分配 IP 地址,使网络环境中的主机动态获得 IP 地址、Gateway 地址、DNS 服务器地址等信息,避免了手动管理 IP 的不便,并能够提升地址的使用率。
DHCP 是一个基于 UDP 的应用层协议,一般的路由器都带有 DHCP 功能,因此路由器也可以看作一个 DHCP 服务器。

当我们连接 WiFi 时,本质就是路由器就会给你动态分配了一个 IP 地址,然后就可以基于这个 IP 地址进行各种上网动作了。


3、分类划分法

过去曾经提出一种划分网络号和主机号的方案,把所有 IP 地址分为五类,如下图所示:

当要判断一个 IP 地址是属于哪一类时,只需要遍历 IP 地址的前 5 个比特位,第几个比特位最先出现 0 值,那么这个 IP 地址对应就属于 A、B、C、D、E 类地址。

这里就是把 IP 地址看成一个大蛋糕,按照 ABCDE 分成若干块,要多少 A 类或者 B 类自己去申请即可,这种划分方法就叫做分类划分法

随着 Internet 的飞速发展,这种划分方案的局限性很快显现出来,大多数组织都申请 类网络地址,导致 B 类地址很快就分配完了,而 A 类却浪费了大量地址。

例如,申请了一个 B 类地址,理论上一个子网内能允许 65536 个主机,A 类地址的子网内的主机数更多。然而实际网络架设中,不会存在一个子网内有这么多的情况,因此大量的 IP 地址都被浪费掉了。


(1)CIDR

针对上面这种情况,在分类划分法的基础上出现了一种新的划分方案,称为 CIDR(Classless Interdomain Routing):

  • 引入一个额外的子网掩码(subnet mask)来区分网络号和主机号
  • 子网掩码也是一个 32 位的正整数,通常用一串 "0" 来结尾。
  • 将 IP 地址和子网掩码进行 “按位与” 操作,得到的结果就是网络号
  • 网络号和主机号的划分与这个 IP 地址是 A 类、B 类还是 C 类无关。

可以给不同的路由器配置不同位数的子网掩码,这样就能看到不同的网络号了。

目的 IP & 当前路由器的子网掩码 = 该报文要去的目的网络

因为不同的路由器一定至少要级联 2 个网络,每一个网络的网络号可能是不同的。每个路由器都要给自己直接链接的网络配置对应的子网掩码。

目标网络和子网掩码是路由器内置好的。

此时一个网络就被更细粒度的划分成了一个个更小的子网,通过不断的子网划分,子网中 IP 地址对应的主机号就越来越短,因此子网当中可用 IP 地址的个数也就越来越少,这也就避免了 IP 地址被大量浪费的情况。

可见,IP 地址与子网掩码做与运算可以得到网络号,主机号从全 到全 就是子网的地址范围。

IP 地址和子网掩码还有一种更简洁的表示方法,例如 140.252.20.68/24,表示 IP 地址为 140.252.20.68,子网掩码的高 24 位是 1,也就是 255.255.255.0。

举例:在某一子网中将 IP 地址的前 24 位作为网络号,那么该网络对应的子网掩码的 32 个比特位中的前 24 位就为 1,剩下的 8 个比特位为 0,将其用点分十机制表示就是 255.255.255.0。
假设该子网当中有一台主机对应的 IP 地址是 140.252.20.68,那么将这个 IP 地址与该网络对应的子网掩码进行 “按位与” 操作后得到的就是 140.252.20.0,这就是这个子网对应的网络号。
实际在用子网掩码与子网当中主机的 IP 地址进行 “按位与” 操作时,本质就是保留了主机 IP 地址中前 24 个比特位的原貌,将剩下的 8 个比特位的值清 0 了而已,也就是将主机号清 0 了,那么子网地址范围就为:【140.252.20.0 ~ 140.252.20.255】


4、特殊的 IP 地址

不是所有的 IP 都会在公网中使用,有些 IP 地址是有特殊的作用的:

  • IP 地址中的主机地址全部设为 0,就成为了网络号,代表这个局域网。
  • IP 地址中的主机地址全部设为 1,就成为了广播地址,用于给同一个链路中相互连接的所有主机发送数据包。
  • 127.* 的 IP 地址用于本机环回测试,通常是 127.0.0.1


5、IP 地址的数量限制

IP 地址(IPv4)是一个 字节 32 位的正整数。那么一共只有 2^32 个 IP 地址,大概是 43 亿左右。

TCP/IP 协议规定,每个主机都需要有一个 IP 地址,这是否意味着一共只有 43 亿台主机能接入网络吗?
实际上, 由于一些特殊的  IP  地址的存在, 数量远不足  43  亿。 另外  IP  地址并非是按照主机台数来配置的, 而是每一个网卡都需要配置一个或多个 IP  地址。

CIDR 只是提高了利用率,在一定程度上缓解了 IP 地址不够用的问题(提高了利用率,减少了浪费,但是 IP 地址的绝对上限并没有增加),仍然不是很够用。


(1)解决 IP 地址不足问题

有三种方式:

  1. 动态分配 IP 地址:只给接入网络的设备分配 IP 地址。因此同一个 MAC 地址的设备,每次接入互联网中,得到的 IP 地址不一定是相同的。
  2. NAT 技术:能够让不同局域网当中同时存在两个相同的 IP 地址(私有 IP 和公网 IP 做转换),NAT 技术不仅能解决 IP 地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。
  3. IPv6:IPv6 用 16 字节 128 位来表示一个 IP 地址,能够大大缓解 IP 地址不足的问题。但  IPv6 并不是 IPv4 的简单升级版,它们是互不相干的两个协议,彼此并不兼容,因此目前  IPv6 还没有普及。

6、私有 IP 地址与共网 IP 地址

如果一个组织内部组建局域网,IP 地址只用于局域网内的通信,而不直接连到 Internet上。理论上 使用任意的 IP 地址都可以,但是 RFC 1918 规定了用于组建局域网的私有 IP 地址。

  • 10.*,前 8 位是网络号,共 16777216 个地址
  • 172.16. 到 172.31.,前 12 位是网络号,共 1048576 个地址
  • 192.168.*,前 16 位是网络号,共 65536 个地址。

包含在这个范围中的,都成为私有 IP,其余的则称为公网 IP(或全局 IP)

因为私有 IP 只能在局域网内出现,所以不同的子网内可能有相同的私有 IP,这样就解决了 IP 不足的问题

可以通过命令:ifconfig 来查看我们这台机器的私网 IP:

可以看到这个 IP 正好在第二种私网 IP 范围内。


7、LAN 口 IP 与 WAN 口 IP

家用路由器:

  1. 对内:面对自己构建的子网
  2. 对外:自己本身也是别人构建子网的一个主机

路由器是可以构建局域网的,而且一定是横跨两个子网。所以路由器至少会配置两个 IP,分别是 LAN 口(子网 IP)和 WAN 口:

  • 对内:LAN 口 IP:表示连接本地网络的端口局域网,主要与家庭网络中的交换机、集线器或 PC 相连。
  • 对外:WAN 口 IP:表示连接广域网的端口自己所在上级子网给自己分配的 IP,一般指互联网。
同一个局域网中,两台主机可以直接进行通信吗?

可以。

家里要上网,首先需要做什么?
  1. 首先需要有运营商在家附近,且家附近有网络覆盖
  2. 联系运营商进行光纤入户
  3. 工作人员上门,调制解配器(猫),无线路由器
  4. 开户,账号,密码,配置路由器(运营商认证的账号、密码)
  5. 配置路由器 —— 设置路由器的 WiFi 名称 + 密码(路由器认证的)
  6. 正常上网,按月/年交费
为什么我们平时使用微信、抖音、美团等 App,但却每个月都充话费给了运营商呢?

基础设施是运营商铺设的。

为什么我们无法访问 Google、fb 等网站呢?

收到了运营商的拦截。

  • 路由器 LAN 口连接的主机都从属于当前这个路由器的子网中。
  • 不同的路由器,子网 IP 其实都是一样的(通常都是 192.168.1.1),子网内的主机 IP 地址不能重复,但是子网之间的 IP 地址就可以重复了。
  • 每一个家用路由器,其实又作为运营商路由器的子网中的一个节点。这样的运营商路由器可能会有很多级,最外层的运营商路由器,WAN 口 IP 就是一个公网 IP 了。
  • 如果希望我们自己实现的服务器程序,能够在公网上被访问到,就需要把程序部署在一台具有外网 IP 的服务器上。这样的服务器可以在阿里云/腾讯云上进行购买。

路由器天然的会构建局域网(子网)

私有网络对应的 IP 是局部的,所以可以在不同的子网中出现重复(大大缓解 IP 不足的问题)

  1. 从运营商机房拉出的网线插在了家用路由器的 WAN 口上。
  2. 个人设备是插在家用路由器的 LAN 口上。

可以看到这两个运营商路由器现在属于一个子网中,如何让他们划分到不同的子网呢?

通过更改子网掩码即可。


8、数据包的转发流程(NAT 技术)

路由器要做的任务:

  1. 将报文中的源 IP 替换成路由器的 WAN 口 IP。
  2. 每经过一个运营商的内网路由器,都要做这个工作(公网路由不需要)。

由上图可知数据刚出来的时候并不是直接到公网上,而是先交给家用路由器,再交给运营商路由器做内网转发,转发到一定程度后再转发到公网,最后由公网到达目标服务器。

现在假设我们要从我们自己的主机访问目标主机:

首先我们自己的主机判断了目标 IP 并不在不在当前局域网,所以会把数据包直接转给家用路由器,家用路由器也发现目标 IP 并不在不在当前局域网,所以就交给运营商路由器,运营商路由器直接连在公网,IP 是唯一确认的,所以就找到了目标服务器,把数据包交给目标服务器即可。

但这样就会出现两个问题:

  1. 私有 IP 不能出现在公网上,目标服务器收到的 src 就是个私有 IP。
  2. 因为私有 IP 在不同的子网里都有,那么响应要交给谁呢?

所以真实的转发场景如下:当数据包到达家用路由器的时候,路由器会把源 IP 替换成 WAN 口 IP

对外路由器(运营商路由器)收到报文后,也会重复上面的操作:

然后就可以把这个替换过的报文转发给目标服务器。此时往回响应的时候 src 就是运营商路由器,这样就是到返回到哪个子网了

子网内的主机需要和外网进行通信,在转发报文时,路由器不断地将源 IP 首部中的 IP 地址在不同内网、不同层级的网络节点中转发,替换(替换成 WAN 口 IP),最终数据包中的 IP 地址成为一个公网 IP,这种技术称为 NAT(Network Address Translation,网络地址转换)。


五、路由

1、数据路由过程

在复杂的网络结构中找出一条通往终点的路线。

举例帮助理解:假设在信息不发达的年代,我们来到了一个陌生的地方,当前只知道目的地名,那该如何到这个地方呢?最好的解决办法就是询问路人,那么这个人要么自己不知道但可以告诉我们该问谁,要么知道具体位置,要么他所处的地方就是目的地,那就不需要找了。

我们自己就是数据包,目的地就是目标 IP,问的路人就是路由器,而路人思考的过程就是查路由表路由的过程就是这样一跳一跳(Hop By Hop)“问路” 的过程。所谓 “一跳” 就是数据链路层中的一个区间,具体在以太网中指从源 MAC 地址到目的 MAC 地址之间的帧传输区间。

决定将数据交付给下一跳路由器时,下一跳路由器一定和我在同一个局域网。

“一跳一跳” 的过程叫作局域网(子网)转发。宏观上而言,我们的网络本质就是一个个子网构成的。

IP 数据包的传输过程也和问路一样,这个过程中会遇到很多路由器,这些路由器会帮助数据包进行路由转发,每当数据包遇到一个路由器后,对应路由器都会查看该数据的目的 IP 地址,路由器决定这个数据包是能直接发送给目标主机,还是需要发送给下一个路由器。依次反复,一直到达目标 IP 地址。

如何判定当前这个数据包该发送到哪里呢?
这个就依靠每个节点内部维护一个路由表。

路由器的查找结果可能有以下三种:

  1. 路由器经过路由表查询后,得知该数据下一跳应该跳到哪一个子网。
  2. 路由器经过路由表查询后,没有发现匹配的子网,此时路由器会将该数据转发给默认路由。
  3. 路由器经过路由表查询后,得知该数据的目标网络就是当前所在的网络,此时路由器就会将该数据转给当前网络中对应的主机。


2、路由表

  • 每个路由器内部会维护一个路由表,在 Windows 下可以用命令:route PRINT 查看路由表


  • Linux 下,可以通过命令:route 查看云服务器上对应的路由表

如果目的 IP 命中了路由表,就直接转发即可。

路由表中的最后一行主要由下一跳地址和发送接口两部分组成,当目的地址与路由表中其它行都不匹配时,就按缺省路由条目规定的接口发送到下一跳地址。

  • Destination:目的网络地址。

  • Gateway:下一跳地址。

  • Genmask:子网掩码。

  • FlagsU 标志表示此条目有效(可以禁用某些条目),正在使用;G 标志表示此条目的下一跳地址是某个路由器的地址,默认网关(路由器)。没有 G 标志的条目表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发。

  • Iface:发送接口。

这台主机有两个网络接口:一个网络接口连到 192.168.10.0/24 网络,另一个网络接口连到 192.168.56.0/24 网络。

如果要发送的数据包的目的地址是 192.168.56.3 呢?
跟第一行的子网掩码做与运算得到 192.168.56.0,与第一行的目的网络地址不符。再跟第二行的子网掩码做与运算得到 192.168.56.0,正是第二行的目的网络地址,因此从 eth1 接口发送出去。由于 192.168.56.0/24 正是与 eth1 接口直接相连的网络,因此可以直接发到目的主机,不需要经路由器转发。
如果要发送的数据包的目的地址是 202.10.1.2 呢?
依次和路由表前几项进行对比,发现都不匹配。按缺省路由条目,从 eth0 接口发出去,发往 192.168.10.1 路由器,由 192.168.10.1 路由器根据它的路由表决定下一跳地址。

(1)查询路由表过程

当 IP 数据包到达路由器时,遍历路由表的每一个条目,拿着目的 IP 依次与路由表中的子网掩码 Genmask 进行 “按位与” 操作,来确定该报文要去的目标网络,对比运算结果与目标网络 Destination,如果一样,将该数据包通过对应的发送接口 Iface 发出。

如果都没有匹配上目标网络 Destination,此时路由器就会将这个数据包发送到默认路由,也就是路由表中目标网络地址中的 default。


(2)总结

  1. 遍历路由表。

  2. 目的 IP & 子网掩码,找到要去的目标网络,没找到就走默认网关。

  3. 通过 Iface 发送。

注意:IP 没有解决设备转发的具体功能,IP 提供的是转发的策略,核心不是转发,而是路径选择。


3、路由表生成算法(选学)

路由表可以由网络管理员手动维护( 静态路由), 也可以通过一些算法自动生成( 动态路由)。一些相关的生成算法, 例如距离向量算法, LS  算法, Dijkstra  算法等。

六、分片与组装

在路由器之间传递的确实是 IP 报文,但真正在网线上跑的是 MAC 帧,MAC 帧是数据链路层的协议。


1、最大传输单元 —— MTU

MAC 帧作为数据链路层的协议,它会将 IP 传下来的数据封装成数据帧,然后发送到网络当中。但 MAC 帧携带的有效载荷的最大长度是有限制的,也就是说 IP 交给 MAC 帧的报文不能超过某个值,也就是链路层一次可以转发到网络的报文大小的限制,这个值就叫作最大传输单元MTU,可修改),大小一般是 1500 字节。


但是 IP 也不能决定单个报文的大小,在网络中决定报文大小的是 TCP。
所以 IP 层自己想了个办法:如果 IP 层要传送的数据超过了 1500 字节,那么就需要先在 IP 层对该数据进行分片,然后再将分片后的数据交给下层 MAC 帧进行发送。

发送方的 IP 层负责分片,接收方的 IP 层负责封装。

而分片是通过 IP 协议报头的这三个字段完成的:

  • 16 位标识:唯一的标识主机发送的报文,如果数据在 IP 层进行了分片,那么每一个分片对应的 id 都是相同的。
  • 3 位标志字段:第一位保留,表示暂时没有规定该字段的意义。第二位表示禁止分片,表示如果报文长度超过 MTU,IP 模块就会丢弃该报文。第三位表示 “更多分片”,如果报文没有进行分片,则该字段设置为 0,如果报文进行了分片,则除了最后一个分片报文设置为 0 以外,其余分片报文均设置为 1。
  • 13 位片偏移:分片相对于原始数据开始处的偏移,表示当前分片在原数据中的偏移位置,实际偏移的字节数是这个值 ×8 得到的。因此除了最后一个报文之外,其他报文的长度必须是 8 的整数倍,否则报文就不连续了。
如何识别报文和报文之间的不同?

通过 16 位标识,不同的报文标识不同,相同报文的分片具有相同的标识。

一个报文没有被分片的标志是什么?

更多分片的标志位是 0,13 位片偏移也为 0。

if(报文 -> 更多分片 & 0x1) return 分片的
else if(报文 -> 片偏移 > 0) return 分片的
else return 独立的报文

2、分片流程

如何识别出哪些分片是开始,哪些是中间,哪些是结尾?
  • 开始:更多分片 1,片偏移 0。
  • 中间:更多分片 1,片偏移不是 0。
  • 结尾:更多分片 0,片偏移不是 0。
中间报文有多个,那么如何保证收全了?

根据偏移量进行升序排序,结合 偏移量 + 自身大小 = 下一个报文的偏移量 扫描整个报文,如果不匹配,中间一定会有丢失,如果成功计算到结尾,就一定收取完整了。

假设 IP 层要发送 2980 字节大小的数据,如果 IP 报头不含选项字段,那么加上报头就是 3000 字节,超过了最大传输单元,需要切分。这里要注意每个切分下来的都是纯数据,每部分数据都要添加报头。可以先切下 1500 字节(含原始的报头),然后还剩下 1500 的纯数据,再切下 1480 的纯数据,加上 20 字节的报头,刚好构成 1500 字节,还剩下 20 字节的纯数据,把这 20 个字节纯数据也加上报头,就是 40 字节。最终就分成了三部分,总长度分别为:1500,1500,40。

接下来就填写三个报头的三个字段:

分片之前,一定是一个独立的 ip 报文。

分片之后,不能只进行直接分片,然后让报头跟着第一个报头就行(错误❌),为了支持组装,所以是每一个分片都要有 IP 报头(正确


3、组装流程

通过 16 位标识来确定这些报文曾经属于一个报文,通过更多分片加上 13 位片偏移来确定该报文有没有被切分以及首尾以及顺序。

先找到分片报文中 13 位片偏移为 0 的分片报文,然后提取出其 IP 报头当中的 16 位总长度字段,通过计算即可得出下一个分片报文所对应的 13 位片偏移,按照此方式依次将各个分片报文拼接起来。
直到拼接到一个 “更多分片” 标志位为 0 的分片报文,此时表明分片报文组装完毕。


4、分片的影响(坏处)

分片行为不是主流,在网络通信里,严重推荐不分片。原因如下:

  1. 在网络层分片和组装的过程,上层(传输和应用)并不知道。
  2. 分片会增加丢包的概率。因为一个报文被切成了多个报文,只要有一个报文丢失了就会造成拼接失败,因为不知道是哪个报文丢了(TCP 不关心分片),而网络层有校验和,如果报文丢弃了,就导致传输层(比如 TCP,而 UDP 就直接丢包了,对 UDP 的影响更大)重传,对整个报文进行重传。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1698847.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DBAPI怎么进行数据格式转换

DBAPI如何进行数据格式的转换 假设现在有个API,根据学生id查询学生信息,访问API查看数据格式如下 {"data":[{"name":"Michale","phone_number":null,"id":77,"age":55}],"msg"…

JVM学习-Class文件结构②

访问标识(access_flag) 在常量池后,紧跟着访问标记,标记使用两个字节表示,用于识别一些类或接口层次的访问信息,包括这个Class是类还是接口,是否定义为public类型,是否定义为abstract类型,如果…

[vue error] vue3中使用同名简写报错 ‘v-bind‘ directives require an attribute value

错误详情 错误信息 ‘v-bind’ directives require an attribute value.eslintvue/valid-v-bind 错误原因 默认情况下,ESLint 将同名缩写视为错误。此外,Volar 扩展可能需要更新以支持 Vue 3.4 中的新语法。 解决方案 更新 Volar 扩展 安装或更新 …

Java 泛型基础

目录 1. 为什么使用泛型 2. 泛型的使用方式 2.1. 泛型类 2.2. 泛型接口 2.3. 泛型方法 3. 泛型涉及的符号 3.1. 类型通配符"?" 3.2. 占位符 T/K/V/E 3.3. 占位符T和通配符?的区别。 4. 泛型不变性 5. 泛型编译时擦除 1. 为什么使用泛型 Java 为…

Django 里的静态资源调用

静态资源:图片,CSS, JavaScript 一共有两种方法 第一种方法 在项目的文件夹里创建名为 static 文件夹 在该文件夹里,添加静态资源 在 settings.py 里添加路径 import os# Static files (CSS, JavaScript, Images) # https://docs.djan…

Git基础命令:带图整理

基础命令 Git 安装 Git下载地址 https://git-scm.com/downloads Git安装(Window/Mac) 选择不同系统安装包安装 检验是否安装成功 出现Git Bash命令行工具或Git GUI工具git --version 查看git安装版本 Git 结构 工作区(Working Direct…

干货收藏 | 掌握ChatGPT提示词的精髓:从小白到高手!!

前言 提示决定了 ChatGPT 的输出。也就是说:GPT 生成的答案质量,完全取决于你“问它”,以及“引导它”的方式,如果你能问得好,引导的好,那么它就会帮你生成让你惊喜的答案,反之则无价值&#x…

国际版Tiktok抖音运营流量实战班:账号定位/作品发布/热门推送/等等-13节

课程目录 1-tiktok账号定位 1.mp4 2-tiktok作品发布技巧 1.mp4 3-tiktok数据功能如何开通 1.mp4 4-tiktok热门视频推送机制 1.mp4 5-如何发现热门视频 1.mp4 6-如何发现热门音乐 1.mp4 7-如何寻找热门标签 1.mp4 8-如何寻找垂直热门视频 1.mp4 9-如何发现热门挑战赛 1…

从垃圾识别到收集器:详细聊聊Java的GC

个人博客 从垃圾识别到收集器:详细聊聊Java的GC | iwts’s blog 前言 聊GC,自然离不开JVM内存模型,建议先了解JVM内存模型相关内容,或者最起码了解堆相关的内容,GC主要处理的就是堆。 这里会从垃圾识别算法->GC算法->JV…

OWASP top10--SQL注入(二)

目录 06:SQL注入提交方式 6.1、get提交 6.2、post提交 6.3、cookie提交 6.4、HTTP Header头提交 07:注入攻击支持类型 7.1、union注入: 7.1.1、union操作符一般与order by语句配合使用 7.1.2、information_schema注入 7.2、基于函数…

【云原生--K8S】K8S python接口研究

文章目录 前言一、搭建ubuntu运行环境1.运行ubuntu容器2.拷贝kubeconfig文件二、python程序获取k8s信息1.获取node信息2.获取svc信息3.常用kubernetes API总结前言 在前面的文章中我们都是通过kubectl命令行来访问操作K8S,但是在实际应用中可能需要提供更方便操作的图形化界面…

数据结构和算法|排序算法系列(二)|冒泡排序

首先需要你对排序算法的评价维度和一个理想排序算法应该是什么样的有一个基本的认知: 《Hello算法之排序算法》 主要内容来自:Hello算法11.3 冒泡排序 我觉得冒泡排序非常有意思,也非常简单,就是不停地交换相邻的元素即可&#…

c语言IO

前言 老是忘记c语言IO操作,故写个文章记录一下 打开文件 fopen FILE *fopen(const char *path, const char *mode);mode 返回值 如果文件成功打开,fopen 返回一个指向 FILE 结构的指针。如果文件打开失败(例如,因为文件不存…

SERVER ——查询(二)

目录 5. top 6. null 7. order by 8. 模糊查询: 9. 聚合函数 5. top top查询:查询表的前几行;下面是代码演示: --top(前面的几个记录) select top 2 * from emp; --查询表的前两列 select top 20 percent *…

解密论文评审过程:SCI论文是匿名送审的吗?

我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 前几天有位学员问我,审稿人能看见我论文的作者和单位信息吗?应该是双方都匿名才更公平啊。 同行评议,在不同的期刊操作还真不一样。有双方…

python数据分析——数据可视化(图形绘制)

数据可视化(图形绘制基础) 前言一、图形绘制基础Matplotlib简介使用过程sin函数示例 二、常用图形绘制折线图的绘制plot示例 散点图的绘制scatter()示例 柱状图的绘制bar示例 箱型图绘制plot.box示例 饼状图的绘制pie示例 三、图形绘制的组合情况多个折线…

代码随想录|Day55|动态规划 part15|● 392.判断子序列 ● 115.不同的子序列

392.判断子序列 class Solution: def isSubsequence(self, s: str, t: str) -> bool: dp [[0] * (len(t) 1) for _ in range(len(s) 1)] for i in range(1, len(s) 1): for j in range(1, len(t) 1): if s[i - 1] t[j - 1]: dp[i][j] dp[i - 1][j - 1] 1 else: dp[i…

3、xss-labs之lecel3

1、测试 开始页面&#xff0c;传入<script>alert(1)</script>&#xff0c;不出意外没有弹窗 2、按住CTRLU&#xff0c;查看返回的前端代码 3、看后端源码 htmlspecialchars() 是一个 PHP 函数&#xff0c;用于将特殊字符转换为 HTML 实体。比如 & &#xff08…

访问元组元素

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在Python中&#xff0c;如果想将元组的内容输出也比较简单&#xff0c;可以直接使用print()函数即可。例如&#xff0c;要想打印上面元组中的untitle…

Java核心:注解处理器

Java提供了一个javac -processor命令支持处理标注有特定注解的类&#xff0c;来生成新的源文件&#xff0c;并对新生成的源文件重复执行。执行的命令大概是这样的: javac -XprintRounds -processor com.keyniu.anno.processor.ToStringProcessor com.keyniu.anno.processor.Po…