HTTP 1.0 / 2.0 / 3.0 底层原理深度解析

从 TCP 到 UDP,从队头阻塞到 QUIC — 彻底搞懂三代 HTTP 的演进逻辑

Overview

三代 HTTP 核心差异一览

维度 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/3
传输层 TCP TCP TCP UDP (QUIC)
加密层 可选 TLS 通常 TLS 1.2+ 强制 TLS 1.3
连接复用 不支持 (短连接) Keep-Alive 串行 多路复用 (单TCP) 多路复用 (独立Stream)
TCP 队头阻塞 有 (TCP 层) 彻底消除
头部压缩 HPACK QPACK
服务端推送 支持 支持
建连 RTT 每请求 1-RTT TCP 复用后减少 1-RTT TCP + TLS 0-RTT / 1-RTT
网络切换 连接断开重建 连接断开重建 连接断开重建 Connection ID 无缝迁移
协议位置 应用层 应用层 应用层 应用层

📊建连耗时对比 (相对)

以典型 RTT=50ms 为例,下面展示各协议从「开始连接」到「第一个字节返回」所需时间:

HTTP/1.0 (新建)
100% (3+ RTT)
HTTP/1.1 + TLS
80% (2~3 RTT)
HTTP/2 + TLS
60% (1~2 RTT)
HTTP/3 (0-RTT)
15% (0-RTT)
HTTP/3 (首次)
25% (1-RTT)

🧱协议栈 HTTP/1.x + HTTP/2

HTTP/1.1 or HTTP/2
TLS 1.2 / 1.3 (可选)
TCP (可靠传输 · 拥塞控制)
IP

🚀协议栈 HTTP/3

HTTP/3
QUIC (流控 · 多路复用 · 0-RTT)
TLS 1.3 (内嵌于 QUIC)
UDP
IP

⚡ QUIC 把 TLS 1.3 内嵌,加密握手和传输握手合并,不再需要单独的 TCP 握手阶段。

HTTP/1.x

HTTP 1.0 & 1.1 底层原理

📌HTTP/1.0 — 短连接时代

  • 连接模型每次请求单独建立 TCP 连接,响应完毕立即关闭
  • 核心问题每个资源需要 3 次 TCP 握手 + 数据传输,巨大开销
  • 头部信息纯文本,无压缩,重复发送 User-Agent / Cookie 等
  • Host 字段不支持(一个 IP 只能挂一个网站)
  • 缓存仅 Expires / If-Modified-Since
  • 发布年份1996(RFC 1945)

🔧HTTP/1.1 — 改良优化

  • Keep-Alive默认长连接,复用 TCP 连接,减少握手
  • 管道化 Pipeline可连续发请求,但响应必须按顺序返回(问题根源)
  • Host 头必须携带 Host,支持虚拟主机
  • 分块传输Transfer-Encoding: chunked,无需预知 Content-Length
  • 缓存增强ETag / Cache-Control / If-None-Match
  • 发布年份1997(RFC 2068)/ 1999(RFC 2616)

🔄HTTP/1.0 单次请求完整流程

以访问一个 HTTPS 页面为例,每个资源都要走完整的 TCP 三次握手 + TLS 握手:

🖥 客户端
🌐 服务器

⚠️ 问题核心:HTTP/1.0 获取一个页面(HTML + 10 张图 + 5 个 JS)= 建立 16 条独立 TCP 连接,共耗费 16×3RTT = 48 个 RTT 光是握手!

🔌HTTP/1.1 Keep-Alive 原理

Keep-Alive 允许在同一个 TCP 连接上串行发送多个 HTTP 请求。服务器返回响应后不立即关闭连接,而是等待下一个请求。通过 Connection: keep-alive 头控制。

HTTP/1.1 串行请求(同一TCP连接)
TCP连接 #1
GET /html
GET /css
GET /js
GET /img
等待...
HTTP/1.0 对比
TCP握手
/html
TCP握手
/css
TCP...

⚠️ Pipeline 的坑:虽然可以批量发请求,但响应必须 按请求顺序 返回。如果第 1 个请求处理慢,后面所有响应都被卡住 → HTTP 层队头阻塞

HTTP/2

HTTP/2 底层原理 — 二进制分帧 & 多路复用

🧩核心革命:二进制分帧层 (Binary Framing Layer)

HTTP/1.x 是纯文本协议。HTTP/2 在 TCP 与 HTTP 之间新增一个 二进制分帧层,把所有数据切成小的二进制帧(Frame),每帧带有 Stream ID,实现在同一个 TCP 连接上并发传输多个流。

Frame Type
HEADERS
请求/响应头
Frame Type
DATA
响应体数据
Frame Type
SETTINGS
连接参数配置
Frame Type
PUSH_PROMISE
服务端主动推送
Frame Type
WINDOW_UPDATE
流量控制
Frame Type
RST_STREAM
取消某个流
Frame Type
GOAWAY
关闭连接通知
Frame Type
PING
心跳检测

🌊多路复用 (Multiplexing) 可视化

HTTP/2 的多个请求以不同 Stream ID 并发传输,帧交织在一条 TCP 连接中:

单条 TCP 连接 Stream 1, 3, 5, 7... 并发(帧交织传输)
Stream 1 (HTML)
H
D1
D2
D3
Stream 3 (CSS)
H
D1
D2
Stream 5 (JS)
等待
H
D1
D2+D3
TCP 连接实际传输序列(帧交织):
S1:H S3:H S1:D1 S5:H S3:D1 S1:D2 S5:D1 S3:D2 S1:D3 S5:D2 S5:D3

✅ 解决了 HTTP/1.x 的 HTTP 层队头阻塞。但由于底层仍是 TCP,如果某个 TCP 包丢失,TCP 的有序重传机制会导致 所有 Stream 都被暂停等待重传 — 这是 TCP 层的队头阻塞,HTTP/2 无能为力。

📦HPACK 头部压缩

静态表(Static Table):61 个常用头字段预定义,如 :method GET → 索引 2,直接用 1 字节代替。

动态表(Dynamic Table):首次发送的自定义头写入动态表,后续复用索引代替,大幅减少重复头传输。

压缩效果示例
HTTP/1.1 头部 ~800 bytes
HTTP/2 HPACK ~50 bytes
压缩比约 6% (减少 94%!)

🚨HTTP/2 的致命问题 — TCP 队头阻塞

本质:TCP 是字节流协议,要求有序可靠交付。如果一个 TCP 数据包(Segment)丢失,TCP 协议栈必须等待该包重传并收到后,才能把缓冲区里的后续数据交给上层。即使上层 HTTP/2 有 3 个流的数据已经全部到达,都得等那个丢失的包。

TCP 接收缓冲区(Stream 混合包序列) Pkt #1 Pkt #2 ❌ 丢包! Pkt #3 阻塞等待 Pkt #4 阻塞等待 Pkt #5 阻塞等待 ··· 所有后续包全部阻塞,等待 Pkt #2 重传 HTTP/2 三个 Stream 状态 Stream 1 数据全部到达 ✅ Stream 3 含丢失包 ❌ Stream 5 数据全部到达 ✅ 但 TCP 层全卡住,无法上交!
HTTP/3 + QUIC

HTTP/3 底层原理 — 基于 UDP 的 QUIC 协议

💡为什么要抛弃 TCP,基于 UDP 自建协议?

TCP 的根本缺陷:

1. 队头阻塞:单个丢包卡住整个连接
2. 握手开销:每次建连至少 1-RTT TCP + 1-RTT TLS
3. 僵化:TCP 逻辑固化在操作系统内核,难以升级
4. 网络切换:IP 变化 → 连接断开,需重建(4元组绑定)

QUIC 的解法:

1. UDP 载体:UDP 只管发包,不排序,不阻塞
2. 应用层自实现可靠传输:Stream 独立确认,丢包只影响该 Stream
3. 用户态协议:运行在应用层,可快速迭代升级
4. Connection ID:不依赖 IP+端口,网络切换无缝迁移

🏗QUIC vs TCP+TLS 协议栈对比

HTTP/2 协议栈
应用(HTTP/2)
TLS 1.3 握手
TCP(可靠传输 · 排序 · 拥塞控制)
IP
建连需要:TCP握手(1RTT) + TLS握手(1RTT) = 2RTT
HTTP/3 + QUIC 协议栈
应用(HTTP/3)
QUIC(流控·多路复用·可靠传输·0-RTT)
TLS 1.3
内嵌
UDP
IP
建连需要:QUIC握手+TLS合并 = 1RTT(重连0-RTT)

🔑QUIC 五大核心机制

① 独立 Stream(消灭队头阻塞)
每个 HTTP 请求对应一个独立 QUIC Stream。某个 Stream 丢包重传,只影响该 Stream,其他 Stream 正常传输。彻底消灭了 TCP 层的队头阻塞。

② 0-RTT 握手
首次连接:QUIC 握手 + TLS 1.3 合并,只需 1-RTT。
再次连接:客户端保存上次的 Session Ticket,直接发送数据,建连 0-RTT(比 TCP+TLS 少 2 个来回)。

③ Connection ID(连接迁移)
传统 TCP 连接由「源IP + 源端口 + 目标IP + 目标端口」四元组标识,手机从 WiFi 切到 4G 时 IP 变了,连接断开。QUIC 用自定义 Connection ID 标识连接,与 IP/端口无关,网络切换完全透明。

④ 改进的拥塞控制
QUIC 不依赖 OS 内核的拥塞控制,默认 Cubic,支持热插拔替换为 BBR 等算法,可针对不同网络场景快速迭代优化。

