TCP/IP 与 HTTP 协议核心机制——从三次握手到 HTTPS 加密
摘要
TCP/IP 协议族是互联网的基石,理解其核心机制对每位后端工程师都至关重要。本文从 TCP 三次握手与四次挥手讲起,深入剖析流量控制、拥塞控制算法,梳理 HTTP/1.1 到 HTTP/3 的演进脉络,详细拆解 HTTPS TLS 1.3 握手过程,并提供基于 Wireshark 的抓包实战分析。全文包含大量流程图解与抓包示例。
一、TCP 三次握手与四次挥手
1.1 三次握手——建立连接
sequenceDiagram
participant C as Client<br/>CLOSED
participant S as Server<br/>LISTEN
Note over C: CLOSED → SYN-SENT
C->>S: ① SYN=1, seq=x
Note over S: LISTEN → SYN-RCVD
S->>C: ② SYN=1, ACK=1, seq=y, ack=x+1
Note over C: SYN-SENT → ESTABLISHED
C->>S: ③ ACK=1, seq=x+1, ack=y+1
Note over S: SYN-RCVD → ESTABLISHED
握手目的:
1. 同步双方的初始序列号(ISN)
2. 协商 TCP 参数(MSS、窗口缩放因子等)
3. 验证双方收发能力正常
三次握手状态变迁:
| 状态 | 含义 | 所在端 |
|---|---|---|
| CLOSED | 初始关闭状态 | 双方 |
| LISTEN | 服务端等待连接 | 服务端 |
| SYN-SENT | 已发 SYN | 客户端 |
| SYN-RCVD | 已收 SYN,已发 SYN+ACK | 服务端 |
| ESTABLISHED | 连接建立 | 双方 |
SYN Flood 攻击原理: 客户端发送大量 SYN 但不完成第三次握手,导致服务端维护大量半连接(SYN-RCVD)队列耗尽资源。缓解手段包括 SYN Cookie、增大 tcp_max_syn_backlog 等。
# 查看 TCP 连接状态
ss -tna | head -20
# 查看半连接队列大小
sysctl net.ipv4.tcp_max_syn_backlog
# 默认 128(现代系统通常 1024)
# 启用 SYN Cookie
sysctl -w net.ipv4.tcp_syncookies=1
1.2 四次挥手——关闭连接
sequenceDiagram
participant C as Client<br/>ESTABLISHED
participant S as Server<br/>ESTABLISHED
Note over C: 应用调用 close()
Note over C: ESTABLISHED → FIN-WAIT-1
C->>S: ① FIN=1, seq=u
Note over S: ESTABLISHED → CLOSE-WAIT
Note over S: 应用仍可发送剩余数据
S->>C: ② ACK=1, seq=v, ack=u+1
Note over C: FIN-WAIT-1 → FIN-WAIT-2
Note over S: 应用调用 close()
Note over S: CLOSE-WAIT → LAST-ACK
S->>C: ③ FIN=1, ACK=1, seq=w, ack=u+1
Note over C: FIN-WAIT-2 → TIME-WAIT
C->>S: ④ ACK=1, seq=u+1, ack=w+1
Note over S: LAST-ACK → CLOSED
Note over C: TIME-WAIT (2MSL) → CLOSED
TIME-WAIT 的作用:
1. 确保最后的 ACK 被对端收到(超时重传 FIN)
2. 等待网络中残留的旧数据包过期,防止干扰新连接
为什么 TIME-WAIT 是 2MSL? MSL(Maximum Segment Lifetime,报文最大生存时间,通常 30s~2min)。等待 2MSL 实现了:发送 ACK 后留出 1MSL 等待可能的 FIN 重传 + 收到重传 FIN 后重新发送 ACK 再到对端所需的另 1MSL。
1.3 TCP Keepalive
TCP Keepalive 检测对端是否存活:
# 查看默认配置
sysctl net.ipv4.tcp_keepalive_time # 7200s(2小时)
sysctl net.ipv4.tcp_keepalive_intvl # 75s
sysctl net.ipv4.tcp_keepalive_probes # 9次
即默认需要 2h + 9 × 75s ≈ 2h11min 才能发现对端死亡。生产环境通常调短:
sysctl -w net.ipv4.tcp_keepalive_time=300
sysctl -w net.ipv4.tcp_keepalive_intvl=30
sysctl -w net.ipv4.tcp_keepalive_probes=3
二、流量控制与拥塞控制
2.1 流量控制——滑动窗口
流量控制(Flow Control)解决 接收端处理能力不足 的问题,通过滑动窗口机制实现。
sequenceDiagram
participant S as Sender
participant R as Receiver
Note over S: 发送窗口 = min(cwnd, rwnd)
S->>R: 发送 1~4(窗口大小=4)
R->>R: 处理到第 2 个数据包<br/>应用层未读取缓冲数据
R->>S: ACK=3, win=2<br/>(缓冲区满,通知窗口缩小)
Note over S: 发送窗口缩小到 2
S->>R: 发送 5~6
R->>R: 应用层读取数据,缓冲区空闲
R->>S: ACK=7, win=5
Note over S: 窗口重新扩大
接收窗口 rwnd 的计算:
rwnd = RecvBuffer - (LastByteRcvd - LastByteRead)
/proc/sys/net/ipv4/tcp_rmem 定义了接收缓冲区的大小范围(min、default、max):
# 查看 TCP 缓冲区配置
cat /proc/sys/net/ipv4/tcp_rmem
# 4096 131072 6291456
# min=4KB, default=128KB, max=6MB
cat /proc/sys/net/ipv4/tcp_wmem
# 4096 16384 4194304
# min=4KB, default=16KB, max=4MB
窗口缩放因子(Window Scaling): TCP 头部窗口字段只有 16 位,最大 65535 字节。通过三次握手中的窗口缩放因子(/proc/sys/net/ipv4/tcp_window_scaling),可将窗口放大到 1GB 级别,适应高带宽网络。
2.2 拥塞控制
拥塞控制(Congestion Control)解决 网络链路负载能力不足 的问题,由发送方自主控制。
| 算法阶段 | 触发条件 | 窗口变化 |
|---|---|---|
| 慢启动(Slow Start) | 连接建立 / 超时重传 | 每 RTT 翻倍(指数增长) |
| 拥塞避免(Congestion Avoidance) | 达到 ssthresh | 每 RTT +1(线性增长) |
| 快速重传(Fast Retransmit) | 收到 3 个重复 ACK | 立即重传丢失段 |
| 快速恢复(Fast Recovery) | 快速重传后 | ssthresh = cwnd/2, cwnd = ssthresh + 3 |
graph TD
A[慢启动开始<br/>cwnd=1 MSS] --> B{发生超时?}
B -->|"ssthresh=cwnd/2
cwnd=1"| A
B -->|"cwnd >= ssthresh"| C[拥塞避免<br/>cwnd += 1/RTT]
C --> D{收到 3 个<br/>重复 ACK?}
D -->|"ssthresh=cwnd/2
cwnd=ssthresh+3"| E[快速恢复]
D -->|"超时"| A
E --> F{收到新 ACK?}
F -->|"是"| C
F -->|"仍然重复 ACK"| E
三种主流拥塞控制算法:
| 算法 | 核心思想 | 适用场景 | Linux 默认版本 |
|---|---|---|---|
| Cubic | 立方函数增长,RTT 无关 | 高带宽长链路 | <= 4.18 默认 |
| BBR | 基于带宽和 RTT 测量建模 | 高丢包率链路 | >= 4.18 可选 |
| BBRv3 | BBR 改进版,更好公平性 | 混合网络 | >= 5.19 可选 |
# 查看当前拥塞控制算法
sysctl net.ipv4.tcp_congestion_control
# net.ipv4.tcp_congestion_control = bbr
# 查看可用的算法
sysctl net.ipv4.tcp_available_congestion_control
# bbr cubic reno
# 切换算法
sysctl -w net.ipv4.tcp_congestion_control=bbr
三、HTTP/1.1 / HTTP/2 / HTTP/3 演进
3.1 HTTP/1.1 —— 基础但有限
graph LR
subgraph HTTP/1.1
C1[Client] -->|"请求1(串行)"| S1[Server]
C1 -->|"请求2(需等请求1完成)"| S1
C1 -->|"请求3(串行)"| S1
end
HTTP/1.1 的局限性:
| 问题 | 表现 | 影响 |
|---|---|---|
| 队头阻塞(HOLB) | 一个请求阻塞后续所有请求 | 页面加载缓慢 |
| 无优先级调度 | 关键资源可能被排在后面 | 影响首屏渲染 |
| 头部冗余 | 每次请求都带完整头部 | 带宽浪费 |
| 无法服务器推送 | 只能客户端请求后才响应 | 增加 RTT |
优化手段(治标不治本):
– 域名分片(Domain Sharding):用多个域名突破并发连接限制
– 雪碧图(CSS Sprites):合并小图为一张大图
– 资源内联:将 CSS/JS 直接内联到 HTML
3.2 HTTP/2 —— 多路复用
HTTP/2 基于 Google SPDY 协议,引入二进制分帧层(Binary Framing):
graph TB
subgraph HTTP/2[单个 TCP 连接]
Stream1["Stream 1 (资源A)"]
Stream2["Stream 2 (资源B)"]
Stream3["Stream 3 (资源C)"]
end
subgraph 帧[二进制帧 - 交错传输]
F1["Frame(Stream1, type=HEADERS)"]
F2["Frame(Stream2, type=DATA)"]
F3["Frame(Stream3, type=HEADERS)"]
F4["Frame(Stream1, type=DATA)"]
F5["Frame(Stream2, type=DATA)"]
end
Stream1 --> F1
Stream1 --> F4
Stream2 --> F2
Stream2 --> F5
Stream3 --> F3
HTTP/2 核心特性:
- 多路复用:一个 TCP 连接内并行处理多个 Stream,解决 HTTP/1.1 队头阻塞
- 头部压缩(HPACK):静态字典 + 动态字典(Huffman 编码),压缩率可达 80%
- 服务器推送(Server Push):服务端可主动推送 CSS/JS 等资源,无需客户端请求
- 流优先级:客户端可声明资源加载优先级
HTTP/2 的缺陷: 队头阻塞从 HTTP 层转移到了 TCP 层——TCP 丢包会导致所有 Stream 暂停,直到丢失的数据包被重传。
3.3 HTTP/3 —— 基于 QUIC
HTTP/3 将传输层从 TCP 替换为 QUIC(基于 UDP):
graph LR
subgraph HTTP/3[HTTP/3 协议栈]
H3[HTTP/3]
QPACK[QPACK 头部压缩]
QUIC[QUIC 传输层
基于 UDP]
TLS1_3[TLS 1.3
内置于 QUIC]
end
subgraph HTTP/2[HTTP/2 协议栈]
H2[HTTP/2]
HPACK[HPACK 头部压缩]
TLS[TLS]
TCP[TCP]
end
subgraph HTTP/1.1[HTTP/1.1 协议栈]
H11[HTTP/1.1]
TLS_1[TLS]
TCP_1[TCP]
end
QUIC 解决了 TCP 的固有问题:
| 问题 | TCP 方案 | QUIC 方案 |
|---|---|---|
| 连接建立延迟 | 1~3 RTT(TCP 握手 + TLS 握手) | 0-RTT(首次 1-RTT,后续 0-RTT) |
| 队头阻塞 | 1 个丢包阻塞全部 Stream | 每个 Stream 独立,丢包不影响其他 |
| 连接迁移 | 切换网络必须重建连接 | 用 Connection ID 标识,IP 变化不断连 |
| 头部阻塞 | 内核态的顺序化 | 用户态调度,无阻塞 |
HTTP/3 的 0-RTT 建立: 首次连接 1-RTT(QUIC 握手 + TLS 1.3 密钥协商),以后连接只需携带上次缓存的连接参数,直接发送请求数据。
四、HTTPS TLS 握手过程
4.1 TLS 1.2 握手(传统的 2-RTT)
sequenceDiagram
participant C as Client
participant S as Server
Note over C: ClientHello<br/>① TLS 版本<br/>② 密码套件列表<br/>③ random_C
C->>S: ClientHello
Note over S: ServerHello<br/>① 选定的TLS版本<br/>② 选定的密码套件<br/>③ random_S<br/>④ 证书链<br/>⑤ ServerKeyExchange<br/>⑥ CertificateRequest
S->>C: ServerHello + Certificate + ServerHelloDone
Note over C: 验证证书<br/>生成 pre-master secret
C->>S: ClientKeyExchange (pre-master)
Note over C,S: 双方计算 master secret
C->>S: ChangeCipherSpec + Finished
S->>C: ChangeCipherSpec + Finished
Note over C,S: ✅ 加密通道建立 (2-RTT)
4.2 TLS 1.3 握手(1-RTT / 0-RTT)
TLS 1.3 大幅简化握手流程,移除不安全的密码套件(RSA 密钥交换、RC4、3DES 等),仅支持 5 个安全套件:
sequenceDiagram
participant C as Client
participant S as Server
Note over C: ① 推测服务器可能支持的 curves<br/>② 直接发送 key_share
C->>S: ClientHello<br/>+ supported_versions<br/>+ key_share (EC公钥)
Note over S: ① 用客户端的 key_share 计算共享密钥<br/>② 发送自己的 key_share<br/>③ 握手过程与加密数据合并
S->>C: ServerHello + key_share + EncryptedExtensions + Certificate + Finished
Note over C,S: ✅ 1-RTT 完成
Note over C,S: 后续请求直接加密传输
C->>S: HTTP 请求(加密)
S->>C: HTTP 响应(加密)
Note over C,S: 再连接 (0-RTT)
C->>S: ClientHello + key_share + 0-RTT 数据
Note over S: 用 PSK 恢复会话
TLS 1.3 的主要改进:
- 降为 1-RTT——握手阶段合并为一次往返
- 0-RTT 恢复——使用预共享密钥(PSK),首次请求即可携带数据(但需要防重放攻击)
- 移除不安全套件——仅支持 AEAD 加密算法(AES-GCM、ChaCha20-Poly1305)
- 简化协商——不支持自定义 TLS 扩展协商,减少攻击面
4.3 HTTPS 部署检查
# 使用 openssl 检查服务器 TLS 配置
openssl s_client -connect blog.jydeep.cn:443 -tls1_3
# 查看支持的密码套件
openssl s_client -connect blog.jydeep.cn:443 -cipher 'ECDHE+AESGCM'
# 使用 nmap 扫描 SSL 配置
nmap --script ssl-enum-ciphers -p 443 blog.jydeep.cn
现代 HTTPS 安全配置:
server {
listen 443 ssl http2;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers on;
# 会话缓存
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# HSTS(HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=63072000" always;
}
五、Wireshark 抓包实战分析
5.1 抓取三次握手
# 抓取特定端口的三次握手
tcpdump -i eth0 -n -c 3 'tcp port 443' -w tcp_handshake.pcap
# 用 Wireshark 命令行分析
tshark -r tcp_handshake.pcap -Y "tcp.flags.syn==1" -T fields \
-e frame.number -e ip.src -e ip.dst -e tcp.seq -e tcp.ack -e tcp.flags
5.2 分析 HTTP/2 多路复用
通过 Wireshark 抓包可以看到 HTTP/2 的 Magic 帧和 SETTINGS 帧协商过程,Stream ID 奇偶区分客户端和服务端发起的流。
5.3 排查 TCP 重传和 ZeroWindow
# 查看 TCP 重传
tshark -r capture.pcap -Y "tcp.analysis.retransmission"
# 查看 Zero Window(接收端缓冲区满)
tshark -r capture.pcap -Y "tcp.window_size == 0"
六、总结
TCP/IP 和 HTTP 协议栈经过 40 多年的演进,已经从简单的可靠传输协议发展为涵盖安全、多路复用、低延迟的复杂体系:
- TCP 连接管理——三次握手同步序列号并协商参数,四次挥手保证优雅关闭,2MSL 的 TIME-WAIT 确保旧数据包不会干扰新连接
- 流量与拥塞控制——滑动窗口解决接收端处理速度不匹配,慢启动/拥塞避免/快速重传/快速恢复四阶段算法保障网络公平性
- HTTP 演进——HTTP/1.1 的队头阻塞被 HTTP/2 的多路复用解决,但 TCP 层面的队头阻塞又由 HTTP/3 的 QUIC 彻底消除
- HTTPS 安全——TLS 1.3 将握手从 2-RTT 优化到 1-RTT(甚至 0-RTT),移除不安全套件,是现代 HTTPS 的基石
理解这些协议原理后,无论是排查网络延迟、优化页面加载速度,还是配置 HTTPS 安全策略,都能从协议层面做出更准确的判断。


暂无评论内容