键入网址到网页显示,期间发生了什么?

小林 Coding

1. HTTP

1.1 解析 URL

对于一个网址,也就是 URL,浏览器首先要做的就是对 URL 进行解析,从而生成发送给 Web 服务器的请求消息。

一般来说,URL 元素的构成方式为:

https://www.example.com:443/path/to/page?query=123#section
\___/   \_____________/\__/\__________/ \_______/ \_____/
|          |          |       |          |         |
协议      主机名       端口     路径       查询参数   片段标识
组成部分 说明
协议 httphttpsftp,决定如何访问资源。
主机名 www.example.com,指向服务器地址(需通过DNS解析为IP)。
端口 :443(HTTPS默认),指定服务端口(默认HTTP=80,HTTPS=443)。
路径 /path/to/page,表示服务器上的资源路径。如果没有指定路径名会访问根目录下的默认文件,一般为 /index.heml/default.html
查询参数 ?query=123,向服务器传递额外参数(格式为 ?key1=value1&key2=value2)。
片段标识 #section,指向页面内的锚点(前端使用,不发送到服务器)。常用于跳转到指定元素,指定不同的目录会有不同的片段标识或是传递简单参数

例如,https://xiaolincoding.com/network/1_base/what_happen_url.html#%E5%AD%A4%E5%8D%95%E5%B0%8F%E5%BC%9F-http

  • https:使用的访问数据的协议
  • xiaolincoding.com:Web 服务器
  • /network/1_base/what_happen_url.html:数据文件路径
  • #%E5%AD%A4%E5%8D%95%E5%B0%8F%E5%BC%9F-http:片段标识,其中 %E5%AD%A4%E5%8D%95%E5%B0%8F%E5%BC%9F 表示中文孤单小弟

1.2 生产 HTTP 请求信息

通过 URL 解析可以得到 Web 服务器和数据文件,接下来就是根据这些信息生成 HTTP 请求消息了。

2. DNS

浏览器解析 URL 并生成 HTTP 消息,需要委托操作系统将消息发送给 Web 服务器。

但是在 URL 中我们一般是指定的域名,因此在发送之前,我们需要先查询服务器域名对应的 IP 地址。

一个很有意思的点是,对于域名 www.server.com,越靠右的位置表示其层级越高,这就有些奇怪。没办法,毕竟域名是外国人发明的,所以在思维上和中国人相反,例如对于城市名,我们一般是说(XX 省 XX 市 XX 区),外国人则说(XX 区 XX 市 XX 省)。

为了加快域名的解析速度,设置了很多缓存:

  1. 浏览器缓存
    • 现代浏览器会缓存 DNS 记录(如 Chrome 默认缓存 1 分钟)。
    • 用户可通过地址栏输入 chrome://net-internals/#dns 查看/清理缓存。
    • 减少直接向操作系统发起查询的次数。
  2. 操作系统(本地 Hosts 文件及系统解析器缓存)
    • 系统调用(如 getaddrinfo)会先查询本地 Hosts 文件(如 /etc/hosts)。
    • 未命中时检查内存中的 DNS 缓存(Windows 可通过 ipconfig/displaydns 查看)。
    • Linux 默认无持久化缓存,但可通过 nscd 服务实现。
  3. 本地域名服务器
    • 由 ISP 或企业配置(如 8.8.8.8 为公共 DNS)。
    • 缓存时间遵循 DNS 记录的 TTL(Time-To-Live),但部分运营商可能不严格遵循。
    • 未命中缓存时,会递归查询根域名服务器→顶级域(如 .com)→权威域名服务器。

3. 协议栈

通过 DNS 找到 IP 地址后,就可以把 HTTP 的传输工作交给操作系统中的协议栈。协议栈的内部分为几个部分,分别承担不同的工作。上层会向下层委托工作,下层接受上层的委托并执行:

应用程序调用 Socket 库,委托协议栈工作。其中:

  • TCP 和 UDP 协议负责接受应用层的 委托执行收发数据的操作
  • IP 协议负责控制网络包收发操作,将网络包发送给对方

此外,IP 协议中还包含 ICMP 协议和 ARP 协议:

  • ICMP 协议用于告知网络包传送过程中产生的错误以及各种控制信息
  • ARP 协议用于根据 IP 地址查询对应的 MAC 地址

IP 最下面的网卡驱动程序负责控制网卡硬件,而最下面的网卡则负责完成实际的收发数据操作,也就是对网线中的信号执行发送和接受操作

3.1 TCP

HTTP 协议是基于 TCP 协议的。

下面是 TCP 报文头部的格式:

  • 源端口号和目的端口号:确定发送方和接收方的应用程序

  • 序号:解决包乱序

  • 确认序列/确认号:期望收到的下一个字节序号,ACK=1 时有效

  • 控制标志

    标志位 名称 作用
    URG Urgent 紧急数据标志(如 Ctrl+C 中断信号)
    ACK Acknowledgment 确认号有效(建立连接后通常 ACK=1)
    PSH Push 接收方应立即推送数据给应用层(如实时聊天)
    RST Reset 强制断开连接(如端口未监听)
    SYN Synchronize 同步序列号(用于三次握手建立连接)
    FIN Finish 请求关闭连接(用于四次挥手)
  • 窗口大小:接收方可用缓冲区大小(流量控制)

  • 校验和:检验头部和数据部分的完整性

  • 紧急指针:URG=1 时有效,指向紧急数据的末尾的下一个位置

  • 可选字段:长度可变(最多 40 字节),常见选项包括:

    • MSS(Maximum Segment Size):协商最大报文段长度(如 1460 字节)。
    • Window Scaling:扩展窗口大小(突破 65535 字节限制)。
    • SACK(Selective ACK):选择性确认,提高重传效率。
    • Timestamp:时间戳,用于 RTT(往返时间)计算。

在 TCP 协议中,紧急指针(Urgent Pointer) 是一个 16 位的字段,仅在 URG=1 时有效,用于标记紧急数据的位置。它的设计是指向紧急数据的最后一个字节的下一个位置(即末尾+1),而不是指向开头。这样设计的原因时:

只要 URG=1,那么从当前位置到紧急指针所指位置的前一个位置,都是紧急数据,也即当前序列号 seq 所指位置就是紧急数据的起点。

那么,为什么当前段的序列号被视为紧急数据的起点呢?紧急数据是有可能跟在普通数据的后面的啊?

TCP 将数据视为一个连续的字节流。紧急指针标记的是这个流中“紧急状态”结束的点。当一个段设置了 URG=1 时,它是在告诉接收端:“从我这个段开始的数据(或者可能包括你已经收到但尚未处理的、在我这个段序列号之前的数据),一直到我用紧急指针标出的那个点,都是紧急的。”

更准确地说,RFC 6093 提到:“例如,如果在一个后续的 TCP 段中接收到紧急指示,并且在 TCP 用户消耗这些数据之前,那么作为‘普通数据’接收的数据可能会变成‘紧急数据’。” 这意味着紧急状态可以覆盖接收缓冲区中从某个点(这个点可能早于当前URG段的序列号)开始到紧急指针指示点的数据。

简而言之,就是如果我们想接受紧急数据,就需要先处理紧急数据之前的普通数据,因为 TCP 是字节流传输的,我们只要读取了前面的字节,才能读取后面的字节,因此从某种意义上来说,紧急数据之前的普通数据也成了紧急数据。

在 HTTP 传输数据之前,要先三次握手建立 TCP 连接,连接建立的过程可以通过状态机来维护。

为什么 TCP 建立连接需要三次握手而不是两次或四次?

  • TCP三次握手是为了确认双方的序列号,这就像一个发送—应答机制,客户端发序列号,服务端返回确认号,此时确认了客户端的序列号。如果是两次握手,只能确认客户端的序列号,无法确认服务端的序列号。三次握手是确认两个序列号最小的连接次数。四次也可以,但是没有必要,需要减少握手的次数,加快连接速度。

顺便补充一下 TCP 断开连接的流程:

为什么客户端在 TIME_WAIT 阶段要等待两个 MSL?

  • MSL 是 Maximum Segment Lifetime,即报文的最大生存时间,它表示报文在网络中存在的最长时间。超过此时间,报文将被丢弃。

  • 因为 TCP 协议是基于 IP 协议的,IP 头部有一个 TTL 字段,它表示数据报可以经过的最大路由数。每经过一个路由器,TTL 值就减 1。当 TTL 值为 0 时,数据报将被丢弃,并且发送 ICMP 报文通知源主机。MSL 和 TTL 的区别在于单位。MSL 的单位是时间,而 TTL 是经过的路由跳数。因此,为了确保报文已经自然消亡,MSL 应该大于或等于 TTL 消耗为 0 的时间。

  • TTL 的值一般是 64,Linux 将 MSL 设置为 30 秒,意味着 Linux 认为数据报文经过 64 个路由器的时间不会超过 30 秒,如果超过了,就认为报文已经消失在网络中了。

  • TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间

    比如,如果被动关闭方没有收到断开连接的最后的 ACK 报文,就会触发超时重发 FIN 报文,另一方接收到 FIN 后,会重发 ACK 给被动关闭方, 一来一去正好 2 个 MSL。

    可以看到 2MSL时长 这其实是相当于至少允许报文丢失一次。比如,若 ACK 在一个 MSL 内丢失,这样被动方重发的 FIN 会在第 2 个 MSL 内到达,TIME_WAIT 状态的连接可以应对。

    为什么不是 4 或者 8 MSL 的时长呢?你可以想象一个丢包率达到百分之一的糟糕网络,连续两次丢包的概率只有万分之一,这个概率实在是太小了,忽略它比解决它更具性价比。

    2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时

3.2 IP

TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成网络包发送给通信对象。

IP 头格式:

接收方的 IP 地址是确定的,即我们通过 DNS 解析出的 Web服务器 IP,问题是客户端的 IP 地址该如何确定呢?特别是在客户端有多个网卡,也即有多个 IP 地址的情况下,应该选择哪一个 IP 地址来发送包?

