一次完整的 HTTP 请求经历什么步骤?
一、HTTP 请求的前置知识:什么是 HTTP?
HTTP(HyperText Transfer Protocol,超文本传输协议)是一种基于TCP/IP 协议簇的应用层协议,用于在客户端(如浏览器、App)和服务器之间传输超文本数据(如 HTML、CSS、JavaScript、图片、JSON 等)。其核心特点包括:
无状态特性:
- 服务器不会记录客户端的历史请求状态,每个HTTP请求都被视为独立的新请求
- 实际业务中通过以下机制解决无状态问题:
- Cookie:客户端存储的小型文本数据(如会话ID)
- Session:服务器端存储的用户会话数据
- Token:基于JWT等技术的认证令牌
连接管理演进:
- HTTP/1.0时期采用"短连接"模式:每次请求需建立新的TCP连接,响应后立即断开(三次握手/四次挥手开销大)
- HTTP/1.1引入"长连接"优化(Keep-Alive):
- 默认保持TCP连接(Connection: keep-alive)
- 可复用同一连接处理多个请求(减少握手开销)
- 通过超时机制自动关闭空闲连接
请求-响应模型:
- 客户端(User Agent)主动发起请求,包含:
- 请求方法(GET/POST等)
- 请求URL
- 请求头(Headers)
- 请求体(Body,可选)
- 服务器返回响应,包含:
- 状态码(200/404等)
- 响应头
- 响应体(实际数据)
- 客户端(User Agent)主动发起请求,包含:
完整请求流程示例(以访问HTTPS网站为例):
URL解析:浏览器解析
https://www.csdn.net
,提取:- 协议类型(HTTPS)
- 主机名(www.csdn.net)
- 默认路径(/,对应首页)
DNS查询:
- 查询本地DNS缓存
- 未命中则发起递归查询(本地DNS服务器→根域名服务器→顶级域名服务器→权威域名服务器)
- 最终获取目标服务器IP地址
TCP连接:
- 三次握手建立TCP连接(SYN→SYN-ACK→ACK)
- HTTPS会额外进行TLS握手(协商加密算法、验证证书、交换密钥等)
发送HTTP请求:
GET / HTTP/1.1 Host: www.csdn.net User-Agent: Mozilla/5.0 Accept: text/html,application/xhtml+xml
服务器处理:
- Web服务器(如Nginx)接收请求
- 可能涉及反向代理、负载均衡、静态资源处理等
- 应用服务器执行动态内容生成
返回响应:
HTTP/1.1 200 OK Content-Type: text/html; charset=UTF-8 Cache-Control: max-age=3600<!DOCTYPE html> <html>...</html>
浏览器渲染:
- 解析HTML构建DOM树
- 加载CSS/JS等资源
- 执行JavaScript代码
- 最终呈现可视化页面
注:HTTPS在HTTP基础上增加了TLS/SSL加密层,提供:
- 数据加密传输
- 服务器身份验证
- 消息完整性校验 通过CA机构颁发的数字证书实现安全通信。
二、一次完整 HTTP 请求的 8 个核心步骤
步骤 1:用户输入 URL 并触发请求
这是整个过程的起点。当用户在浏览器地址栏输入 URL(Uniform Resource Locator,统一资源定位符)并按下回车后,浏览器会启动一系列复杂的处理流程。这个过程涉及多个关键步骤和技术细节。
URL 组成与解析细节
URL 的标准格式为:<scheme>://<domain>:<port>/<path>?<query>#<fragment>
以 https://www.csdn.net/blog/article?id=123
为例:
协议(Scheme):
https
表示使用 HTTPS 协议- HTTPS 是 HTTP 的安全版本,在 HTTP 基础上增加了 TLS/SSL 加密层
- 默认使用 443 端口(HTTP 使用 80 端口)
- 加密过程涉及证书验证和密钥交换等步骤
域名(Domain):
www.csdn.net
- 可能包含多级子域名
- 需要通过 DNS 系统解析为 IP 地址
- 浏览器会先检查本地 DNS 缓存(包括:
- 浏览器缓存
- 操作系统缓存
- hosts 文件记录
- 路由器缓存)
端口(Port):未显式指定时使用默认端口
- HTTPS: 443
- HTTP: 80
- 其他常见服务端口:
- FTP: 21
- SSH: 22
路径(Path):
/blog/article
- 表示服务器上的资源路径
- 可能对应实际文件路径或虚拟路径
查询参数(Query):
id=123
- 以
?
开头 - 多个参数用
&
连接 - 用于向服务器传递额外信息
- 以
片段标识(Fragment):
#section
(示例中未出现)- 用于定位页面内特定位置
- 不会发送到服务器
浏览器处理流程
输入预处理:
- 自动补全协议(如输入
csdn.net
会自动补全为https://csdn.net
) - 处理特殊字符编码
- 检查是否为有效 URL
- 自动补全协议(如输入
缓存检查:
- 查询浏览器缓存策略
- 检查强缓存(Cache-Control/Expires)
- 检查协商缓存(Last-Modified/ETag)
安全验证:
- 对 HTTPS 网站验证证书有效性
- 检查 HSTS 预加载列表
- 验证混合内容风险
准备网络请求:
- 创建请求报文
- 设置请求头(User-Agent, Accept等)
- 处理 Cookie 等本地存储数据
实际应用场景
- 电商网站:
https://www.example.com/products?category=electronics&sort=price
- 社交平台:
https://api.social.com/users/123/posts?limit=10
- 搜索引擎:
https://search.com/results?q=web+development&page=2
步骤 2:DNS 解析:将域名转换为 IP 地址
背景介绍
互联网通信本质上是基于IP地址(如180.101.50.242)进行的,但IP地址难以记忆。为了方便用户使用,发明了域名系统(DNS),将人类友好的域名(如www.csdn.net)转换为机器可识别的IP地址。
DNS解析原理
DNS解析是一个分层查询的过程,采用分布式数据库架构。整个系统由根DNS服务器、顶级域DNS服务器、权威DNS服务器和本地DNS服务器组成,形成层级关系。
详细解析步骤
浏览器缓存查询
- 现代浏览器都会维护一个DNS缓存表
- 缓存时间受TTL(Time To Live)值控制,通常在几分钟到几小时
- 示例:Chrome浏览器可通过chrome://net-internals/#dns查看缓存
操作系统缓存查询
- 操作系统层面也维护DNS缓存
- Windows系统可通过
ipconfig /displaydns
命令查看 - Linux系统可通过
/etc/hosts
文件配置静态映射 - macOS同时使用mDNSResponder进行本地解析
本地DNS服务器查询
- 通常由ISP(互联网服务提供商)分配
- 企业网络可能使用自建DNS服务器
- 常见的公共DNS服务器:
- Google DNS:8.8.8.8
- Cloudflare DNS:1.1.1.1
- 阿里DNS:223.5.5.5
根DNS服务器查询
- 全球共13组根服务器(A-M)
- 采用任播技术实现全球分布式部署
- 只负责返回顶级域服务器的地址
顶级域DNS服务器查询
- 根据域名后缀(如.com、.net)查询对应的服务器
- 负责管理该顶级域下的权威服务器信息
权威DNS服务器查询
- 域名注册时指定的DNS服务器
- 存储着最准确的域名解析记录
- 常见的记录类型包括:
- A记录:IPv4地址
- AAAA记录:IPv6地址
- CNAME记录:别名记录
- MX记录:邮件服务器记录
结果缓存与返回
- 本地DNS服务器会缓存结果
- 缓存时间由权威服务器返回的TTL值决定
- 最终将IP地址返回给请求的客户端
实际应用场景
- 网站访问:用户输入域名后自动完成解析
- 邮件发送:通过MX记录查找邮件服务器地址
- CDN加速:根据用户位置返回最优节点IP
- 负载均衡:通过DNS轮询实现简单的流量分发
性能优化
- DNS预取(Prefetching):浏览器提前解析页面中的链接
- 持久连接:减少重复解析次数
- 智能DNS:根据用户位置返回最优IP
安全考虑
- DNSSEC:防止DNS欺骗
- DNS over HTTPS/TLS:加密DNS查询
- 防火墙规则:限制DNS查询来源
至此,浏览器获得目标服务器的IP地址,可以开始建立TCP连接。整个DNS解析过程通常在毫秒级别完成,但对网页加载速度有重要影响。
步骤 3:建立 TCP 连接(三次握手)
HTTP 协议是基于 TCP 协议实现的,因此在发送 HTTP 请求之前,客户端(通常是浏览器)需要与服务器建立一个可靠的 TCP 连接。这个建立连接的过程被称为"三次握手"(Three-Way Handshake),其核心目的是确保客户端和服务器之间的双向通信通道是可靠和同步的。
三次握手详解
"三次握手"的根本目的是要确认双方的发送和接收能力都正常运作。这个过程可以形象地理解为两个人初次见面时的互相确认:
第一次握手(客户端→服务器):
- 客户端向服务器发送一个SYN(Synchronize,同步)报文
- 报文头部包含:
- SYN=1(表示这是一个连接请求)
- 随机生成的初始序列号seq=x(通常是一个随机数)
- 此时客户端进入SYN_SENT状态
- 示例:假设客户端选择的初始序列号x=123456789
第二次握手(服务器→客户端):
- 服务器收到SYN报文后,如果同意建立连接:
- 发送SYN+ACK报文(同时包含同步和确认)
- 报文头部包含:
- SYN=1(表示这也是一个同步请求)
- ACK=1(表示确认客户端的SYN请求)
- 确认号ack=x+1(即123456790,表示期望收到客户端的下一个序列号)
- 服务器自己生成的随机序列号seq=y
- 此时服务器进入SYN_RCVD状态
- 示例:假设服务器选择的初始序列号y=987654321
- 服务器收到SYN报文后,如果同意建立连接:
第三次握手(客户端→服务器):
- 客户端收到SYN+ACK报文后:
- 发送ACK确认报文
- 报文头部包含:
- ACK=1
- 确认号ack=y+1(即987654322)
- 序列号seq=x+1(即123456790)
- 此时客户端进入ESTABLISHED状态
- 服务器收到ACK后也进入ESTABLISHED状态
- 客户端收到SYN+ACK报文后:
为什么需要三次握手?
三次握手的必要性主要体现在以下几个方面:
防止历史连接初始化导致的资源浪费:
- 假设网络延迟导致客户端第一次发送的SYN报文延迟到达
- 客户端会超时重发新的SYN报文并建立连接
- 如果延迟的SYN后来到达服务器,而只有两次握手:
- 服务器会直接建立连接并等待数据
- 但客户端已经不需要这个连接
- 导致服务器资源被无效占用
同步双方的初始序列号:
- 三次握手确保双方都确认了对方的初始序列号
- 这是TCP可靠传输的基础
避免资源浪费:
- 在SYN Flood攻击场景下,服务器在第二次握手后需要保留资源
- 三次握手确保只有客户端完成最后一次ACK后才完全建立连接
实际应用场景
在Web浏览中,当你在浏览器地址栏输入网址并回车时:
- 浏览器首先通过DNS解析获取服务器IP
- 然后发起TCP三次握手过程
- 只有在三次握手完成后,才会发送HTTP请求
这个过程通常只需要几十毫秒就能完成,但对于需要高并发的服务器来说,优化TCP连接建立过程(如TCP Fast Open技术)可以显著提升性能。
步骤 4:(HTTPS 专属)TLS/SSL 握手:建立加密通道
当使用 HTTPS(Hypertext Transfer Protocol Secure)而非 HTTP 时,在 TCP 连接建立后,必须进行 TLS/SSL(Transport Layer Security/Secure Sockets Layer)握手过程。这一过程用于建立加密的通信通道,确保数据在传输过程中不会被窃取或篡改,提供了数据机密性、完整性和身份验证三大安全特性。
TLS/SSL 握手的主要目的是:
- 协商加密算法
- 交换会话密钥
- 验证服务器身份
详细握手流程(以 TLS 1.2 为例):
客户端 Hello(Client Hello):
- 客户端向服务器发送初始握手信息,包含:
- 支持的 TLS 版本(如 TLS 1.2、TLS 1.3)
- 支持的加密套件列表(如 TLS_RSA_WITH_AES_256_GCM_SHA384)
- 随机数 Client Random(用于后续密钥生成)
- 支持的压缩方法(可选,现代浏览器通常禁用压缩)
- 扩展字段(如服务器名称指示 SNI)
- 客户端向服务器发送初始握手信息,包含:
服务器 Hello(Server Hello):
- 服务器从客户端提供的选项中选择:
- 确定使用的 TLS 版本(必须与客户端支持版本兼容)
- 选定的加密套件(从客户端提供的列表中选取最强的可用套件)
- 生成随机数 Server Random
- 确定是否启用压缩
- 服务器还会发送:
- 会话ID(用于会话恢复)
- 扩展字段响应
- 服务器从客户端提供的选项中选择:
服务器证书(Certificate):
- 服务器发送其数字证书链,包含:
- 服务器公钥(用于后续密钥交换)
- 证书颁发机构(CA)信息
- 有效期信息
- 域名信息(Subject Alternative Name)
- 数字签名(由CA私钥生成)
- 常见证书颁发机构:Let's Encrypt、DigiCert、GoDaddy、GlobalSign等
- 服务器发送其数字证书链,包含:
服务器密钥交换(可选,Server Key Exchange):
- 在某些密钥交换算法(如DH、ECDH)下需要额外交换参数
- 包含服务器临时密钥对的公钥部分
证书请求(可选,Certificate Request):
- 在双向认证时,服务器会要求客户端提供证书
- 包含服务器信任的CA列表
服务器 Hello 完成(Server Hello Done):
- 表示服务器已完成初始握手信息的发送
- 等待客户端响应
客户端证书验证:
- 客户端验证服务器证书:
- 检查证书链完整性
- 验证签名有效性
- 检查有效期
- 验证域名匹配
- 检查证书吊销状态(通过CRL或OCSP)
- 验证失败时浏览器会显示警告(如NET::ERR_CERT_DATE_INVALID)
- 客户端验证服务器证书:
客户端密钥交换(Client Key Exchange):
- 客户端生成预主密钥(Pre-Master Secret)
- 使用服务器公钥加密(RSA)或执行密钥协商(ECDHE)
- 发送给服务器
证书验证(可选,Client Certificate):
- 在双向认证时,客户端发送其证书
证书验证(可选,Certificate Verify):
- 客户端证明持有证书对应的私钥
会话密钥生成:
- 双方使用以下参数计算主密钥:
- Client Random
- Server Random
- Pre-Master Secret
- 通过伪随机函数(PRF)生成:
- 主密钥(Master Secret)
- 会话密钥(Session Key)
- 初始化向量(IV)
- 双方使用以下参数计算主密钥:
加密切换通知(Change Cipher Spec):
- 双方通知对方后续通信将使用协商的加密参数
完成消息(Finished):
- 包含所有握手消息的HMAC
- 验证握手过程未被篡改
- 确认双方拥有相同的会话密钥
握手优化技术:
会话恢复:
- 通过会话ID或会话凭证(Session Ticket)快速恢复会话
- 避免完整握手过程
TLS 1.3 改进:
- 减少握手往返次数(1-RTT或0-RTT)
- 移除不安全的加密算法
- 简化握手流程
常见加密套件示例:
RSA密钥交换:
- TLS_RSA_WITH_AES_128_CBC_SHA
- TLS_RSA_WITH_AES_256_GCM_SHA384
ECDHE密钥交换:
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
安全注意事项:
- 必须禁用不安全的加密套件(如RC4、DES)
- 确保使用强随机数生成器
- 定期更新服务器证书
- 启用OCSP Stapling减少验证延迟
- 考虑启用HSTS防止降级攻击
握手完成后,所有HTTP通信数据都会使用协商的会话密钥进行加密传输,通常采用对称加密算法(如AES)以保证传输效率。
步骤 5:发送 HTTP 请求详解
当 TCP 连接(或 HTTPS 的 TLS/SSL 加密通道)建立后,客户端(浏览器)会按照 HTTP 协议的规范,构造一个 HTTP 请求报文,并通过已建立的连接发送给服务器。
5.1 HTTP 请求报文结构
HTTP 请求报文由三部分组成:
- 请求行(Request Line):必需部分,包含请求方法、请求路径和协议版本
- 请求头(Request Headers):必需部分,包含多个键值对形式的元数据
- 请求体(Request Body):可选部分,仅在 POST、PUT 等包含数据的请求方法中存在
5.1.1 完整请求报文示例(POST 请求)
POST /api/login HTTP/1.1
Host: www.csdn.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/118.0.0.0
Content-Type: application/json
Content-Length: 45
Cookie: JSESSIONID=abc123xyz;
Accept: application/json{"username":"test","password":"123456"}
5.2 请求报文详细解析
5.2.1 请求行(Request Line)
请求行包含三个关键元素,用空格分隔:
[请求方法] [请求路径] [HTTP版本]
1. 请求方法(定义客户端对资源的操作意图):
GET
:获取资源(如浏览网页、查询数据)- 特点:参数通常拼接在URL中(如
/search?q=keyword
) - 示例:访问新闻网站首页
GET /news HTTP/1.1
- 特点:参数通常拼接在URL中(如
POST
:提交/创建资源(如用户登录、表单提交)- 特点:参数放在请求体中,安全性更好
- 示例:用户登录
POST /login HTTP/1.1
PUT
:更新资源(全量替换)- 示例:更新用户信息
PUT /users/123 HTTP/1.1
- 示例:更新用户信息
DELETE
:删除资源- 示例:删除文章
DELETE /articles/456 HTTP/1.1
- 示例:删除文章
HEAD
:类似GET但只返回响应头- 用途:检查资源是否存在或获取元数据
PATCH
:部分更新资源OPTIONS
:获取服务器支持的通信选项
2. 请求路径:
- 表示目标资源在服务器上的位置
- 可以是绝对路径(如
/images/logo.png
)或完整URL(代理请求时) - 可能包含查询字符串(如
/search?q=term&page=2
)
3. HTTP版本:
- 常见版本:HTTP/1.1(最广泛)、HTTP/2(支持多路复用)、HTTP/3(基于QUIC)
- 版本协商:客户端声明支持的最高版本,服务器选择合适版本响应
5.2.2 请求头(Request Headers)
请求头以键值对形式提供附加信息,每个头字段占一行。常见重要头字段:
头字段 | 说明 | 示例值 |
---|---|---|
Host | 服务器域名(HTTP/1.1必需) | www.example.com |
User-Agent | 客户端软件标识 | Mozilla/5.0 (Macintosh) |
Cookie | 客户端存储的会话信息 | sessionid=abc123 |
Content-Type | 请求体数据格式 | application/json |
Content-Length | 请求体字节数 | 1024 |
Accept | 可接受的响应格式 | text/html,application/xhtml+xml |
Authorization | 认证凭证 | Bearer xyz123 |
Referer | 来源页面URL | https://www.google.com/ |
Cache-Control | 缓存控制指令 | no-cache |
5.2.3 请求体(Request Body)
存在条件:
- POST、PUT、PATCH等需要传输数据的请求方法
- 即使允许包含请求体的方法(如GET),服务器也可能忽略
常见数据格式:
application/x-www-form-urlencoded
- 传统表单默认格式
- 示例:
username=test&password=123456
multipart/form-data
- 文件上传时使用
- 包含boundary分隔不同部分
application/json
- 现代API常用格式
- 示例:
{"username":"test","password":"123456"}
text/xml
- XML格式数据
- 示例:
<user><name>test</name></user>
5.3 请求发送过程
- 报文构造:客户端按照HTTP规范组装请求报文
- 编码传输:
- HTTP/1.1:纯文本格式传输
- HTTP/2:二进制分帧,支持多路复用
- 连接管理:
- 非持久连接:发送请求后立即关闭
- 持久连接(HTTP/1.1默认):保持连接复用
- 等待响应:请求发送完成后,客户端进入等待状态,准备接收服务器响应
5.4 实际应用示例
场景:用户提交登录表单
- 浏览器检测到表单提交事件
- 根据form的method和action属性确定:
- 请求方法:POST
- 请求路径:/login
- 收集表单数据并编码(默认application/x-www-form-urlencoded)
- 添加必要请求头:
POST /login HTTP/1.1 Host: www.example.com Content-Type: application/x-www-form-urlencoded Content-Length: 29 Cookie: csrftoken=abc123username=test&password=123456
- 通过已建立的TCP连接发送请求
技术说明:
- 现代单页应用(SPA)通常使用fetch/XHR发送JSON格式请求
- RESTful API设计遵循HTTP方法语义(GET查、POST增、PUT改、DELETE删)
- 安全考虑:敏感参数应放在POST请求体中,而非URL查询字符串
步骤 6: 服务器处理请求并返回 HTTP 响应
服务器接收到客户端的 HTTP 请求后,会按照以下详细流程进行处理并返回响应:
6.1 服务器接收请求
服务器端通常采用多层次的架构来处理请求:
负载均衡层:对于高流量网站,可能首先由负载均衡器(如 AWS ELB、F5等)接收请求,根据负载均衡策略分发到不同的服务器节点。
Web服务器层:常见Web服务器包括:
- Nginx:高性能的异步事件驱动服务器
- Apache:传统的多进程/多线程服务器
- IIS:微软的Web服务器
应用服务器层:根据技术栈不同,可能包括:
- Java EE:Tomcat、Jetty、WildFly
- Python:uWSGI、Gunicorn(配合Nginx)
- Node.js:原生HTTP服务器或Express等框架
6.2 应用程序处理请求
应用程序处理流程通常包括:
请求解析:
- 解析请求行(方法、URI、协议版本)
- 解析请求头(User-Agent、Accept、Cookie等)
- 解析请求体(对于POST/PUT请求)
路由匹配:根据URL路径匹配对应的控制器或处理函数
- 静态路由:如
/about
- 动态路由:如
/users/:id
- 静态路由:如
中间件处理:
- 身份验证(检查JWT或Session)
- 请求日志记录
- 请求参数验证
- 跨域处理(CORS)
业务逻辑处理:
- 数据库操作(MySQL、MongoDB等)
- 缓存操作(Redis、Memcached)
- 外部API调用
- 文件操作
响应生成:
- 设置响应状态码
- 设置响应头
- 生成响应体
6.3 HTTP 响应报文详细结构
HTTP响应报文的标准格式如下:
[状态行]
[响应头]
[空行]
[响应体]
详细示例:
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Tue, 21 Oct 2025 08:30:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 32
Connection: keep-alive
Cache-Control: max-age=3600
Set-Cookie: token=def456ghi; Path=/; HttpOnly; Secure; SameSite=Strict
X-Request-ID: 5e9b8f7d6c5b4a3b2c1d0e0f
Strict-Transport-Security: max-age=31536000; includeSubDomains{"code":200,"message":"登录成功","data":{}}
6.4 各部分核心含义详解
状态行
包含三个部分:
- HTTP版本:如
HTTP/1.1
或HTTP/2
- 状态码:3位数字代码
- 状态描述:人类可读的简短描述
常见状态码扩展说明:
2xx 成功:
- 200 OK:标准成功响应
- 201 Created:资源创建成功
- 202 Accepted:请求已接受但尚未处理完成
- 204 No Content:成功执行但无内容返回
3xx 重定向:
- 301 Moved Permanently:永久重定向
- 302 Found:临时重定向
- 307 Temporary Redirect:临时重定向(保持方法不变)
- 308 Permanent Redirect:永久重定向(保持方法不变)
4xx 客户端错误:
- 400 Bad Request:请求语法错误
- 401 Unauthorized:需要认证
- 403 Forbidden:认证通过但无权限
- 404 Not Found:资源不存在
- 405 Method Not Allowed:请求方法不被允许
- 429 Too Many Requests:请求过于频繁
5xx 服务器错误:
- 500 Internal Server Error:通用服务器错误
- 502 Bad Gateway:上游服务器无效响应
- 503 Service Unavailable:服务暂时不可用
- 504 Gateway Timeout:上游服务器响应超时
响应头详解
常见响应头及其作用:
安全相关:
- Strict-Transport-Security:强制HTTPS
- Content-Security-Policy:内容安全策略
- X-Frame-Options:防止点击劫持
- X-XSS-Protection:XSS防护
缓存控制:
- Cache-Control:缓存指令
- ETag:资源标识符
- Last-Modified:最后修改时间
内容相关:
- Content-Type:MIME类型和编码
- Content-Encoding:压缩方式(gzip, deflate)
- Content-Language:内容语言
CORS相关:
- Access-Control-Allow-Origin
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
响应体类型
根据Content-Type可以分为:
文本类:
- text/html:HTML文档
- text/css:样式表
- text/javascript:JS代码
- text/plain:纯文本
媒体类:
- image/jpeg:JPEG图片
- image/png:PNG图片
- audio/mpeg:MP3音频
- video/mp4:MP4视频
应用数据类:
- application/json:JSON数据
- application/xml:XML数据
- application/pdf:PDF文档
- application/octet-stream:二进制流
多部分类型:
- multipart/form-data:表单文件上传
- multipart/byteranges:部分内容响应
服务器完成响应处理后,会通过建立的TCP连接将响应数据发送回客户端。对于Keep-Alive连接,该TCP连接可能会被保持用于后续请求;否则在响应完成后会关闭连接。
步骤 7:TCP 连接关闭(四次挥手)
四次挥手过程
当客户端接收到服务器返回的 HTTP 响应后,若不再需要与服务器进行通信(如页面加载完成且短期内无后续请求),则需要关闭 TCP 连接,这一过程通过 "四次挥手"(Four-Way Handshake)完成。
步骤说明(以客户端为 A,服务器为 B 为例)
第一次挥手(A→B):
- 客户端 A 发送 FIN 报文:
- FIN=1(表示请求终止连接)
- seq=u(u 是客户端 A 已发送数据的最后一个字节的序号 + 1)
- 状态变化:
- 客户端 A 进入 FIN_WAIT_1 状态
- 服务端 B 仍处于 ESTABLISHED 状态
- 客户端 A 发送 FIN 报文:
第二次挥手(B→A):
- 服务器 B 发送 ACK 报文:
- ACK=1(确认收到 FIN)
- ack=u+1(确认序号)
- seq=v(v 是服务器 B 已发送数据的最后一个字节的序号 + 1)
- 状态变化:
- 客户端 A 进入 FIN_WAIT_2 状态
- 服务端 B 进入 CLOSE_WAIT 状态
- 此时服务器 B 可能仍在传输剩余数据
- 服务器 B 发送 ACK 报文:
第三次挥手(B→A):
- 服务器 B 发送 FIN 报文:
- FIN=1
- ACK=1
- ack=u+1
- seq=w(w 是服务器 B 补充发送数据后的最后一个字节的序号 + 1)
- 状态变化:
- 服务端 B 进入 LAST_ACK 状态
- 客户端 A 仍处于 FIN_WAIT_2 状态
- 服务器 B 发送 FIN 报文:
第四次挥手(A→B):
- 客户端 A 发送 ACK 报文:
- ACK=1
- ack=w+1
- seq=u+1
- 状态变化:
- 客户端 A 进入 TIME_WAIT 状态(默认等待 2MSL)
- 服务端 B 收到后进入 CLOSED 状态
- 客户端 A 在 TIME_WAIT 状态结束后(约4分钟)进入 CLOSED 状态
- 客户端 A 发送 ACK 报文:
关键概念解释
TIME_WAIT 状态
- 持续时间:通常为 2MSL(Maximum Segment Lifetime)
- MSL 值:RFC 793 建议 2 分钟,但实际实现中通常为30秒到2分钟
- 作用:
- 确保最后一个 ACK 能到达服务器(否则服务器会重传 FIN)
- 让网络中滞留的旧报文段失效,避免影响新的连接
为什么需要四次挥手
TCP 是全双工通信协议,关闭连接需要分别确认两个方向的数据传输终止:
客户端→服务器方向:
- 第一次挥手:客户端通知服务器"我要关闭发送通道"
- 第二次挥手:服务器确认收到关闭请求
服务器→客户端方向:
- 第三次挥手:服务器通知客户端"我也要关闭发送通道"
- 第四次挥手:客户端确认收到关闭请求
异常情况处理
FIN_WAIT_2 状态滞留:
- 如果服务器迟迟不发送 FIN,客户端会超时关闭
- 可通过设置 tcp_fin_timeout 参数控制超时时间
LAST_ACK 状态滞留:
- 如果客户端不发送最后一个 ACK,服务器会重传 FIN
- 重传次数由 tcp_orphan_retries 参数控制
TIME_WAIT 状态优化:
- 高并发服务器可启用 tcp_tw_reuse 或 tcp_tw_recycle
- 但要注意 NAT 环境下可能带来的问题
实际应用示例
场景:Web 浏览器关闭网页时的连接终止过程
- 浏览器(客户端)发送 FIN 报文给服务器
- 服务器立即回复 ACK,但可能仍在发送图片等资源
- 服务器完成所有数据发送后发送 FIN
- 浏览器确认后连接进入 TIME_WAIT 状态
- 2MSL 时间后连接完全关闭
步骤 8:浏览器渲染页面(仅针对 HTML 响应)
当服务器返回的响应体是 HTML 页面(Content-Type: text/html)时,客户端(通常是浏览器)会执行一系列复杂的渲染流程,将原始的HTML文档转换为用户可视化的网页界面。这个过程涉及多个互相配合的子模块,包括HTML解析器、CSS解析器、JavaScript引擎和渲染引擎等。下面详细介绍浏览器渲染页面的完整过程:
8.1 解析 HTML,构建 DOM 树
浏览器接收到的HTML文档本质上是一个字节流,渲染引擎首先需要将这些字节转换为字符(通常使用UTF-8编码),然后将字符转换为标记(Tokens)。这个转换过程由HTML解析器完成,它遵循HTML5规范定义的解析算法:
- 标记化(Tokenization):将HTML文档分解为合法的HTML元素标记,如开始标签、结束标签、属性名和值等。
- 构建DOM节点:将标记转换为DOM节点对象,每个HTML元素对应一个DOM节点。
- 构建DOM树:根据HTML的嵌套关系,将DOM节点组织成树形结构。
以以下HTML代码为例:
<!DOCTYPE html>
<html>
<head><title>CSDN首页</title>
</head>
<body><h1>欢迎访问CSDN</h1><p>专注于IT技术交流</p>
</body>
</html>
浏览器会构建如下DOM树结构:
Document(根节点)
├── html(根元素节点)├── head(子节点)│ └── title(子节点)│ └── 文本节点 "CSDN 首页"└── body(子节点)├── h1(子节点)│ └── 文本节点 "欢迎访问 CSDN"└── p(子节点)└── 文本节点 "专注于 IT 技术交流"
资源加载处理
在解析HTML过程中,遇到以下资源时会触发额外的处理:
CSS资源(
<link rel="stylesheet">
):- 浏览器会创建新的网络请求异步加载CSS文件
- HTML解析可以继续,不会被CSS加载阻塞
- 但渲染会被阻塞,直到CSSOM构建完成
JavaScript资源(
<script>
):- 默认情况下,HTML解析会被暂停
- 浏览器会同步加载并执行JavaScript代码
- 执行完成后才继续HTML解析
- 可以使用
async
或defer
属性改变加载行为:async
:异步加载,加载完成后立即执行defer
:异步加载,延迟到文档解析完成后再执行
8.2 解析 CSS,构建 CSSOM 树
CSS解析是构建CSS对象模型(CSSOM)的过程。与DOM树类似,CSSOM也是一棵树状结构,但有以下特点:
- CSS解析是逐步进行的,从最通用的规则到最具体的规则
- CSS具有层叠特性,后定义的规则可能覆盖前面的规则
- 浏览器会计算每个DOM节点最终应用的样式(computed style)
例如,对于以下CSS代码:
body { font-size: 16px; color: #333;
}
h1 { font-size: 24px; font-weight: bold;
}
p { color: #666;
}
浏览器会构建如下CSSOM树结构:
CSSStyleSheet
├── body规则
│ ├── font-size: 16px
│ └── color: #333
├── h1规则
│ ├── font-size: 24px
│ └── font-weight: bold
└── p规则└── color: #666
8.3 结合 DOM 树与 CSSOM 树,生成渲染树
渲染树(Render Tree)是实际显示在屏幕上的内容的结构表示。构建渲染树的过程包括:
遍历DOM树:从根节点开始,访问每个可见节点
- 不包含
display: none
的元素 - 包含
visibility: hidden
的元素(仍占据空间) - 不包含
<head>
、<meta>
等不可见元素
- 不包含
匹配CSS规则:为每个可见节点查找并应用对应的CSS样式
- 考虑CSS选择器的优先级和特异性
- 处理继承的样式属性
创建渲染对象:为每个可见节点创建对应的渲染对象
- 包含节点的几何信息和样式信息
- 不同类型的节点(如文本、图片)会有不同的渲染对象
构建渲染树结构:按照DOM树的层次组织渲染对象
8.4 布局(Layout):计算元素位置和大小
布局阶段(也称为"重排"或"回流")确定每个渲染对象在视口中的确切位置和尺寸。这个过程包括:
计算几何信息:
- 确定每个元素的宽度、高度
- 计算元素相对于视口的位置(x,y坐标)
- 考虑盒模型(content-box或border-box)的影响
处理布局模式:
- 常规流(Normal Flow):默认的文档流布局
- 浮动(Float):元素脱离文档流,向左或向右浮动
- 定位(Position):absolute、fixed、relative等定位方式
- Flex布局:弹性盒模型布局
- Grid布局:网格布局系统
布局计算过程:
- 从根渲染对象开始递归计算
- 父渲染对象先确定自己的宽度
- 然后依次放置子渲染对象
- 最后确定所有元素的确切位置
8.5 绘制(Painting):将元素渲染到屏幕
绘制阶段将布局计算出的几何信息转换为屏幕上实际的像素。这个过程包括:
绘制顺序:
- 按照渲染树的顺序绘制
- 先绘制背景和边框
- 然后绘制内容(文本、图片等)
- 最后绘制浮动和定位元素
绘制内容:
- 文本:使用字体引擎渲染文字
- 图片:解码并渲染图像数据
- 边框和背景:根据CSS样式绘制
绘制优化:
- 浏览器可能会将页面分成多个图层
- 只重绘需要更新的部分
- 使用GPU加速某些绘制操作
8.6 合成(Compositing):合并图层并显示
现代浏览器使用合成技术来提高渲染性能:
图层划分:
- 某些元素会被提升为单独的图层(如transform、opacity等)
- 每个图层有自己的绘制缓冲区
- 图层可以独立于其他图层进行绘制和变换
图层绘制:
- 主线程将图层信息提交给合成线程
- 合成线程将图层分成图块(tiles)
- 栅格化线程池将图块栅格化为位图
合成显示:
- 合成线程收集所有图块的绘制命令(draw quads)
- 通过GPU进程将最终图像显示在屏幕上
- 利用垂直同步(VSync)信号实现流畅的动画效果
渲染性能优化
了解渲染流程后,可以采取以下优化措施:
减少重排和重绘:
- 避免频繁修改样式
- 使用transform和opacity实现动画
- 批量DOM操作
优化CSS选择器:
- 避免过度复杂的选择器
- 减少通配符和后代选择器的使用
合理使用图层:
- 谨慎使用will-change属性
- 避免过多的图层导致内存消耗
JavaScript优化:
- 将脚本放在body底部或使用defer/async
- 避免长时间运行的JavaScript阻塞渲染
至此,浏览器完成了从HTML文档到可视化页面的完整转换过程,用户可以开始与页面进行交互。
三、总结与扩展
3.1 关键优化点
在实际开发中,为了提升 HTTP 请求的效率和用户体验,通常会针对以下环节进行优化(附具体实现方法和应用场景):
DNS 优化:
- 浏览器缓存:Chrome默认缓存DNS记录60秒
- DNS预解析:在HTML头部添加
<link rel="dns-prefetch" href="//example.com">
- HTTPDNS方案:如阿里云HTTPDNS服务,典型应用场景包括移动端APP
- 示例:电商网站首页预解析商品详情页域名
TCP 优化:
- Keep-Alive配置:Apache中设置
KeepAliveTimeout 15
- HTTP/2多路复用:需服务端和客户端同时支持
- TFO快速打开:Linux内核3.7+支持,需设置
net.ipv4.tcp_fastopen=3
- 实际案例:视频网站通过TCP优化减少20%的首屏时间
- Keep-Alive配置:Apache中设置
HTTPS 优化:
- Session Resumption:配置Nginx的
ssl_session_cache shared:SSL:10m
- 证书优化:使用ECDSA证书替代RSA证书
- HTTP/3部署:需服务端支持QUIC协议
- 典型场景:金融类APP普遍采用TLS优化方案
- Session Resumption:配置Nginx的
缓存优化:
- 静态资源缓存策略:
- HTML:
Cache-Control: no-cache
- JS/CSS:
Cache-Control: max-age=31536000
- HTML:
- CDN缓存规则:设置边缘节点缓存时间为7天
- 示例:Vue项目通过
contenthash
实现长期缓存
- 静态资源缓存策略:
渲染优化:
- DOM操作优化:
- 使用
requestAnimationFrame
进行批处理 - 虚拟DOM技术(如React)
- 使用
- 图层管理:
- 使用
will-change: transform
创建独立图层 - 避免过度使用
position: fixed
- 使用
- 懒加载实现:
- 图片:
<img loading="lazy">
- 组件:Vue的
<Suspense>
组件
- 图片:
- DOM操作优化:
3.2 HTTP 版本演进
HTTP协议各版本核心特性及典型应用场景对比:
版本 | 主要特性 | 性能瓶颈 | 应用案例 |
---|---|---|---|
HTTP/1.0 | 基本请求响应模型 | 每次请求新建TCP连接 | 早期静态网站 |
HTTP/1.1 | 长连接、管道化 | 队头阻塞 | 现代Web应用基础 |
HTTP/2 | 二进制分帧、多路复用、头部压缩 | TCP层队头阻塞 | 大型电商网站、流媒体平台 |
HTTP/3 | QUIC协议、0-RTT、改进的拥塞控制 | 需要UDP支持 | 移动端应用、跨国服务 |
各版本演进细节:
HTTP/1.1管道化问题:
- 虽然支持请求管道化,但响应必须按序返回
- 实际应用中常被禁用
HTTP/2重大改进:
- 二进制帧结构:
- HEADERS帧(元数据)
- DATA帧(内容)
- 服务器推送示例:
http2_push /style.css;
- 二进制帧结构:
HTTP/3创新点:
- QUIC协议特点:
- 基于UDP实现可靠传输
- 内置TLS 1.3
- 连接迁移能力(切换网络不断连)
- 部署要求:
- 需要同时监听UDP 443端口
- 客户端支持(Chrome 87+)
- QUIC协议特点: