Skip to content

HTTP/HTTPS 协议演进

每个请求都需要建立新的 TCP 连接,请求完成后立即关闭。

问题:
每个请求 = 三次握手 + 请求/响应 + 四次挥手
一个网页有 100 个资源 → 100 次 TCP 连接建立/释放 → 延迟极高

关键改进

  1. 持久连接(Keep-Alive):默认启用,同一 TCP 连接可发多个请求,Connection: keep-alive
  2. 管道化(Pipelining):允许在收到响应前发送下一个请求(理论上)
  3. 分块传输(Chunked Transfer Encoding):不需要预先知道响应体大小
  4. Host 头:支持虚拟主机(同一 IP 多个域名)
  5. 缓存控制:Cache-Control、ETag/If-None-Match、Last-Modified/If-Modified-Since

遗留问题

队头阻塞(Head-of-Line Blocking):
虽然管道化允许发送多个请求,但响应必须按请求顺序返回
如果第一个请求处理慢,后续请求的响应被阻塞
→ 浏览器通常为同一域名打开 6 个并行 TCP 连接来绕过此限制

核心改进

1. 二进制分帧(Binary Framing)
HTTP 1.x 是文本协议,HTTP/2 将消息分割为二进制帧(Frame)
帧类型:HEADERS、DATA、SETTINGS、RST_STREAM、PUSH_PROMISE 等
2. 多路复用(Multiplexing)★★★
在单个 TCP 连接上并发传输多个请求/响应
每个请求/响应对应一个流(Stream),流有独立 ID
帧可以交错发送,接收方按 Stream ID 重组
HTTP 1.1:请求1 → 等响应1 → 请求2 → 等响应2
HTTP 2.0:请求1 ┐ ┌ 响应1
请求2 ┤→ 单个TCP连接 →├ 响应3(先完成)
请求3 ┘ └ 响应2
3. 头部压缩(HPACK)
HTTP 头部在请求间有大量重复(Cookie、User-Agent 等)
HPACK 算法:维护动态表,用索引引用已发送的头部字段
压缩率可达 80-90%
4. 服务端推送(Server Push)
服务端可主动推送客户端尚未请求的资源
如:请求 HTML,服务端主动推送 CSS/JS(浏览器未来可能会移除此特性)
5. 流优先级(Stream Priority)
客户端可设置流的权重和依赖关系,让重要资源优先传输

HTTP 2.0 的问题:TCP 层面的队头阻塞仍然存在。一个 TCP 包丢失会导致所有流的所有帧等待重传。

HTTP 3.0 不再使用 TCP,改用 QUIC(Quick UDP Internet Connections)
QUIC 基于 UDP,在应用层实现了可靠传输
核心优势:
1. 彻底解决队头阻塞
QUIC 的流是完全独立的,一个流的丢包不影响其他流
2. 0-RTT / 1-RTT 连接建立
首次连接:1-RTT(QUIC 握手包含 TLS 1.3 握手)
恢复连接:0-RTT(使用缓存的会话密钥,直接发数据)
对比 TCP + TLS 1.2:需要 3-RTT(TCP 握手 + TLS 握手)
3. 连接迁移(Connection Migration)
用连接 ID 标识连接,而非 IP:Port
切换网络(如 WiFi → 4G)不需要重建连接
4. 无中间件僵化(Ossification)问题
QUIC 包完全加密,中间路由器无法解析修改,便于协议升级

客户端 服务端
│ │
│── ClientHello ─────────────────────────► │
│ (TLS版本, 支持的加密套件, 随机数Client-Random)
│ │
│ ◄──────────────────────────── ServerHello│
│ (选定的加密套件, 随机数Server-Random) │
│ ◄─────────────────────────────Certificate│
│ (服务端证书,含公钥) │
│ ◄──────────────── ServerKeyExchange(可选) │
│ (ECDHE参数等) │
│ ◄─────────────────────────── ServerHelloDone
│ │
│── ClientKeyExchange ───────────────────► │
│ (发送 Pre-Master Secret,用服务端公钥加密)│
│── ChangeCipherSpec ─────────────────────►│
│── Finished ─────────────────────────────►│
│ (用协商好的密钥加密的握手摘要) │
│ │
│ ◄──────────────────────── ChangeCipherSpec│
│ ◄──────────────────────────────── Finished│
│ │
└──────── 加密通信开始(对称加密)────────────┘
密钥派生:
Master Secret = PRF(Pre-Master Secret, Client-Random, Server-Random)
→ 从 Master Secret 派生出:
client write key / server write key(对称加密密钥)
client write MAC key / server write MAC key(消息认证密钥)
client write IV / server write IV(初始向量)
TLS 1.2:2-RTT(ClientHello → ServerHello+Cert+Done → ClientKeyExchange+Finished → Finished)
TLS 1.3:1-RTT
1. 移除了 RSA 密钥交换(只保留 ECDHE,支持前向保密)
2. ClientHello 中直接携带密钥协商参数(key_share),服务端收到后立即派生密钥
3. 移除了不安全的加密套件(RC4、DES、3DES、MD5、SHA-1)
4. 0-RTT 恢复(Session Resumption with Early Data):
使用 PSK(Pre-Shared Key),客户端可在第一个包中携带加密应用数据
注意:0-RTT 数据不具备前向保密性,且易受重放攻击,应用场景需谨慎
证书链(Chain of Trust):
根 CA 证书(Root CA)← 内置于操作系统/浏览器,自签名,高度可信
└── 中间 CA 证书(Intermediate CA)← 由根 CA 签发
└── 服务器证书(End Entity)← 由中间 CA 签发,含域名和公钥
验证流程:
1. 浏览器收到服务器证书
2. 读取签发者(Issuer)字段,找到中间 CA 证书(服务器通常一并发送)
3. 用中间 CA 的公钥验证服务器证书的签名
4. 再向上,用根 CA 的公钥验证中间 CA 证书的签名
5. 根 CA 证书在本地受信任列表中 → 验证通过
6. 检查证书有效期、域名、CRL/OCSP(是否被吊销)

