在网络通信的浩瀚海洋中,TCP(传输控制协议)以其提供可靠的、面向连接的数据传输服务而闻名。为了确保数据能够准确无误地从发送方到达接收方,并避免出现混乱,TCP设计了一套精密的机制来建立和终止连接。这套机制的核心便是我们今天要深入探讨的——TCP三次握手(Three-Way Handshake)和TCP四次挥手(Four-Way Wave)。
无论是您正在浏览网页、收发电子邮件,还是进行在线游戏,这些背后都离不开TCP连接的默默支撑。理解三次握手与四次挥手,不仅能帮助我们更深层次地掌握网络工作原理,也能在遇到网络问题时提供排查思路。
TCP三次握手(Three-Way Handshake):构建可靠连接的基石
TCP三次握手是客户端与服务器之间建立连接的过程。这个过程形象地比喻为两个人打电话前确认彼此都准备就绪,可以听到对方声音的过程。它确保了双方都能够发送和接收数据,为后续可靠的数据传输奠定了基础。
为什么需要三次握手?
很多人会疑问,为什么不是两次握手呢?两次握手,即客户端发送一个请求,服务器回复一个确认,然后就建立连接。然而,这种方式存在潜在的风险:
- 防止已失效的连接请求报文段突然又传送到了服务器,因而产生错误。 设想客户端发送的第一个连接请求报文段在网络中滞留了很长时间。如果采用两次握手,这个“迟到”的请求到达服务器后,服务器会立即建立连接。而此时客户端可能早已超时并重发了新的连接请求,并已与服务器建立了新的连接,甚至已经关闭。如果旧的请求突然到达并建立连接,服务器会因此分配资源,导致资源浪费,甚至可能向一个已经关闭的客户端发送数据。
- 确保双方都具备发送和接收能力。 三次握手可以明确地验证客户端能发送,服务器能接收;服务器能发送,客户端能接收。缺一不可,只有双方都确认了各自的收发能力,连接才是真正可靠的。
三次握手的详细步骤
以下是TCP三次握手的具体流程:
-
第一次握手:客户端发送连接请求(SYN报文)
客户端(Client)向服务器(Server)发送一个SYN(Synchronize Sequence Number)报文段,请求建立连接。这个报文段中包含:
- SYN标志位: 置为1,表示这是一个连接请求报文。
- 序列号(Sequence Number, SEQ): 客户端会随机生成一个初始序列号(Initial Sequence Number, ISN_C),例如`seq = x`。这个序列号代表客户端打算发送数据的第一个字节的编号。
此时,客户端进入
SYN_SENT(同步已发送)状态。客户端 → 服务器:
SYN=1, seq=x -
第二次握手:服务器确认并同意连接(SYN+ACK报文)
服务器收到客户端的SYN报文后,如果同意建立连接,会向客户端发送一个SYN+ACK报文段作为响应。这个报文段中包含:
- SYN标志位: 置为1,表示服务器也请求建立连接。
- ACK标志位: 置为1,表示对客户端SYN报文的确认。
- 确认号(Acknowledgment Number, ACK_NUM): 服务器会把客户端的
seq + 1作为确认号,即ack_num = x + 1,表示服务器已准备好接收客户端从x + 1开始的数据。 - 序列号(Sequence Number, SEQ): 服务器也会随机生成一个初始序列号(ISN_S),例如`seq = y`,代表服务器打算发送数据的第一个字节的编号。
此时,服务器进入
SYN_RCVD(同步已接收)状态。服务器 → 客户端:
SYN=1, ACK=1, seq=y, ack_num=x+1 -
第三次握手:客户端发送确认(ACK报文)
客户端收到服务器的SYN+ACK报文后,会向服务器发送一个ACK报文段进行最终确认。这个报文段中包含:
- ACK标志位: 置为1,表示对服务器SYN报文的确认。
- 确认号(Acknowledgment Number, ACK_NUM): 客户端会把服务器的
seq + 1作为确认号,即ack_num = y + 1,表示客户端已准备好接收服务器从y + 1开始的数据。 - 序列号(Sequence Number, SEQ): 客户端的序列号为
x + 1(或继续使用x,因为此ACK报文不携带数据,不消耗序列号)。
此时,客户端进入
ESTABLISHED(已建立)状态。服务器收到这个ACK报文后,也进入ESTABLISHED状态。客户端 → 服务器:
ACK=1, seq=x+1, ack_num=y+1
至此,TCP三次握手完成,客户端和服务器之间的连接正式建立,可以开始可靠的数据传输了。
握手过程中关键字段解析
- SYN (Synchronize Sequence Numbers): 同步序列号。当SYN=1时,表示该报文段是一个连接请求或连接接受报文。
- ACK (Acknowledgment): 确认。当ACK=1时,表示确认号字段有效。
- SEQ (Sequence Number): 序列号。当前报文段第一个字节的序列号。
- ACK_NUM (Acknowledgment Number): 确认号。期望收到对方下一个报文段第一个字节的序列号。
- ISN (Initial Sequence Number): 初始序列号。在TCP连接建立时由双方随机生成的第一个序列号。它有助于提高连接的安全性,防止猜测并减轻一些攻击。
小贴士: 在三次握手中,只有SYN报文和带有SYN标志的ACK报文会消耗一个序列号。纯ACK报文(不携带数据)不消耗序列号。一旦连接建立,每个发送的字节都会消耗一个序列号。
TCP四次挥手(Four-Way Wave):优雅地终止连接
当客户端或服务器不再需要连接时,它们会通过TCP四次挥手来断开连接。与三次握手不同,四次挥手是双向的,因为TCP是全双工通信,即数据可以同时在两个方向上传输。任何一方都可以发起关闭连接的请求。
为什么需要四次挥手?
TCP连接是全双工的,这意味着客户端和服务器都可以独立地发送和接收数据。当一方完成数据发送,发出FIN报文时,表示它不再有数据要发送,但它可能仍然需要接收对方的数据。因此:
- 发起方关闭它的发送通道。
- 接收方回复ACK,表示已收到关闭请求,但它的发送通道可能还有数据要发送(因为发送和接收是独立的)。
- 当接收方也发送完所有数据后,它才会发送FIN报文来关闭它的发送通道。
- 发起方收到FIN后,再发送ACK确认。
这个过程可以看作是:甲对乙说“我没东西要给你了”(第一次挥手),乙回应“好的,我知道了”(第二次挥手),然后乙说“我也没有东西要给你了”(第三次挥手),甲回应“好的,我知道了”(第四次挥手)。
四次挥手的详细步骤
假设客户端发起关闭连接(当然,服务器也可以发起):
-
第一次挥手:客户端发送关闭请求(FIN报文)
客户端的数据发送完毕后,向服务器发送一个FIN(Finish)报文段,表示它已经没有数据要发送了,请求关闭连接。这个报文段中包含:
- FIN标志位: 置为1,表示发送方已完成数据发送。
- ACK标志位: 通常也置为1,表示对之前收到数据的确认。
- 序列号(SEQ): 假设为`u`(为之前发送的最后一个字节的序列号+1)。
- 确认号(ACK_NUM): 假设为`v`(为之前接收到的最后一个字节的序列号+1)。
此时,客户端进入
FIN_WAIT_1(终止等待1)状态。客户端 → 服务器:
FIN=1, ACK=1, seq=u, ack_num=v -
第二次挥手:服务器确认关闭请求(ACK报文)
服务器收到客户端的FIN报文后,会发送一个ACK报文段作为响应。这个报文段中包含:
- ACK标志位: 置为1。
- 确认号(ACK_NUM):
u + 1,表示已收到客户端的FIN报文。 - 序列号(SEQ):
v(如果服务器此时没有数据要发送,则保持原序列号)。
此时,服务器进入
CLOSE_WAIT(关闭等待)状态。这表示服务器已收到客户端的关闭请求,但它可能还有数据要发送给客户端。在发送完所有数据之前,它不会关闭自己的发送通道。客户端收到这个ACK报文后,进入
FIN_WAIT_2(终止等待2)状态,等待服务器发送最后的FIN报文。服务器 → 客户端:
ACK=1, seq=v, ack_num=u+1 -
第三次挥手:服务器发送关闭请求(FIN报文)
当服务器也发送完所有数据后,会向客户端发送一个FIN报文段,表示它也准备关闭发送通道。这个报文段中包含:
- FIN标志位: 置为1。
- ACK标志位: 置为1。
- 序列号(SEQ): 假设为`w`(为服务器之前发送的最后一个字节的序列号+1)。
- 确认号(ACK_NUM):
u + 1(与第二次挥手相同)。
此时,服务器进入
LAST_ACK(最后确认)状态,等待客户端的最后确认。服务器 → 客户端:
FIN=1, ACK=1, seq=w, ack_num=u+1 -
第四次挥手:客户端发送最终确认(ACK报文)
客户端收到服务器的FIN报文后,会发送一个ACK报文段进行最终确认。这个报文段中包含:
- ACK标志位: 置为1。
- 确认号(ACK_NUM):
w + 1,表示已收到服务器的FIN报文。 - 序列号(SEQ):
u + 1。
此时,客户端进入
TIME_WAIT(时间等待)状态,并等待2MSL(最长报文段生存时间)后才关闭连接。服务器收到这个ACK报文后,立即进入CLOSED状态。客户端 → 服务器:
ACK=1, seq=u+1, ack_num=w+1
在客户端的TIME_WAIT状态结束后,客户端也进入CLOSED状态,至此整个TCP连接彻底关闭。
挥手过程中特殊状态解析:TIME_WAIT
TIME_WAIT是TCP连接终止过程中一个非常重要的状态,它只出现在主动关闭连接的一方(通常是客户端)。它的存在是为了:
- 确保最后一个ACK报文到达服务器: 即使在第四次挥手时,客户端发送的最后一个ACK报文丢失,服务器会因为没有收到ACK而重发FIN报文。如果客户端不处于
TIME_WAIT状态而直接关闭,它就无法收到这个重发的FIN,也就无法发送ACK,服务器将永远处于LAST_ACK状态。TIME_WAIT确保了客户端有足够的时间重新发送ACK。 - 防止“旧连接”的数据包干扰“新连接”: 在2MSL时间内,任何迟到的、属于该连接的数据包都会被丢弃。这防止了在同一个端口上建立新连接时,迟到的旧连接数据包被新连接错误地接收,造成数据混淆。
MSL (Maximum Segment Lifetime): 最长报文段生存时间,指一个报文段在网络中能够存活的最长时间。通常设置为30秒、1分钟或2分钟。因此,2MSL的时间意味着报文段在网络中往返一趟并能及时被处理所需的最长时间。
尽管
TIME_WAIT状态对于TCP的可靠性至关重要,但如果系统中存在大量处于TIME_WAIT状态的连接,可能会导致端口资源耗尽,从而影响新连接的建立。在某些高并发服务器场景下,会通过配置系统参数来调整TIME_WAIT的持续时间或允许重用TIME_WAIT端口。
TCP握手与挥手:异同点剖析
虽然三次握手和四次挥手都是TCP连接生命周期中的关键阶段,但它们有着不同的目的和实现细节。
共同点:
- 都使用SYN、ACK、FIN等TCP标志位来控制流程。
- 都涉及序列号和确认号,以确保报文的有序和可靠传输。
- 都包含状态转换,描述连接在不同阶段的生命周期。
不同点:
- 目的: 握手是为了建立连接,挥手是为了终止连接。
- 报文数量: 握手通常是三次报文,挥手通常是四次报文。
- 标志位组合:
- 握手过程中主要是SYN和ACK的组合。
- 挥手过程中主要是FIN和ACK的组合。
- 原因: 握手只需要确认双方的收发能力,而挥手需要分别关闭双方的发送通道,因为TCP是全双工的,一端关闭发送不代表立刻停止接收,也不代表另一端已无数据发送。
- TIME_WAIT状态: 挥手过程中(主动关闭方)会进入
TIME_WAIT状态,而握手过程没有类似状态。
理解这些差异对于调试网络问题和优化网络应用程序性能至关重要。
常见问题 (FAQ)
以下是一些关于TCP三次握手与四次挥手过程的常见问题及解答。
为何TCP三次握手不能减少为两次?两次握手最大的问题在于无法有效防止“已失效的连接请求报文段”对服务器造成干扰。假设客户端发送的第一个连接请求在网络中滞留,在客户端重发新请求并建立连接后,旧的滞留请求突然到达服务器。如果只有两次握手,服务器会错误地认为这是一个新的连接请求并建立连接,造成资源浪费,且可能向一个已关闭的客户端发送数据。三次握手通过客户端的第三个ACK确认,确保了服务器能够识别并忽略这些失效的连接请求,提高了连接的健壮性。
为何TCP四次挥手不能简化为三次?TCP连接是全双工的,即数据可以同时在两个方向上传输。当一方(比如客户端)发起FIN请求关闭连接时,仅仅表示它已经没有数据要发送了,但服务器可能还有未发送完的数据。服务器接收到FIN后,会先回复一个ACK表示收到关闭请求,然后继续发送它剩余的数据。只有当服务器也发送完所有数据后,才会再次发送FIN报文来关闭自己的发送通道。因此,需要分开两个步骤(第二次和第三次挥手)来处理,不能合并为一次,以允许服务器有机会传输完剩余数据。这就是四次挥手的核心原因。
如何理解TIME_WAIT状态?它有什么作用?TIME_WAIT状态是TCP主动关闭连接方在发送完最后一个ACK报文后进入的一种特殊状态,持续2MSL(最长报文段生存时间)。它的主要作用有两个:一是确保最后一个ACK报文能够成功到达被动关闭方。如果ACK丢失,被动关闭方会重发FIN,TIME_WAIT状态下的主动关闭方可以再次发送ACK。二是防止网络中迟到的旧数据包被新建立的同源同目的连接错误接收,造成数据混乱。通过等待2MSL,可以确保所有与旧连接相关的数据包都已在网络中消失。
TCP具有超时重传机制。如果在握手或挥手过程中某个报文丢失,发送方在设定的超时时间内没有收到相应的确认报文,它会重传丢失的报文段。例如,如果客户端的SYN报文丢失,客户端会等待一段时间后重发SYN。如果服务器的SYN+ACK报文丢失,服务器会重发SYN+ACK。这个机制保证了TCP连接建立和终止的可靠性。
FIN报文和ACK报文是否可以携带数据?FIN报文(用于关闭连接)是可以携带数据的,但通常情况下它不携带数据,只作为控制报文使用。如果FIN报文携带着数据,那么它的序列号会消耗掉,并且会和数据一起被确认。而纯ACK报文(不带SYN或FIN标志,且不包含应用层数据)本身是不消耗序列号的,也不会携带应用层数据,它仅仅用于确认收到的数据。
总结
TCP三次握手和四次挥手是构建和维护互联网可靠通信的基石。三次握手通过精妙的序列号和确认号机制,确保了客户端和服务器都具备发送和接收数据的能力,有效防止了“失效请求”的干扰,从而可靠地建立了连接。而四次挥手则以其分段关闭的特性,优雅地处理了全双工连接的终止,并借助TIME_WAIT状态确保了最终的可靠性以及对未来连接的保护。
深入理解这些底层机制,不仅能帮助我们更好地理解网络应用的行为,也能为我们在面对网络性能问题或故障时提供重要的诊断线索。正是这些看似简单的报文交换,支撑起了我们今天丰富多彩的数字生活。

