
浏览器输入一个网址发生了什么(三) IP模块封装、ARP协议、IP协议、ICMP协议和网卡原理
本系列文章归于: 计算机网络基础
上一节我们探索了网络消息在协议栈的TCP模块内是如何封装、发送和接收: 浏览器输入一个网址发生了什么(二) TCP模块封装和传输机制
本节将会补充协议栈中的 IP 模块对报文的封装,以及网络包是如何通过网卡发送出去计算机。
1. IP 模块封装 IP 头部
当数据报文从 TCP 模块传递给 IP 模块时, IP 模块需要封装 IP 头部。 IP 头部中包含以下信息,其中接收方的 IP 由
TCP 模块传递给 IP 模块,而发送方的 IP是什么 需要取决于 IP 模块通过电脑的哪一块网卡发送(由上一节我们知道 IP
不是与主机绑定,而是与主机上的网卡绑定,如果主机上有多个网卡,则该主机可以有多个 IP )。

为了得知包由主机上的哪块网卡发送, IP 模块会根据目标 IP 为条件查询本机的“路由表”,该路由表在 windows 系统中可以通过
route print 命令显示,如下:

首先 IP 模块会拿目标 ip 和第 1 列字段进行比对,这里的比对不是全值匹配,而是只匹配其网络号。而网络号的位数由第 2 列字段决定。
例如,当目标 IP 是 192.168.1.21 时会匹配到倒数第 3 行,因为这一行要求比对的网络号是前 24 位,而
192.168.1.21 和 192.168.1.0 的前 24 位相同,都是 192.168.1 。同理,如果目标 IP 是
10.10.1.166 则匹配到第 3 行。
如果目标 IP 与路由表中的所有条目都不匹配,则会匹配到第 1 行,第一行的网络号全为 0
,即默认网关(网关本质也是一个转发路由器),也意味着这个包将被发送到局域网中默认的一个路由器,由该路由器进行转发。
第 4 列 Interface 代表网卡的网络接口,也就是本机网卡的 IP 。第 3 列 Gateway
代表包要被发送到的下一个路由器的 IP 。
如果目标 IP 是 192.168.1.21 ,则会匹配到倒数第 3 行,此时 IP 模块会用第 4 列的 10.10.1.16
代表的网卡发送包到第 3 列的 10.10.1.2 这个路由器。
从上面的路由表来看,该主机只有 10.10.1.16 这 1 个 IP , 1 块网卡。
第 5 列表示从发送方 IP 到目标 IP 需要经历多少跳,即包传输的距离。
回到正题,除了发送方 IP 和目标 IP , IP 头部还需要填写协议号,它表示包的内容是来自哪个模块的。例如,如果是 TCP
模块委托的内容,则设置为 06 (十六进制),如果是 UDP 模块委托的内容,则设置为 17 (十六进制),这些值都是按照规则来设置的。
在现在我们使用的浏览器中, HTTP 请求消息都是通过 TCP 来传输的,因此这里就会填写表示 TCP 的 06
(十六进制)。其他字段内也需要填写相应的值,但对大局没什么影响,这里先省略。
2. IP 模块封装以太网用的 MAC 头部
IP 头部中的 IP 地址可以由网络层的 IP 模块进行识别(当包到达接收方的 IP 模块后,在 IP 模块中会检查包头部的目标 IP
地址是否为接收方的 IP 地址,如果不是则丢弃包;除了 IP 地址外, IP 模块还会识别 IP 头部中的其他信息如协议号等)。
但是在以太网(以太网是局域网中的一种)中,有很多设备只有数据链路层而没有网络层,没有 IP 模块,因此也无法识别 IP
头部,例如集线器等。此时需要为包添加以太网能识别的 MAC 头部,如下所示:

MAC 头部实际上也是 IP 模块生成的,它包含了接收方和发送方的 MAC 地址,此时包从 IP 包变成以太网包。以太网包的内容不一定是
IP 包,也可以是 ARP 包等,可以从 MAC 头部的以太网类型字段得知。
MAC 地址也是网卡的硬件地址。 IP 和 MAC 都是网卡的地址,但 IP 地址是可以动态变化的,这取决于主机所处的网络环境,而 MAC
地址是固定的,当网卡生产时就写入到 ROM 中,在上一步中根据目标 IP 查询路由表得知该由哪块网卡发送包后,再从这块网卡中读取 MAC
地址并写入 MAC 头部即可。
下面是 MAC 地址的格式

