每天多学一点:一个数据包从发出到被接收一路究竟是怎么过来的

从“当键入网址后,到网页显示,其间究竟发生了什么” 开始说起

总的来说,大致可以分为以下几个步骤:

  1. 浏览器解析 URL,生成 HTTP 报文
  2. 通过 DNS 查询真实的 IP 地址
  3. 使用 TCP/IP 协议栈对数据包进行切割、打包、封装
  4. 利用 MAC 地址确定收发两点的终始点
  5. 通过网卡讲数据包从数字信号转换为电信号,通过网线传输到交换机/路由器
  6. 通过路由器将数据包转发到公网下一个节点
  7. 通过层层转发,数据包到达服务器
  8. 服务器开始对数据包进行解包,取出数据,生成响应

接下来,对每个步骤进行详细的拆解,拿着放大镜,一起好好剖析数据包一路上的所见所闻

应用层

HTTP

第一步,浏览器解析用户输入的 URL,生成 HTTP 请求报文:

HTTP 报文格式

第二步,生成的 HTTP 消息需要委托操作系统发送给服务器,发送之前,需要根据服务器域名,查询出对应的 IP 地址,这时候,需要用到 DNS 服务器来完成 HOST -> IP 的转换

DNS 域名解析的工作过程:

DNS工作流程

传输层 & 网络层

协议栈

通过 DNS 获取到 IP 后,数据包的传输工作将交给协议栈进行下一步的处理

协议栈

应用程序通过调用 Socket 库,将数据包委托给协议栈进行处理

协议栈分为上下两部分:

  1. 上部分负责收发数据的 TCP/UDP 协议
  2. 下部分利用IP协议控制网络数据包的收发操作,包括:
    • ICMP 用于反馈数据包传输过程中产生的错误及负责各种控制信息
    • ARP 根据IP地址查询相应的以太网MAC地址

IP 下层的网卡驱动负责控制网卡硬件,网卡硬件负责实际的收发操作,完成数据包 数字信号 <---> 电信号 的转换

TCP(传输层)

TCP 报文格式:

TCP报文

  • 源端口号和目标端口号:定位收发应用
  • 序号: 解决包乱序的问题
  • 确认号:解决丢包问题
  • 状态位:
    • URG:紧急指针(urgent pointer)有效。
    • ACK:确认序号(acknowledgment number)有效。
    • SYN:发起一个新连接。
    • RST:重置连接。
    • PSH:接收方应该尽快将这个报文交给应用层,而不是等到整个缓冲区都填满了再交付。
    • FIN:发送方已经没有数据要发送了。
      TCP 是面向连接的,带状态位的包的收发,会引起双方状态变更
  • 窗口大小:用于流量控制和拥塞控制

TCP 三次握手

建立 TCP 连接实质是双方维护一个状态机,在连接建立的过程中,双方的状态变化时序图如下:

TCP三次握手

  • 初始,ClIENT 与 SERVER 都处于 CLOSED 状态
  • 服务器首先开始监听某个端口,此时处于 LISTEN 状态
  • CLIENT 主动发起连接 SYN, 之后处于 SYN_SENT 状态
  • SERVER 返回一个 SYN, 并且 ACK 回复 CLIENT 的 SYN, 之后处于 SYN_RECV 状态
  • CLIENT 收到 SERVER 的 SYNACK后,ACK 回复 CLIENT 的 ACK,然后处于 ESTABLISHED 状态,因为此时已经确认一发一收成功
  • SERVER 收到 CLIENT 回复的 ACK 之后,也处于 ESTABLISHED,因为此时也已确认一发一收成功

三次握手的目的是确保双发都有收发数据的能力

TCP 数据分割

MTU: Maximum Transmission Unit,物理层最大传输单元,以太网中一般为 1500 字节
MSS: Max Segment Size,除去 IPTCP 报文头部之后,一个网络包能容纳的TCP数据的最大长度。即,TCP 协议单次能够运载的最大数据量
FCS: Frame Check Sequence,帧校验序列

MTU & MSS

如果 HTTP 报文数据超过了 MSS的长度,这时 TCP 会将报文以 MSS 长度切割成一块块的数据包分开发送。每一块数据包都会拼接 TCPIP 头信息,交由 IP 协议来发送数据。

双方建立连接后,TCP 报文中的数据部分就是存放 HTTP 报文的部分,组装好 TCP 报文之后,就交由网络层进行进一步处理

IP协议簇 (网络层)

TCP 协议在执行连接、收发、断开等操作时,都需要委托 IP 协议将数据封装成网络包发送给通信接收方

IP 报文格式:

IP报文

  • 源地址IP:客户端出口 IP 地址
  • 目标地址:通过 DNS 域名解析得到的服务器的 IP

一个设备可能不仅仅有一张网卡,当设备拥有多个网卡时,源地址IP应该填写哪个地址呢?

这时候需要根据路由表规则,来判断哪一个网卡作为源地址IP

假设系统环境是 Linux 环境,使用 route -n 命令查看当前系统的路由表

Linux路由表

可以看出,第一条记录为默认网关, 当所有地址匹配不到时,会将数据包发送到网关 10.0.20.1

