IP网络层

Linux socket 内核结构体关系

自上而下梳理调用链条:

  1. task_struct(进程) 每个进程,里面有 files_struct,保存这个进程所有打开的文件描述符。

  2. files_struct → fd_array\[\] 文件描述符数组。socket 也是文件,socket()系统调用,分配一个 fd,数组里存struct file*

  3. struct file 一切打开的文件都用这个结构体。socket 作为特殊文件,private_data指向 struct socket

  4. struct socket socket 层,系统调用相关接口API(eg:send() recv() accept())。 里面成员:struct sock *sk,指向真正网络协议层对象。

struct sock 才是真正内核网络对象,socket 是上层封装给系统调用用的壳子。

5.struct sock

发送队列、接收队列、TCP 序号、拥塞控制、三次握手、重传、滑动窗口,全部在这里。

C 没有类继承,用结构体嵌套实现继承: struct inet_sock 包含 struct sockstruct tcp_sock 包含 struct inet_connection_sock;一层套一层。(看图)

  • struct inet_sock:IP 层信息,源 IP、目的 IP 等。
  • struct inet_connection_sock:面向连接(TCP)专用(udp没有这一层,udp不需要建立连接)。
  • struct tcp_sock:TCP 专属,存序号、窗口、重传、拥塞控制全部 TCP 细节。

struct sock 实现多态,体现在哪里

多态体现在两点

1. 指针强制转换

拿到 struct sock *sk,可以直接强转成子类:struct tcp_sock *tp = (struct tcp_sock *)sk;或者

struct udp_sock *tp = (struct udp_sock *)sk;同一个基类指针,实际指向不同子类对象,这就是多态。

2. 函数指针(虚函数表)

struct sock里面存一堆函数指针,比如: sk->sk_sendmsgsk->sk_recvmsg

  • TCP 创建 socket 的时候,把这些函数指针赋值成 TCP 版本函数;
  • UDP 创建 socket 的时候,赋值成 UDP 版本函数。

内核统一调用 sk->sk_sendmsg(sk,...)不用管是 TCP 还是 UDP,自动跑对应协议的函数。

串起来的流程

用户调用read/recv(fd,...) 进程 task_struct → files_struct → fd 找到 struct file → private_data 拿到 struct socket → socket 的 sk 指针找到 struct sock。 内核就访问 sk->sk_receive_queue,把队列里的 sk_buff 数据拷贝到用户空间。

udp结构 (没有struct inet_connection_sock这一层)

IP网络层

1.网络层 vs 传输层

  • IP(网络层)= 只有 "能力" IP 只管:把数据包从一台主机送到另一台主机。 只能做到尽力交付,不保证可靠,会丢包、乱序、重复,
  • TCP(传输层)= 负责 "策略" 在源主机、目的主机两端做:确认、重传、序号、流量控制,实现 100% 可靠交付

一个有策略一个有能力,就能把数据100%可靠的从一个源主机发到目标主机

ip协议和核心作用和构成

  1. IP 协议作用 提供尽力交付 的能力:把数据报从 A 主机,跨多个网络送到 B 主机。

只是 "很大概率送到",不保证可靠,会丢包、乱序、重复。

2.解决的问题:主机到主机

  • IP 地址用来唯一标识一台主机。
  • IP 地址 = 目标网络号 + 目标主机号
  • 路由本质:数据包从一个子网,转发进入另一个子网。

对比:

  • IP(网络层):主机 → 主机,靠 IP 地址。
  • TCP/UDP(传输层):进程 → 进程,靠端口号。

3.几个名词

  • 路由器 :配 IP 地址,专门做路由转发;工作在网络层

现代路由器本身是小型计算机,也可以跑应用层程序,但转发数据包核心逻辑只到网络层。即不会再向上解析传输层 TCP/UDP 头部,更不会交给本机应用层程序处理,转发流程在底层就完成。

  • 节点:一个路由器,一个主机都可以叫做节点

IP协议头格式

