“输入 URL 回车后,页面是怎么显示出来的?” —— 它能串联起计算机网络五层模型的所有核心知识点。本文会按照 应用层→传输层→网络层→数据链路层→物理层 的顺序,把每个环节掰开揉碎讲清楚,保证新手看完就能吃透。
首先我们过一下完整流程框架:
- 浏览器输入 URL 并回车
- DNS 解析:把域名翻译成服务器的 IP 地址
- 建立 TCP 连接:三次握手搞定可靠传输通道
- 发送 HTTP/HTTPS 请求:向服务器要网页资源
- 服务器处理请求并返回响应:把 HTML 等数据发回浏览器
- 浏览器解析渲染页面:加载 HTML、CSS、JS、图片等资源
- 关闭 TCP 连接:四次挥手断开通道
下面我们针对每个环节,做详细扩展。
一、 应用层:一切的起点 ——URL 解析与 DNS 域名解析
应用层是直接和用户 “打交道” 的层级,我们输入 URL、解析域名、发送请求的操作,都发生在这一层。
1. 第一步:浏览器解析 URL(Uniform Resource Locators 统一资源定位器)
当你在浏览器地址栏输入 https://www.example.com:8080/path/to/index.html?name=test#top 并回车时,浏览器做的第一件事,就是拆解 URL 的每一部分,搞清楚 “要去哪里、要拿什么、用什么方式拿”。
我们以这个完整 URL 为例,拆解它的 6 个核心组成部分,每个部分的作用和细节如下:
| 组成部分 | 示例内容 | 核心作用 | 细节 |
|---|---|---|---|
| 协议部分 | https:// |
规定浏览器和服务器的通信规则 | 常见协议: 1. http://:超文本传输协议,明文传输,端口默认 802. https://:HTTP+SSL/TLS,加密传输更安全,端口默认 4433. 其他: ftp://(文件传输)、file://(本地文件) |
| 域名部分 | www.example.com |
服务器的 “可读名字”,对应唯一 IP | 域名≠IP:域名是为了方便人记忆,IP 才是服务器在网络中的真实地址 也可以直接输入 IP 访问,比如 http://127.0.0.1:8080(本地服务器) |
| 端口部分 | :8080 |
标记服务器上的 “具体服务入口” | 1. 端口范围 0-65535,0-1023 是系统保留端口(如 80、443) 2. 省略时用默认端口:HTTP 默认 80,HTTPS 默认 443 3. 作用:一台服务器可以开多个端口,提供不同服务(如 8080 跑 Java Web,3306 跑 MySQL) |
| 资源路径部分 | /path/to/index.html |
服务器上的具体文件位置 | 1. 以/开头,对应服务器的 “资源根目录”2. 现在很多路径是虚拟路径:不是服务器上真实的文件位置,而是后端框架(如 Spring Boot)映射的接口地址 |
| 参数部分 | ?name=test |
浏览器传给服务器的额外数据 | 1. 格式:?key1=value1&key2=value2,多个参数用&分隔2. 特点:参数会暴露在地址栏,长度有限制(不同浏览器不一样),适合传递少量非敏感数据 3. 注意:如果参数有中文,会自动进行 URL 编码(如 “张三”→ %E5%BC%A0%E4%B8%89) |
| 锚点部分 | #top |
页面内的定位标记 | 注意:: 1. 锚点不会发送给服务器!浏览器解析到 #时,只会在本地页面滚动到对应 id 的位置(如<div id="top"></div>)2. 作用:实现页面内的快速跳转,比如长文档的目录链接 |
拆解完 URL 后,浏览器确认了协议是 HTTPS、目标服务器是www.example.com、端口是 8080,接下来就要解决核心问题:www.example.com对应的 IP 地址是什么? —— 这就是 DNS 解析的工作。
2. 第二步:DNS 域名解析 —— 把 “名字” 翻译成 “地址”
DNS(Domain Name System 域名系统)的作用,相当于互联网的 “电话簿”—— 通过域名查 IP,或者通过 IP 查域名(反向解析)。
我们用 “查字典” 的比喻,一步步讲清楚浏览器是怎么找到 IP 的,全程遵循 “先本地,后远程” 的原则,优先查缓存,提高效率。
(1) 浏览器本地缓存查询
浏览器会先查自己的DNS 缓存—— 这是浏览器为了加快访问速度,保存的最近访问过的域名 - IP 映射记录,有过期时间(TTL)。
- 比如你昨天刚访问过
www.example.com,今天再访问,浏览器直接从缓存里拿 IP,不用走后续步骤。 - 怎么看?Chrome 浏览器可以输入
chrome://net-internals/#dns查看本地 DNS 缓存。
(2) 操作系统本地缓存查询
如果浏览器缓存里没有,就会去查操作系统的本地 DNS 缓存。
- Windows 系统:缓存存在系统服务里,可以用命令
ipconfig /displaydns查看,ipconfig /flushdns清空。 - Linux/Mac 系统:可以用
dig命令查询,缓存由 nscd 服务管理。
(3) 本地 Hosts 文件查询
如果系统缓存也没有,浏览器会去读本地 Hosts 文件—— 这是一个没有扩展名的文本文件,手动配置域名和 IP 的映射。
- Windows 路径:
C:\Windows\System32\drivers\etc\hosts - Linux/Mac 路径:
/etc/hosts - 新手小技巧:可以在 Hosts 文件里写
127.0.0.1 www.example.com,这样访问这个域名时,会直接指向本地服务器,常用于开发测试。
(4) 向本地 DNS 服务器发起请求(递归查询)
如果前面三步都没找到,真正的 “远程查询” 才开始。浏览器会向本地 DNS 服务器(一般是你的路由器,或者运营商提供的 DNS 服务器,比如电信的 114.114.114.114)发起请求。
这个过程是递归查询:你只需要告诉本地 DNS 服务器 “我要查www.example.com的 IP”,剩下的事不用管,它会帮你搞定,查不到就返回错误。
(5) 本地 DNS 服务器的迭代查询(核心步骤)
本地 DNS 服务器拿到请求后,如果自己的缓存里没有,就会进行迭代查询,相当于 “逐级问路”,从根 DNS 服务器开始,一级一级往下查:
问根 DNS 服务器:“请问www.example.com的 IP 在哪里?”
根 DNS 服务器是互联网的最高级 DNS 服务器,全球只有 13 台(实际上已经是200多台了),它不知道具体 IP,但会告诉你:“.com 域名的管理服务器在这,你去问它。”
问顶级域名服务器(.com 服务器):顶级域名服务器管理.com、.cn、.org 等后缀的域名,它会告诉你:“example.com的域名服务器在这,你去问它。”
问权威 DNS 服务器(example.com服务器):这是example.com域名的专属 DNS 服务器,记录了这个域名的所有解析记录(A 记录:域名→IPv4;AAAA 记录:域名→IPv6)。它会直接返回
www.example.com对应的 IP 地址。
(6) 结果返回
权威 DNS 服务器把 IP 返回给本地 DNS 服务器,本地 DNS 服务器缓存这个结果,再把 IP 返回给浏览器。
到这里,应用层的核心工作就完成了 —— 浏览器拿到了服务器的IP 地址和端口号,接下来要进入传输层,建立可靠的连接。
二、 传输层:建立 TCP 连接 —— 三次握手的 “可靠约定”
应用层的 HTTP/HTTPS 协议,必须基于 TCP 协议来传输数据 —— 因为 HTTP 本身是 “无连接、无状态” 的,而 TCP 的 “面向连接、可靠传输” 特性,能保证数据不会丢、不会乱序。
传输层的核心工作,就是通过三次握手,在浏览器(客户端)和服务器之间建立一条稳定的 “数据传输通道”。
我们先明确两个核心概念:
- 客户端 / 服务器:浏览器是客户端,目标服务器是服务端。
- 序列号(seq)/ 确认号(ack):TCP 通过这两个编号保证数据可靠,seq 是自己发送数据的编号,ack 是==期望对方下一次发送的编号==(= 对方上一次发的 seq + 数据长度)。
- 标志位:SYN(请求建立连接)、ACK(确认收到数据)、FIN(请求关闭连接)。
TCP 三次握手的详细过程
你可以把三次握手理解为客户端和服务器的 “通话约定”:
| 握手次数 | 发起方 | 发送的内容 | 核心作用 | 通俗理解 |
|---|---|---|---|---|
| 第一次握手 | 客户端→服务器 | SYN=1,seq=x(随机生成的初始序列号) | 客户端:“我想和你建立连接,我的初始序列号是 x,你收到了吗?” | “喂,你在吗?我想和你说话。” |
| 第二次握手 | 服务器→客户端 | SYN=1,ACK=1,seq=y,ack=x+1 | 服务器:“我收到了(ack=x+1),我也想和你建立连接,我的初始序列号是 y,你收到了吗?” | “我在,我也想和你说话,你听到我的回应了吗?” |
| 第三次握手 | 客户端→服务器 | ACK=1,seq=x+1,ack=y+1 | 客户端:“我收到了(ack=y+1),我们的连接可以建立了!” | “我听到了,那我们开始聊天吧!” |
新手必知的三次握手关键点
为什么需要三次握手? —— 防止 “失效的连接请求” 占用服务器资源。
比如客户端发的连接请求因为网络延迟,很久才到服务器,服务器回了确认,但客户端已经超时了,不会处理这个确认。如果只有两次握手,服务器会以为连接建立了,一直等客户端发数据,浪费资源;三次握手的话,客户端没发第三次确认,服务器就知道连接没建立成功。
三次握手成功后:客户端和服务器都进入
ESTABLISHED(已建立连接)状态,这条通道是全双工的 —— 双方可以同时发数据。
补充:HTTPS 的额外步骤 ——SSL/TLS 握手
如果是 HTTPS 协议,在 TCP 三次握手之后,还会多一步SSL/TLS 握手,目的是建立加密通道,防止数据被窃听、篡改:
- 客户端向服务器发送支持的 SSL/TLS 版本、加密套件列表。
- 服务器选择加密套件,返回数字证书(包含服务器公钥)。
- 客户端验证证书合法性(比如是否由可信机构颁发)。
- 客户端生成一个随机的 “会话密钥”,用服务器公钥加密后发给服务器。
- 服务器用自己的私钥解密,拿到会话密钥。
- 双方用会话密钥进行对称加密通信(对称加密速度快,适合传输大量数据)。
到这里,传输层的连接就完全建立好了,接下来应用层的 HTTP 请求,就可以在这条可靠的通道里传输了。
三、 应用层再发力:发送 HTTP 请求 & 服务器处理并返回响应
TCP 连接建立后,浏览器就可以向服务器发送HTTP 请求报文,索要网页资源了。这一步依然属于应用层,是 HTTP 协议的核心交互过程。
1. HTTP 请求报文的结构
HTTP 请求报文由请求行、请求头、空行、请求体四部分组成,我们用一个实际的 GET 请求例子来拆解:
GET /path/to/index.html?name=test HTTP/1.1 (请求行)
Host: www.example.com:8080 (请求头)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0
Accept: text/html,application/xhtml+xml
Connection: keep-alive
(空行)
(请求体:GET请求一般没有请求体)
请求行:核心三要素 —— 请求方法、请求路径、HTTP 版本
常见请求方法:
GET:从服务器获取资源(如网页、图片),参数在 URL 里,安全且幂等(多次请求结果一样)POST:向服务器提交数据(如表单、文件),参数在请求体里,长度无限制其他:
PUT(更新资源)、DELETE(删除资源)、HEAD(只获取响应头)
请求头:键值对形式,传递额外信息
核心请求头:
Host:目标服务器的域名和端口(必须有,因为一台服务器可能托管多个域名)User-Agent:客户端的浏览器和系统信息(服务器可以根据这个返回适配的页面)Connection: keep-alive:HTTP 1.1 默认开启,复用 TCP 连接,不用每次请求都三次握手,提高效率
空行:必须有,用来分隔请求头和请求体
请求体:存放要提交的数据,GET 请求一般为空,POST 请求才有(比如表单数据
username=test&password=123456)
2. 服务器处理请求的过程
服务器收到 HTTP 请求后,会按照以下步骤处理:
- 解析请求报文:Web 服务器(如 Nginx、Tomcat)先解析请求行、请求头,拿到请求方法、资源路径、参数等信息。
- 路由匹配:如果是动态网页(如 Java Web、Spring Boot 项目),后端框架会根据请求路径,匹配对应的控制器方法(如
@RequestMapping("/path/to/index"))。 - 业务逻辑处理:控制器方法执行业务逻辑(如查询数据库、调用其他接口)。
- 生成响应数据:业务逻辑处理完后,生成要返回的内容 —— 可以是静态 HTML 文件,也可以是动态生成的 JSON 数据。
- 封装 HTTP 响应报文:把响应内容封装成 HTTP 响应报文,返回给浏览器。
3. HTTP 响应报文的结构(和请求报文对应)
HTTP 响应报文由状态行、响应头、空行、响应体四部分组成,例子如下:
HTTP/1.1 200 OK (状态行)
Server: Nginx/1.21.6 (响应头)
Content-Type: text/html; charset=utf-8
Content-Length: 1024
Connection: keep-alive
(空行)
<!DOCTYPE html> (响应体:HTML代码)
<html>
<head><title>Example</title></head>
<body><h1>Hello World</h1></body>
</html>
状态行:核心三要素 ——HTTP 版本、状态码、状态描述
核心状态码:
200 OK:请求成功,服务器返回数据301 Moved Permanently:永久重定向(比如域名更换)302 Found:临时重定向(比如登录后跳转到首页)404 Not Found:请求的资源不存在(路径写错了)500 Internal Server Error:服务器内部错误(后端代码 bug)
响应头:键值对形式,告诉浏览器怎么处理响应体
核心响应头:
Content-Type:响应体的类型(MIME 类型),如text/html(HTML 文件)、image/jpeg(图片)、application/json(JSON 数据)Content-Length:响应体的长度(字节数)Set-Cookie:服务器向浏览器设置 Cookie(用于保存用户状态,比如登录信息)
空行:分隔响应头和响应体
响应体:服务器返回的实际数据,比如 HTML 代码、图片二进制数据、JSON 字符串
四、 网络层 + 数据链路层 + 物理层:数据的 “快递运输” 过程
前面我们讲了应用层和传输层的交互,但数据从客户端到服务器,不是直接飞过去的 —— 需要经过网络层、数据链路层、物理层的层层封装和传输,这个过程就像寄快递:
1. 数据封装:从应用层到物理层(层层打包)
当浏览器要发送 HTTP 请求报文时,数据会从上层到下层,每一层都加上自己的 “头部信息”,这个过程叫封装:
- 应用层:生成 HTTP 请求报文(数据)
- 传输层:加上 TCP 头部(包含源端口、目的端口、seq、ack),变成TCP 报文段
- 网络层:加上 IP 头部(包含源 IP、目的 IP),变成IP 数据报
- 数据链路层:加上 MAC 头部(包含源 MAC 地址、目的 MAC 地址)和尾部(校验码),变成数据帧
- 物理层:把数据帧转换成二进制比特流(0 和 1),通过网线、WiFi 等物理介质传输
2. 路由转发:IP 数据报的 “寻路之旅”(网络层核心)
网络层的核心是IP 协议,负责将 IP 数据报从客户端 “送” 到服务器,关键是路由转发:
- IP 地址的作用:源 IP 是客户端的地址,目的 IP 是服务器的地址,相当于快递的 “寄件人地址” 和 “收件人地址”。
- 路由器的工作:数据在传输过程中,会经过多个路由器。每个路由器都有一张路由表,记录了 “到某个 IP 网段,应该往哪个端口转发”。路由器收到 IP 数据报后,会根据目的 IP 查路由表,选择最优路径,把数据报转发给下一个路由器,直到送到目标服务器所在的局域网。
- 注意:网络层的 IP 协议是无连接、不可靠的 —— 它只管转发,不管数据有没有丢、有没有乱序;可靠性由传输层的 TCP 协议保证(TCP 会重传丢失的报文段)。很像渣男对吧!
3. 数据解封装:从物理层到应用层(层层拆包)
当数据帧到达服务器所在的物理网卡后,会从下层到上层,每一层都拆掉自己的头部信息,这个过程叫解封装:
- 物理层:把比特流转换成数据帧,传给数据链路层
- 数据链路层:拆掉 MAC 头部和尾部,校验数据是否正确,把 IP 数据报传给网络层
- 网络层:拆掉 IP 头部,把 TCP 报文段传给传输层
- 传输层:拆掉 TCP 头部,根据端口号,把 HTTP 请求报文传给应用层的 Web 服务器
- 应用层:Web 服务器解析 HTTP 请求报文,开始处理请求
总结:数据从客户端到服务器,经历了 “封装→传输→解封装” 的过程;五层模型的每一层,都只关心自己的头部信息,上层的数据对它来说就是 “透明的负载”。
五、 浏览器的 “重头戏”:解析响应 & 渲染页面
浏览器收到服务器返回的 HTTP 响应后,如果响应体是 HTML 代码,就会进入解析和渲染阶段—— 这是前端的核心工作,也是页面最终显示的关键。
这个阶段会触发多次 HTTP 请求,因为 HTML 页面里通常包含很多 “子资源”(CSS、JS、图片、字体等),浏览器需要逐个请求这些资源,才能完整显示页面。
我们把这个过程拆成6 个核心步骤:
1. 解析 HTML,生成 DOM 树(Document Object Model 文档对象模型)
浏览器会逐行解析 HTML 代码,把每个标签(如<html>、<body>、<h1>)转换成 DOM 树的一个节点,节点包含标签的属性、文本内容等信息。
比如这段 HTML:
<html> <body> <h1>Hello</h1> <p>World</p> </body> </html>生成的 DOM 树结构是:
html→body→h1、p。解析过程中,如果遇到阻塞性资源(如
<script>标签),会暂停解析 HTML,先下载并执行 JS—— 因为 JS 可能会修改 DOM 树(比如document.write())。- 优化技巧:把
<script>标签放在</body>前面,或者加defer/async属性,避免阻塞 HTML 解析。
- 优化技巧:把
2. 解析 CSS,生成 CSSOM 树(CSS Object Model CSS 对象模型)
浏览器会解析 HTML 中的<style>标签,以及<link rel="stylesheet">引用的外部 CSS 文件,生成 CSSOM 树 ——CSSOM 树记录了所有样式规则(如h1 { color: red; })。
- CSSOM 树和 DOM 树一样,也是树形结构,样式规则会继承到子节点(比如给
body设置font-size,子节点h1、p都会继承)。 - 注意:CSS 是渲染阻塞的 —— 浏览器必须等 CSSOM 树生成完,才能进行下一步的布局,因为 CSS 会影响元素的位置和大小。
3. 结合 DOM 树和 CSSOM 树,生成渲染树(Render Tree)
渲染树是 ** 只包含 “可见元素”** 的树形结构:
- 会忽略不可见的元素,比如
<head>标签、display: none的元素。 - 每个节点都会附加对应的样式规则(从 CSSOM 树继承或匹配)。
- 比如前面的例子,渲染树的节点是:
body→h1(红色)、p(默认样式)。
4. 布局(Layout):计算元素的位置和大小(回流 / 重排)
浏览器会根据渲染树,计算每个元素的几何属性:
- 位置:元素在页面中的 x、y 坐标
- 大小:元素的宽度、高度、内边距、外边距
- 这个过程也叫回流(Reflow)或重排—— 如果后续修改了元素的几何属性(如
width、margin),浏览器会重新执行布局,性能开销较大。
5. 绘制(Painting):把元素画到屏幕上(重绘)
布局完成后,浏览器会遍历渲染树,把每个元素的视觉属性(如颜色、背景、边框)画到屏幕上,这个过程叫绘制(Painting)。
- 如果修改了元素的视觉属性(如
color、background-color),不会影响布局,只会触发重绘(Repaint)—— 重绘的性能开销比回流小。
6. 合成(Compositing):分层显示,提升性能
现代浏览器会把页面分成多个图层(比如<video>、<canvas>、transform属性的元素会单独分层),每个图层独立绘制,最后合成到一起显示在屏幕上。
- 优势:如果只修改某个图层的内容,不用重绘整个页面,只需要重绘这个图层,再合成一次 —— 这是前端性能优化的重要手段(比如用
transform做动画,比用top/left更流畅)。
7. 加载子资源,重复请求 - 渲染过程
在解析 HTML 的过程中,浏览器遇到<img>、<link>、<script>等标签时,会触发新的 HTTP 请求:
- 比如
<img src="logo.jpg">:浏览器会根据src的 URL,重复前面的 DNS 解析、TCP 连接、HTTP 请求过程,下载图片资源,然后把图片绘制到对应的位置。 - 浏览器会并发请求多个子资源(一般是 6-8 个,不同浏览器不一样),但会遵循同源策略(默认不能跨域请求资源,除非服务器设置 CORS 头)。
直到所有子资源都加载完成,页面才会完全显示在浏览器窗口中。
六、 传输层:关闭 TCP 连接 —— 四次挥手的 “告别流程”
当页面加载完成后,浏览器和服务器就可以关闭 TCP 连接了。由于 TCP 是全双工连接,关闭连接需要四次挥手,比三次握手多一次 —— 因为双方都需要单独告诉对方 “我没有数据要发了”。
TCP 四次挥手的详细过程
把四次挥手理解为客户端和服务器的 “告别对话”:
| 挥手次数 | 发起方 | 发送的内容 | 核心作用 | 通俗理解 |
|---|---|---|---|---|
| 第一次挥手 | 客户端→服务器 | FIN=1,seq=u | 客户端:“我没有数据要发了,我的序列号是 u,你收到了吗?” | “我说完了,轮到你了。” |
| 第二次挥手 | 服务器→客户端 | ACK=1,seq=v,ack=u+1 | 服务器:“我收到了(ack=u+1),我还有点数据没发完,你等我一下。” | “我知道你说完了,我还有点话要说。” |
| 第三次挥手 | 服务器→客户端 | FIN=1,ACK=1,seq=w,ack=u+1 | 服务器:“我也没有数据要发了,我的序列号是 w,你收到了吗?” | “我说完了,我们可以结束了。” |
| 第四次挥手 | 客户端→服务器 | ACK=1,seq=u+1,ack=w+1 | 客户端:“我收到了(ack=w+1),请等我 2MSL 时间,我确认你收到了我的确认,我们的连接就关闭了。” | “我知道你说完了,我等一会儿,确认你收到消息,就挂了。” |
四次挥手关键点
为什么需要四次挥手? —— TCP 是全双工连接,双方都需要独立关闭发送通道。
第一次和第二次挥手,关闭客户端→服务器的通道;第三次和第四次挥手,关闭服务器→客户端的通道。
2MSL 等待时间:客户端发完第四次挥手的确认后,会等待 2 倍的 MSL(Maximum Segment Lifetime,报文段最大生存时间),再关闭连接。
目的是:确保服务器收到第四次确认;如果服务器没收到,会重发第三次挥手的 FIN 报文,客户端在 2MSL 时间内收到后,会重新发第四次确认。
特殊情况:Connection: keep-alive:如果 HTTP 请求头里设置了
Connection: keep-alive,TCP 连接不会立即关闭,会被复用,用来传输后续的子资源请求(如 CSS、JS),减少三次握手的开销。
七、 总结:从 URL 回车到页面显示的完整流程图(新手必背)
我们把所有步骤串起来,形成一个完整的闭环:
输入URL回车
↓
浏览器解析URL(协议、域名、端口、路径)
↓
DNS解析(本地缓存→Hosts→本地DNS→根DNS→顶级DNS→权威DNS)→ 拿到IP地址
↓
传输层:TCP三次握手(客户端→SYN → 服务器→SYN+ACK → 客户端→ACK)→ 建立连接
↓
(HTTPS专属)SSL/TLS握手 → 建立加密通道
↓
应用层:浏览器发送HTTP请求报文(请求行+请求头+空行+请求体)
↓
数据封装(应用层→传输层→网络层→数据链路层→物理层)
↓
网络层:IP路由转发 → 数据通过物理介质传输到服务器
↓
数据解封装(物理层→数据链路层→网络层→传输层→应用层)
↓
服务器解析HTTP请求 → 处理业务逻辑 → 返回HTTP响应报文(状态行+响应头+空行+响应体)
↓
浏览器接收响应 → 解析HTML生成DOM树 → 解析CSS生成CSSOM树 → 合成渲染树
↓
布局(计算位置大小)→ 绘制(画到屏幕)→ 合成(分层显示)
↓
请求HTML中的子资源(CSS、JS、图片等)→ 重复上述流程
↓
页面完全显示 → 传输层:TCP四次挥手 → 关闭连接