面试官:从「敲下一个 URL」到「页面出现在屏幕」都经历了什么?

一句话总结:

敲下 URL → 解析地址 → DNS 找人 → (TCP/QUIC+TLS) 打通线路 → 发 HTTP 请求 → 服务器回资源 → 浏览器边下边渲染 → 四次挥手收尾

地址解析 -- URL 解析(输入处理)

用户在地址栏输入 URL 后,首先对输入的URL进行语法解析,会处理异常情况(如缺失协议补全、无效字符转义等),最终确定 "要访问的服务器" 和 "要获取的资源"。

对协议、域名、端口号、路径/资源、查询参数、锚点等进行解析

  1. 协议httphttpsftp等(默认http,但现代浏览器可能自动升级为https

  2. 域名 :如www.example.com(需转换为 IP 地址)。

  3. 端口号 :如80(HTTP 默认)、443(HTTPS 默认),未指定则用协议默认端口。

  4. 路径 / 资源 :如/index.html(服务器上的资源路径)。

  5. 查询参数 :如?id=1&name=test(键值对,用于向服务器传递额外信息)。

  6. 锚点 :如#section1(页面内定位,不发送到服务器)。

  7. 检查 HSTS 预加载列表 (仅 HTTPS):

    • 如果协议是 https://http://(且之前访问过该域并设置了 HSTS),浏览器会先检查其HSTS (HTTP Strict Transport Security) 预加载列表
    • 如果域名在这个列表中,浏览器会强制 使用 HTTPS 连接,即使你输入的是 http://。这是安全措施,防止降级攻击。

DNS解析

简单概括如下:

  1. 检查缓存:依次查询浏览器缓存、操作系统缓存(hosts文件)、路由器缓存
  2. 递归 / 迭代查询:若缓存未命中,向本地DNS服务器请求,最终通过根域名服务器、顶级域名服务器、权威域名服务器获取IP地址。
  3. 结果缓存:将域名与IP的映射存入缓存,TTL由DNS记录控制

步骤拆解

用户输入URL,提取域名(例如:www.example.com

1. 检查本地缓存,如果缓存命中则直接使用缓存IP
2. 如果没有缓存,则发起查询请求,通常有两种查询方式
  1. 迭代查询

    1. 先向其本地域名服务器进行递归查询
    2. 本地域名服务器采用迭代查询,先向下一个根域名服务器查询
    3. 根域名服务器告诉本地域名服务器,下一次查询的顶级域名服务器的IP地址
    4. 本地域名服务器向顶级域名服务器进行查询
    5. 顶级域名服务器告诉本地域名服务器,下一次应查询的权限域名服务器的IP地址
    6. 本地域名服务器向权限域名服务器进行查询
    7. 权限域名服务器告诉本地域名服务器,所查询的主机的IP地址
    8. 本地域名服务器最后把查询结果告诉主机

时序图展示:

sequenceDiagram participant Client as 客户端 participant Resolver as 本地DNS服务器 participant Root as 根服务器 participant TLD as TLD服务器 participant Auth as 权威服务器 Note over Client: 1. 发起初始查询 Client->>Resolver: DNS请求:www.example.com? Note over Resolver: 2. 本地服务器迭代查询 Resolver->>Root: 查询 www.example.com Root-->>Resolver: 推荐 .com TLD地址 Resolver->>TLD: 查询 www.example.com TLD-->>Resolver: 推荐 example.com 权威地址 Resolver->>Auth: 查询 www.example.com Auth-->>Resolver: 返回A记录 (192.0.2.1) Note over Resolver: 3. 返回最终结果 Resolver-->>Client: 最终响应:192.0.2.1
  1. 递归查询

    在递归查询的情况下,本地域名服务器只需向根域名服务器查询一次,后面的几次查询都是在其他几个域名服务器之间进行的(迭代查询的3-6),在第7步的时候,本地域名服务器从根域名服务器得到了所需的IP地址,最后在第8个步骤的时候,本地域名服务器把查询结果告诉主机。

    时序图展示:

sequenceDiagram participant Client as 客户端 participant Resolver as 递归解析器 participant Root as 根服务器 participant TLD as TLD服务器 participant Auth as 权威服务器 Note over Client: 1. 发起递归查询 Client->>Resolver: DNS请求:www.example.com? Note right of Client: 要求递归解析器
返回最终结果 Note over Resolver: 2. 递归解析器完成所有查询 Resolver->>Root: 查询 www.example.com Root-->>Resolver: 返回 .com TLD地址 Resolver->>TLD: 查询 www.example.com TLD-->>Resolver: 返回 example.com 权威地址 Resolver->>Auth: 查询 www.example.com Auth-->>Resolver: 返回A记录 (192.0.2.1) Note over Resolver: 3. 返回最终结果 Resolver-->>Client: 最终响应:192.0.2.1
3. 缓存

为了提高DNS查询效率,并减轻根域名服务器的负荷和减少互联网上的DNS查询报文数量,在域名服务器中广泛地使用了高速缓存(有时也称为高速缓存域名服务器)。

高速缓存用来存放最近查询过的域名以及从何处获得域名映射(mapping:指两个集合元素之间的一种对应规则)信息的记录。

建立TCP连接

接下来就是建立TCP连接,三次握手的阶段了

sequenceDiagram participant Client as 浏览器(客户端) participant Server as 服务器 Note over Client, Server: 三次握手开始 Client->>Server: SYN 报文(SYN=1, seq=x) Note left of Client: 客户端发送 SYN
状态:SYN_SENT Server->>Client: SYN-ACK 报文(SYN=1, ACK=1, seq=y, ack=x+1) Note right of Server: 服务器发送 SYN-ACK
状态:SYN_RECEIVED Client->>Server: ACK 报文(ACK=1, seq=x+1, ack=y+1) Note left of Client: 客户端发送 ACK
状态:ESTABLISHED Note right of Server: 状态:ESTABLISHED Note over Client, Server: 连接建立成功
开始传输数据
1. 第一次握手:客户端发送 SYN 报文

浏览器(客户端)向服务器发送TCP报文:设置SYN=1,请求建立连接,询问服务器是否准备好建立连接

此时客户端状态为 SYN_SENT

2. 第二次握手:服务器回复 SYN-ACK 报文

服务器收到SYN后回复:设置SYN=1和ACK=1(确认请求),并生成自己的初始序列号seq=y,确认号 ack=x+1,表示已经收到客户端的x

服务器状态:SYN_RECEIVED

3. 第三次握手:客户端发送 ACK 报文

客户端收到SYN-ACK后回复:设置ACK=1,序列号seq=x+1,确认号ack=y+1(表示已经收到服务器的y),确认服务器的响应,连接正式建立

双方状态:SYN_RECEIVED

字段解释:

字段 含义
SYN 同步标志位(Synchronize),用于发起连接
ACK 确认标志位(Acknowledgment),用于确认收到的数据
seq 序列号(Sequence Number),标识数据字节的顺序
ack 确认号(Acknowledgment Number),值为对方seq+1,表示期望下次收到的序号
ISN 初始序列号(Initial Sequence Number),随机生成,防止安全问题

完成TCP连接后,进行TLS握手(交换密钥、验证证书),建立加密通道,后续文章做出详细的https握手流程解释

拓展:为什么需要三次握手?

  1. 防止历史连接 -- 避免因网络延迟导致的国企连接请求被误认为新请求
  2. 同步初始序列号 -- 双方交换初始序列号(ISN),确保后续数据传输有序
  3. 验证双方通信 -- 确保双方收发能力正常

发起HTTP请求

按照 HTTP 协议格式构建请求报文,将报文通过 TCP 连接发送给服务器

关于一些http请求头我之前也有整理过,如果有需要参考的可以看一下

http请求头一览

HTTP请求格式如下:

  • 请求行方法 URL 协议版本(如GET /index.html HTTP/1.1)。
  • 请求头 :键值对信息,如Host: www.example.com(指定域名)、User-Agent: Chrome/112.0.0.0(浏览器标识)、Accept: text/html(可接收的资源类型)等。
  • 请求体 :仅POSTPUT等方法有(如表单数据、JSON 等),GET方法无请求体。

GET请求示例:

ini 复制代码
GET /api/data?user=123 HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Cookie: session=abc
Connection: keep-alive

POST请求示例:

bash 复制代码
POST /login HTTP/1.1
Host: www.example.com
Content-Type: application/json
Content-Length: 42
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X)

{"username": "john", "password": "123456"}

服务器端处理请求

篇幅过多,简要概括如下;

  1. 反向代理与负载均衡:请求可能先到达Nginx反向代理,转发到应用服务器集群
  2. 应用服务器处理:执行后端代码,访问数据库或缓存,生成动态内容
  3. 返回响应:状态码(200 OK)、响应头以及响应体

浏览器解析与渲染

一、解析与构建DOM树

  1. 字节流解码 浏览器将接收的字节流 (Bytes)根据编码(如UTF-8)转换为字符串

  2. 生成Tokens HTML解析器将字符串转换为W3C标准Token (如<html><div>)。

  3. 构建DOM树

    • 根据Token层级关系构建DOM节点,最终形成树状结构。

    • 关键特性

      • 预加载扫描器:并行加载CSS/JS/图片资源,不阻塞解析。
      • 阻塞行为 :遇到<script>(无async/defer)暂停解析,执行JS。

二、CSS解析与样式计算

  1. 解析CSS

    • 解析外部CSS、内联样式,生成CSSOM树(CSS Object Model)。

    • 特性:

      • 层叠规则 :解决样式冲突(优先级:!important > 内联 > ID > Class > 标签)。
      • 继承 :部分样式(如font-size)向子节点传递。
  2. 构建渲染树(Render Tree)

    • 合并DOM树与CSSOM树,生成包含可见元素的渲染树。
    • 排除元素:display: none<head>等不可见节点。

三、布局(Layout / Reflow)

  1. 计算布局

    • 遍历渲染树,计算每个节点的精确位置和尺寸(盒模型)。
    • 视口尺寸变化、字体加载等会触发重排(重新布局)。

四、绘制(Painting)

  1. 生成绘制指令

    • 将布局结果转换为绘制命令(如"在坐标(10,10)绘制100x50矩形")。
    • 分为多个图层(Layer)以提高效率。
  2. 栅格化(Rasterization)

    • 合成线程 将图层分割为图块(Tiles)。
    • 栅格线程池 将图块转为位图(存储在GPU内存中)。

五、合成与显示(Compositing)

  1. 合成帧

    • 合成线程整合位图、处理变换(Transform/Opacity)生成合成帧
    • 优势:跳过布局与绘制,直接合成(性能最佳)。
  2. 显示图像

    • 合成帧通过IPC传递给浏览器进程,最终由GPU渲染到屏幕。
    • 同步刷新率(如60Hz),实现流畅显示。

其中可能涉及到子资源加载的时候,引发DOM回流或重绘

断开连接(四次挥手)

TCP 的四次挥手是用于终止一个已建立的 TCP 连接的过程。它确保了双方都能完成数据的发送和接收,并确认对方已准备好关闭连接。

第一次挥手

主动关闭发送FIN(FIN=1,Seq=u),客户端决定不再发送数据,希望关闭到服务器的连接

状态 FIN_WAIT_1,不再发送数据,但是仍可以接收数据

第二次挥手

服务端发送ACK(ACK=1,Ack=u+1)确认,服务端进入CLOSE_WAIT状态(半关闭状态),客户端收到ACK后进入FIN_WAIT_2状态

客户端不再发送数据,服务器端不再接收数据,但是服务器端到客户端的连接仍然打开(即可能还有数据发送给客户端,客户端也还能接收数据)

第三次挥手

服务端发送FIN+ACK报文(FIN=1,ACK=1,Seq=v,ACK=U+1),服务端进入:LAST_ACK状态

等待客户端对FIN报文的最终确认

第四次挥手

客户端回复ACK确认(ACK=1,Ack=v+1),客户端进入TIME_WAIT状态,等待2MSL,服务端收到ACK后立即关闭连接,客户端2MSL超时后关闭连接

双方进入CLOSED状态

图表展示:

sequenceDiagram participant Client as 主动关闭方 (Client) participant Server as 被动关闭方 (Server) Note over Client: 初始状态: ESTABLISHED Note over Server: 初始状态: ESTABLISHED rect rgb(255, 240, 240) Client ->> Server: [FIN] Seq=u (FIN=1) Note left of Client: 进入 FIN_WAIT_1 状态 end rect rgb(240, 255, 240) Server -->> Client: [ACK] Ack=u+1 (ACK=1) Note right of Server: 进入 CLOSE_WAIT 状态
(半关闭状态) Note left of Client: 收到ACK后
进入 FIN_WAIT_2 状态 end rect rgb(240, 240, 255) Server ->> Client: [FIN, ACK] Seq=v, Ack=u+1
(FIN=1, ACK=1) Note right of Server: 进入 LAST_ACK 状态 end rect rgb(255, 255, 240) Client -->> Server: [ACK] Ack=v+1 (ACK=1) Note left of Client: 进入 TIME_WAIT 状态
等待 2MSL (通常30s-2min) Server -->> Client: 收到ACK后立即关闭连接 Note right of Server: 进入 CLOSED 状态 Note left of Client: 2MSL超时后
进入 CLOSED 状态 end

此文章是整理的一道常见面试题,随后整理了一篇文章出来,不算详细,算作是记录,希望大家给出意见,随后会对每个步骤进行深挖,下一篇文章先对浏览器渲染流程步骤进行详解

相关推荐
老坛0019 分钟前
2025决策延迟的椭圆算子分析:锐减协同工具的谱间隙优化
前端
老坛00110 分钟前
从记录到预测:2025新一代预算工具如何通过AI实现前瞻性资金管理
前端
今禾13 分钟前
" 当Base64遇上Blob,图像转换不再神秘,让你的网页瞬间变身魔法画布! "
前端·数据可视化
华科云商xiao徐17 分钟前
高性能小型爬虫语言与代码示例
前端·爬虫
十盒半价18 分钟前
深入理解 React useEffect:从基础到实战的全攻略
前端·react.js·trae
攀登的牵牛花19 分钟前
Electron+Vue+Python全栈项目打包实战指南
前端·electron·全栈
iccb101319 分钟前
我是如何实现在线客服系统的极致稳定性与安全性的
前端·javascript·后端
一大树20 分钟前
Vue3祖孙组件通信方法总结
前端·vue.js
不要进入那温驯的良夜21 分钟前
跨平台UI自动化-Appium
前端
海底火旺22 分钟前
以一个简单的React应用理解数据绑定的重要性
前端·css·react.js