类比:两地之间选什么样交通方式,根据业务需求选择走那一条路。

  • 4 位版本号 version 指定 IP 协议版本,IPv4:0100;IPv6:0110

  • 4 位头部长度 header length 单位:4 字节。 普通 IP 头部 20 字节:20/4 = 5,该字段填 5;最大值 15,对应 IP 头部最大 60 字节。

  • 8 位服务类型 TOS 3 位优先权(已废弃) + 4 位 TOS + 1 位保留位(必须 0)。 4 个 TOS 选项:最小延时、最大吞吐量、最高可靠性、最小成本,互斥只能选一个。

  • ssh/telnet:看重最小延时

  • ftp:看重最大吞吐量

  • 16 位总长度 total length 整个 IP 数据报(头部 + 数据)总字节数。

  • 16 位标识 id 标记主机发出的 IP 报文。一个报文被分片之后,所有分片拥有相同 id

  • 3 位标志字段第 1 位:保留。第 2 位:禁止分片位。置 1 禁止分片;报文超过 MTU 直接丢弃。第 3 位:更多分片。最后一片置 0,其余分片置 1,相当于结束标记。

  • 13 位分片偏移 fragment offset 表示分片在原始报文的位置。 实际字节偏移 = 该字段 × 8。 除最后分片外,其余分片数据长度必须是 8 字节整数倍。

  • 8 位生存时间 TTL 最大跳数,常见初始值 64。 每经过一台路由器,TTL‑1;减为 0 还没到达目的地,报文直接丢弃。 主要是用来防防止出现路由循环

  • 8 位协议 标识上层是什么协议,例如 tcp、udp。

  • 16 位头部校验和 CRC 校验,只校验 IP 头部,检测头部是否损坏。

  • 32 位源地址、32 位目的地址 发送方 IP、接收方 IP。

  • 选项字段 长度不定,最多 40 字节,一般省略。

同样2个问题

分用问题8位协议字段。IP 收到报文,剥掉 IP 头,看这个值,确定把数据交给上层 TCP/UDP/ICMP哪一个。

**解包问题:**根据4位头部长度,确定头部有多长,data指针偏移这么多就是数据开始部分,并且还有16 位总长度 ,减去4位头部长度,还知道数据一共有多长

理解子网划分

把整个互联网类比成一所大学:

  • IP 地址 = 学号:院号+学号
  • 子网(网络号) = 学院编号(计算机学院、理学院、机械学院...)
  • 主机号 = 学院里面的学生个人编号

eg:06 321 (06代表机械学院,321代表自己的学号,02理学院,03经管学院......)

  • 06网络号(机械学院)
  • 321主机号(李四本人,学院内部编号)

IP = 目标网络号 + 目标主机号

找目标主机,先找目标网络(学院),再找网络内部主机(学生)

校园例子理解转发

  • 张三在计算机学院( 01) 捡到了机械学院李四(子06)的钱包
  • 钱包上写学号06321
  • 张三发现目标学号不属于本院,01才是计算机学院,06不是,但他单单知道不是本学院,不清楚是哪一个具体的学院(理学院,材料.....) 所以交给本院的院学生会主席,再由他发信息到校群询问 不走内网转发,交给出入口路由器,子网转发
  • 机械学院的院学生会主席看到后发现06是本学院的标识,就再在机械学院的群里找到321对应的同学

理解对应关系

校园故事 网络中的对应概念
张三主机,在计算机学院(01) 源主机,处在源子网01
钱包上面写学号06321 IP 报文头部写目标 IP 地址(网络号 06 + 主机号 321)
张三对比:目标学院06≠自己学院01不在同一个学院 源主机拿目标 IP,和自己子网掩码做运算,得到目标网络号,和本机网络号对比:不在同一个子网
张三不知道机械学院具体在哪,自己送不到,交给本院学生会主席(出入口) 不能内网转发,交给路由器
学生会主席在校园主干(校群)转发,跨学院传递钱包 公网转发 。一路上各个路由器只看网络号 06 ,不关心主机号 321,一跳一跳转发
钱包到达机械学院,机械学院学生会主席看到06是本院编号 数据包抵达目标子网;路由器识别目标网络号和自己接口网络号匹配,即子网匹配
在机械学院内部,根据321找到李四同学 本子网内,根据主机号 321,交付给最终目标主机李四

