TCP精细讲解:四次挥手与连接断开

科技   2024-12-17 11:18   上海  
  TCP的“四次挥手”过程是什么样的? 

在TCP连接的生命周期中,四次挥手是连接断开、终止的过程。以客户端主动发起连接断开过程为例,以下是TCP四次挥手的详细步骤:
第一次挥手:
客户端接收到应用层的连接断开指令,向服务器发送一个带有FIN标志位的TCP包,由于连接断开之前已经产生过数据交互,因此该TCP包必须同时携带一个ACK标志位,关于第一次挥手包的序列号和确认号,假设包的Seq号为X,Ack号为Y,无载荷数据。
第二次挥手:

服务器成功接收到FIN包后,向客户端发送一个带有ACK标志位的TCP包,即ACK包,表示确认收到。此时,从客户端向服务器方向的数据发送通道已经成功关闭。

由于第一次挥手包的Ack号预示了服务器即将使用的序列号,所以第二次挥手包的Seq号应该为Y。

虽然FIN包无载荷数据,但FIN标志位本身会被视为一个字节的载荷数据,用于进行确认,所以第二次挥手包的Ack号为X+1
第三次挥手:

服务器发送完所有数据,接收到应用层的连接断开指令后,向客户端发送一个带有FIN标志位的TCP包。该包不携带载荷数据。

由于第二次挥手与第三次挥手之间未发送任何数据也未接收任何数据,不存在交互,因此第三次挥手包的Seq号和Ack号与第二次挥手的Seq号和Ack号相同,即Seq号为Y,Ack号为X+1

特殊情况:第二次挥手之后,服务器发送了部分数据才进行第三次挥手,此时Seq号为Y+n,n为数据的长度

第四次挥手:

客户端成功接收到FIN包后,向服务器发送一个ACK包,关闭服务器到客户端方向的连接传输通道,此时,双向数据发送通道均已成功关闭。

第四次挥手包的Seq号为X+1,Ack号为Y+1

  从时序图看TCP四次挥手  

一个典型的由客户端主动发起的TCP四次挥手,如图1所示:

图1:客户端主动发起的四次挥手

如图1所示,7号包为客户端主动发起的FIN包,携带ACK位,Seq号为6669,即上文中描述的X值,Ack号为6961,即上文中描述的Y值。

一个典型的由服务器主动发起的TCP四次挥手,如图2所示:

图2:服务器主动发起的四次挥手

如图2所示,3877号包为客户端主动发起的FIN包,携带ACK位,Seq号为1942946842,即上文中描述的X值,Ack号为3335712973,即上文中描述的Y值。
  半关闭机制  

由于TCP连接数据发送是双向的,因此,连接的断开也是双向的。客户端和服务器两个方向的数据发送通道并不需要同时关闭。若一方发起FIN包后,另一方的应用数据仍未发送完成,可以先响应ACK包,在发送数据结束后再择机发送FIN包,结束另一方向连接。在TCP连接已经关闭了一个方向的数据发送通道时,会话状态被称为“半关闭”。图3描述了一个典型的半关闭情况

图3:TCP半关闭

如图3所示,服务器发送了69号FIN/ACK包,客户端响应70号ACK包,此时,服务器往客户端方向的数据发送通道已经关闭,由于另一方向暂未关闭,因此客户端仍可继续发送数据,如73、74号PSH包,即携带载荷数据的TCP包。
  连接断开失败的典型的故障实例  

下图展示了一个较为典型的“连接断开失败”交互时序图:

图4:连接断开失败

如图4所示,客户端发送FIN/ACK包后(由于网络架构和镜像点位设置原因,该包被重复捕获了2次),服务器响应了FIN/ACK包(该包同样重复捕获),这是服务器将四次挥手中的第二、第三次回收合并为一个数据包发送的情况,随后客户端返回了ACK包。至此,客户端认为双向连接关闭完毕,服务器将在收到这个ACK包后关闭连接。

不巧的是,服务器未收到最后这个ACK包,于是服务器认为自己之前发送的FIN/ACK包丢失,多次重传FIN/ACK包,但客户端未进行响应。导致了客户端与服务器之间关于此连接的状态“信息不对称”。雪上加霜的是,客户端和服务器中间部署了防火墙产品,服务器重传的FIN/ACK包未通过防火墙的策略,导致服务器久久不能关闭此连接。

来自科来公众号

Qt教程
致力于Qt教程,Qt技术交流,研发
 最新文章