从零理解 TCP/IP 协议栈:网络世界的通用语言
每天我们都在和 TCP/IP 打交道:刷网页、看视频、聊微信。但当 HTTP 请求到达服务器时,数据是怎么一层层”剥洋葱”传递的? 本文从最基础的概念讲起,带你穿透协议栈,理解每一次网络通信背后的原理。
想象一个没有协议的世界:你写的数据,对方根本看不懂。就像两个人说话如果语言不通,只能鸡同鸭讲。
协议就是通信双方约定的规则:
- 数据格式
- 传输顺序
- 错误处理
- 状态管理
1
2
3
4
5
6
7
7 应用层 ← HTTP、FTP、DNS、SMTP
6 表示层 ← 数据格式、加密
5 会话层 ← 建立/维护会话
4 传输层 ← TCP、UDP
3 网络层 ← IP、ICMP、ARP
2 数据链路层 ← 以太网、Wi-Fi
1 物理层 ← 网线、光纤、无线电
1
2
3
4
4 应用层 ← HTTP、DNS、SSH
3 传输层 ← TCP、UDP
2 网络层 ← IP、ICMP
1 网络接口层 ← 以太网、ARP
每层只关心自己的事,通过接口与上下层通信。这种设计的好处是解耦——改一层的实现不影响其他层。
1
2
3
4
5
6
7
8
9
应用层: [HTTP 请求数据]
↓ 加 HTTP 头
传输层: [TCP 头 | HTTP 数据]
↓ 加 TCP 头
网络层: [IP 头 | TCP 头 | HTTP 数据]
↓ 加 IP 头
链路层: [帧头 | IP 头 | TCP 头 | HTTP 数据 | 帧尾]
↓ 加帧头帧尾
物理层: 10101010100011010... (比特流)
接收方则是逆向解封装,像剥洋葱一样一层层去掉头部。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- 源端口、目的端口:区分应用
- 序列号:标识数据字节位置
- 确认号:期望收到的下一个字节
- 窗口大小:流量控制
- 标志位:SYN、ACK、FIN、RST 等
1
2
3
4
5
6
7
8
Client Server
|--- SYN, seq=x -------------->|
| |
|<-- SYN+ACK, seq=y, ack=x+1 --|
| |
|--- ACK, seq=x+1, ack=y+1 --->|
| |
[连接建立]
为什么要三次而不是两次?
为了防止已失效的连接请求突然传到服务器导致错误。如果是两次,网络延迟的旧请求到达服务器,服务器以为建立连接但客户端已经放弃,造成资源浪费。
1
2
3
4
5
6
7
8
9
10
Client Server
|--- FIN, seq=x -------------->|
| |
|<-- ACK, ack=x+1 -------------|
| |
|<-- FIN, seq=y ---------------|
| |
|--- ACK, ack=y+1 ------------>|
| |
[等待 2MSL 后关闭]
为什么是四次?
因为 TCP 是全双工的,客户端说”我说完了”,服务器也要说”我也说完了”。两次只能关一个方向。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
LISTEN
| SYN
v
+-- SYN_SENT ---+
| | SYN+ACK
v v
SYN_RCVD <----- SYN_RCVD
| ACK | ACK
v v
ESTABLISHED ESTABLISHED
| FIN
v
FIN_WAIT_1 ---+
| ACK | FIN
v v
FIN_WAIT_2 CLOSE_WAIT
| FIN | FIN+ACK
v v
TIME_WAIT LAST_ACK
| | ACK
+--- CLOSED <---+
问题:发送方太快,接收方来不及处理。
解决:接收方告诉发送方自己的窗口大小(缓冲区剩余空间)。
1
2
接收方窗口 = 64KB
发送方最多发送 64KB 数据,等 ACK 后才能发更多
问题:网络本身拥塞,大量包丢失重传更糟。
解决:四个核心算法
- 慢启动:从 1 个 MSS 开始,每收到 ACK 翻倍(指数增长)
- 拥塞避免:达到阈值后线性增长
- 快重传:收到 3 个重复 ACK 立即重传
- 快恢复:跳过慢启动,直接进入拥塞避免
1
2
3
4
5
6
7
8
9
10
cwnd
^
| /\
| / \ ← 拥塞发生,阈值减半
| / \
| / \____ 慢启动阈值
| / \
| / \____ 拥塞避免(线性)
| /
+-------------------------> 时间
1
2
3
4
5
6
7
8
9
10
11
12
13
yum install -y tcpdump
tcpdump -i eth0 port 80 -A -s 0
tcpdump -i eth0 port 80 -nn
12:00:00.000 IP 192.168.1.10.54321 > 192.168.1.20.80: Flags [S], seq 100
12:00:00.001 IP 192.168.1.20.80 > 192.168.1.10.54321: Flags [S.], seq 200, ack 101
12:00:00.002 IP 192.168.1.10.54321 > 192.168.1.20.80: Flags [.], ack 201
Flags 含义:
- S = SYN
- . = ACK
- F = FIN
- R = RST
- P = PSH(推送数据)
UDP(User Datagram Protocol)极简:
1
2
3
4
5
+----------+----------+----------+----------+
| Src Port | Dst Port | Length | Checksum |
+----------+----------+----------+----------+
| Data |
+--------------------------------------+
- 无连接:直接发,不握手
- 不可靠:不保证送达、不保证顺序
- 无流量控制:发就完了
- 轻量:头部只有 8 字节
- DNS 查询
- 视频/语音通话(偶尔丢包不致命)
- 实时游戏
- 广播/组播
会,而且 UDP 也不通知你。靠应用层处理:
- 前向纠错(FEC)
- 重传请求(ARQ)
- 冗余编码
IPv4:32 位,4 段十进制,共 43 亿个地址。
1
2
3
4
5
6
A 类:1.0.0.0 - 126.255.255.255 (大型网络)
B 类:128.0.0.0 - 191.255.255.255 (中型)
C 类:192.0.0.0 - 223.255.255.255 (小型)
D 类:224.0.0.0 - 239.255.255.255 (组播)
E 类:240.0.0.0 - 255.255.255.255 (实验)
1
2
255.255.255.0 = /24 = 前 24 位是网络号,后 8 位是主机号
192.168.1.0/24:网络 192.168.1.0,主机 1-254
无类域间路由,灵活划分网络。
128 位,理论上 2^128 个地址,几乎用不完。
1
2001:0db8:85a3:0000:0000:8a2e:0370:7334
1
2
3
4
5
HTTP/0.9 (1991):只有 GET,纯文本
HTTP/1.0 (1996):支持 POST、HEAD、状态码
HTTP/1.1 (1997):持久连接、管道、分块传输
HTTP/2 (2015):二进制、多路复用、HPACK 压缩
HTTP/3 (2018):基于 QUIC(UDP),彻底解决队头阻塞
1
浏览器 → DNS 解析 → TCP 连接 → 发送请求 → 接收响应 → 渲染页面
1
2
3
4
5
6
7
Client Server
|--- Client Hello ------->| (支持的加密算法、随机数)
|<-- Server Hello --------| (选定的算法、证书)
|--- Key Exchange ------->| (用证书公钥加密 pre-master secret)
|<-- Finished ------------|
|--- Finished ----------->|
| [加密通信开始] |
TLS 1.3 把握手从 2-RTT 降到 1-RTT,甚至 0-RTT。
1
2
3
4
. (根域)
└── com (顶级域 TLD)
└── example.com (二级域)
└── www.example.com (子域)
1
2
浏览器缓存 → 系统缓存 → hosts → LocalDNS →
根 DNS → 顶级 DNS → 权威 DNS → 返回 IP
- A:域名 → IPv4
- AAAA:域名 → IPv6
- CNAME:域名 → 域名(别名)
- MX:邮件服务器
- NS:权威 DNS 服务器
- TXT:任意文本(SPF、DKIM 验证)
1
2
3
4
5
6
7
8
9
10
11
12
dig example.com
dig @8.8.8.8 example.com
dig +trace example.com
nslookup example.com
host example.com
测试连通性。
1
2
ping -c 4 example.com
查看路径。
1
2
traceroute example.com
查看连接。
1
2
ss -tulnp
ss -tan | head
抓包分析。
1
tcpdump -i any host 8.8.8.8 and port 53
图形化抓包工具,网络协议学习的瑞士军刀。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.core.somaxconn = 65535
net.ipv4.tcp_rmem = 4096 87380 6291456
net.ipv4.tcp_wmem = 4096 65536 6291456
net.ipv4.tcp_congestion_control = bbr
- 多路复用:一个连接并发多个请求
- 头部压缩:HPACK 算法
- 服务端推送(已废弃)
- 静态资源分发到边缘节点
- 智能调度到最近的节点
- 减少回源
攻击者发大量 SYN 不回 ACK,占满半连接队列。
防御:SYN Cookie(net.ipv4.tcp_syncookies = 1)、防火墙、专业 Anti-DDoS 设备。
局域网内发送伪造 ARP 包,拦截流量。
防御:静态 ARP 绑定、ARP 防火墙。
攻击者在客户端和服务端之间伪造证书。
防御:客户端校验证书、HSTS、Certificate Pinning。
- 《TCP/IP 详解 卷 1:协议》(W. Richard Stevens)
- 《计算机网络:自顶向下方法》(Kurose)
- 《HTTP 权威指南》(David Gourley)
- 《图解 HTTP》(上野宣)
- 用 Wireshark 抓自己电脑的 HTTP 请求,逐字段分析
- 用 Python 写一个简单的 Socket 客户端/服务器
- 部署一个网站,自己抓包看完整过程
TCP/IP 协议栈看似复杂,但核心思想很简单:
- 分层:每层只管自己的事
- 封装:发送时层层打包,接收时层层解包
- 可靠性:TCP 用握手、确认、重传保证数据不丢
- 效率:滑动窗口、拥塞控制优化传输
理解了这些,你再看任何网络问题:
- 为什么 502? 后端没响应 → TCP 连接失败
- 为什么慢? 网络拥塞或服务器处理慢
- 为什么丢包? 路由器过载或链路不稳
网络协议是程序员的底层能力,越深入学习,你解决问题的能力就越强。