HTTPS 防中间人攻击(MITM)的关键:证书必须由受信任 CA 签发,攻击者无法伪造合法证书(除非 CA 被攻破或用户安装了恶意根证书)。


强缓存(不发请求,直接用本地缓存):
Cache-Control: max-age=3600 ← 优先级更高
Expires: Wed, 21 Oct 2023 ... ← HTTP 1.0 时代,受客户端时间影响
协商缓存(发请求询问服务端缓存是否有效):
ETag / If-None-Match ← 基于内容指纹(哈希),优先级更高
Last-Modified / If-Modified-Since ← 基于最后修改时间(精度到秒)
流程:
第一次请求 → 服务端返回 200 + 资源 + ETag + Cache-Control
第二次请求(max-age 未过期)→ 直接使用本地缓存(200 from cache / 200 from disk cache)
第二次请求(max-age 已过期)→ 携带 If-None-Match: <ETag值> 发给服务端
├── 资源未变 → 服务端返回 304 Not Modified(无响应体,节省带宽)
└── 资源已变 → 服务端返回 200 + 新资源 + 新ETag

状态码含义说明
200OK请求成功
201Created资源创建成功(POST/PUT)
204No Content成功但无响应体(DELETE)
301Moved Permanently永久重定向,浏览器缓存新地址
302Found临时重定向,不缓存
304Not Modified协商缓存命中,使用本地缓存
400Bad Request请求格式错误
401Unauthorized未认证(需要登录)
403Forbidden已认证但无权限
404Not Found资源不存在
405Method Not Allowed请求方法不支持
429Too Many Requests限流
500Internal Server Error服务端内部错误
502Bad Gateway网关收到上游无效响应(如 Nginx 转发失败)
503Service Unavailable服务不可用(如过载、维护)
504Gateway Timeout网关等待上游超时

WebSocket 是基于 TCP 的全双工通信协议,通过 HTTP 握手升级建立。

升级握手:
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== ← 随机 base64
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= ← SHA1(Key + GUID)
建立后:
• 不再是 HTTP,使用 WebSocket 帧格式
• 全双工:服务端可主动推送,无需轮询
• 适合场景:实时聊天、实时数据推送、在线游戏、协同编辑

WebSocket vs SSE vs 长轮询

方式全双工服务端推送适用场景
长轮询模拟简单通知,实现简单
SSE否(单向)服务端推送流数据(日志、股票)
WebSocket双向实时通信(聊天、游戏)

Q:HTTP 2.0 相比 1.1 的主要改进是什么?

多路复用(单连接并发多请求,解决 HTTP 层面的队头阻塞)、二进制分帧(解析效率高)、头部压缩(HPACK,减少重复头部传输)、服务端推送(主动推送资源)。

Q:HTTPS 如何防止中间人攻击?

通过 CA 证书体系。服务端证书由受信任 CA 签发,包含服务端公钥和域名信息。中间人无法伪造合法的 CA 签名证书(私钥未泄露的前提下),浏览器验证证书有效性和域名匹配后才建立加密连接。

Q:HTTP 的缓存机制是怎样的?

分强缓存和协商缓存。强缓存(Cache-Control: max-age)未过期时直接使用本地缓存,不发请求。过期后发送协商请求(携带 ETag 或 Last-Modified),服务端判断资源未变则返回 304(节省带宽),已变则返回 200 和新资源。

Q:301 和 302 的区别?

301 是永久重定向,浏览器会缓存新地址,后续直接访问新地址不再请求原地址;302 是临时重定向,每次都会请求原地址再跳转。SEO 场景中,301 能传递页面权重,302 不能。

Q:HTTP 3.0 为什么使用 UDP?

HTTP 2.0 虽然解决了 HTTP 层面的队头阻塞,但 TCP 层面的队头阻塞依然存在(一个 TCP 包丢失阻塞所有流)。QUIC 基于 UDP,在应用层实现了独立的流,一个流的丢包不影响其他流,同时支持 0-RTT 快速连接建立和连接迁移。