举个栗子,根据上面的路由表,假设目标地址为 172.18.58.3

  1. 首先,先和第2条记录的子网掩码 255.255.252.0 进行与运算, 得到结果为 172.18.56.0Destination10.0.20.0 不匹配,则继续匹配下一条记录
  2. 与第3条记录的子网掩码 255.255.0.0 做与运算,得到结果为 172.18.0.0Destination172.17.0.0 不匹配,则继续匹配下一条记录
  3. 与第4条记录的子网掩码 255.255.0.0 做与运算,得到结果为 172.18.0.0Destination172.18.0.0 匹配成功,所以将使用 docker0 网卡的IP地址作为IP包头的源地址

数据链路层 & 物理层

MAC & ARP(数据链路层)

MAC 头部是以太网使用的头部,包含了收发双方的MAC地址等信息

MAC报文格式

一般在 TCP/IP 通信里,MAC包头的协议类型只使用:

  • 0800: IP 协议
  • 0806: ARP 协议

MAC 收发双方确认过程:

发送方的MAC地址是在网卡生产时写入到 ROM 里的,只需要将这个值读取出来写入到MAC头部即可

接收方的MAC地址获取需要根据上面得到的目标地址 IP,查询到对应的 MAC 地址,写入到MAC头部

根据 IP 获取设备的 MAC 地址

此时需要通过 ARP 协议找到路由器的 MAC 地址,ARP 协议会在以太网中以广播的形式,发送查询 MAC 地址的数据包,如果有设备应答,则将设备的MAC地址写入到MAC头部

至此,就完成了 MAC 头部的构造,后续操作系统会将查询结果存放到 ARC 缓存,缓存时间为几分钟

在发包时:

  1. 先查询 ARP 缓存,如果命中,则无需发送ARP广播,直接使用缓存中的MAC地址
  2. 当缓存Miss时,则发送ARP广播查询

Linux 系统中,可以使用 arp -a 命令来查看 ARP 缓存中的内容

ARP缓存内容

网卡(数据链路层 & 物理层)

IP 协议生成的数据包只是存放在内存中的二进制数字信息,无法直接跨设备传送。因此,需要将进行 数字信息 -> 电信号 的转换,才能在网线中传输

这一过程由操作系统控制驱动程序操作网卡完成,网卡驱动从 IP 模块获取到数据包之后,将其复制到网卡的缓存区中,在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列

  • 起始帧分界符是一个用来表示包起始位置的标记
  • 末尾的 FCS 用来检查包传输过程中是否有损坏

最后网卡会将数据包转化成电信号,通过网线发送出去

交换机(数据链路层)

交换机的设计是将网络包原封不动的转发到目的地

  1. 首先,对到达网口的电信号进行接收并转换为数字信号
  2. 通过包末尾的 FCS 校验错误,通过则放到缓冲区

计算机的网卡本身具有MAC地址,并通过核对收到的包接收方的MAC地址判断是否是发给自己的,不是发给自己的包则丢弃;相比较,交换机的端口不核对接收方的MAC地址,而是直接接收所有的包并放到缓存区中。因此,与网卡不同,交换机的端口不具有MAC地址

将包存入缓冲区后,需要查询这个包的接收方MAC地址是否已经在MAC地址表里有记录了,交换机MAC地址表主要包含两个信息:

  • 设备的MAC地址
  • 该设备连接在交换机的哪个端口上

交换机内部有一张MAC地址与网线端口的映射表。当接收到包时,会将相应的端口号和发送MAC地址写入表中

交换机MAC地址表

当交换机根据MAC地址表查询MAC地址,然后将信号发送到对应的端口。若地址表中找不到指定的MAC地址,可能是:

  • 具有该地址的设备还没有向交换机发送过包
  • 该设备一段时间未上线导致地址从地址表中被移除了

这种情况下,交换机无法判断应该将包转发到哪个端口,此时会将包转发到除了源端口外的所有端口上,只有相应的接收者才会收到包,其他设备则会忽略这个包

此外,如果接收方的MAC地址是一个广播地址,那么交换机会将包发送到除了源端口外的所有端口。以下两个属于广播地址:

  • MAC地址中的 FF:FF:FF:FF:FF:FF
  • IP地址中的 255.255.255.255

路由器(网络层 & 数据链路层)

路由器与交换机的区别

网络包通过交换机后,到达路由器,并被转发到下一个路由器或目标设备。这一步的转发工作原理与交换机类似,也是通过查表判断包转发的端口,不过在具体的操作过程上,两者有如下区别:

  • 路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有MAC地址和IP地址
  • 交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有MAC地址

路由器基本原理

  • 路由器的端口具有MAC地址,可以作为以太网的收发方;同时还具有IP地址,从这个意义上来说,它与计算机的网卡是一样的
  • 当转发包时,首先路由器会接收发给自己的以太网包,然后路由表查询转发端口,由相应的端口作为发送方将以太网包发送出去

路由器的包收发操作

  • 电信号到达网口,将电信号转换为数字信号,通过包末尾的 FCS 进行错误校验
  • 检查MAC头部中的接收方MAC地址,如果是发给自己的就放到缓冲区中,否则丢弃这个包

完成包接收操作后,路由器会去掉包开头的MAC头部,根据目标更新收发双方的MAC地址,直到数据包到达最终目的地

路由表转发与之前说的 Linux 路由表部分一致

服务器与客户端(解包 <=> 封装)

数据包解包&封装过程

至此,一个数据包从发出开始,一路披荆斩棘,历经坎坷,总算到达目的地了

Bravo!

参考 & 引用

探究!一个数据包在网络中的心路历程