TCP 三次握手与四次挥手详解
一、TCP 连接建立(三次握手)
详细过程:
-
第一次握手(SYN):
- 客户端发送 SYN=1 标志(同步序列号)
- 随机生成初始序列号 seq=x
- 进入
SYN_SENT
状态
-
第二次握手(SYN-ACK):
- 服务器收到 SYN 后,发送 SYN=1 和 ACK=1
- 确认号 ack=x+1(期待收到 x+1)
- 随机生成自己的序列号 seq=y
- 进入
SYN_RCVD
状态
-
第三次握手(ACK):
- 客户端确认收到 SYN-ACK
- 发送 ACK=1,ack=y+1,seq=x+1
- 双方进入
ESTABLISHED
状态
关键点:
- 为什么需要三次?
防止历史重复连接初始化导致的资源浪费(两次无法确认客户端接收能力) - 序列号作用:
解决网络包乱序、重复问题,保证可靠性
二、TCP 连接释放(四次挥手)
详细过程:
-
第一次挥手(FIN):
- 主动方发送 FIN=1,seq=u
- 进入
FIN_WAIT_1
状态
-
第二次挥手(ACK):
- 被动方发送 ACK=1,ack=u+1
- 进入
CLOSE_WAIT
状态 - 主动方收到后进入
FIN_WAIT_2
-
第三次挥手(FIN):
- 被动方处理完数据后,发送 FIN=1,seq=v
- 进入
LAST_ACK
状态
-
第四次挥手(ACK):
- 主动方发送 ACK=1,ack=v+1
- 进入
TIME_WAIT
状态(等待 2MSL) - 被动方收到后关闭连接
关键点:
- 为什么需要四次?
TCP 是全双工的,必须分别关闭两个方向的数据流 - TIME_WAIT 状态:
- 等待 2MSL(Maximum Segment Lifetime,默认 60s)
- 确保最后一个 ACK 到达对方
- 让网络中残留的旧报文失效
三、状态机转换图
连接建立:
CLOSED → SYN_SENT → ESTABLISHED
CLOSED → SYN_RCVD → ESTABLISHED
连接释放:
ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED
ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED
四、常见问题
Q1:为什么不能用两次握手建立连接?
- 可能造成历史重复连接占用服务器资源
- 无法确认客户端的接收能力是否正常
Q2:TIME_WAIT 状态过多的危害?
- 占用端口资源(高并发时可能耗尽端口)
- 解决方案:
- 调整
net.ipv4.tcp_tw_reuse
参数 - 使用 SO_REUSEADDR 套接字选项
- 调整
Q3:如果最后一次 ACK 丢失会怎样?
- 被动方会重传 FIN 包
- 主动方在 TIME_WAIT 期间能响应这些重传
五、Wireshark 抓包示例
三次握手:
1. [SYN] Seq=0
2. [SYN, ACK] Seq=0, Ack=1
3. [ACK] Seq=1, Ack=1
四次挥手:
1. [FIN, ACK] Seq=1, Ack=1
2. [ACK] Seq=1, Ack=2
3. [FIN, ACK] Seq=1, Ack=2
4. [ACK] Seq=2, Ack=2
六、编程实现(Python 伪代码)
# 服务端
def server():s = socket.socket()s.bind(('0.0.0.0', 80))s.listen()conn, addr = s.accept() # 完成三次握手conn.close() # 触发四次挥手# 客户端
def client():s = socket.socket()s.connect(('server', 80)) # 发起三次握手s.close() # 发起四次挥手