得知发送方的 MAC 地址容易,那如何得知接收方的 MAC 地址呢? 这里需要使用 ARP 协议和广播 。
ARP 协议(地址解析协议) 是一个实现从 IP 地址到 MAC 地址的映射,即询问目标 IP 其 对应的 MAC
地址的协议。 ARP 协议仅用于 IPv4 , IPv6 则是使用 NDP (邻居发现协议)。
ARP 协议与 IP 协议处于同一层,也是网络层协议,因此 ARP 报文是在网络层被封装的网络包。
ARP 的工作机制
首先 IP 模块会封装 ARP 请求包, ARP 请求包中包括发送方的 IP 地址和 MAC 地址,以及目标主机的 IP
地址(这段话中的目标主机的IP地址其实是下一跳路由器的IP地址而非最终互联网另一端的目标IP)和 MAC 地址(由于不知道目标主机的 MAC
地址,因此会填写为全 0 )。
客户端计算机通过交换机向局域网内同一链路的所有机器发送 ARP 请求(即广播),该 ARP
请求包在数据链路层会被同一链路上所有的主机、路由器和交换机接收和解析。

交换机接收到这个 ARP 请求包(应该说是包含该 ARP
包的帧)会将其广播到同一链路的其他机器(因为交换机只负责数据的转发,它没有网络层且它没有被分配 IP );
之后这个包被路由器或主机接收到,则他们会检测 ARP 包头部的目标 IP 地址,如果不是自己的 IP 地址则会丢弃该包。如果是则会将他们自己的
MAC 地址写入到 ARP 响应包返回给发送方主机。并且目标主机会将发送方主机的 IP 地址和 MAC 地址的映射关系缓存到 ARP
表中。
ARP 表是每个主机内存储着其他机器的 IP 和 MAC 地址的映射关系表。通过查询 ARP 缓存就不用每次查询 MAC 地址时都去进行
ARP 请求广播,从而防止 ARP 大量广播。但是 ARP 表中的每条缓存都有自己的有效期,过期则删除,有效期在几分钟到十几分钟。可以通过
arp -a 命令查看 ARP 表内容。
回到正题,将目标主机的 MAC 地址写入到 MAC 头部之后, MAC 包就封装好了,接下来 IP 模块会将 MAC
包传递给网卡,网卡对 MAC 包进一步封装为帧(无论是IP包还是 ARP 包,都会在网卡被封装为帧之后才发送出去)。
需要注意的是,此时网络包MAC头部的接收方MAC地址是下一跳路由器的MAC地址而非互联网另一端目标主机的MAC地址。当然如果这个包的目标主机本身就是局域网中的一台机器,那么发送ARP请求时的ARP头部的目标IP就可能是目标主机的IP而非路由器IP,那么请求的MAC地址就直接是这个目标主机的MAC地址。
3. 网络包封装成帧
在介绍数据包封装成帧并发送之前先介绍一下网卡和数据在网卡传输的路径
网卡和网卡驱动程序
IP
模块生成的网络包是存在内存中的一堆数字信息,需要被转为电或光信号才能在网线传输,而网卡负责这一任务。网卡本身是硬件,无法完成这些任务,还需要网卡驱动程序对网卡进行操作和控制。
网卡在电脑开机时会进行初始化,初始化时网卡驱动程序会读取 ROM 中的 MAC 地址并分配给网卡中的 MAC 模块,此时网卡的 MAC
地址才生效。
下图是网卡的结构以及网络包是如何在网卡中处理和发送的:

其中, MAC 模块负责将 IP 包封装为帧,并检测接收到其他机器发送过来的帧是否正确( FCS 校验)以及接收到的包中的目标 MAC
地址是否是自己的 MAC 地址; PHY(MUA) 负责将帧从数字信息转为能在网线传输的电信号。
IP 模块将以太网包传递给网卡驱动,网卡驱动会将其复制到网卡内的缓冲区,再向 MAC 模块发送发送包的命令。 MAC
模块将包从缓冲区取出在开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列,此时网络包被封装成帧。