相关概念:

内网转发(本子网内转发)

源主机和目标主机在同一个子网(同一个学院),不需要走出本学院,不经过外部路由器,学院内部直接交付

公网转发(跨子网转发)

源主机和目标主机不在同一个子网(不同学院) ,本学院找不到目标主机,把数据包交给出入口路由器,跨多个子网转发。路上只看网络号,抵达目标子网才看主机号

为什么要做子网划分(为什么大学要分学院)

子网划分其实就是给网络预先做分组编号。

  1. 如果不划分子网,全网所有主机全部平铺在一起。找一台主机就要在全部主机里线性遍历查找效率极低。
  2. 子网划分相当于预先分组 。路由查找本质是淘汰过程 :先把大量无关子网直接淘汰掉(比如要找01学院的我就可以直接淘汰02,03等等其他学院的),只留下目标所在子网,再在子网内部找主机,大幅提升查找效率。(先找学院再找学生)

网段划分

路由器

  1. 核心能力:连接多个不同子网(构建子网) 路由器有多个网络接口每个接口配一个 IP,每个 IP 属于不同子网

图中:一个接口192.168.128.1(属于 128 网段),另一个接口192.168.144.1(属于 144 网段)。 这两个 IP,就是各自子网的网关地址

网关地址就是路由器接入对应本子网的网口的 IP值。

  • 192.168.128.10 这台机器,网关填192.168.128.1
  • 192.168.144.11 这台机器,网关填192.168.144.1

内网路由:

所以加上掩码和网关的话上面的内网路由(192.全都是内网,不涉及公网转发)完整过程:

源:192.168.128.10,目标:192.168.144.11

  1. 源主机做分别那源ip和目标ip做掩码运算,发现目标网络号(192.168.144.0)和自己的网络号(192.168.128.0)不在同一个子网。
  2. 不能内网转发,数据包交给网关192.168.128.1(路由器)。
  3. 路由器收到包,从另一个接口192.168.144.1转发出去。
  4. 到达192.168.144.0/24子网,根据主机号找到192.168.144.11主机

DHCP 协议

为什么要有: 如果没有 DHCP,就需要手动静态填写 IP、掩码、网关,家里设备多会很麻烦,还容易 IP 冲突。

  1. 协议层级:应用层协议,运行在路由器上面。

我们电脑手机的内网 IP,不是自己写死的,是 DHCP 服务器分配来的

2.干什么 主机刚接入网络,自己没有 IP。主机向全网广播 DHCP 请求,路由器的 DHCP 服务收到请求,给主机分配:

  • IP 地址
  • 子网掩码
  • 网关地址(路由器接口 IP)
  • DNS 服务器地址

拿到这套参数,才可以正常上网。

DHCP就是路由器在应用层的体现,路由器上运行着应用层的 DHCP 服务

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

随着 Internet 的飞速发展,这种划分方案的局限性很快显现出来

  • A 类:一个大网,主机位 24 位,一个网络可以有一千六百多万台主机
  • B 类:主机位 16 位,一个网络最多 65534 台主机(6 万 5 千多)
  • C 类:主机位 8 位,一个网最多 254 台主机

现实问题: 几乎没有哪家公司,一个子网里面真的放 6 万台机器。 很多企业规模就几千台设备,想要比 C 类(254 台)大一点的网段,只能申请一整个 B 类地址

申请一个 B 类,直接拿到 65534 个主机 IP。但公司实际只用 2000 个,剩下六万多个 IP 直接闲置,别人不能用,就白白浪费。 A 类网络更恐怖,一个 A 类一千六百多万地址,绝大多数机构根本用不完,大量地址被占住闲置。

结果:

  • B 类地址被疯狂申请,很快全部耗尽;
  • A 类很多网络拿在手里,大片地址空着,没法回收分给别人。

