🔄 TCP 三次握手与四次挥手(全网最清晰版)
TCP 是面向连接的可靠传输协议,三次握手 用于建立连接,四次挥手 用于断开连接。下面我用图解 + 详细步骤 + 状态变化 + 面试重点,帮你彻底搞懂。
一、TCP 三次握手(建立连接)
📊 流程图
sequenceDiagram
participant C as 客户端 (Client)
participant S as 服务端 (Server)
Note over C,S: 1. 第一次握手
C->>S: SYN=1, seq=x
Note over C: 状态:SYN_SENT
Note over S: 状态:LISTEN → SYN_RCVD
Note over C,S: 2. 第二次握手
S->>C: SYN=1, ACK=1, seq=y, ack=x+1
Note over C: 状态:SYN_SENT → ESTABLISHED
Note over C,S: 3. 第三次握手
C->>S: ACK=1, seq=x+1, ack=y+1
Note over S: 状态:SYN_RCVD → ESTABLISHED
Note over C,S: 连接建立成功
📝 详细步骤
步骤 发送方 → 接收方 报文内容 发送方状态 接收方状态 第一次 客户端 → 服务端 SYN=1, seq=xSYN_SENTLISTEN → SYN_RCVD第二次 服务端 → 客户端 SYN=1, ACK=1, seq=y, ack=x+1SYN_SENT → ESTABLISHEDSYN_RCVD第三次 客户端 → 服务端 ACK=1, seq=x+1, ack=y+1ESTABLISHEDSYN_RCVD → ESTABLISHED
🔍 关键字段解释
字段 含义 作用 SYN 同步序列号 发起一个新连接 ACK 确认 确认收到数据 seq 序列号 本报文的第一个字节的序号 ack 确认号 期望收到对方下一个报文的序列号
❓ 为什么是三次,不是两次或四次?
次数 问题 解释 两次 ❌ 无法确认客户端接收能力 服务端发 SYN+ACK 后无法知道客户端是否收到 三次 ✅ 刚好确认双方收发能力 1. 客户端发 SYN → 服务端知道客户端能发 2. 服务端回 SYN+ACK → 客户端知道自己能发能收,服务端能发 3. 客户端回 ACK → 服务端知道自己能发能收,客户端能收 四次 ❌ 浪费 服务端的 SYN 和 ACK 可以合并一次发送
二、TCP 四次挥手(断开连接)
📊 流程图
📝 详细步骤
步骤 发送方 → 接收方 报文内容 发送方状态 接收方状态 第一次 客户端 → 服务端 FIN=1, seq=uESTABLISHED → FIN_WAIT_1ESTABLISHED → CLOSE_WAIT第二次 服务端 → 客户端 ACK=1, seq=v, ack=u+1FIN_WAIT_1 → FIN_WAIT_2CLOSE_WAIT第三次 服务端 → 客户端 FIN=1, ACK=1, seq=w, ack=u+1FIN_WAIT_2 → TIME_WAITCLOSE_WAIT → LAST_ACK第四次 客户端 → 服务端 ACK=1, seq=u+1, ack=w+1TIME_WAIT (2MSL后 CLOSED)LAST_ACK → CLOSED
🔑 关键状态解释
状态 含义 出现时机 FIN_WAIT_1 主动方已发 FIN,等待 ACK 第一次挥手后 FIN_WAIT_2 主动方已收到 ACK,等待对方 FIN 第二次挥手后 CLOSE_WAIT 被动方收到 FIN,等待上层应用关闭 第一次挥手后 LAST_ACK 被动方已发 FIN,等待最后一个 ACK 第三次挥手后 TIME_WAIT 主动方收到 FIN,发完 ACK 后等待 2MSL 第四次挥手后
❓ 为什么是四次,不是三次?
原因 解释 TCP 是全双工 双方都需要独立关闭自己的通道 数据可能未传完 服务端收到 FIN 后可能还有数据要发,不能立即关 ACK 和 FIN 分开 服务端的 ACK 只是确认收到 FIN,等数据发完再发自己的 FIN
⏱️ TIME_WAIT 为什么要等 2MSL?
原因 解释 1. 确保最后一个 ACK 到达 如果 ACK 丢失,服务端会重发 FIN,客户端还有时间响应 2. 防止旧报文干扰 等待 2MSL 确保该连接的所有旧报文都已消失,不会干扰新连接
MSL :Maximum Segment Lifetime,报文最大生存时间,通常 30秒 – 2分钟
三、面试高频问题
Q1:为什么建立连接是三次握手,断开是四次挥手?
连接 挥手 服务端可以合并 SYN 和 ACK 服务端的 ACK 和 FIN 不能合并 因为建立连接时服务端没有数据要发 因为断开时服务端可能还有数据要发
Q2:TIME_WAIT 过多怎么办?
# 查看 TIME_WAIT 数量
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
# 优化内核参数(/etc/sysctl.conf)
net.ipv4.tcp_tw_reuse = 1 # 允许重用 TIME_WAIT socket
net.ipv4.tcp_tw_recycle = 1 # 快速回收(NAT环境慎用)
net.ipv4.tcp_fin_timeout = 30 # FIN-WAIT-2 超时时间
# 生效
sysctl -p
Q3:CLOSE_WAIT 过多说明什么?
问题 原因 解决 CLOSE_WAIT 堆积 应用程序没有正确调用 close() 检查代码,确保 socket 用完关闭 常见于 Java/PHP 程序 未捕获异常导致资源未释放 用 lsof -p PID 查句柄数
Q4:如何抓包看三次握手?
tcpdump -i eth0 -nn port 80 -c 3 -w handshake.pcap
# 然后用 Wireshark 打开,Filter: tcp.flags.syn==1
📊 四、状态变化汇总表
角色 握手状态变化 挥手状态变化 客户端 CLOSED → SYN_SENT → ESTABLISHEDESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED服务端 LISTEN → SYN_RCVD → ESTABLISHEDESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED
✅ 五、一句话记忆法
过程 记忆口诀 三次握手 你听得到吗?我听到了,我也听得到 四次挥手 我要关了 → 好的等我发完 → 我发完了 → 好的再见
需要我帮你分析抓包文件或排查实际连接问题吗?
发表回复