主题
📦 TCP 协议深入:三次握手与四次挥手
TCP 为什么可靠?因为有握手建立连接、确认重传丢失、挥手优雅断开
TCP 特性一览
| 特性 | 怎么做到的 |
|---|---|
| 面向连接 | 三次握手建立,四次挥手断开 |
| 可靠传输 | 序号 + 确认 + 重传 |
| 流量控制 | 滑动窗口 |
| 拥塞控制 | 慢启动、拥塞避免 |
| 全双工 | 双方可同时收发 |
TCP 报文结构
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口 | 目的端口 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 序列号(SEQ) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 确认号(ACK Number) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据偏移 | 保留 | U A P R S F | 窗口大小 |
| | | R C S S Y I | |
| | | G K H T N | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 校验和 | 紧急指针 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+关键标志位:
| 标志 | 全称 | 含义 |
|---|---|---|
| SYN | Synchronize | 请求建立连接 |
| ACK | Acknowledgment | 确认收到 |
| FIN | Finish | 请求断开连接 |
| RST | Reset | 强制断开 |
| PSH | Push | 立即推给应用层 |
三次握手
客户端 服务器
│ │
│ ① SYN=1, SEQ=x │ 客户端:"我想连你"
│─────────────────────────────→│
│ │
│ ② SYN=1, ACK=1 │ 服务器:"收到,我也想连你"
│ SEQ=y, ACK=x+1 │
│←─────────────────────────────│
│ │
│ ③ ACK=1, SEQ=x+1, ACK=y+1 │ 客户端:"收到"
│─────────────────────────────→│
│ │
│ ████████████ 正常通信 ██████████ │为什么三次不是两次? 防止已失效的连接请求到达服务器——服务器回了 SYN+ACK,但客户端已经不认了,白白打开连接。
四次挥手
客户端 服务器
│ │
│ ① FIN=1, SEQ=u │ 客户端:"我发完了"(主动关闭)
│─────────────────────────────→│
│ │
│ ② ACK=1, SEQ=v, ACK=u+1 │ 服务器:"收到你发完了"
│←─────────────────────────────│ (客户端→服务器方向关闭)
│ │
│ 服务器可能还有数据要发 │
│ │
│ ③ FIN=1, ACK=1, SEQ=w │ 服务器:"我也发完了"
│ ACK=u+1 │
│←─────────────────────────────│
│ │
│ ④ ACK=1, SEQ=u+1, ACK=w+1 │ 客户端:"收到"
│─────────────────────────────→│
│ │
│ ⏳ TIME_WAIT 2MSL(约 120s)│
│ 确保服务器收到最后的 ACK │
│ │为什么是四次不是三次? TCP 是全双工的——客户端说"我发完了"(FIN),服务器可能还有数据要传给客户端。所以 ACK 和 FIN 分开发。
TIME_WAIT 是什么
主动关闭方(先发 FIN 的那方)进入 TIME_WAIT 状态,持续 2MSL(约 60-120 秒)。
ss -tan | grep TIME_WAIT | wc -l为什么需要 TIME_WAIT?
- 确保最后一个 ACK 到达服务器——ACK 丢了服务器会重发 FIN,客户端可以在 TIME_WAIT 期间重 ACK
- 让网络上残留的旧连接包消失(2MSL = 包的最大生存时间 × 2)
TIME_WAIT 太多怎么办?
bash
# 查看当前 TIME_WAIT 数
ss -tan state time-wait | wc -l
# 调内核参数减少(服务器端)
# 开启 TIME_WAIT 重用
echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf
# 减少 TIME_WAIT 时长(谨慎)
# echo "net.ipv4.tcp_fin_timeout = 30" >> /etc/sysctl.conf
sudo sysctl -pTCP 状态流转(全貌)
+---------+ ---------\ active OPEN
| CLOSED | \ -----------
+---------+<---------\ \ create TCB
| ^ \ \ snd SYN
passive OPEN | | CLOSE \ \
------------ | | ---------- \ \
create TCB | | delete TCB \ \
V | \ \
+---------+ CLOSE | \
| LISTEN | ---------- | |
+---------+ delete TCB | |
rcv SYN | | SEND | |
----------- | | ------- | V
+---------+ snd SYN,ACK / \ snd SYN +---------+
| |<----------------- ------------------>| |
| SYN | rcv SYN | SYN |
| RCVD |<----------------------------------------------->| SENT |
| | snd ACK | |
| | | |
+---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+
| -------------- | | -----------
| x | | snd ACK
| V V
| CLOSE +---------+
| ------- | ESTAB | ESTABLISHED
| snd FIN +---------+
V CLOSE | | rcv FIN
+---------+ ------- | | -------
| FIN | snd FIN / \ snd ACK +---------+
| WAIT-1 |<---------------- ------------------>| CLOSE |
+---------+ rcv FIN \ | WAIT |
| rcv ACK of FIN ------- | +---------+
| -------------- snd ACK | CLOSE | |
V x V ------- | |
+---------+ +---------+ snd FIN V |
| FIN | |CLOSING | +---------+ |
| WAIT-2 | +---------+ | LAST | |
+---------+ rcv ACK of FIN | rcv ACK of FIN | ACK | |
| rcv FIN ------------ | ------------ +---------+ |
| ------- x V x | |
\ snd ACK +---------+delete TCB V |
------------------------>| TIME |-------------------->+---------+ |
| WAIT | | CLOSED | |
+---------+ +---------+ |
|
TCP Connection State Diagram用 tcpdump 看握手和挥手
bash
# 抓 TCP 三次握手
sudo tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0' -n
# 输出示例
# S → SYN
# S. → SYN+ACK
# . → ACK
# F → FIN
# F. → FIN+ACK
# 三次握手:
# 10:00:00.001 IP 192.168.1.100.54321 > 124.222.126.171.443: Flags [S], seq 123
# 10:00:00.002 IP 124.222.126.171.443 > 192.168.1.100.54321: Flags [S.], seq 456, ack 124
# 10:00:00.003 IP 192.168.1.100.54321 > 124.222.126.171.443: Flags [.], ack 457🎯 本章要点
- 三次握手 = SYN → SYN+ACK → ACK,建立双向连接
- 四次挥手 = FIN → ACK → FIN → ACK,TCP 是全双工要分开关
- TIME_WAIT 持续 2MSL(~120s),是正常的,大量 TIME_WAIT 调
tcp_tw_reuse - 用
tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-fin) != 0'看握手和挥手 - TCP 状态图记不住没关系,记住 LISTEN / ESTABLISHED / TIME_WAIT / CLOSED 四个就行
加载练习题中...