⑤ 强制加密
所有 QUIC 数据包(包括握手包)全部加密,连接元数据也加密,防止中间设备劫持(解决了 HTTPS 降级攻击问题)。TLS 1.3 内嵌于 QUIC 传输层,是不可绕过的设计。

⑥ QPACK 头部压缩
HTTP/2 的 HPACK 依赖单一有序流,HTTP/3 改为 QPACK,支持在多路并发 Stream 下安全高效压缩头部,不会因为乱序导致解压错误。

📡QUIC 0-RTT 握手流程

🖥 客户端
🌐 服务器
HOL Blocking

队头阻塞 — 三代协议的不同表现

🎯什么是队头阻塞(Head-of-Line Blocking)?

队头阻塞是指:在一个有序处理队列中,排在头部的请求/包因为某种原因阻塞了,导致队列中所有后续请求/包都必须等待,无法继续处理的现象。

在 HTTP 协议演进中,它在三个不同的层次被逐步解决:

HTTP/1.1 HTTP 层队头阻塞 — Pipeline 响应必须有序
场景:发出 4 个请求,第 1 个响应慢(服务器处理时间长)
请求队列
GET /slow-api ⏱️ 处理中...
GET /css
GET /js
GET /img
响应
等待 /slow-api...
⚠️ 这三个早就好了,但必须等前面
→ HTTP/2 通过多路复用解决了这个问题(各 Stream 独立,不用排队)
HTTP/2 TCP 层队头阻塞 — 丢包导致所有 Stream 暂停
场景:3 个 Stream 并发传输,TCP 层 Packet #5 丢失
Stream 1
P1 ✓
P3 ✓
⚠️ 等 TCP 重传 Packet #5...
Stream 3
P2 ✓
P5 ❌
⚠️ 等 TCP 重传 Packet #5...
Stream 5
P4 ✓
P6 ✓
⚠️ 等 TCP 重传 Packet #5...
→ Stream 1 和 Stream 5 的数据其实都到了,但 TCP 层阻塞,HTTP/2 无能为力!
HTTP/3 彻底消灭队头阻塞 — Stream 级别独立重传
场景:3 个 QUIC Stream 并发,Stream 3 的某个包丢失
Stream 1
P1 ✓
P3 ✓
P5 ✓
完成 ✅
Stream 3
P2 ✓
P4 ❌
重传 P4...
P4 ✓
Stream 5
P6 ✓
P7 ✓
P8 ✓
完成 ✅
→ Stream 3 重传期间,Stream 1 和 Stream 5 完全不受影响,继续正常传输!

📊队头阻塞层次对比

协议HTTP 应用层 HOLTCP 传输层 HOL根本原因
HTTP/1.1 ❌ 存在(Pipeline) ❌ 存在 响应必须按请求顺序 + TCP 有序交付
HTTP/2 ✅ 消除(多路复用) ❌ 依然存在 二进制分帧解决应用层,但 TCP 层无法改变
HTTP/3 ✅ 消除 ✅ 消除(UDP+独立Stream) QUIC Stream 独立重传,丢包不跨 Stream 传播
Handshake

握手开销对比 — 每 RTT 都是真实延迟

🔌TCP 三次握手

TCP 建立连接必须完成三次握手,耗费 1 个 RTT(Round Trip Time,往返时延):

Client Server SYN (seq=x) SYN-ACK (seq=y, ack=x+1) ACK (ack=y+1) 1 RTT 可发数据

TCP 握手后才能开始 TLS 握手,所以 HTTPS = 1RTT TCP + 1~2 RTT TLS。

🔐TLS 1.3 握手(HTTP/2 使用)

Client Server ClientHello + 支持的密码套件 + 密钥共享 ServerHello + 证书 + 密钥共享 + Finished Finished (握手完成,开始加密) Application Data (HTTP/2 帧) 1 RTT TLS TLS 1.3 比 TLS 1.2 少一个 RTT(已经很快了,但 QUIC 还能更快)

QUIC 0-RTT — 握手与数据合并

Client Server Initial + TLS ClientHello + 0-RTT Data (HTTP 请求) Handshake + TLS ServerHello + HTTP响应 Handshake Complete + 后续 HTTP 请求 更多 HTTP 响应数据... 0-RTT! 数据 1 RTT ✅ 0-RTT 场景(曾连接过):发出第一个包就带着 HTTP 请求数据,直接拿到响应!

0-RTT 的工作原理:客户端上次连接时,服务器会颁发一个 Session Ticket(会话票据),客户端保存。再次连接时,客户端在第一个 Initial 包里直接携带这个 Ticket + 加密的 HTTP 请求数据,服务器验证 Ticket 后直接处理请求,无需任何额外握手往返。