此时就需要借助 路由表 规则了,通过路由表规则,根据目标 IP 地址,配合子网掩码,找到最佳匹配路由表条目,从而确定使用哪一个网卡以及使用哪一个网关。

3.3 MAC

TCP 负责在应用间传输数据,IP 负责在网络间传输数据,MAC 则负责在具体的两台计算机检查传输数据。

MAC 头格式:

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

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

发送方的 MAC 地址获取就比较简单了,MAC 地址是在网卡生产时写入到 ROM 里的,只要将这个值读取出来写入到 MAC 头部就可以了。

接收方的 MAC 地址就要借助 ARP 协议了。

  • 当然,为了加速查找,ARP 也是有缓存的。

4. 出口:网卡

网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。

负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序

网卡驱动获取网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列

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

最后网卡会将包转为电信号,通过网线发送出去。

至此,要发送的数据才真正从发送方离开,准备踏入目的地。

5. 送别者:交换机

下面来看一下包是如何通过交换机的。交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备

然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同。

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

将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。

交换机的 MAC 地址表主要包含两个信息:

  • 一个是设备的 MAC 地址,
  • 另一个是该设备连接在交换机的哪个端口上。

地址表中找不到指定的 MAC 地址。这可能是因为具有该地址的设备还没有向交换机发送过包,或者这个设备一段时间没有工作导致地址被从地址表中删除了。

这种情况下,交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。

这样做不会产生什么问题,因为以太网的设计本来就是将包发送到整个网络的,然后只有相应的接收者才接收包,而其他设备则会忽略这个包

有人会说:“这样做会发送多余的包,会不会造成网络拥塞呢?”

其实完全不用过于担心,因为发送了包之后目标设备会作出响应,只要返回了响应包,交换机就可以将它的地址写入 MAC 地址表,下次也就不需要把包发到所有端口了。

局域网中每秒可以传输上千个包,多出一两个包并无大碍。

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

以下两个属于广播地址:

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

数据包通过交换机转发抵达了路由器,准备要离开土生土长的子网了。

6. 出境大门:路由器

路由器与交换机的区别

网络包经过交换机之后,现在到达了路由器,并在此被转发到下一个路由器或目标设备。

这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。

不过在具体的操作过程上,路由器和交换机是有区别的。

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

路由器基本原理

路由器的端口具有 MAC 地址,因此它就能够成为以太网的发送方和接收方;同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。

当转发包时,首先路由器端口会接收发给自己的以太网包,然后路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去。

路由器的包接收操作

首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验。

如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。

总的来说,路由器的端口都具有 MAC 地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。

查询路由表确定输出端口

完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。

MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃

接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作。

路由器的发送操作

接下来就会进入包的发送操作

首先,我们需要根据路由表的网关列判断对方的地址。

  • 如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。
  • 如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点

知道对方的 IP 地址之后,接下来需要通过 ARP 协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。

路由器也有 ARP 缓存,因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求。

接下来是发送方 MAC 地址字段,这里填写输出端口的 MAC 地址。还有一个以太类型字段,填写 0800 (十六进制)表示 IP 协议。

网络包完成后,接下来会将其转换成电信号并通过端口发送出去。这一步的工作过程和计算机也是相同的。

发送出去的网络包会通过交换机到达下一个路由器。由于接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。

接下来,下一个路由器会将包转发给再下一个路由器,经过层层转发之后,网络包就到达了最终的目的地。

不知你发现了没有,在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。

7. 服务器

终于,数据抵达服务器。

数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部,查看是否和服务器自己的 MAC 地址符合,符合就将包收起来。

接着继续扒开数据包的 IP 头,发现 IP 地址符合,根据 IP 头中协议项,知道自己上层是 TCP 协议。

于是,扒开 TCP 的头,里面有序列号,需要看一看这个序列包是不是我想要的,如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃。TCP头部里面还有端口号, HTTP 的服务器正在监听这个端口号。

于是,服务器自然就知道是 HTTP 进程想要这个包,于是就将包发给 HTTP 进程。

服务器的 HTTP 进程看到,原来这个请求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里。

HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址,目的地址是客户端 IP 地址。

穿好头部衣服后,从网卡出去,交由交换机转发到出城的路由器,路由器就把响应数据包发到了下一个路由器,就这样跳啊跳。

最后跳到了客户端的城门把守的路由器,路由器扒开 IP 头部发现是要找城内的人,于是又把包发给了城内的交换机,再由交换机转发到客户端。

客户端收到了服务器的响应数据包后,同样也非常的高兴,客户能拆快递了!

于是,客户端开始扒皮,把收到的数据包的皮扒剩 HTTP 响应报文后,交给浏览器去渲染页面,一份特别的数据包快递,就这样显示出来了!

最后,客户端要离开了,向服务器发起了 TCP 四次挥手,至此双方的连接就断开了。

posted @ 2025-05-21 19:18  光風霽月  阅读(27)  评论(0)    收藏  举报