一句话总结:
敲下 URL → 解析地址 → DNS 找人 → (TCP/QUIC+TLS) 打通线路 → 发 HTTP 请求 → 服务器回资源 → 浏览器边下边渲染 → 四次挥手收尾
地址解析 -- URL 解析(输入处理)
用户在地址栏输入 URL 后,首先对输入的URL进行语法解析,会处理异常情况(如缺失协议补全、无效字符转义等),最终确定 "要访问的服务器" 和 "要获取的资源"。
对协议、域名、端口号、路径/资源、查询参数、锚点等进行解析
-
协议 :
http
、https
、ftp
等(默认http
,但现代浏览器可能自动升级为https
) -
域名 :如
www.example.com
(需转换为 IP 地址)。 -
端口号 :如
80
(HTTP 默认)、443
(HTTPS 默认),未指定则用协议默认端口。 -
路径 / 资源 :如
/index.html
(服务器上的资源路径)。 -
查询参数 :如
?id=1&name=test
(键值对,用于向服务器传递额外信息)。 -
锚点 :如
#section1
(页面内定位,不发送到服务器)。 -
检查 HSTS 预加载列表 (仅 HTTPS):
- 如果协议是
https://
或http://
(且之前访问过该域并设置了 HSTS),浏览器会先检查其HSTS (HTTP Strict Transport Security) 预加载列表。 - 如果域名在这个列表中,浏览器会强制 使用 HTTPS 连接,即使你输入的是
http://
。这是安全措施,防止降级攻击。
- 如果协议是
DNS解析
简单概括如下:
- 检查缓存:依次查询浏览器缓存、操作系统缓存(hosts文件)、路由器缓存
- 递归 / 迭代查询:若缓存未命中,向本地DNS服务器请求,最终通过根域名服务器、顶级域名服务器、权威域名服务器获取IP地址。
- 结果缓存:将域名与IP的映射存入缓存,TTL由DNS记录控制
步骤拆解
用户输入URL,提取域名(例如:www.example.com
)
1. 检查本地缓存,如果缓存命中则直接使用缓存IP
2. 如果没有缓存,则发起查询请求,通常有两种查询方式
-
迭代查询
- 先向其本地域名服务器进行递归查询
- 本地域名服务器采用迭代查询,先向下一个根域名服务器查询
- 根域名服务器告诉本地域名服务器,下一次查询的顶级域名服务器的IP地址
- 本地域名服务器向顶级域名服务器进行查询
- 顶级域名服务器告诉本地域名服务器,下一次应查询的权限域名服务器的IP地址
- 本地域名服务器向权限域名服务器进行查询
- 权限域名服务器告诉本地域名服务器,所查询的主机的IP地址
- 本地域名服务器最后把查询结果告诉主机
时序图展示:
-
递归查询
在递归查询的情况下,本地域名服务器只需向根域名服务器查询一次,后面的几次查询都是在其他几个域名服务器之间进行的(迭代查询的3-6),在第7步的时候,本地域名服务器从根域名服务器得到了所需的IP地址,最后在第8个步骤的时候,本地域名服务器把查询结果告诉主机。
时序图展示:
返回最终结果 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连接,三次握手的阶段了
状态: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握手流程解释。
拓展:为什么需要三次握手?
- 防止历史连接 -- 避免因网络延迟导致的国企连接请求被误认为新请求
- 同步初始序列号 -- 双方交换初始序列号(ISN),确保后续数据传输有序
- 验证双方通信 -- 确保双方收发能力正常
发起HTTP请求
按照 HTTP 协议格式构建请求报文,将报文通过 TCP 连接发送给服务器
关于一些http请求头我之前也有整理过,如果有需要参考的可以看一下
HTTP请求格式如下:
- 请求行 :
方法 URL 协议版本
(如GET /index.html HTTP/1.1
)。 - 请求头 :键值对信息,如
Host: www.example.com
(指定域名)、User-Agent: Chrome/112.0.0.0
(浏览器标识)、Accept: text/html
(可接收的资源类型)等。 - 请求体 :仅
POST
、PUT
等方法有(如表单数据、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"}
服务器端处理请求
篇幅过多,简要概括如下;
- 反向代理与负载均衡:请求可能先到达Nginx反向代理,转发到应用服务器集群
- 应用服务器处理:执行后端代码,访问数据库或缓存,生成动态内容
- 返回响应:状态码(200 OK)、响应头以及响应体
浏览器解析与渲染
一、解析与构建DOM树
-
字节流解码 浏览器将接收的字节流 (Bytes)根据编码(如UTF-8)转换为字符串。
-
生成Tokens HTML解析器将字符串转换为W3C标准Token (如
<html>
、<div>
)。 -
构建DOM树
-
根据Token层级关系构建DOM节点,最终形成树状结构。
-
关键特性:
- 预加载扫描器:并行加载CSS/JS/图片资源,不阻塞解析。
- 阻塞行为 :遇到
<script>
(无async/defer)暂停解析,执行JS。
-
二、CSS解析与样式计算
-
解析CSS
-
解析外部CSS、内联样式,生成CSSOM树(CSS Object Model)。
-
特性:
- 层叠规则 :解决样式冲突(优先级:
!important
> 内联 > ID > Class > 标签)。 - 继承 :部分样式(如
font-size
)向子节点传递。
- 层叠规则 :解决样式冲突(优先级:
-
-
构建渲染树(Render Tree)
- 合并DOM树与CSSOM树,生成包含可见元素的渲染树。
- 排除元素:
display: none
、<head>
等不可见节点。
三、布局(Layout / Reflow)
-
计算布局
- 遍历渲染树,计算每个节点的精确位置和尺寸(盒模型)。
- 视口尺寸变化、字体加载等会触发重排(重新布局)。
四、绘制(Painting)
-
生成绘制指令
- 将布局结果转换为绘制命令(如"在坐标(10,10)绘制100x50矩形")。
- 分为多个图层(Layer)以提高效率。
-
栅格化(Rasterization)
- 合成线程 将图层分割为图块(Tiles)。
- 栅格线程池 将图块转为位图(存储在GPU内存中)。
五、合成与显示(Compositing)
-
合成帧
- 合成线程整合位图、处理变换(Transform/Opacity)生成合成帧。
- 优势:跳过布局与绘制,直接合成(性能最佳)。
-
显示图像
- 合成帧通过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状态
图表展示:
(半关闭状态) 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
此文章是整理的一道常见面试题,随后整理了一篇文章出来,不算详细,算作是记录,希望大家给出意见,随后会对每个步骤进行深挖,下一篇文章先对浏览器渲染流程步骤进行详解