这就是有类 IP 划分的巨大缺陷:网络大小只能固定三档(A/B/C),不能灵活选网段大小,造成 IP 大量浪费

针对这种情况提出了新的划分方案,称为 CIDR (Classless Interdomain Routing)(无类别域间路由)

CIDR

CIDR 就是干掉 A/B/C 这个死板分类,可以自由规定网络位多长。

CIDR 写法:IP / 前缀长度

例子:192.168.1.0 /22 /22前 22 位是网络位,剩下 32‑22 =10 位是主机位

简单计算

IPv4 一共 32 位

  • /24:网络 24 位,主机 8 位 → 和传统 C 类一样
  • /16:网络 16 位,主机 16 位 → 和传统 B 类一样
  • /8:网络 8 位,主机 24 位 → 和传统 A 类一样

但还可以写 /21 /22 /23,这是 ABC 分类做不到的(靠子网掩码实现)

子网掩码和 CIDR 的对应

CIDR 前缀 子网掩码
/24 255.255.255.0
/23 255.255.254.0
/22 255.255.252.0
/16 255.255.0.0

理解子网掩码

网络号=子网掩码&IP地址

IP 固定:140.252.20.68,这是一个 B 类 IP。

例子 1:子网掩码 255.255.255.0

掩码二进制:11111111.11111111.11111111.00000000

  • 前面 24 个1:网络位
  • 末尾 8 个0主机位(主机号)

①求网络号(网络地址):IP & 掩码(按位与)

只看最后 1 字节: IP 最后 1 字节:680100 0100 掩码最后 1 字节:00000 0000

复制代码
    0100 0100
&   0000 0000
------------------------------------------
    0000 0000 → 0

网络号:140.252.20.0

主机号全部为 0 → 网络地址,代表这个子网本身,不能分配给电脑

②求广播地址:把全部主机号置 1

主机位是后面 8 位,全部写 1:11111111 =255 广播地址:140.252.20.255

主机号全部为 1 → 广播地址,用于广播,不能分配给电脑

③子网范围

网络号 ~ 广播地址140.252.20.0 ~ 140.252.20.255

④可用主机数量

主机位 8 位:2^8-2 = 254台 减 2:扣掉【主机全 0 的网络地址】、【主机全 1 的广播地址】 真正能用 IP:140.252.20.1 ~ 140.252.20.254


例子 2:子网掩码 255.255.255.240

掩码二进制:11111111.11111111.11111111.11110000

  • 前面 28 个1:网络位
  • 末尾 4 个0主机位(主机号)

①求网络号(按位与 &)

IP 末尾字节680100 0100 掩码末尾字节2401111 0000

复制代码
    0100 0100
&   1111 0000
------------------------------------------
    0100 0000 → 64

网络号:140.252.20.64

这里主机 4 位全部是 0,网络地址,不给主机用。

②求广播地址:全部主机号置1

网络号末尾字节:0100 0000 把后面 4 位主机号改成 1:0100 1111 =79 广播地址:140.252.20.79

主机 4 位全部是 1,广播地址,不给主机用。

③子网范围

140.252.20.64 ~ 140.252.20.79

可用主机数量

主机位 4 位:2^4-2 =14台 真正能用 IP:140.252.20.65 ~ 140.252.20.78

主机号全 0、全 1 是什么

  1. 主机号全部比特为 0 :网络地址(网段号),用来标识这整个子网,不能分配给主机
  2. 主机号全部比特为 1 :广播地址,向本子网所有设备发广播数据包,不能分配给主机

子网掩码的作用(结合两张图理解)

可以通过设置掩码二进制中 1 的个数,重新设置网络号,充分利用 IP 地址资源

同样 IP:140.252.20.68

  • 掩码255.255.255.0(24 个 1) → 网络号140.252.20.0,子网很大,256 个 IP
  • 掩码255.255.255.240(28 个 1) → 网络号140.252.20.64,子网很小,16 个 IP
  1. 改变掩码里面1的数量,同一个 IP 算出来的网络号直接变,子网大小就被我们人为切割
  • 1 越多 → 网络位变长,主机位变短,子网就越小(即一个子网内的主机数少)。
  • 靠子网掩码切出很多小小的子网,需要多大切多大,剩下 IP 留给别的子网使用,IP 就不会浪费 。相当于是动态的形式了,不是以前固定写死了的类别

