面试官:从「敲下一个 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

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

相关推荐
文火冰糖的硅基工坊14 分钟前
[嵌入式系统-146]:五次工业革命对应的机器人形态的演进、主要功能的演进以及操作系统的演进
前端·网络·人工智能·嵌入式硬件·机器人
2401_8370885026 分钟前
ResponseEntity - Spring框架的“标准回复模板“
java·前端·spring
yaoganjili35 分钟前
用 Tinymce 打造智能写作
前端
angelQ42 分钟前
Vue 3 中 ref 获取 scrollHeight 属性为 undefined 问题定位
前端·javascript
Dontla1 小时前
(临时解决)Chrome调试避免跳入第三方源码(设置Blackbox Scripts、将目录添加到忽略列表、向忽略列表添加脚本)
前端·chrome
我的div丢了肿么办1 小时前
js函数声明和函数表达式的理解
前端·javascript·vue.js
云中雾丽1 小时前
React.forwardRef 实战代码示例
前端
朝歌青年说1 小时前
一个在多年的技术债项目中写出来的miniHMR热更新工具
前端
沐怡旸1 小时前
【底层机制】【Android】AIDL原理与实现机制详解
android·面试
Moonbit1 小时前
倒计时 2 天|Meetup 议题已公开,Copilot 月卡等你来拿!
前端·后端