Skip to content

📦 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 |                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           校验和               |          紧急指针               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

关键标志位:

标志全称含义
SYNSynchronize请求建立连接
ACKAcknowledgment确认收到
FINFinish请求断开连接
RSTReset强制断开
PSHPush立即推给应用层

三次握手

  客户端                         服务器
    │                              │
    │  ① 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?

  1. 确保最后一个 ACK 到达服务器——ACK 丢了服务器会重发 FIN,客户端可以在 TIME_WAIT 期间重 ACK
  2. 让网络上残留的旧连接包消失(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 -p

TCP 状态流转(全貌)

                          +---------+ ---------\      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 四个就行
加载练习题中...

有问题或补充?欢迎留言