IP 地址的数量限制

我们知道,IP 地址 (IPv4) 是一个 4 字节 32 位的正整数。那么一共只有 2 的 32 次方 个 IP 地址,大概是 43 亿左右。而 TCP/IP 协议规定,每个主机都需要有一个 IP 地址。

这意味着,一共只有 43 亿台主机能接入网络么?

实际上,由于一些特殊的 IP 地址的存在,数量远不足 43 亿;另外 IP 地址并非是按照主机台数来配置的,而是每一个网卡都需要配置一个或多个 IP 地址。

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

  • 动态分配 IP 地址:只给接入网络的设备分配 IP 地址。因此同一个 MAC 地址的设备,每次接入互联网中,得到的 IP 地址不一定是相同的;
  • NAT 技术 (后面会重点介绍);
  • IPv6: IPv6 并不是 IPv4 的简单升级版。这是互不相干的两个协议,彼此并不兼容;IPv6 用 16 字节 128 位来表示一个 IP 地址;但是目前 IPv6 还没有普及

私有 IP & 公网 IP

私有 IP(内网 IP,局域网用)3 段固定范围

  1. 10.*
  2. 172.16.* ~ 172.31.*
  3. 192.168.*

私有 IP只能局域网内部通信,不能跑到互联网(公网)上面传输。(后面解释)

公网 IP(全局 IP)

除上面三段以外,其余全部是公网 IP。 公网 IP 全世界唯一,互联网上通信必须用公网 IP。

NAT过程

核心规则:每经过一台做 NAT 的路由器,就把报文的src源IP改成这台路由器自己 WAN 口对外 IP,目的 IP 全程不变

完整流程:

家里电脑发出原始包

电脑:192.168.1.201,访问抖音122.77.241.3

复制代码
src:192.168.1.201     dst:122.77.241.3

源是内网私有 IP。

2、经过自家家用路由器 NAT

路由器 WAN 口 IP:10.1.1.2,把源 IP 替换成 WAN 口 IP

复制代码
src:10.1.1.2      dst:122.77.241.3

私有 IP192.168.1.201从 IP 头部消失,路由器内部 NAT 表记录:哪个连接对应家里哪台主机。

3、再经过运营商路由器(又一层 NAT)

运营商路由器 WAN 口公网 IP:122.77.241.4,再次改写源 IP

复制代码
src:122.77.241.4     dst:122.77.241.3

现在报文带着公网源 IP,进入广域网发给抖音服务器。

抖音服务器收到包,只看见源是公网122.77.241.4,完全看不见原始内网192.168.1.201

4、抖音回复报文(回程)

抖音发回来的报文:

复制代码
src:122.77.241.3    dst:122.77.241.4

目标地址是运营商公网 IP。 互联网路由根据目标 IP,一定会把这个包送到这个公网 IP 所属的运营商路由器

运营商查自己 NAT 转换表,把目标 IP 改回10.1.1.2,转发给你家路由器; 你家路由器再查 NAT 表,目标改回内网192.168.1.201,内网转发交给电脑。

为什么一定要NAT 地址转换

因为内网 IP 可能重复(内网 IP 可以重复;但是同一个子网内部 IP 不能重复。)

A 家电脑:192.168.1.201;B 家电脑也可以是 192.168.1.201 ,IP 一模一样。数据包根本无法正确回送根本不知道谁是哪一个,通信直接报废。(也解释了私有 IP不能直接在公网路由转发的原因)

所以靠 NAT 转换,把重复的内网 IP 换成独一无二的公网 IP 去互联网通信

NAT 解决 IP 地址不足,体现在哪里

  1. 私有 IP 地址:可以全世界重复使用
  2. 公网 IP 地址:全世界必须唯一,不能重复