报头是一串像 10101010 …这样 1 和 0 交替出现的比特序列,长度为 56
比特,它的作用是确定包的读取时机。起始帧分界符是一个用来表示包起始位置的标记。末尾的 FCS
(帧校验序列)用来检查包传输过程中因噪声导致的波形紊乱、数据错误。
4. 向集线器发送网络包
包封装成帧后, MAC 模块从报头开始将数字信息按每个比特转换成电信号,然后由 PHY ,或者叫 MAU
的信号收发模块发送出去。在网线中实际的输出信号如下

发送信号的操作分为两种,一种是使用集线器的半双工模式,另一种是使用交换机的全双工模式。
在使用集线器的半双工模式中,发送和接收不能同时进行, PHY(MAU)
模块会保证网线中没有其他设备发送信号给自己才会将自己的信号发送出去(如果有则等待其他信号传输完)。如果网线中有其他信号在传输的情况下发送出去自己的信号,则两组信号会发生碰撞。在全双工模式中,发送和接收能够同时进行。
电信号由 PHY(MUA) 信号收发模块发送给自己连接的集线器,再由集线器转发给连接到该集线器上的所有设备。数据在网络中(局域网中)传递的过程如下:

信号经集线器发给所有设备后,这些设备都会接收到这个信号,然后由他们的 PHY(MUA) 模块从电信号转为通用格式给 MAC 模块。
MAC 模块先将信号转为数字信息(还原为数据帧),然后检测数据帧是否受到噪声干扰而紊乱,即把包开头到结尾的比特套用公式计算出的内容与帧末尾的 FCS
比对,如果不一致则会被当做错误包丢弃。
如果 FCS 校验没问题, MAC 模块会检测包 MAC 头部的接收方 MAC 地址是否是自己的 MAC
地址,不是则直接丢弃,是则存到网卡缓冲区中。
之后网卡会发出中断信号给 CPU 。换句话说,计算机不会一直监控网卡的活动而是在运行其他的任务,因此操作系统是无法得知包到达网卡这件事。
因此包到达后需要由网卡向扩展总线中的中断信号线发送信号,该信号线通过中断控制器连接到 CPU , CPU
接收到中断信号后会挂起正在处理的任务,切换到操作系统的中断处理程序,中断处理程序会调用网卡驱动,网卡驱动从网卡缓冲区取出包交给 TCP/IP 协议栈。
协议栈中, IP 模块会校验 IP 头部格式是否正确,以及包的头部接收方 IP 地址是否是自己的地址,如果不是自己的地址, IP 模块会通过
ICMP 消息将错误告知发送方,但不会将这个包转发给真正的目标 IP 主机。
接下来需要再说一下 ICMP 互联网控制协议。
该协议用于在主机和路由器之间发送控制消息,这里的控制消息是指“网络不通”,“主机是否可到达”等消息,这些消息不传输用户数据。 ICMP
位于网络层,但是在 IP 之上, ICMP 报文和 TCP 与 UDP 一样会被添加 IP 头部封装为包。 ICMP
是无连接的协议。
ICMP 的主要功能有两个:
a. 确认 IP 包是否成功到达目标地址,当 IP
包从源发送方到目标主机之间的任何一个设备(如路由器)出错(如包无法到达下一跳的设备),则中间的设备会生成 ICMP 数据包发送给源发送方。
b. 网络诊断, ICMP 可以检测两台设备之间能否互连,即主机 A 能否到达主机 B ,以及主机 A 与主机 B 的连接速度,准确报告
ICMP 包到达目标地的时间。 ping 和 traceroute 命令就是通过发送 ICMP 包实现的功能。其中 traceroute
可以显示两台机器之间可能的路径并测量数据包在 IP 网络上的时延, ping 则是 traceroute 的简化版,功能与
traceroute 类似。
IP 包不可到达目标主机情况下发送 ICMP 包的流程:

图中,主机 A 发送了一个 IP 数据包,其目标地址是主机 B 。 IP 包经过路由器 1 到达路由器 2 ,假设路由器 2
和主机 B 在同一链路,为了根据主机 B 的 IP 获取 B 的 MAC 地址,路由器 2 在局域网内广播了 ARP
包。由于主机 B 关机导致该 ARP 包没有被响应,多次重发 ARP 无果后,路由器 2 会生成一个类型为“目标不可到达类型”的 ICMP
包经过路由器 1 发送主机 A 。
“目标不可到达”是其中的一种 ICMP 消息类型。
主要的ICMP 的消息类型如下
通知类型(十进制数)
|
具体内容
—|—
0
|
回送应答(Echo Reply)
3
|
目标不可达(Destination Unreachable)
4
|
原点抑制(Source Quench)
5
|
重定向或改变路由(Redirect)
8
|
回送请求(Echo Request)
9
|
路由器公告(Router Advertisement)
10
|
路由器请求(Router Solicitation)
11
|
ICMP 超时(Time Exceeded)
17
|
地址子网请求(Address Mask Request)
18
|
地址子网应答(Address Mask Reply)
ICMP 通知类型分为两类:对于发送错误消息的 ICMP 报文叫差错报文;对于为了采集信息和配置的 ICMP 报文叫信息类报文(如 ping
和 traceroute 命令所发的 ICMP 报文)。
ping 命令检测两端主机能否互连情况下发送 ICMP 包的流程

图中主机 A 执行 ping 命令检测能否与主机 B 通信,此时主机 A 会生成一个通知类型为“ 8 回送请求”的 ICMP 包给
B 。 ICMP 包经过多个路由器如果能够到达主机 B 则 B 接收到“回送请求”的 ICMP 包后会返回一个“ 0 回送应答”的
ICMP 包给 A ,这个包会沿着原路经过路由器 4~1 到达主机 A ,主机 A 就知道能够与 B 通信,以及包送达 B
花费的时间。
回到正题,当接收方主机发现数据包头部的接收方 IP 地址不是自己的 IP 地址,则会由 IP 模块生成一个类型为 “目标不可到达类型”的
ICMP 包给源发送方。
如果 IP 正确,则这个包会被接收。如果这个包是经过分片的,则 IP 模块会将其暂存到内存中,等 IP 头部具有相同 ID
号的包全部到达,会根据 ID 号以及 IP 头部的“分片偏移量”将所有分片重组完整的 IP 包。(这里的分片不是指 TCP
模块对应用程序数据 [ 即 http 消息 ] 的拆分,而是指路由器对某个以太网包内部 IP 包数据 [ 即 TCP 头部
+http 消息 ] 的拆分,这部分知识会在之后介绍路由器时介绍)。
之后,这个完整 IP 包的数据会被递交给 TCP 模块, TCP 模块根据 IP 头部的接收方 IP 地址和 TCP
头部的接收方端口号查找对应的套接字,根据套接字记录的通信状态执行相应操作:如果包的数据是应用程序数据则返回确认接收的 ACK
包,并将数据存放到缓冲区等待应用程序来读取;如果是建立或断开连接的控制包,则返回响应控制包,并告知应用程序建立和断开连接的操作状态。
说到这里,我们可以说已经大概了解了发送方数据从本机的应用层到本机网络层的传输和处理,以及接收方接收数据时数据从接收方机器的网络层到应用层的传输与处理过程了。
下节预告:浏览器输入一个网址发生了什么(四) 网络包在局域网中的传输
欢迎在评论区留言表达看法,阿沛会一一作出回复。
如果本文对大家有帮助,麻烦大家动动小手点个免费的“赞”或“在看”,大家的鼓励就是阿沛持续更新的动力~

-- 往期精彩 –
计算机网络基础(三) 网络层之IP地址、ARP协议、ICMP协议和IP协议详解
计算机网络基础(四) 网络层之路由选择协议、RIP协议、OSPF协议、BGP协议以及路由器的构成
阿里一面:说说看线程池的执行流程和原理?线程池的拒绝策略有哪些?如何确定线程池的核心线程数?如何优雅的关闭线程池?