第 1 章:HTTP 概述与历史
第 1 章:HTTP 概述与历史
HTTP 是互联网上应用最广泛的协议之一。理解它的历史演进与核心设计哲学,是深入学习的第一步。
1.1 什么是 HTTP
HTTP(Hypertext Transfer Protocol,超文本传输协议) 是一种用于分布式、协作式、超媒体信息系统的应用层协议。它是万维网(World Wide Web)的数据通信基础。
核心特征
| 特征 | 说明 |
|---|---|
| 客户端-服务器模型 | 客户端发起请求,服务器返回响应 |
| 无状态(Stateless) | 每个请求独立,服务器不保留之前的请求信息 |
| 可扩展 | 通过自定义头部字段和方法扩展功能 |
| 基于文本 | HTTP/1.x 使用人类可读的文本格式 |
| 传输层无关 | 理论上可运行在任何可靠的传输层之上 |
1.2 HTTP 的历史演进
版本时间线
1991 ── HTTP/0.9 ── 最初版本,仅支持 GET
1996 ── HTTP/1.0 ── RFC 1945,引入头部字段、状态码
1997 ── HTTP/1.1 ── RFC 2068 → RFC 2616 → RFC 9112,持久连接、分块传输
2015 ── HTTP/2 ── RFC 7540 → RFC 9113,多路复用、头部压缩
2022 ── HTTP/3 ── RFC 9114,基于 QUIC(UDP)
HTTP/0.9(1991)
最初的 HTTP 由 Tim Berners-Lee 在 CERN 设计,极其简单:
GET /index.html
响应直接返回 HTML,没有头部、没有状态码:
<html>
<body>Hello World</body>
</html>
📝 注意:HTTP/0.9 没有任何元数据机制,无法传输非 HTML 内容。
HTTP/1.0(1996)
引入了关键特性:
GET /page.html HTTP/1.0
Host: www.example.com
User-Agent: Mozilla/5.0
HTTP/1.0 200 OK
Date: Tue, 15 Nov 2024 08:12:31 GMT
Content-Type: text/html
Content-Length: 137
<html><body>Hello</body></html>
新增特性:
- 版本号随请求行发送
- 状态码(如 200、404)
- 头部字段(Content-Type、User-Agent 等)
- 支持非 HTML 内容(图片、视频等)
局限:
- 每个请求/响应后关闭 TCP 连接
- 无法在一个连接上发送多个请求
HTTP/1.1(1997)
HTTP/1.1 是使用时间最长的版本,核心改进:
| 改进项 | 说明 |
|---|---|
| 持久连接 | 默认开启 Connection: keep-alive,复用 TCP 连接 |
| Host 头 | 必须包含 Host,支持虚拟主机 |
| 分块传输 | Transfer-Encoding: chunked |
| 缓存增强 | ETag、If-None-Match 等协商缓存机制 |
| 管道化 | 允许在同一连接上连续发送多个请求(实际支持有限) |
GET /index.html HTTP/1.1
Host: www.example.com
Connection: keep-alive
GET /style.css HTTP/1.1
Host: www.example.com
Connection: keep-alive
HTTP/2(2015)
HTTP/2 由 Google 的 SPDY 协议演进而来:
| 特性 | 说明 |
|---|---|
| 二进制分帧 | 不再是纯文本,使用二进制帧 |
| 多路复用 | 一个 TCP 连接上并发多个请求/响应 |
| 头部压缩 | HPACK 算法压缩头部,减少冗余 |
| 服务器推送 | 服务器主动推送资源 |
| 流优先级 | 客户端可指定请求优先级 |
# 使用 Python 演示 HTTP/2 客户端
import httpx
async def fetch_http2():
async with httpx.AsyncClient(http2=True) as client:
response = await client.get("https://www.example.com")
print(f"HTTP 版本: {response.http_version}") # HTTP/2
print(f"状态码: {response.status_code}")
HTTP/3(2022)
HTTP/3 的最大变化是传输层从 TCP 改为 QUIC(基于 UDP):
传统: HTTP → TCP → IP
HTTP/3: HTTP → QUIC(UDP) → IP
核心优势:
| 优势 | 说明 |
|---|---|
| 消除队头阻塞 | 丢包只影响单个流,不阻塞其他流 |
| 0-RTT 连接 | 首次连接后可实现零往返延迟 |
| 连接迁移 | 网络切换(WiFi → 4G)连接不断 |
| 内置 TLS 1.3 | 安全性开箱即用 |
1.3 请求-响应模型
HTTP 采用经典的 客户端-服务器(Client-Server) 模型:
┌──────────┐ 请求 (Request) ┌──────────┐
│ │ ─────────────────────────────→ │ │
│ Client │ │ Server │
│ │ ←───────────────────────────── │ │
└──────────┘ 响应 (Response) └──────────┘
请求的组成
GET /api/users?page=1 HTTP/1.1 ← 请求行 (Method + Path + Version)
Host: api.example.com ← 请求头
Accept: application/json
Authorization: Bearer eyJhbG...
← 空行
← 请求体(GET 无)
响应的组成
HTTP/1.1 200 OK ← 状态行 (Version + Status + Reason)
Content-Type: application/json ← 响应头
Content-Length: 85
Cache-Control: max-age=3600
{"users": [{"id": 1, "name": "Alice"}]} ← 响应体
完整示例:用 Python 发送请求与响应
# server.py — 一个简单的 HTTP 服务器
import json
from http.server import HTTPServer, BaseHTTPRequestHandler
class SimpleHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/api/hello':
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
body = json.dumps({"message": "Hello, HTTP!"})
self.wfile.write(body.encode())
else:
self.send_response(404)
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'Not Found')
if __name__ == '__main__':
server = HTTPServer(('localhost', 8080), SimpleHandler)
print("Server running on http://localhost:8080")
server.serve_forever()
# 启动服务器
python server.py
# 发送请求
curl http://localhost:8080/api/hello
# 输出: {"message": "Hello, HTTP!"}
1.4 无状态特性
HTTP 是 无状态协议(Stateless Protocol)——每个请求都是独立的,服务器不会"记住"之前的请求。
无状态的含义
请求 1: GET /login ──→ 服务器处理,返回成功
请求 2: GET /profile ──→ 服务器不认识你,因为不知道你已经登录
为什么设计为无状态?
| 优势 | 说明 |
|---|---|
| 简单 | 服务器无需维护会话状态 |
| 可伸缩 | 请求可被任意服务器处理,便于水平扩展 |
| 可靠性 | 单个服务器故障不影响其他请求 |
| 缓存友好 | 无状态的响应更容易缓存 |
如何实现"有状态"的应用?
尽管 HTTP 无状态,实际应用中我们需要状态管理:
| 方案 | 原理 | 适用场景 |
|---|---|---|
| Cookie | 服务器通过响应设置 Cookie,客户端自动携带 | 浏览器会话 |
| Session | 服务端存储会话数据,客户端通过 Cookie 中的 Session ID 访问 | 传统 Web 应用 |
| Token (JWT) | 服务器签发令牌,客户端每次请求携带 | API、移动端 |
| URL 参数 | 将状态信息放在 URL 中 | 分页、简单状态 |
# 用 Cookie 实现有状态的请求
import requests
session = requests.Session()
# 登录 — 服务器设置 Session Cookie
session.post("https://example.com/login", data={
"username": "alice",
"password": "secret"
})
# 后续请求自动携带 Cookie
profile = session.get("https://example.com/profile")
print(profile.json())
1.5 HTTP 的适用场景
适合的场景
| 场景 | 原因 |
|---|---|
| Web 页面浏览 | HTTP 的本职工作 |
| RESTful API | 无状态、标准化,非常适合 API 设计 |
| 文件传输 | 支持大文件、断点续传 |
| 微服务通信 | 服务间通过 HTTP/gRPC 通信 |
| 物联网(IoT) | 轻量级请求,CoAP 基于类似模型 |
不太适合的场景
| 场景 | 问题 | 替代方案 |
|---|---|---|
| 实时双向通信 | HTTP 是单向请求模型 | WebSocket |
| 高频推送 | 轮询效率低 | SSE / WebSocket / gRPC streaming |
| 极低延迟 | TCP 握手 + TLS 开销 | QUIC (HTTP/3) / UDP 协议 |
| 广播/组播 | HTTP 是一对一模型 | MQTT / AMQP |
1.6 业务场景:电商网站的 HTTP 请求
以下是一个典型电商网站页面加载时的 HTTP 请求序列:
1. GET /index.html → 获取 HTML 文档
2. GET /style.css → 获取样式表
3. GET /app.js → 获取 JavaScript
4. GET /api/products → 获取商品列表 (API)
5. GET /image/product1.jpg → 获取商品图片
6. POST /api/cart → 添加商品到购物车
7. GET /api/user/profile → 获取用户信息(携带 Token)
# 用 curl 模拟以上流程
# 1. 获取首页
curl -O https://shop.example.com/index.html
# 2. 调用 API(带认证)
curl -H "Authorization: Bearer <token>" \
-H "Accept: application/json" \
https://shop.example.com/api/products
# 3. 添加购物车
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"product_id": 42, "quantity": 1}' \
https://shop.example.com/api/cart
1.7 HTTP 与其他协议的对比
| 协议 | 层级 | 传输方式 | 典型应用 |
|---|---|---|---|
| HTTP | 应用层 | 请求-响应 | Web、API |
| WebSocket | 应用层 | 全双工 | 实时聊天、游戏 |
| gRPC | 应用层(HTTP/2) | 远程过程调用 | 微服务 |
| FTP | 应用层 | 文件传输 | 大文件上传下载 |
| SMTP | 应用层 | 邮件发送 | 邮件系统 |
| MQTT | 应用层 | 发布-订阅 | IoT、消息推送 |
⚠️ 注意事项
- HTTP 与 HTTPS:HTTP 本身不加密,HTTPS = HTTP + TLS,生产环境必须使用 HTTPS
- 版本兼容:HTTP/1.1 仍是使用最广泛的版本,但 HTTP/2 正在快速普及
- 无状态不等于无会话:应用层可以通过 Cookie/Token 实现会话管理
- HTTP/2 需要 TLS:虽然规范不要求,但浏览器都要求 HTTP/2 必须使用 TLS
🔗 扩展阅读
- RFC 9110 — HTTP Semantics
- MDN — HTTP 概述
- HTTP/2 FAQ
- A Brief History of HTTP
- High Performance Browser Networking — HTTP
下一章:第 2 章:HTTP 基础要素 — 请求方法、状态码、头部字段、消息结构