所以大量主机可以重复使用私有内网 IP,经过NAT 转换之后,一整个局域网内所有设备,共用同一个公网 IP 去访问外网 。大大节约 IP 资源,

补充:

  1. 运营商掌控上网权限 家里所有上网报文,全部先经过运营商机房路由器。
  • 欠费:运营商直接丢弃你的报文,不让转发到公网,就上不了网。
  • 运营商可以决定你的数据包能不能去往互联网。
  1. 报文会经过运营商设备,明文数据会被看到 你的数据包全部过运营商机房,所以如果携带有有些"敏感"信息就不能成功转到

长城技术:代指网络审查、过滤,运营商可以拦截、丢弃特定报文。

相关概念:

局域网,内网,子网不用太过于纠结,可以当作一个东西

LAN(局域网)LAN口的值就是网关

WAN(广域网)WAN口的值可能是公网ip,也可能是运营商二级 NAT后的值(私网ip)

网关:是一台具体设备的 IP 地址(路由器 LAN 口的值),是一个实实在在的主机 IP(192.168.1.1)

网络号:是一个网段的编号,代表一整个局域网,不是一台设备(192.168.1.0)

尝试理解公网

真实的网络结构非常复杂,即涉及到划分公网 IP 的组织,ICANN,还要在全球范围内进行区域划分, 比如亚太,北美,欧洲等,又要考虑各个国家内部的 ISP 代理,整体拓扑非常复杂,我们简化所有过程,简单理解公网即可

公网类比成全世界城市交通网

  1. 最上层:俄罗斯、美国、英国等国家,各自占一个大网段(如1.0.0.0/8),国家之间靠国际骨干网相连,路由器跑 BGP 路由协议,互相交换全网路由信息。
  2. 国家内部:再拆分各个省份(陕西5.1.0.0/16、河南5.2.0.0/16、浙江5.3.0.0/16),国内骨干网互通。
  3. 省份内部:再拆各个城市(西安5.1.16.0/20、延安5.1.32.0/20)。
  4. 城市下面才是普通家庭 / 公司内网。

公网 IP 每一段,层层划分:国家→省份→城市,每一级路由器都有路由表,知道某个网段该往哪个接口转发。

问题 1:国外俄罗斯要访问目标地址:5.1.16.X

完整流程:

  1. 俄罗斯主机发包,源是俄罗斯公网 IP,目标5.1.16.X。本机掩码运算,发现目标不在本国网段,交给本国网关。
  2. 俄罗斯路由器查路由表:5.0.0.0/8属于中国,把数据包转发到国际骨干网,送到中国的国家级路由器。
  3. 中国骨干路由器:目标5.1.16.X属于5.1.0.0/16(陕西省),转发给陕西省的出口路由器。
  4. 陕西省路由器:目标5.1.16.X属于西安网段5.1.16.0/20,转发给西安的出入口路由器。
  5. 西安路由器继续转发,把数据包送到5.1.16.X这台主机。

全程:IP 源目 IP 不改变,没有 NAT,只有每一跳二层 MAC 帧头不断更换。 回程报文原路返回。


问题 2:河北的报文,发往5.1.16.X

  1. 河北主机发包,目标5.1.16.X,判断不在本网段,交给河北网关。
  2. 河北路由器查路由:目标网段5.1.16.0/20属于陕西5.1.0.0/16,交给国内骨干网
  3. 国内骨干网转发给陕西省路由器。
  4. 陕西路由器再交给西安路由器。
  5. 最终送达主机5.1.16.X

关键点:每一级路由器只看目标 IP 的网络号 ,查表选择下一跳,不需要知道目标主机具体在哪条线下。

路由

  • 路由表可以使用 route 命令查看
  • 如果目的 IP 命中了路由表,就直接转发即可;
  • 路由表里的default(缺省路由 / 默认路由)条目 : 包含下一跳网关地址本机发送接口。当数据包的目标 IP,和路由表里上面所有条目全都匹配不上的时候,就走这条缺省路由,从指定接口,把包发给这个下一跳网关。
  • 路由表的 Destination 是目的网络地址,Genmask 是子网掩码,Gateway 是下一跳地址,Iface 是发送接口,Flags 中的 U 标志表示此条目有效 (可以禁用某些条目),G 标志表示此条目的下一跳地址是某个路由器的地址,没有 G 标志的条目表示目的网络地址是与本机接口直接相连的网络,不必经路由器转发;

