深入理解TCP:三次握手与四次挥手(含面试高频考点)
TCP (Transmission Control Protocol) 作为互联网核心协议簇中的基石,保证了数据传输的可靠性和顺序性。而要理解 TCP 的可靠性,就必须掌握其经典的连接管理机制——三次握手与四次挥手。
本文将用通俗易懂的比喻和清晰的技术图解,带你彻底搞懂这两个过程,并深入剖析面试中关于它们的高频考点。
一、三次握手:建立可靠的连接
在进行数据传输之前,通信双方(客户端和服务器)必须建立一个可靠的连接。这个建立连接的过程,就是“三次握手”。
一个生动的比喻:打电话
想象一下你给朋友打电话确认双方都能正常通信的过程:
- 你:“喂,能听到我说话吗?” (发送
SYN
)- 朋友:“我能听到你,你能听到我吗?” (回复
SYN+ACK
)- 你:“我也能听到你,那我们可以开始聊天了。” (发送
ACK
)
经过这三步,双方都确认了彼此的发送和接收能力都正常,通话(连接)才正式建立。
技术流程详解
- 第一次握手 (
SYN
)- 从哪到哪:客户端 -> 服务器
- 做什么:客户端发送一个
SYN
(Synchronize) 报文段,其中包含一个随机生成的初始序列号seq = x
。 - 状态变化:客户端进入
SYN_SENT
状态,等待服务器的确认。
- 第二次握手 (
SYN+ACK
)- 从哪到哪:服务器 -> 客户端
- 做什么:服务器收到客户端的
SYN
后,如果同意连接,则回复一个报文段。该报文段包含两个关键信息:- 自己的
SYN
标志位,以及一个随机生成的初始序列号seq = y
。 - 一个
ACK
(Acknowledgment) 标志位,以及确认号ack = x + 1
,表示已成功收到客户端的第一个报文。
- 自己的
- 状态变化:服务器进入
SYN_RCVD
状态。
- 第三次握手 (
ACK
)- 从哪到哪:客户端 -> 服务器
- 做什么:客户端收到服务器的
SYN+ACK
后,会发送最后一个ACK
报文段作为确认。该报文段包含确认号ack = y + 1
。 - 状态变化:此报文发送后,客户端和服务器都进入
ESTABLISHED
状态,标志着 TCP 连接正式建立成功,可以开始双向传输数据。
三次握手的核心目标
确保客户端和服务器双方都确认了自己的发送能力和对方的接收能力都正常。
二、四次挥手:优雅地断开连接
TCP 连接是全双工的,意味着数据可以在两个方向上同时流动。因此,连接的断开也需要双方各自独立地关闭自己的发送通道,这个过程就是“四次挥手”。
一个形象的比喻:挂电话
- 你:“我的话说完了,准备挂了。” (发送
FIN
)- 朋友:“好的,你的意思我收到了。” (回复
ACK
)- (朋友可能还有几句补充的话要说,说完之后…)
- 朋友:“我也说完了,现在可以挂了。” (发送
FIN
)- 你:“好的,那我们都挂了吧。” (回复
ACK
)
这个过程确保了双方都不会在对方还有话要说时就突然挂断。
技术流程详解
- 第一次挥手 (
FIN
)- 从哪到哪:主动关闭方 -> 被动关闭方(例如,客户端 -> 服务器)
- 做什么:主动方数据发送完毕,发送一个
FIN
(Finish) 报文段,请求关闭连接。序列号为seq = u
。 - 状态变化:主动方进入
FIN_WAIT_1
状态。
- 第二次挥手 (
ACK
)- 从哪到哪:被动关闭方 -> 主动关闭方
- 做什么:被动方收到
FIN
后,先回复一个ACK
报文段,确认号为ack = u + 1
。这表示“你的关闭请求我收到了,但我这边可能还有数据没发完,请稍等”。 - 状态变化:被动方进入
CLOSE_WAIT
状态。主动方收到ACK
后,进入FIN_WAIT_2
状态。
- 第三次挥手 (
FIN
)- 从哪到哪:被动关闭方 -> 主动关闭方
- 做什么:被动方确认自己的数据也已发送完毕,便向主动方发送一个
FIN
报文段,序列号为seq = v
。 - 状态变化:被动方进入
LAST_ACK
状态,等待对方的最终确认。
- 第四次挥手 (
ACK
)- 从哪到哪:主动关闭方 -> 被动关闭方
- 做什么:主动方收到被动方的
FIN
后,发送最后一个ACK
报文段,确认号为ack = v + 1
。 - 状态变化:主动方进入
TIME_WAIT
状态。在等待2MSL
(Maximum Segment Lifetime,报文最大生存时间) 后,才进入CLOSED
状态。被动方收到这个ACK
后,立即进入CLOSED
状态。连接正式断开。
三、面试高频考点:从“是什么”到“为什么”
理解上述流程只是基础,面试官更喜欢考察你对协议设计背后“为什么”的思考。
关于三次握手
1. 为什么是三次握手,而不是两次或四次?
核心答案:为了防止已失效的连接请求报文突然又传送到服务器,从而导致服务器资源浪费。
- 两次握手的隐患:想象一个场景:客户端发送的第一个
SYN
(请求A)因网络拥堵被滞留了。客户端没收到回应,超时后重发了第二个SYN
(请求B)。请求B正常建立了连接,数据传输完毕后连接被释放。此时,滞留的请求A终于到达了服务器。如果是两次握手,服务器收到请求A后会立即建立连接,并等待客户端发数据。但客户端此时并不会发送任何数据,因为它认为连接早已关闭,这就导致服务器白白浪费了资源。 - 三次握手如何解决:第三次握手(客户端的最终
ACK
)就是为了解决这个问题。在上述场景中,服务器收到失效的请求A并发回SYN+ACK
后,客户端会发现这个确认号不是自己期望的,就不会发送第三次的ACK
。服务器收不到ACK
,就知道这是一个失效请求,就不会建立连接。 - 结论:三次是确认双方通信正常的最小次数。四次则没有必要,因为第三次握手时已经可以携带数据了。
2. 什么是SYN Flood攻击?
这是一种利用TCP协议缺陷的拒绝服务(DoS)攻击。
- 原理:攻击者伪造大量不存在的IP地址,并向服务器疯狂发送
SYN
报文。服务器收到后会回复SYN+ACK
并进入SYN_RCVD
状态,等待第三次握手的ACK
。由于源IP是伪造的,服务器永远等不到ACK
,这些半连接会耗尽服务器的连接资源(队列),使其无法响应正常用户的请求。
关于四次挥手
1. 为什么挥手需要四次?
核心答案:因为TCP是全双工的,连接的关闭需要双方协商,并且要处理好数据“半关闭”的状态。
- 当主动方(如客户端)发送
FIN
请求关闭时,仅仅是表示“我这边没有数据要发给你了”。 - 但被动方(服务器)此时可能还有未发送完的数据,不能立即关闭。所以它需要先回复一个
ACK
(第二次挥手),告诉客户端“你的关闭请求我收到了”,让自己进入CLOSE_WAIT
状态。 - 然后服务器继续发送完自己的数据,等数据都发完了,再发送一个
FIN
(第三次挥手)告诉客户端“我这边也准备好了,可以关闭了”。这个过程保证了数据的完整性,所以ACK
和FIN
通常需要分开发送,从而构成了四次挥手。
2. TIME_WAIT
状态有什么作用?为什么要等待2MSL?
这是四次挥手中最常被问到的问题,非常关键。
- 作用一:保证连接的可靠关闭。 这是为了确保被动关闭方能收到最后一个
ACK
报文。如果这个ACK
在网络中丢失了,被动方会因为收不到确认而超时重传FIN
报文。主动方在TIME_WAIT
状态下就能再次收到这个重传的FIN
,并重发一次ACK
,确保对方能正常关闭。如果没有TIME_WAIT
,主动方发完ACK
就直接关闭了,将无法处理重传的FIN
,导致对方无法正常关闭。 - 作用二:防止旧连接的延迟报文干扰新连接。 假设没有
TIME_WAIT
,连接A刚关闭,一个具有相同“四元组”(源IP、源端口、目标IP、目标端口)的新连接B马上建立。此时,网络中可能还存在连接A的延迟报文,这个报文可能会被新连接B误接收,造成数据混乱。2MSL
的时长足以让本次连接中双向传输的所有报文都从网络中自然消失,从而保证新连接的纯净。
3. 如果服务器出现大量的CLOSE_WAIT
状态,可能是什么原因?
根本原因:通常是应用程序的BUG。
CLOSE_WAIT
状态的含义是:对端已经发送了 FIN
请求关闭,而我方已经回复了 ACK
,正在等待本地程序关闭套接字(Socket)。 如果程序长时间不执行 close()
操作,连接就会一直停留在 CLOSE_WAIT
状态,占用系统文件描述符和内存资源。因此,遇到大量 CLOSE_WAIT
,应立即排查服务器端代码逻辑。
总结
- 三次握手是建立连接的“你好,我好,开始吧”的过程,确保了双方通信能力的正常。
- 四次挥手是断开连接的“我说完了,我收到了,我也说完了,好的”的过程,保证了数据的完整和连接的优雅关闭。
理解这两个过程背后的“为什么”,比单纯记住流程本身更加重要,也是衡量一个开发者网络基础是否扎实的标尺。