TCP 与 HTTP
TCP/IP 协议栈和 HTTP 协议是网络编程的核心基础。深入理解 TCP 连接管理、可靠传输机制,以及 HTTP 协议的演进历程,是 Java 开发者进行网络编程、性能优化和问题排查的必备技能。
面试高频考点:TCP 三次握手/四次挥手、TCP 可靠传输机制、HTTP 各版本差异、HTTPS 原理、HTTP 状态码
一、TCP 协议概述
1.1 TCP/IP 协议栈
TCP/IP 是互联网的基础协议栈,分为四层:
┌─────────────────────────────────────────────────────────────┐
│ TCP/IP 协议栈 │
├─────────────────────────────────────────────────────────────┤
│ 应用层 │ HTTP、FTP、SMTP、DNS、SSH │
├───────────────┼─────────────────────────────────────────────┤
│ 传输层 │ TCP(可靠)、UDP(不可靠) │
├───────────────┼─────────────────────────────────────────────┤
│ 网络层 │ IP、ICMP、ARP │
├───────────────┼─────────────────────────────────────────────┤
│ 网络接口层 │ Ethernet、Wi-Fi、PPP │
└─────────────────────────────────────────────────────────────┘1.2 TCP 特点
| 特性 | 说明 |
|---|---|
| 面向连接 | 通信前需要建立连接 |
| 可靠传输 | 保证数据无差错、不丢失、不重复、按序到达 |
| 面向字节流 | 把数据看成一连串无结构的字节流 |
| 全双工 | 双向通信,双方可同时发送和接收 |
| 点对点 | 每条 TCP 连接只能有两个端点 |
二、TCP 三次握手
2.1 三次握手过程
┌─────────────────────────────────────────────────────────────┐
│ TCP 三次握手 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client Server │
│ │ │ │
│ │ ──────── SYN ────────► │ │
│ │ seq=x, SYN=1 │ │
│ │ │ │
│ │ ◄─────── SYN+ACK ─────── │ │
│ │ seq=y, ack=x+1, SYN=1, ACK=1 │ │
│ │ │ │
│ │ ──────── ACK ────────► │ │
│ │ seq=x+1, ack=y+1, ACK=1 │ │
│ │ │ │
│ │ 连接建立完成 │ │
│ │
└─────────────────────────────────────────────────────────────┘2.2 三次握手详解
| 步骤 | 名称 | 说明 |
|---|---|---|
| 第一次 | SYN_SENT | 客户端发送 SYN 包,进入 SYN_SENT 状态 |
| 第二次 | SYN_RCVD | 服务端收到 SYN,回复 SYN+ACK,进入 SYN_RCVD 状态 |
| 第三次 | ESTABLISHED | 客户端收到 SYN+ACK,回复 ACK,进入 ESTABLISHED 状态 |
2.3 为什么是三次握手?
原因1:防止历史连接
假设采用两次握手:
Client Server
│ │
│── SYN(旧) ─────────────►│ ← 第一个 SYN 延迟到达
│ │ (客户端已关闭的请求)
│ │
│◄───── SYN+ACK ──────────│ ← 服务端建立连接
│ │
│ 客户端发现是旧请求 │
│ 无法通知服务端关闭 │
│ │ ← 服务端资源浪费
三次握手可以让客户端确认服务端的 SYN+ACK
如果发现是旧请求,可以发送 RST 终止连接
原因2:同步双方初始序列号
TCP 是可靠传输,需要序列号来保证有序性
三次握手才能确保双方的初始序列号都被确认
Client Server
│ │
│── SYN, seq=x ──────────►│ 第一次:服务端确认 x
│ │
│◄── SYN+ACK, seq=y ──────│ 第二次:客户端确认 y
│ │
│── ACK, ack=y+1 ────────►│ 第三次:服务端确认客户端收到 y2.4 半连接队列与全连接队列
┌─────────────────────────────────────────────────────────────┐
│ TCP 连接队列 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client Server │
│ │ │ │
│ │── SYN ─────────────►│ │
│ │ ▼ │
│ │ ┌─────────────────┐ │
│ │ │ 半连接队列 │ SYN_RCVD 状态 │
│ │ │ (SYN Queue) │ 存储 SYN 请求 │
│ │ └────────┬────────┘ │
│ │ │ │
│ │◄── SYN+ACK ────────│ │
│ │ │ │
│ │── ACK ─────────────►│ │
│ │ ▼ │
│ │ ┌─────────────────┐ │
│ │ │ 全连接队列 │ ESTABLISHED 状态 │
│ │ │ (Accept Queue) │ 等待应用 accept() │
│ │ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘| 队列 | 说明 | 溢出后果 |
|---|---|---|
| 半连接队列 | 存放 SYN_RCVD 状态的连接 | SYN Flood 攻击利用此队列 |
| 全连接队列 | 存放 ESTABLISHED 状态的连接 | 新连接被丢弃或发送 RST |
2.5 SYN Flood 攻击与防御
SYN Flood 攻击原理:
攻击者发送大量 SYN 包,但不回复 ACK
导致半连接队列溢出,正常连接无法建立
防御措施:
1. SYN Cookies
- 不分配资源,通过加密算法生成序列号
- 收到 ACK 时验证合法性
2. 增加半连接队列大小
net.ipv4.tcp_max_syn_backlog = 8192
3. 缩短 SYN_RCVD 状态超时时间
net.ipv4.tcp_synack_retries = 2
4. 启用 SYN Cookies
net.ipv4.tcp_syncookies = 1三、TCP 四次挥手
3.1 四次挥手过程
┌─────────────────────────────────────────────────────────────┐
│ TCP 四次挥手 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client Server │
│ │ │ │
│ │ ──────── FIN ────────► │ │
│ │ seq=u, FIN=1, ACK=1 │ │
│ │ (主动关闭方) │ │
│ │ │ │
│ │ ◄─────── ACK ──────── │ │
│ │ seq=v, ack=u+1, ACK=1 │ │
│ │ │ │
│ │ ◄─────── FIN ──────── │ │
│ │ seq=w, FIN=1, ACK=1 │ │
│ │ (被动关闭方) │ │
│ │ │ │
│ │ ──────── ACK ────────► │ │
│ │ seq=u+1, ack=w+1, ACK=1 │ │
│ │ │ │
│ │ 等待 2MSL 后关闭 │ │
│ │
└─────────────────────────────────────────────────────────────┘3.2 四次挥手状态变化
┌───────────────┐ ┌───────────────┐
│ Client │ │ Server │
├───────────────┤ ├───────────────┤
│ ESTABLISHED │ │ ESTABLISHED │
│ │ │ │ │
│ ▼ │ ──── FIN ────► │ │ │
│ FIN_WAIT_1 │ │ ▼ │
│ │ │ ◄─── ACK ──── │ CLOSE_WAIT │
│ ▼ │ │ │ │
│ FIN_WAIT_2 │ │ ▼ │
│ │ │ ◄─── FIN ──── │ LAST_ACK │
│ ▼ │ │ │ │
│ TIME_WAIT │ ──── ACK ────► │ ▼ │
│ │ │ │ CLOSED │
│ ▼ │ │ │
│ CLOSED │ │ │
└───────────────┘ └───────────────┘3.3 为什么是四次挥手?
原因:TCP 是全双工通信
每个方向的连接需要单独关闭:
Client Server
│ │
│───── FIN ────────►│ Client → Server 方向关闭
│ │ Server 不再接收 Client 数据
│ │ 但 Server 还可以发送数据
│◄──── ACK ─────────│
│ │
│ │ Server 发送剩余数据...
│ │
│◄──── FIN ─────────│ Server → Client 方向关闭
│───── ACK ────────►│
│ │
为什么不能像三次握手那样合并?
三次握手时:
- 服务端的 SYN 和 ACK 可以合并发送(SYN+ACK)
四次挥手时:
- 服务端收到 FIN 后,可能还有数据要发送
- 必须等数据发送完才能发送自己的 FIN
- 所以 ACK 和 FIN 分开发送3.4 TIME_WAIT 状态
为什么需要 TIME_WAIT?
1. 确保最后的 ACK 能到达服务端
- 如果 ACK 丢失,服务端会重传 FIN
- TIME_WAIT 允许重传 ACK
2. 等待旧连接的延迟数据消失
- 防止旧连接的数据被新连接接收
为什么是 2MSL?
MSL (Maximum Segment Lifetime) = 报文最大生存时间
Client Server
│ │
│◄──── FIN ──────────│
│───── ACK ──────────►│ ACK 可能丢失
│ │
│ │ 等待 1 MSL
│◄──── FIN ─────────│ Server 重传 FIN
│ │
│───── ACK ──────────►│ 重传 ACK
│ │
│ 等待 1 MSL │ 确保新 ACK 到达
│ │
总共等待 2 MSL,确保可靠关闭3.5 TIME_WAIT 过多的问题
# 查看 TIME_WAIT 数量
netstat -an | grep TIME_WAIT | wc -l
# 问题:
# 1. 占用端口资源(客户端)
# 2. 占用内存资源
# 解决方案:
# 1. 开启端口复用
net.ipv4.tcp_tw_reuse = 1
# 2. 开启快速回收(可能导致问题)
net.ipv4.tcp_tw_recycle = 1 # 已废弃
# 3. 减少 TIME_WAIT 时间
net.ipv4.tcp_fin_timeout = 30
# 4. 优化最大连接数
net.ipv4.tcp_max_tw_buckets = 5000四、TCP 可靠传输机制
4.1 序列号与确认应答
┌─────────────────────────────────────────────────────────────┐
│ TCP 序列号机制 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Sender Receiver │
│ │ │ │
│ │─────── seq=1, data ─────────────────────►│ │
│ │ │ │
│ │◄───────────── ack=2 ─────────────────────│ │
│ │ │ │
│ │─────── seq=2, data ─────────────────────►│ │
│ │ │ │
│ │◄───────────── ack=3 ─────────────────────│ │
│ │ │ │
│ │
│ seq:数据第一个字节的序号 │
│ ack:期望收到的下一个字节的序号 │
│ │
└─────────────────────────────────────────────────────────────┘4.2 超时重传
┌─────────────────────────────────────────────────────────────┐
│ TCP 超时重传 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Sender Receiver │
│ │ │ │
│ │─────── seq=1, data ─────────────────────►│ 数据丢失 │
│ │ × │ │
│ │ │ │
│ │ 等待 RTO 时间 │ │
│ │ │ │
│ │─────── seq=1, data ─────────────────────►│ 重传 │
│ │ │ │
│ │◄───────────── ack=2 ─────────────────────│ │
│ │ │ │
│ │
│ RTO (Retransmission Timeout):重传超时时间 │
│ RTO 应略大于 RTT (Round-Trip Time) │
│ │
└─────────────────────────────────────────────────────────────┘RTO 计算(Karn 算法):
RTO 计算:
1. 测量 RTT 样本
RTT_sample = 收到 ACK 的时间 - 发送数据的时间
2. 计算加权平均 RTT
RTT_SRTT = (1 - α) × RTT_SRTT + α × RTT_sample
α 通常为 0.125
3. 计算 RTT 偏差
RTT_VAR = (1 - β) × RTT_VAR + β × |RTT_SRTT - RTT_sample|
β 通常为 0.25
4. 计算 RTO
RTO = RTT_SRTT + 4 × RTT_VAR
注意:重传的数据不参与 RTT 计算(Karn 算法)4.3 快速重传
┌─────────────────────────────────────────────────────────────┐
│ TCP 快速重传 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Sender Receiver │
│ │ │ │
│ │─────── seq=1 ───────────────────────────►│ │
│ │◄───────────── ack=2 ─────────────────────│ │
│ │ │ │
│ │─────── seq=2 ────────× (丢失) │ │
│ │ │ │
│ │─────── seq=3 ───────────────────────────►│ │
│ │◄───────────── ack=2 ─────────────────────│ 期望收2 │
│ │ │ │
│ │─────── seq=4 ───────────────────────────►│ │
│ │◄───────────── ack=2 ─────────────────────│ 重复 ACK │
│ │ │ │
│ │─────── seq=5 ───────────────────────────►│ │
│ │◄───────────── ack=2 ─────────────────────│ 重复 ACK │
│ │ │ │
│ │ 收到 3 个重复 ACK,立即重传 seq=2 │ │
│ │ │ │
│ │─────── seq=2 ───────────────────────────►│ 快速重传 │
│ │◄───────────── ack=6 ─────────────────────│ │
│ │
│ 快速重传:收到 3 个重复 ACK,立即重传,不等超时 │
│ │
└─────────────────────────────────────────────────────────────┘4.4 滑动窗口
┌─────────────────────────────────────────────────────────────┐
│ TCP 滑动窗口 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 发送方窗口 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 已发送确认 │ 已发送未确认 │ 可用窗口 │ │
│ │ ┌────────────┐ │ ┌───────────────┐ │┌───────────┐│ │
│ │ │ 1 2 3 4 │ │ │ 5 6 7 8 │ ││ 9 10 ... ││ │
│ │ └────────────┘ │ └───────────────┘ │└───────────┘│ │
│ │ ←────── 已发送并收到确认 ──────→ │ ← 可发送 → │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 接收方窗口 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 已接收处理 │ 可接收窗口 │ │
│ │ ┌────────────┐│┌────────────────────────────────────┐│ │
│ │ │ 1 2 3 4 │││ 5 6 7 8 9 10 ... ││ │
│ │ └────────────┘│└────────────────────────────────────┘│ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 窗口大小由接收方通告(Flow Control) │
│ Window Size = 接收缓冲区可用空间 │
│ │
└─────────────────────────────────────────────────────────────┘4.5 选择性确认(SACK)
┌─────────────────────────────────────────────────────────────┐
│ TCP SACK │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统 ACK:只能告知丢失的第一个包 │
│ SACK:可以告知已收到的多个数据块 │
│ │
│ Sender Receiver │
│ │ │ │
│ │─────── seq=1 ───────────────────────────►│ │
│ │─────── seq=2 ────────× (丢失) │ │
│ │─────── seq=3 ───────────────────────────►│ │
│ │─────── seq=4 ────────× (丢失) │ │
│ │─────── seq=5 ───────────────────────────►│ │
│ │ │ │
│ │◄── ack=2, SACK=[3,4], SACK=[5,6] ────────│ │
│ │ │ │
│ │ 只需重传 seq=2 和 seq=4 │ │
│ │ │ │
│ │
│ SACK 选项格式: │
│ ┌────────┬────────┬──────────────┬──────────────┐ │
│ │ Kind=5 │ Length │ Left Edge 1 │ Right Edge 1 │ ... │
│ └────────┴────────┴──────────────┴──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘五、TCP 流量控制与拥塞控制
5.1 流量控制
┌─────────────────────────────────────────────────────────────┐
│ TCP 流量控制 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 目的:防止发送方发太快,接收方来不及处理 │
│ 方法:接收方通告窗口大小 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 接收方缓冲区 │ │
│ │ ┌────────────────────────────────────────────────┐│ │
│ │ │████████████████████│ 可用 ││ │
│ │ └────────────────────────────────────────────────┘│ │
│ │ 已接收未处理 ← 接收窗口 → │ │
│ │ rwnd = 缓冲区大小 - 已接收未处理 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 零窗口探测: │
│ 当 rwnd=0 时,发送方定时发送 1 字节探测包 │
│ 接收方回复当前窗口大小 │
│ │
└─────────────────────────────────────────────────────────────┘5.2 拥塞控制
┌─────────────────────────────────────────────────────────────┐
│ TCP 拥塞控制 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 目的:防止过多数据注入网络,避免网络拥塞 │
│ 方法:发送方维护拥塞窗口 (cwnd) │
│ │
│ 发送窗口 = min(cwnd, rwnd) │
│ │
│ 四种算法: │
│ │
│ 1. 慢启动 (Slow Start) │
│ cwnd 初始 = 1 MSS │
│ 每收到一个 ACK,cwnd 加倍 │
│ 指数增长:1 → 2 → 4 → 8 → ... │
│ │
│ 2. 拥塞避免 (Congestion Avoidance) │
│ 当 cwnd >= ssthresh 时 │
│ 每个 RTT,cwnd + 1 MSS │
│ 线性增长:... → 8 → 9 → 10 → ... │
│ │
│ 3. 快速重传 (Fast Retransmit) │
│ 收到 3 个重复 ACK │
│ 立即重传,不等超时 │
│ │
│ 4. 快速恢复 (Fast Recovery) │
│ ssthresh = cwnd / 2 │
│ cwnd = ssthresh + 3 MSS │
│ 进入拥塞避免阶段 │
│ │
└─────────────────────────────────────────────────────────────┘5.3 拥塞控制状态机
┌─────────────────────────────────────────────────────────────┐
│ TCP 拥塞控制状态机 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ 慢启动 │ │
│ │ (cwnd×2) │ │
│ └──────┬───────┘ │
│ │ │
│ cwnd >= ssthresh │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ ┌─────────│ 拥塞避免 │◄─────────┐ │
│ │ │ (cwnd+1) │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ │ ┌───────────┴───────────┐ │ │
│ │ │ │ │ │
│ 超时 │ │ 3 个重复 ACK │ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌──────────────┐ ┌──────────────┐ │
│ │ │ 快速重传 │──►│ 快速恢复 │ │
│ │ │ │ │ (cwnd/2+3) │ │
│ │ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ 收到新数据的 ACK │ │
│ │ │ │
│ ▼ │ │
│ ssthresh = cwnd/2 │ │
│ cwnd = 1 MSS │ │
│ 回到慢启动 │ │
│ │
└─────────────────────────────────────────────────────────────┘5.4 流量控制 vs 拥塞控制
| 对比项 | 流量控制 | 拥塞控制 |
|---|---|---|
| 目的 | 保护接收方 | 保护网络 |
| 对象 | 点对点 | 全局网络 |
| 机制 | 滑动窗口 | 拥塞窗口 |
| 反馈 | 接收方通告 | 网络拥塞信号 |
六、HTTP 协议概述
6.1 HTTP 基本概念
HTTP (HyperText Transfer Protocol) 是应用层协议,基于 TCP,用于 Web 通信。
┌─────────────────────────────────────────────────────────────┐
│ HTTP 请求-响应模型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client (Browser) Server │
│ │ │ │
│ │ ──────── Request ────────────► │ │
│ │ │ │
│ │ ◄─────── Response ─────────── │ │
│ │ │ │
│ │
│ 请求: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ GET /index.html HTTP/1.1 │ │
│ │ Host: www.example.com │ │
│ │ User-Agent: Mozilla/5.0 │ │
│ │ Accept: text/html │ │
│ │ │ │
│ │ (请求体 - 可选) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 响应: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HTTP/1.1 200 OK │ │
│ │ Content-Type: text/html │ │
│ │ Content-Length: 1234 │ │
│ │ │ │
│ │ <html>...</html> │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘6.2 HTTP 请求方法
| 方法 | 说明 | 是否有请求体 |
|---|---|---|
| GET | 获取资源 | 否 |
| POST | 提交数据 | 是 |
| PUT | 更新资源 | 是 |
| DELETE | 删除资源 | 否 |
| HEAD | 获取响应头 | 否 |
| OPTIONS | 获取支持的方法 | 否 |
| PATCH | 部分更新 | 是 |
6.3 GET vs POST
| 对比项 | GET | POST |
|---|---|---|
| 数据位置 | URL 中 | 请求体中 |
| 数据长度 | 受 URL 长度限制 | 无限制 |
| 安全性 | 参数暴露在 URL | 相对安全 |
| 缓存 | 可被缓存 | 不缓存 |
| 历史记录 | 参数保留在浏览器历史 | 不保留 |
| 幂等性 | 幂等 | 非幂等 |
| 书签 | 可收藏为书签 | 不可 |
七、HTTP 协议版本差异
7.1 HTTP 版本演进
┌─────────────────────────────────────────────────────────────┐
│ HTTP 版本演进 │
├─────────────────────────────────────────────────────────────┤
│ │
│ HTTP/1.0 (1996) │
│ ├── 短连接:每次请求需要新建 TCP 连接 │
│ ├── 无法复用连接 │
│ └── 性能差 │
│ │
│ HTTP/1.1 (1997) │
│ ├── 持久连接:Connection: keep-alive │
│ ├── 管道化:可同时发送多个请求 │
│ ├── Host 头:支持虚拟主机 │
│ └── 队头阻塞:响应必须按顺序 │
│ │
│ HTTP/2.0 (2015) │
│ ├── 二进制分帧:提高解析效率 │
│ ├── 多路复用:一个连接并行多个请求/响应 │
│ ├── 头部压缩:HPACK 算法 │
│ └── 服务端推送:主动推送资源 │
│ │
│ HTTP/3.0 (2022) │
│ ├── 基于 QUIC:UDP 替代 TCP │
│ ├── 解决队头阻塞:独立的流 │
│ ├── 连接迁移:网络切换不断开 │
│ └── 0-RTT:快速建立连接 │
│ │
└─────────────────────────────────────────────────────────────┘7.2 HTTP/1.0 vs HTTP/1.1
HTTP/1.0:
┌─────────────────────────────────────────────────────────────┐
│ Client Server │
│ │ │ │
│ │── TCP 三次握手 ────────────────────►│ │
│ │◄────────────────────────────────── │ │
│ │ │ │
│ │── Request 1 ──────────────────────►│ │
│ │◄── Response 1 ─────────────────────│ │
│ │ │ │
│ │── TCP 四次挥手 ──────────────────►│ │
│ │◄────────────────────────────────── │ │
│ │ │ │
│ │ (下一个请求需要重新建立连接) │ │
│ │
└─────────────────────────────────────────────────────────────┘
HTTP/1.1 持久连接:
┌─────────────────────────────────────────────────────────────┐
│ Client Server │
│ │ │ │
│ │── TCP 三次握手 ────────────────────►│ │
│ │ │ │
│ │── Request 1 ──────────────────────►│ │
│ │◄── Response 1 ─────────────────────│ │
│ │ │ │
│ │── Request 2 ──────────────────────►│ 复用连接 │
│ │◄── Response 2 ─────────────────────│ │
│ │ │ │
│ │── Request 3 ──────────────────────►│ │
│ │◄── Response 3 ─────────────────────│ │
│ │ │ │
│ │── Connection: close ──────────────►│ │
│ │── TCP 四次挥手 ──────────────────►│ │
│ │
└─────────────────────────────────────────────────────────────┘7.3 HTTP/1.1 队头阻塞
HTTP/1.1 管道化问题:
Request 1 ────────────────────────────────────►
Request 2 ────────────────────────────────────►
Request 3 ────────────────────────────────────►
◄─────── Response 1 (耗时 2s) ────────────────
│ 等待...
▼
◄─────── Response 2 (耗时 0.1s) ───────────────
│ 虽然 Response 2 早处理完
│ 但必须等 Response 1 先返回
▼
◄─────── Response 3 (耗时 0.1s) ───────────────
问题:前面的响应慢,阻塞后面的响应7.4 HTTP/2.0 多路复用
HTTP/2.0 多路复用:
┌─────────────────────────────────────────────────────────────┐
│ 二进制分帧层 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Stream 1: Frame 1, Frame 2, Frame 3 │
│ Stream 2: Frame 1, Frame 2 │
│ Stream 3: Frame 1, Frame 2, Frame 3, Frame 4 │
│ │
│ ──────── 在一个 TCP 连接上交错传输 ────────► │
│ │
│ 接收方根据 Stream ID 重新组装 │
│ │
└─────────────────────────────────────────────────────────────┘
特点:
1. 一个 TCP 连接可以并行多个请求/响应
2. 请求和响应可以交错发送
3. 解决了 HTTP 层的队头阻塞
4. 但 TCP 层仍可能阻塞(丢包导致)7.5 HTTP/3.0 QUIC
┌─────────────────────────────────────────────────────────────┐
│ HTTP/3.0 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统 HTTP (TCP): │
│ ┌────────────────────────────────────────────────────┐ │
│ │ HTTP/1.1 │ HTTP/2.0 │ │
│ ├──────────┴─────────────────────────────────────────┤ │
│ │ TCP │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ TLS │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ IP │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ HTTP/3.0 (QUIC): │
│ ┌────────────────────────────────────────────────────┐ │
│ │ HTTP/3.0 │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ QUIC │ │
│ │ (可靠性 + 加密 + 多路复用) │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ UDP │ │
│ ├────────────────────────────────────────────────────┤ │
│ │ IP │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ QUIC 优势: │
│ 1. 解决 TCP 队头阻塞:独立的 Stream │
│ 2. 连接迁移:Connection ID 替代四元组 │
│ 3. 快速连接:0-RTT / 1-RTT │
│ 4. 内置加密:TLS 1.3 集成 │
│ │
└─────────────────────────────────────────────────────────────┘7.6 HTTP 版本对比总结
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2.0 | HTTP/3.0 |
|---|---|---|---|---|
| 连接方式 | 短连接 | 持久连接 | 多路复用 | QUIC |
| 队头阻塞 | 有 | 有 | HTTP层解决 | 完全解决 |
| 头部压缩 | 无 | 无 | HPACK | QPACK |
| 二进制协议 | 否 | 否 | 是 | 是 |
| 服务端推送 | 无 | 无 | 有 | 有 |
| 传输层 | TCP | TCP | TCP | UDP |
八、HTTPS 原理
8.1 HTTP vs HTTPS
┌─────────────────────────────────────────────────────────────┐
│ HTTP vs HTTPS │
├─────────────────────────────────────────────────────────────┤
│ │
│ HTTP: │
│ ┌──────────┐ 明文传输 ┌──────────┐ │
│ │ Client │ ───────────────► │ Server │ │
│ └──────────┘ └──────────┘ │
│ │
│ 问题: │
│ - 数据可被窃听(明文传输) │
│ - 数据可被篡改(无完整性校验) │
│ - 身份可被伪造(无身份验证) │
│ │
│ HTTPS: │
│ ┌──────────┐ 加密传输 ┌──────────┐ │
│ │ Client │ ───────────────► │ Server │ │
│ └──────────┘ └──────────┘ │
│ │ │ │
│ └─────── TLS/SSL ─────────────┘ │
│ │
│ 安全: │
│ - 加密:数据无法窃听 │
│ - 完整性:数据无法篡改 │
│ - 认证:确保服务器身份 │
│ │
└─────────────────────────────────────────────────────────────┘8.2 TLS/SSL 握手过程
┌─────────────────────────────────────────────────────────────┐
│ TLS 1.2 握手过程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ Client Server │
│ │ │ │
│ │── ClientHello ──────────────────────────►│ │
│ │ 支持的加密套件、TLS版本、随机数 │ │
│ │ │ │
│ │◄── ServerHello ───────────────────────── │ │
│ │ 选定的加密套件、TLS版本、随机数 │ │
│ │ │ │
│ │◄── Certificate ───────────────────────── │ │
│ │ 服务器证书 │ │
│ │ │ │
│ │◄── ServerHelloDone ───────────────────── │ │
│ │ │ │
│ │── ClientKeyExchange ────────────────────►│ │
│ │ 加密的预主密钥 │ │
│ │ │ │
│ │── ChangeCipherSpec ─────────────────────►│ │
│ │── Finished ─────────────────────────────►│ │
│ │ │ │
│ │◄── ChangeCipherSpec ──────────────────── │ │
│ │◄── Finished ──────────────────────────── │ │
│ │ │ │
│ │ 开始加密通信 │ │
│ │
└─────────────────────────────────────────────────────────────┘8.3 TLS 1.3 握手优化
┌─────────────────────────────────────────────────────────────┐
│ TLS 1.3 握手过程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ TLS 1.2: 2 RTT │
│ ────────────────────────────────────────────► │
│ ◄──────────────────────────────────────────── │
│ ────────────────────────────────────────────► │
│ ◄──────────────────────────────────────────── │
│ │
│ TLS 1.3: 1 RTT │
│ ────────────────────────────────────────────► │
│ ◄──────────────────────────────────────────── │
│ │
│ TLS 1.3 0-RTT (恢复会话): │
│ ────────────────────────────────────────────► │
│ (立即发送应用数据) │
│ │
│ 优化措施: │
│ 1. 精简加密套件:移除不安全的算法 │
│ 2. 合并消息:ClientHello 携带密钥材料 │
│ 3. PSK 恢复:支持 0-RTT │
│ │
└─────────────────────────────────────────────────────────────┘8.4 数字证书与 CA
┌─────────────────────────────────────────────────────────────┐
│ 数字证书验证链 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ Root CA │ 根证书(内置在操作系统/浏览器) │
│ │ (自签名) │ │
│ └──────┬───────┘ │
│ │ 签发 │
│ ▼ │
│ ┌──────────────┐ │
│ │ Intermediate │ 中间证书 │
│ │ CA │ │
│ └──────┬───────┘ │
│ │ 签发 │
│ ▼ │
│ ┌──────────────┐ │
│ │ Server │ 服务器证书 │
│ │ Certificate │ (www.example.com) │
│ └──────────────┘ │
│ │
│ 验证过程: │
│ 1. 浏览器收到服务器证书 │
│ 2. 用中间 CA 公钥验证服务器证书签名 │
│ 3. 用根 CA 公钥验证中间 CA 证书签名 │
│ 4. 根 CA 证书在信任列表中,验证通过 │
│ │
└─────────────────────────────────────────────────────────────┘8.5 对称加密 vs 非对称加密
| 对比项 | 对称加密 | 非对称加密 |
|---|---|---|
| 密钥 | 加密解密用同一密钥 | 公钥加密,私钥解密 |
| 速度 | 快 | 慢 |
| 安全性 | 密钥传输风险 | 安全 |
| 算法 | AES、DES、ChaCha20 | RSA、ECC |
| 用途 | 数据加密 | 密钥交换、数字签名 |
HTTPS 混合加密:
HTTPS 使用混合加密:
1. 握手阶段:非对称加密
- 安全交换对称密钥
- 使用 RSA 或 ECDHE
2. 数据传输阶段:对称加密
- 使用协商好的对称密钥
- 高效加密大量数据
混合加密 = 非对称加密的安全性 + 对称加密的高效性九、HTTP 状态码详解
9.1 状态码分类
| 类别 | 说明 | 常见状态码 |
|---|---|---|
| 1xx | 信息响应 | 100 Continue |
| 2xx | 成功 | 200 OK, 201 Created, 204 No Content |
| 3xx | 重定向 | 301 永久重定向, 302 临时重定向, 304 Not Modified |
| 4xx | 客户端错误 | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found |
| 5xx | 服务端错误 | 500 Internal Error, 502 Bad Gateway, 503 Service Unavailable |
9.2 常见状态码详解
┌─────────────────────────────────────────────────────────────┐
│ 常见 HTTP 状态码 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 2xx 成功 │
│ ├── 200 OK:请求成功 │
│ ├── 201 Created:创建成功(POST) │
│ ├── 202 Accepted:已接受,异步处理中 │
│ └── 204 No Content:成功但无返回内容 │
│ │
│ 3xx 重定向 │
│ ├── 301 Moved Permanently:永久重定向 │
│ │ SEO 权重转移,浏览器缓存 │
│ ├── 302 Found:临时重定向 │
│ │ SEO 权重不转移,用于临时跳转 │
│ ├── 304 Not Modified:资源未修改 │
│ │ 协商缓存命中,使用本地缓存 │
│ └── 307/308:保持请求方法的重定向 │
│ │
│ 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:网关超时 │
│ │
└─────────────────────────────────────────────────────────────┘9.3 301 vs 302 vs 307
┌─────────────────────────────────────────────────────────────┐
│ 重定向状态码对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 301 Moved Permanently │
│ - 永久重定向 │
│ - 浏览器会缓存新地址 │
│ - SEO 权重转移到新地址 │
│ - POST → GET(历史遗留问题) │
│ │
│ 302 Found │
│ - 临时重定向 │
│ - 浏览器不缓存 │
│ - SEO 权重不转移 │
│ - POST → GET(历史遗留问题) │
│ │
│ 307 Temporary Redirect │
│ - 临时重定向(严格版) │
│ - 保持请求方法不变 │
│ - POST → POST │
│ │
│ 308 Permanent Redirect │
│ - 永久重定向(严格版) │
│ - 保持请求方法不变 │
│ - POST → POST │
│ │
│ 使用场景: │
│ - 网站永久迁移:301 │
│ - 临时维护页面:302 │
│ - 表单提交后重定向:307 │
│ │
└─────────────────────────────────────────────────────────────┘9.4 401 vs 403
| 状态码 | 含义 | 场景 |
|---|---|---|
| 401 | 未认证 | 未登录、Token 无效 |
| 403 | 已认证但无权限 | 已登录但权限不足 |
示例:
用户访问管理后台:
1. 未登录
GET /admin
Response: 401 Unauthorized
→ 跳转到登录页
2. 已登录普通用户
GET /admin
Response: 403 Forbidden
→ 显示无权限页面
3. 已登录管理员
GET /admin
Response: 200 OK
→ 正常显示管理后台十、HTTP 请求头与响应头
10.1 常用请求头
┌─────────────────────────────────────────────────────────────┐
│ 常用 HTTP 请求头 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 通用请求头: │
│ ├── Host: www.example.com # 目标主机 │
│ ├── User-Agent: Mozilla/5.0 # 客户端标识 │
│ ├── Accept: text/html # 可接受的响应类型 │
│ ├── Accept-Language: zh-CN # 语言偏好 │
│ ├── Accept-Encoding: gzip, br # 支持的压缩算法 │
│ └── Connection: keep-alive # 连接控制 │
│ │
│ 缓存相关: │
│ ├── If-Modified-Since: date # 条件请求 │
│ ├── If-None-Match: "etag" # ETag 验证 │
│ └── Cache-Control: no-cache # 缓存控制 │
│ │
│ 认证相关: │
│ ├── Authorization: Bearer token # 认证令牌 │
│ └── Cookie: sessionid=xxx # Cookie │
│ │
│ 内容相关: │
│ ├── Content-Type: application/json # 请求体类型 │
│ ├── Content-Length: 1234 # 请求体长度 │
│ └── Content-Encoding: gzip # 请求体编码 │
│ │
│ CORS 相关: │
│ ├── Origin: https://example.com # 请求来源 │
│ └── Access-Control-Request-Method # 预检请求方法 │
│ │
└─────────────────────────────────────────────────────────────┘10.2 常用响应头
┌─────────────────────────────────────────────────────────────┐
│ 常用 HTTP 响应头 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 通用响应头: │
│ ├── Server: nginx # 服务器标识 │
│ ├── Date: Mon, 01 Jan 2024 # 响应时间 │
│ └── Connection: keep-alive # 连接控制 │
│ │
│ 内容相关: │
│ ├── Content-Type: text/html # 响应体类型 │
│ ├── Content-Length: 1234 # 响应体长度 │
│ ├── Content-Encoding: gzip # 压缩算法 │
│ └── Content-Disposition: attachment# 下载文件名 │
│ │
│ 缓存相关: │
│ ├── Cache-Control: max-age=3600 # 缓存时间 │
│ ├── ETag: "abc123" # 资源版本标识 │
│ ├── Last-Modified: date # 最后修改时间 │
│ └── Expires: date # 过期时间 │
│ │
│ 安全相关: │
│ ├── Strict-Transport-Security # HSTS │
│ ├── X-Content-Type-Options: nosniff# MIME 类型嗅探禁止 │
│ ├── X-Frame-Options: DENY # 防止点击劫持 │
│ └── X-XSS-Protection: 1; mode=block# XSS 保护 │
│ │
│ CORS 相关: │
│ ├── Access-Control-Allow-Origin # 允许的源 │
│ ├── Access-Control-Allow-Methods # 允许的方法 │
│ └── Access-Control-Allow-Headers # 允许的头 │
│ │
└─────────────────────────────────────────────────────────────┘10.3 Cache-Control 详解
┌─────────────────────────────────────────────────────────────┐
│ Cache-Control 指令 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 请求头指令: │
│ ├── no-cache # 强制向服务器验证 │
│ ├── no-store # 不缓存任何内容 │
│ ├── max-age=seconds # 可接受的缓存时间 │
│ └── only-if-cached # 只使用缓存 │
│ │
│ 响应头指令: │
│ ├── public # 可被任何缓存存储 │
│ ├── private # 只能被浏览器缓存 │
│ ├── no-cache # 使用前必须验证 │
│ ├── no-store # 不缓存 │
│ ├── max-age=seconds # 缓存有效时间 │
│ ├── must-revalidate # 过期后必须验证 │
│ └── immutable # 资源永不变化 │
│ │
│ 示例: │
│ Cache-Control: public, max-age=31536000, immutable │
│ # 静态资源:缓存一年,永不重新验证 │
│ │
│ Cache-Control: no-cache │
│ # 每次使用前必须向服务器验证 │
│ │
│ Cache-Control: no-store │
│ # 敏感数据,禁止缓存 │
│ │
└─────────────────────────────────────────────────────────────┘10.4 强缓存 vs 协商缓存
┌─────────────────────────────────────────────────────────────┐
│ HTTP 缓存策略 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 强缓存(不发请求): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Client Server │ │
│ │ │ │ │ │
│ │ │── GET /app.js ──────────►│ │ │
│ │ │ │ │ │
│ │ │ 检查本地缓存 │ │ │
│ │ │ Cache-Control 未过期 │ │ │
│ │ │ │ │ │
│ │ │ 直接使用缓存 (200 OK │ │ │
│ │ │ from disk/memory cache) │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 响应头:Cache-Control, Expires │
│ │
│ 协商缓存(发请求验证): │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Client Server │ │
│ │ │ │ │ │
│ │ │── GET /app.js ──────────►│ │ │
│ │ │ If-None-Match: "etag" │ │ │
│ │ │ │ │ │
│ │ │ │ 资源未修改 │ │
│ │ │◄── 304 Not Modified ────│ │ │
│ │ │ │ │ │
│ │ │ 使用本地缓存 │ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 响应头:ETag + If-None-Match │
│ Last-Modified + If-Modified-Since │
│ │
└─────────────────────────────────────────────────────────────┘十一、常见面试题
Q1: TCP 三次握手为什么不能是两次?
A:
- 防止历史连接:两次握手无法让客户端拒绝服务端的旧连接请求
- 同步序列号:TCP 需要确认双方的初始序列号,两次握手只能确认一个方向的序列号
- 防止资源浪费:如果只有两次握手,服务端收到 SYN 就建立连接,容易受到 SYN Flood 攻击
Q2: TCP 四次挥手为什么客户端要等待 2MSL?
A:
- 确保最后的 ACK 到达:如果 ACK 丢失,服务端会重发 FIN,2MSL 时间足够让客户端收到重发的 FIN 并重发 ACK
- 等待旧数据消失:确保旧连接的数据在网络中消失,不会被新连接接收
Q3: TCP 如何保证可靠传输?
A:
- 序列号和确认应答:保证数据有序、无丢失
- 超时重传:丢失的数据自动重传
- 快速重传:收到 3 个重复 ACK 立即重传
- 流量控制:滑动窗口防止接收方溢出
- 拥塞控制:慢启动、拥塞避免、快速恢复防止网络拥塞
- 校验和:检测数据传输错误
Q4: HTTP/1.1 如何解决队头阻塞?
A: HTTP/1.1 无法完全解决队头阻塞,只能缓解:
- 持久连接:复用 TCP 连接,减少握手开销
- 管道化:可以连续发送多个请求,但响应必须按序返回
- 实际方案:浏览器对同一域名开启多个 TCP 连接(通常 6 个)
真正的解决方案是 HTTP/2.0 的多路复用。
Q5: HTTPS 是如何保证安全的?
A:
- 加密:混合加密机制,握手用非对称加密,传输用对称加密
- 完整性:消息认证码(MAC)保证数据不被篡改
- 认证:数字证书 + CA 链验证服务器身份
Q6: GET 和 POST 有什么区别?
A:
| 对比项 | GET | POST |
|---|---|---|
| 参数位置 | URL 中 | 请求体中 |
| 参数长度 | 受限 | 无限制 |
| 安全性 | 参数暴露 | 相对安全 |
| 缓存 | 可缓存 | 不可缓存 |
| 幂等性 | 幂等 | 非幂等 |
| 历史记录 | 保留 | 不保留 |
Q7: 什么是 HTTP 的幂等性?
A: 幂等性指多次执行相同请求,结果与执行一次相同。
- 幂等方法:GET、PUT、DELETE、HEAD、OPTIONS
- 非幂等方法:POST
幂等示例:
DELETE /users/1 → 删除用户1,多次删除结果相同
非幂等示例:
POST /orders → 创建订单,多次请求会创建多个订单Q8: HTTP/2.0 相比 HTTP/1.1 有什么改进?
A:
- 二进制分帧:将报文分成更小的帧,提高解析效率
- 多路复用:一个连接可并行多个请求/响应,解决队头阻塞
- 头部压缩:HPACK 算法压缩请求头
- 服务端推送:主动推送客户端可能需要的资源
- 请求优先级:可以给请求设置优先级
Q9: 什么是跨域?如何解决?
A: 跨域是浏览器的同源策略限制,协议、域名、端口任一不同就是跨域。
解决方案:
- CORS:服务端设置 Access-Control-Allow-Origin
- 代理服务器:通过同源服务器转发请求
- JSONP:利用 script 标签(仅支持 GET)
- WebSocket:不受同源策略限制
Q10: 解释 TCP 的滑动窗口机制?
A: 滑动窗口是实现流量控制和可靠传输的核心机制:
- 发送方维护发送窗口,接收方维护接收窗口
- 接收方通过 ACK 通告接收窗口大小
- 发送方根据窗口大小控制发送速率
- 窗口可以向前滑动,实现连续的数据传输
十二、总结
| 知识点 | 核心内容 | 面试关键词 |
|---|---|---|
| TCP 连接管理 | 三次握手、四次挥手 | SYN Flood、TIME_WAIT、2MSL |
| TCP 可靠传输 | 序列号、确认应答、重传 | 超时重传、快速重传、SACK |
| TCP 流量/拥塞控制 | 滑动窗口、拥塞窗口 | 慢启动、拥塞避免、快速恢复 |
| HTTP 协议 | 请求响应、状态码、头部 | GET vs POST、缓存策略 |
| HTTP 版本 | 1.0/1.1/2.0/3.0 | 多路复用、QUIC、队头阻塞 |
| HTTPS | TLS 握手、证书验证 | 对称/非对称加密、CA |
最佳实践清单:
- 理解 TCP 连接建立的完整过程
- 掌握 TIME_WAIT 状态的意义和优化方法
- 了解 TCP 拥塞控制算法
- 熟悉 HTTP 各版本的差异
- 掌握 HTTPS 的工作原理
- 理解 HTTP 缓存策略
- 能够排查网络连接问题
最后更新:2026年3月16日