转发过程例 1: 如果要发送的数据包的目的地址是 192.168.56.3

  • 跟第一行的子网掩码做与运算得到 192.168.56.0,与第一行的目的网络地址不符
  • 再跟第二行的子网掩码做与运算得到 192.168.56.0,正是第二行的目的网络地址,因此从 eth1 接口发送出去;

转发过程例 2: 如果要发送的数据包的目的地址是 202.10.1.2

  • 依次和路由表前几项进行对比,发现都不匹配;
  • 按缺省路由条目,从 eth0 接口发出去,发往 192.168.10.1 路由器;
  • 由 192.168.10.1 路由器根据它的路由表决定下一跳地址;

TCP的16 位标识 ID,**3 位标志,**13 位片偏移

  1. 16 位标识 ID :同一个大 IP 数据报拆分出来的所有分片,拥有相同标识 ID;接收方靠这个 ID,把一堆分片识别成属于同一个原始报文。
  2. 3 位标志
    • 最高位保留;
    • DF 位:不分片位 。DF=1,路由器禁止分片,包太大直接丢弃,返回 ICMP;
    • MF 位:更多分片位。MF=1:后面还有分片;MF=0这是最后一片
  3. 13 位片偏移 :代表本片数据,相对于原始报文数据开头的偏移(以8 字节为单位)。接收方根据偏移,把分片按顺序拼回完整数据。

核心结论:IP 报文可以进行分片与重组 。 重要:只要任意一个分片丢失,整个原始报文直接作废,全部重传。接收方不会上交残缺数据,只会等待全部分片,缺一片就组装失败。

MTU、MSS,为什么会分片

  • MTU(链路层最大传输单元) :数据链路层一帧最多能承载的数据字节,以太网默认 1500 字节
  • MSS(最大段大小) :TCP 传给 IP 层的数据部分(不含报头)最大长度。 MSS = MTU − IP头部(20) − TCP头部(20) = 1460字节

MSS 只属于 TCP,和 UDP 无关。UDP 没有 MSS 概念。

分片发生过程

传输层给网络层交出大数据包,IP 总长度 > MTU (1500) ,路由器就把这个 IP 报文切割成多个分片,一个个转发。 分片是网络层 (IP) 被迫做的事情,不是主流,不推荐(后面会再讲为什么不是主流)

分片坏处:只要丢任意一片,整个报文报废,丢包概率大幅上升。

结论:过多分片,会导致丢包概率变大

怎么减少分片

分片发生在 IP 层;但避免分片的工作,交给传输层(TCP)做。(减少分片的本质在传输层) TCP 知道 MSS,每次交给 IP 的数据不超过 MSS (1460),加上 IP 头 + TCP 头,整个 IP 报文刚好 1500,就不会触发 IP 分片

UDP 没有 MSS 机制,UDP 发大数据,直接交给 IP,就会触发 IP 分片。

为什么滑动窗口不一次性发大报文,要多次发呢?

所以现在看来主要原因就是为了避免IP分片,保证可靠性,二来就是如果有一段丢了,只重传丢失那一小段,不用重传全部,节省开销

怎么做到IP 分片识别、收全判断、组装?

1.怎么判断一个 IP 包是不是分片报文

两种情况代表这是分片: a. MF(更多分片)=1:后面还有别的分片,肯定是分片包。

b. MF=0,但是片偏移 > 0 :说明这是最后一片分片,属于分片。

那什么是原始完整不分片的包:MF=0 && 片偏移=0。两个条件同时满足,代表没有被分片。

2、接收方怎么把分片归为一组,怎么判断是否全部收齐

a. 16 位标识 ID 相同的分片,说明属于同一个原始大报文,能够放到一组。

