TCP连接四次挥手的过程,为什么是四次?

15 分钟阅读 1562 字 + 1303 词
假设客户端主动调用关闭连接,发送FIN报文给服务端,并且客户端进入FIN_WAIT1状态,然后服务端收到这个报文后,read会返回EOF,并且进入CLOSE_WAIT,并发送ACK报文给客户端,服务端在这个状态还是可以发送数据的。客户端收到这个ACK后,会进入FIN_WAIT2状态。
当服务端主动调用关闭连接函数的时候,发送一个FIN报文给客户端,服务端此时进入LAST_ACK状态。
客户端收到这个FIN报文后,会返回一个ACK报文,并进入TIME_WAIT状态,这个状态等待2个MSL时间才会关闭。服务端收到这个ACK后就会进入 CLOSE状态。
为什么需要四次挥手?
因为 TCP是全双工通信,两次挥手只能释放一端到另一端的TCP连接。
并且,只有四次挥手,才能确保 双方都能接收到最后一个数据报的ACK 。如果 使用三次握手 的话,服务器在发送最后一个数据段后立即关闭连接,而客户端可能因为网络原因接收不到这个数据,那么也就无法正常关闭。
参考答案:
image-20240918100057136
(1)四次挥手的过程
  1. 第一次挥手 客户端发送一个 FIN 报文给服务端 ,表示自己要断开数据传送,报文中会 指定一个序列号 (seq=x) 。然后, 客户端进入 FIN-WAIT-1 状态
  2. 第二次挥手 服务端收到 FIN 报文后 回复 ACK 报文 给客户端,且把客户端的序列号值 +1 ,作为ACK报文的序列号 (seq=x+1) 。然后, 服务端进入 CLOSE-WAIT(seq=x+1) 状态 客户端进入 FIN-WAIT-2 状态
  3. 第三次挥手 :服务端也要断开连接时, 发送 FIN 报文给客户端,且指定一个序列号 (seq=y+1) ,随后服务端进入 LAST-ACK 状态
  4. 第四次挥手 客户端收到 FIN 报文后,发出 ACK 报文进行应答 ,并把服务端的序列号值 +1 作为 ACK 报文序列号 (seq=y+2) 此时客户端进入 TIME-WAIT 状态 服务端在收到客户端的 ACK 报文后进入 CLOSE 状态 如果客户端等待 2MSL 没有收到回复,才关闭连接。
  • 客户端主动调用关闭连接的函数,于是就会发送 FIN 报文,这个 FIN 报文代表客户端不会再发送数据了,进入 FIN_WAIT_1 状态;
  • 服务端收到了 FIN 报文,然后马上回复一个 ACK 确认报文,此时服务端进入 CLOSE_WAIT 状态。在收到 FIN 报文的时候,TCP 协议栈会为 FIN 包插入一个文件结束符 EOF 到接收缓冲区中,服务端应用程序可以通过 read 调用来感知这个 FIN 包,这个 EOF 会被 放在已排队等候的其他已接收的数据之后 ,所以必须要得继续 read 接收缓冲区已接收的数据;
  • 接着,当服务端在 read 数据的时候,最后自然就会读到 EOF,接着 read() 就会返回 0,这时服务端应用程序如果有数据要发送的话,就发完数据后才调用关闭连接的函数,如果服务端应用程序没有数据要发送的话,可以直接调用关闭连接的函数 ,这时服务端就会发一个 FIN 包,这个 FIN 报文代表服务端不会再发送数据了,之后处于 LAST_ACK 状态;
  • 客户端接收到服务端的 FIN 包,并发送 ACK 确认包给服务端,此时客户端将进入 TIME_WAIT 状态;
  • 服务端收到 ACK 确认包后,就进入了最后的 CLOSE 状态;
  • 客户端经过 2MSL 时间之后,也进入 CLOSE 状态;
(2)为什么需要四次挥手
TCP 是全双工通信,可以双向传输数据。 任何一方都可以在数据传送结束后 发出连接释放的通知 ,待对方确认后 进入半关闭状态 。 当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后才会完全关闭 TCP 连接。 因此两次挥手可以释放一端到另一端的 TCP 连接,完全释放连接一共需要四次挥手
只有通过四次挥手,才可以确保双方都能接收到对方的最后一个数据段的确认 ,主动关闭方在发送完最后一个 ACK 后进入 TIME-WAIT 状态,这是 为了确保被动关闭方接收到最终的 ACK ,如果被动关闭方没有接收到,它可以重发 FIN 报文,主动关闭方可以再次发送 ACK
而如果使用三次挥手,被动关闭方可能在发送最后一个数据段后立即关闭连接,而主动关闭方可能因为网络原因接受不到这个数据段,那么也就无法正常关闭,而此时被动关闭方已经关闭连接了,也无法重传这个数据段。
为什么 TCP 挥手需要四次呢?
服务器收到客户端的 FIN 报文时,内核会马上回一个 ACK 应答报文, 但是服务端应用程序可能还有数据要发送,所以并不能马上发送 FIN 报文,而是将发送 FIN 报文的控制权交给服务端应用程序
  • 如果服务端应用程序有数据要发送的话,就发完数据后,才调用关闭连接的函数;
  • 如果服务端应用程序没有数据要发送的话,可以直接调用关闭连接的函数,
从上面过程可知,**是否要发送第三次挥手的控制权不在内核,而是在被动关闭方(上图的服务端)的应用程序,因为应用程序可能还有数据要发送,由应用程序决定什么时候调用关闭连接的函数,当调用了关闭连接的函数,内核就会发送 FIN 报文了,**所以服务端的 ACK 和 FIN 一般都会分开发送。
FIN 报文一定得调用关闭连接的函数,才会发送吗?
不一定。
如果进程退出了,不管是不是正常退出,还是异常退出(如进程崩溃),内核都会发送 FIN 报文,与对方完成四次挥手。
计算机网络 - TCP 协议原理总结 | 春水煎茶 (writings.sh)