TCP 连接的生命周期概览
TCP (传输控制协议) 是一种面向连接、可靠、基于字节流的传输层协议。它确保数据从发送方可靠地传输到接收方,不丢失、不重复、按顺序。为了实现这种可靠性,TCP 在数据传输之前需要建立连接,传输结束后需要终止连接。这两个关键过程分别由“三次握手”和“四次挥手”完成。
这两个机制是TCP协议栈的核心组成部分,它们发生在网络协议栈的传输层。所有的应用层数据(如HTTP、FTP、SMTP等)在通过TCP传输时,都必须遵守这两套严格的连接管理流程。
是什么:TCP 三次握手
TCP三次握手(Three-Way Handshake)是TCP/IP协议中用于建立一个TCP连接的协议过程。它的核心目的是在客户端和服务器之间建立可靠的连接,并初始化TCP参数,如序列号(Sequence Number)和最大报文段长度(MSS)。
- 本质:这是一个客户端与服务器交互三次,互相确认彼此接收和发送能力的对话过程。
- 涉及报文:主要涉及SYN(同步)和ACK(确认)标志位的TCP报文段。
- 作用:
- 使客户端和服务器都能够确认对方的收发能力。
- 同步双方的初始序列号(ISN),确保后续数据传输的有序性和可靠性。
- 协商一些连接参数,如MSS。
如何:TCP 三次握手的具体步骤
三次握手通常由客户端发起,服务器响应。
-
第一次握手 (SYN):
- 发起方:客户端
- 动作:客户端发送一个TCP报文段到服务器,该报文段的SYN标志位被设置为1(SYN=1),表示这是一个连接请求。同时,客户端会选择一个初始序列号(ISN_c),并将其放入报文段的序列号字段中(
Seq = ISN_c)。客户端进入SYN-SENT状态。 - 目的:客户端告诉服务器:“我想和你建立连接,我的初始序列号是ISN_c,你能听到我吗?”
-
第二次握手 (SYN-ACK):
- 发起方:服务器
- 动作:服务器收到客户端的SYN报文后,如果同意建立连接,会发送一个自己的TCP报文段作为响应。该报文段的SYN标志位和ACK标志位都被设置为1(SYN=1, ACK=1)。其中,SYN=1表示服务器也想建立连接,ACK=1表示对客户端SYN报文的确认。服务器也会选择一个自己的初始序列号(ISN_s),放入序列号字段(
Seq = ISN_s)。同时,它会将客户端的初始序列号加1,作为确认号(Ack = ISN_c + 1),放入确认号字段。服务器进入SYN-RECEIVED状态。 - 目的:服务器告诉客户端:“我收到了你的连接请求(ACK),我也同意建立连接(SYN),我的初始序列号是ISN_s,你收到我的消息了吗?”
-
第三次握手 (ACK):
- 发起方:客户端
- 动作:客户端收到服务器的SYN-ACK报文后,会再次发送一个TCP报文段。该报文段的ACK标志位被设置为1(ACK=1),表示对服务器SYN报文的确认。其序列号是第一次握手发送的ISN_c加1(
Seq = ISN_c + 1),确认号是服务器的ISN_s加1(Ack = ISN_s + 1)。客户端进入ESTABLISHED状态。 - 目的:客户端告诉服务器:“我收到了你的确认和连接请求,现在我们都准备好进行数据传输了。”
至此,客户端和服务器都进入ESTABLISHED(已建立)状态,可以开始进行可靠的数据传输了。
为什么:TCP 三次握手为何是三次?
这通常是面试中最常被问到的问题之一。
TCP三次握手的核心目的是为了保证连接的可靠建立,避免因为网络延迟或报文丢失而导致的“已失效的连接请求报文段”对服务器的干扰。
-
为什么不是两次?
如果只有两次握手(客户端SYN -> 服务器SYN-ACK),客户端发送请求后直接认为连接建立,服务器响应后也认为连接建立。在这种情况下,考虑以下场景:
客户端发送的第一个连接请求报文段(假设由于网络拥堵而延迟)在某个时刻到达服务器。服务器收到后,发送SYN-ACK并进入ESTABLISHED状态。但此时客户端可能已经因为超时重传,发送了第二个连接请求,并与服务器建立了新的连接,甚至已经关闭。当那个延迟的、旧的连接请求报文段最终到达服务器时,服务器会认为这是一个新的连接请求,并发送SYN-ACK。如果只有两次握手,服务器会因此建立一个无用的连接(客户端已经不复存在或不关心),浪费服务器资源,可能导致服务器资源耗尽,甚至可能错误地将旧数据发送给一个错误的“客户端”。
三次握手中的第三次ACK,正是为了让客户端确认服务器确实收到了它的第一次请求,并且服务器也确认自己已经准备好。同时,服务器通过接收客户端的最终ACK,能够确认客户端也收到了自己的SYN-ACK,并且客户端也准备好了。这样可以有效防止上述“已失效的连接请求报文段”的问题。
-
为什么不是四次或更多次?
理论上,四次或更多次的握手也能实现可靠连接,但它们会增加不必要的网络开销和延迟。三次握手是满足可靠性需求的最小次数,它已经足够验证双方的发送和接收能力,并同步初始序列号。增加更多的握手次数并不能带来显著的可靠性提升,反而增加了资源的消耗。
-
关于序列号和确认号的作用:
- 序列号 (Sequence Number – Seq):是当前发送报文段的第一个字节在整个字节流中的序号。它确保了数据传输的有序性。
- 确认号 (Acknowledgement Number – Ack):是期望收到对方下一个报文段的第一个字节的序列号。它表示发送方已经成功接收到确认号之前的所有数据。
在三次握手过程中,Ack = 对方的ISN + 1的形式,正是双方在互相确认“我收到了你发的第一个字节(即你的ISN),我期待你下一个字节从ISN+1开始”。这种机制在后续数据传输中也持续使用,确保可靠性。
-
MSS (最大报文段长度) 协商:
在三次握手过程中,通常会在SYN报文和SYN-ACK报文中携带TCP选项,用于协商双方能够支持的最大报文段长度(MSS)。MSS是指TCP报文段中数据部分的长度,不包含TCP首部和IP首部。协商一个合适的MSS可以避免IP分片,提高传输效率。通常,MSS会被设置为MTU(最大传输单元)减去TCP和IP首部的大小。
是什么:TCP 四次挥手
TCP四次挥手(Four-Way Handshake)是TCP/IP协议中用于终止一个TCP连接的协议过程。它的核心目的是优雅地关闭连接,确保双方所有数据都已发送和接收完毕,并释放占用的资源。
- 本质:这是一个客户端与服务器各自独立关闭其发送通道和接收通道的过程。由于TCP是全双工的,每个方向的数据流需要独立关闭。
- 涉及报文:主要涉及FIN(结束)和ACK(确认)标志位的TCP报文段。
- 作用:
- 确保连接双方的数据都已发送完毕并被对方确认接收。
- 安全地释放连接双方占用的端口号、缓存、控制块等系统资源。
- 处理“半关闭”状态,允许一方关闭其发送通道,但仍能接收数据。
如何:TCP 四次挥手的具体步骤
四次挥手可以由客户端或服务器任何一方发起。这里以客户端发起关闭为例:
-
第一次挥手 (FIN):
- 发起方:客户端(主动关闭方)
- 动作:客户端完成数据发送后,向服务器发送一个FIN报文段,其FIN标志位被设置为1(FIN=1)。这表示客户端没有更多数据要发送了。客户端进入
FIN_WAIT_1状态。 - 目的:客户端告诉服务器:“我没有数据要发送给你了,我想关闭我的发送通道。”
-
第二次挥手 (ACK):
- 发起方:服务器(被动关闭方)
- 动作:服务器收到客户端的FIN报文后,发送一个ACK报文段作为确认,其ACK标志位被设置为1(ACK=1)。确认号是客户端FIN报文的序列号加1(
Ack = Seq_of_FIN + 1)。服务器进入CLOSE_WAIT状态。此时,服务器可能还有数据要发送给客户端。 - 目的:服务器告诉客户端:“我收到了你关闭发送通道的请求,但我可能还有数据要发送给你,请你继续接收。”
-
第三次挥手 (FIN):
- 发起方:服务器(被动关闭方)
- 动作:当服务器也没有数据要发送给客户端时,它会向客户端发送一个FIN报文段,其FIN标志位被设置为1(FIN=1)。服务器进入
LAST_ACK状态。 - 目的:服务器告诉客户端:“我也没有数据要发送给你了,我准备关闭我的发送通道。”
-
第四次挥手 (ACK):
- 发起方:客户端(主动关闭方)
- 动作:客户端收到服务器的FIN报文后,发送一个ACK报文段作为确认,其ACK标志位被设置为1(ACK=1)。确认号是服务器FIN报文的序列号加1(
Ack = Seq_of_FIN + 1)。客户端进入TIME_WAIT状态。 - 目的:客户端告诉服务器:“我收到了你关闭发送通道的请求。”
客户端在TIME_WAIT状态停留2MSL(Maximum Segment Lifetime,最大报文段生命周期)后,自动进入CLOSED状态。服务器在收到客户端的最后一个ACK报文后,也进入CLOSED状态。至此,连接完全关闭。
为什么:TCP 四次挥手为何是四次?
四次挥手之所以是四次而不是三次,是因为TCP连接是全双工的(Full-Duplex)。这意味着数据可以同时在两个方向上独立传输。
-
全双工的特性:
当一方(比如客户端)发起FIN报文时,它只是表示自己没有数据要发送了,但它仍然可以接收来自对方的数据。此时,连接处于“半关闭”(Half-Close)状态。服务器可能仍有数据需要发送给客户端。因此,服务器不能立即关闭自己的发送通道,而是先回复一个ACK表示收到客户端的FIN,然后继续发送剩余数据。只有当服务器也完成了所有数据的发送后,它才会发送自己的FIN报文来关闭自己的发送通道。
这使得关闭过程分为两个独立的步骤:
- 一方关闭其发送流,并由对方确认。
- 另一方关闭其发送流,并由对方确认。
这四个步骤刚好对应四次挥手。如果只进行三次,就无法处理被动关闭方还有数据要发送的情况。
-
半关闭状态:
第二次挥手后,服务器进入
CLOSE_WAIT状态,而客户端进入FIN_WAIT_2状态。此时,客户端已经不能发送数据,但仍能接收数据;服务器仍能发送数据,也能接收数据。这种状态允许服务器在关闭连接之前,将所有剩余的数据发送给客户端,确保数据不丢失。
重要状态解析:TIME_WAIT
在四次挥手过程中,主动关闭连接的一方(上例中的客户端)会进入TIME_WAIT状态。这个状态非常重要,通常会持续2MSL(Maximum Segment Lifetime,最大报文段生命周期)。
-
什么是MSL?
MSL是任何IP数据报在互联网中最大可能存活的时间。这个值通常在30秒到2分钟之间,RFC 793推荐是2分钟。2MSL是为了确保:
-
确保最后一个ACK报文到达:
主动关闭方发送的最后一个ACK报文有可能会丢失。如果这个ACK丢失,被动关闭方会重传其FIN报文。主动关闭方处于
TIME_WAIT状态时,如果接收到重传的FIN,会重新发送ACK并重置2MSL计时器。如果主动关闭方不等待直接关闭,那么重传的FIN报文将得不到确认,导致被动关闭方长时间处于LAST_ACK状态,占用资源。 -
防止“已失效的连接请求报文段”在网络中出现:
与三次握手类似,这个等待时间可以确保本次连接中所有在网络中滞留的、延迟的报文段都已消逝。如果主动关闭方立即释放端口,并立即用同一端口建立一个新连接,那么旧连接中迟到的报文段可能会被新连接接收到,导致数据混乱。2MSL的时间确保了在新的连接建立之前,旧连接的所有报文都已经从网络中消失,避免“串线”问题。
-
确保最后一个ACK报文到达:
-
TIME_WAIT状态的缺点及优化:
大量的
TIME_WAIT连接会占用系统资源,尤其是在高并发的服务器上。每个TIME_WAIT状态的连接都会占用一个端口号和一定的内存资源。在某些特定场景下,可以考虑优化,例如设置SO_REUSEADDRsocket选项,允许在TIME_WAIT状态的端口被立即重用(但这样做有风险,可能导致上述“串线”问题),或者调整系统参数缩短MSL时间(不推荐)。
TCP 状态机:握手与挥手过程中的流转
理解TCP的状态机对于把握连接的建立和终止过程至关重要。以下是简化版的常见状态流转:
客户端状态:
CLOSED-> (connect) ->SYN-SENTSYN-SENT-> (SYN-ACK received, ACK sent) ->ESTABLISHEDESTABLISHED-> (close, FIN sent) ->FIN_WAIT_1FIN_WAIT_1-> (ACK received) ->FIN_WAIT_2FIN_WAIT_2-> (FIN received, ACK sent) ->TIME_WAITTIME_WAIT-> (2MSL timeout) ->CLOSED服务器状态:
CLOSED-> (listen) ->LISTENLISTEN-> (SYN received, SYN-ACK sent) ->SYN-RECEIVEDSYN-RECEIVED-> (ACK received) ->ESTABLISHEDESTABLISHED-> (FIN received, ACK sent) ->CLOSE_WAITCLOSE_WAIT-> (close, FIN sent) ->LAST_ACKLAST_ACK-> (ACK received) ->CLOSED
可以看到,CLOSE_WAIT状态出现在被动关闭方,表示它已收到对方的FIN,但本地应用尚未调用close()来关闭连接。而TIME_WAIT状态出现在主动关闭方,是其在发送完最后一个ACK后,等待2MSL以确保连接的可靠终止。
异常情况与安全性考量
-
握手/挥手过程中的超时与重传:
TCP协议在设计时就考虑了报文丢失的情况。如果在握手或挥手过程中,某一方没有收到期望的ACK,它会启动计时器并重传相应的报文段(如SYN、FIN或ACK)。重传几次后如果仍未收到确认,则认为连接建立失败或连接中断。例如,客户端在
SYN-SENT状态时,若长时间未收到SYN-ACK,会重传SYN。 -
连接重置 (RST):
当TCP收到一个不属于当前连接的报文段,或收到一个无效的报文段(如连接已关闭但仍收到数据),或者一方试图连接一个没有监听的端口时,可能会发送一个RST(复位)报文段。RST报文段会立即终止连接,不经过四次挥手过程,这是一种粗暴的关闭方式,不会等待数据发送完毕,可能会导致数据丢失。
-
SYN洪泛攻击 (SYN Flood):
这是一种利用TCP三次握手弱点进行的拒绝服务(DoS)攻击。攻击者发送大量的SYN报文到目标服务器,但从不发送最终的ACK报文来完成三次握手。服务器在收到SYN后会为每个请求分配资源(在
SYN-RECEIVED状态),等待客户端的ACK。由于攻击者不发送ACK,服务器的连接队列很快被耗尽,新的合法连接请求无法被处理,从而导致服务中断。防范措施(简述):
- SYN Cookies:服务器不为每个SYN请求分配完整资源,而是将连接信息编码在SYN-ACK报文的序列号中。只有当收到合法的ACK后才分配资源。
- SYN Proxy:代理服务器在接收到SYN后,代替客户端完成三次握手,确认客户端合法后再将连接转发给后端服务器。
- 调整系统参数:如增大半开连接队列长度、缩短SYN-RECEIVED状态的超时时间等。
总结来说,TCP的三次握手和四次挥手是TCP可靠性的基石。它们通过精密的报文交换和状态管理,确保了连接的可靠建立和优雅关闭,同时处理了网络中可能出现的各种复杂情况,使得应用程序可以在不可靠的网络之上实现可靠的数据传输。深入理解这些机制对于网络编程、故障排查和系统优化都至关重要。