b. 判断有没有收全: 必须收到MF=0 的那一片(最后分片)。 只有拿到最后一片,才知道整个报文总长度。 三种收不全丢包场景:

  1. 第一片丢失(片偏移 = 0 的分片丢了)
  2. 中间某一片丢失(不太好判断,看第3步组装的过程)
  3. 结尾分片丢失(MF=0 那片丢了)

只要任意一片没到,接收方就组装失败,不会上交上层,等待超时直接全部丢

3.组装(重组)过程

  1. 同一标识 ID 的全部分片,按照片偏移从小到大升序排序
  2. 校验连续:当前分片片偏移 + 当前分片数据长度(按8字节单位) = 下一片的片偏移。(如果不连续不就相当于中间一片丢了吗)
  3. 拼接到一起,还原完整 IP 数据,交给上层传输层。

eg:

注意:片偏移单位 = 8 字节

原始 IP 数据:4000 字节,被路由器分成 3 片:

分片 片偏移 (8 字节为单位) 数据字节数 MF 标志
第 1 片 0 1480 1(还有分片)
第 2 片 185 1480 1(还有分片)
第 3 片 370 1040 0(最后一片)

换算:1480 ÷ 8 = 185

  1. 第 1 片片偏移 0,数据占 185 个 8 字节单元。
  2. 0 + 185 = 185正好等于第 2 片的片偏移 185,连续。
  3. 第 2 片 1480 字节,同样 185 个单元;185+185 = 370 → 和第 3 片片偏移 370 对上。

接收方重组步骤

  1. 三个分片 ID 相同,把它们拿出来,按片偏移从小到大排:0 →185 →370
  2. 校验连续: 0+185 =185185+185 =370 ✔是连续的
  3. 片偏移为0的第1片收到,加上连续所以中间也没有丢失,最后收到 MF=0 的最后一片,确认全部收齐。
  4. 直接把三段数据拼接,恢复 4000 字节完整数据,上交 TCP/UDP

丢包场景

如果第 2 片丢了: 收到分片:0、370。 0+185 !=370,发现中间缺口。收不到全部分片,直接全部丢弃,不交给上层

MSS补充:

TCP 希望发送报文段的时候,尽量避免在网络层发生 IP 分片 ,接收方也同样要保障分片处理的问题。因此 TCP 在三次握手阶段,双方会互相通告自己的 MSS ,最终取两者的最小值作为连接的 MSS。(所以一般就不会分片,即不是主流)

握手阶段就协商确定 MSS,发送方按照这个 MSS 填充数据,绝大多数情况下就不会触发 IP 分片,大大减少分片发生的概率。

注意:路径上的 MTU 是有可能动态变化的,但不影响 MSS 的基础逻辑,上层 TCP 会基于协商好的 MSS 控制报文大小。

相关推荐
白狐_7981 小时前
408 计算机网络|HTTP 的 RTT 怎么数:用多道题讲清楚
网络协议·计算机网络·http
Discipline~Hai1 小时前
Linux网络编程03-HTTP协议
linux·运维·服务器·网络·网络协议·http·linux网络编程
CV-杨帆1 小时前
在自己的服务器上搭建VLLM 以Qwen3.5-0.8B与Qwen3.5-4B为模型基础
运维·服务器·vllm
程序猿乐锅1 小时前
【计算机组成原理 | 第八章】I/O系统
运维·服务器·网络
Shadow(⊙o⊙)1 小时前
数据链路层ARP协议、NAT路由技术、代理服务器、内网穿透、内网打洞、交换机+集线器
网络·网络协议·tcp/ip
青瓦梦滋2 小时前
Linux高级IO
linux·运维·服务器·网络·网络协议·tcp/ip
꯭自꯭闭꯭2 小时前
达梦(DM8)安装测试
linux·运维·服务器·数据库
牢姐与蒯2 小时前
Linux进程(七).进程控制
linux·运维·服务器·ubuntu
Mr. zhihao2 小时前
Linux 内存惰性分配:从虚拟地址到物理页的深度解析
linux·服务器·内存分配