1.前言
TCP(传输控制协议)是一种面向连接的、可靠的传输层协议,广泛应用于 HTTP、FTP、SSH 等网络通信。三次握手(Three-way Handshake) 用于建立连接,四次挥手(Four-way Handshake) 用于终止连接。理解这两个过程对网络编程、性能优化和故障排查至关重要。
本文将详细解析 TCP 三次握手和四次挥手 的流程、状态变化及常见问题。
2.TCP 三次握手(连接建立)
三次握手的目标是 同步双方的初始序列号(ISN) ,并确认双方的收发能力正常。
2.1 三次握手流程
最初,客户端处于closed状态,服务器处于listen状态
-
第一次握手(SYN)
- 客户端 发送
SYN=1, seq=ISN(c),进入SYN_SENT状态。 - 报文内容:
SYN=1, ACK=0, seq=ISN(c) - 作用:告知服务端,客户端希望建立连接,并指定初始序列号
ISN(c)。
- 客户端 发送
-
第二次握手(SYN + ACK)
-
服务端 收到
SYN后,回复SYN=1, ACK=1, seq=ISN(s), ack=ISN(c)+1,进入SYN_RCVD状态。 -
报文内容:
SYN=1, ACK=1, seq=ISN(s), ack=ISN(c)+1 -
作用:
- 确认客户端的
SYN(ack=ISN(c)+1)。 - 发送自己的初始序列号
ISN(s)。
- 确认客户端的
-
-
第三次握手(ACK)
- 客户端 收到
SYN+ACK后,发送ACK=1, seq=ISN(c)+1, ack=ISN(s)+1,进入ESTABLISHED状态。 - 服务端 收到
ACK后,也进入ESTABLISHED状态。 - 报文内容:
ACK=1, seq=ISN(c)+1, ack=ISN(s)+1 - 作用:确认服务端的
SYN,连接正式建立。
- 客户端 收到
2.2 为什么需要三次握手?
- 防止历史重复连接初始化(避免旧的
SYN报文干扰)。 - 同步双方的初始序列号(ISN) ,确保数据按序传输。
- 验证双方的收发能力(客户端能发
SYN,服务端能收SYN并回ACK)。
面试题:为什么不是两次握手?
答:> 第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
3.TCP 四次挥手(连接终止
由于 TCP 是全双工的,关闭连接需要 双方各自发送 FIN 和 ACK,因此需要四次交互。
3.1 四次挥手流程
-
第一次挥手(FIN)
- 主动关闭方(客户端) 发送
FIN=1, seq=u,进入FIN_WAIT_1状态。 - 作用:通知服务端,客户端不再发送数据,但仍可接收数据。
- 主动关闭方(客户端) 发送
-
第二次挥手(ACK)
- 服务端 收到
FIN后,回复ACK=1, ack=u+1, seq=v,进入CLOSE_WAIT状态。 - 客户端 收到
ACK后进入FIN_WAIT_2状态。 - 作用:确认客户端的
FIN,但服务端可能仍有数据要发送(半关闭状态)。
- 服务端 收到
-
第三次挥手(FIN)
- 服务端 发送完剩余数据后,发送
FIN=1, ACK=1, seq=w, ack=u+1,进入LAST_ACK状态。 - 作用:通知客户端,服务端也准备关闭连接。
- 服务端 发送完剩余数据后,发送
-
第四次挥手(ACK)
- 客户端 收到
FIN后,回复ACK=1, ack=w+1, seq=u+1,进入TIME_WAIT状态,等待 2MSL(Maximum Segment Lifetime)后关闭。 - 服务端 收到
ACK后立即进入CLOSED状态。 - 作用:确保服务端收到最后的
ACK,防止FIN重传。
- 客户端 收到
3.2 为什么需要四次挥手?
- TCP 是全双工的,双方需独立关闭自己的数据流。
- 服务端可能在
FIN前仍有数据要发送(CLOSE_WAIT阶段)。 TIME_WAIT确保最后一个ACK可靠到达,避免旧报文干扰新连接。
面试题:为什么
TIME_WAIT需要等待 2MSL?
答:
- 确保最后一个
ACK到达服务端(如果丢失,服务端会重传FIN)。- 让网络中残留的旧报文失效,避免影响后续新连接。
4.常见问题
4.1 三次握手的 SYN 洪泛攻击
-
攻击方式:攻击者发送大量
SYN但不回复ACK,耗尽服务端资源。 -
防御方法:
- SYN Cookie(不存储半连接状态)。
- 限制
SYN请求速率。
4.2 大量 CLOSE_WAIT 状态的连接
- 原因:服务端未及时调用
close(),导致连接长期处于CLOSE_WAIT。 - 解决方案:检查代码是否漏掉
socket.close(),或调整SO_LINGER参数。
4.3 TIME_WAIT 过多影响性能
-
现象:高并发短连接时,端口被
TIME_WAIT占用,无法复用。 -
优化方法:
- 启用
SO_REUSEADDR复用TIME_WAIT端口。 - 调整
tcp_tw_reuse(Linux 内核参数)。
- 启用
5. 总结
| 阶段 | 三次握手(建立连接) | 四次挥手(关闭连接) |
|---|---|---|
| 目的 | 同步初始序列号,确认通信能力 | 双方独立关闭数据流,确保可靠终止 |
| 交互次数 | 3 次(SYN → SYN+ACK → ACK) | 4 次(FIN → ACK → FIN → ACK) |
| 关键状态 | SYN_SENT、SYN_RCVD | FIN_WAIT_1、CLOSE_WAIT、TIME_WAIT |
| 典型问题 | SYN 洪泛攻击 | TIME_WAIT 过多、CLOSE_WAIT 堆积 |
理解 TCP 握手与挥手机制,有助于优化网络编程、排查连接问题。建议结合 Wireshark 抓包 实践分析,加深印象。
参考资料:
- RFC 793(TCP 协议标准)
- 《TCP/IP 详解 卷1:协议》
- Linux
net.ipv4.tcp_tw_reuse参数说明
希望这篇博客对你有所帮助!欢迎讨论与指正。 🚀