前言
TCP(传输控制协议)是互联网协议(IP)中的一种重要传输层协议,用于在通信的计算机之间建立可靠的、有序的和错误校验的数据传输。在TCP连接中,数据传输是双向的,因此需要一种机制来开始和结束连接。这就是所谓的“握手”和“挥手”。TCP四次挥手是TCP连接断开过程中的一个重要环节,它确保了数据传输的完整性和可靠性。
TCP四次挥手过程和状态变迁
TCP的四次挥手过程发生在两个端点都准备好关闭连接时。以下是四次挥手的详细步骤:
-
FIN:当一方完成数据发送并决定关闭连接时,它会发送一个FIN报文段,请求关闭连接。此时,发送方进入FIN_WAIT_1状态,等待接收方的确认。
-
ACK:接收方收到FIN报文段后,会发送一个ACK报文段作为回应,表示同意关闭连接。此时,接收方进入CLOSE_WAIT状态。
-
FIN:一旦接收方完成所有数据接收并准备关闭连接,它也会发送一个FIN报文段,表示请求关闭连接。此时,发送方进入TIME_WAIT状态。
-
ACK:接收方发送一个ACK报文段作为回应,确认收到FIN报文段并完成关闭连接。
以下是使用Java代码模拟TCP四次挥手的示例:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket; public class TCPHandshake { public static void main(String[] args) { try { // 创建Socket连接 Socket socket = new Socket("localhost", 8000); System.out.println("Connected to server"); // 获取输入输出流 BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter output = new PrintWriter(socket.getOutputStream(), true); // 第一次挥手:客户端发送FIN报文段请求关闭连接 output.println("FIN"); System.out.println("Sent FIN"); // 等待服务器响应 String response = input.readLine(); System.out.println("Received response: " + response); // 第二次挥手:服务器发送ACK报文段确认关闭连接请求 output.println("ACK"); System.out.println("Sent ACK"); // 等待客户端响应 response = input.readLine(); System.out.println("Received response: " + response); // 第三次挥手:客户端发送FIN报文段请求关闭连接 output.println("FIN"); System.out.println("Sent FIN"); // 等待服务器响应 response = input.readLine(); System.out.println("Received response: " + response); // 第四次挥手:服务器发送ACK报文段确认关闭连接请求 output.println("ACK"); System.out.println("Sent ACK"); // 关闭连接和流 socket.close(); input.close(); output.close(); } catch (Exception e) { e.printStackTrace(); } }
}
第一次挥手:主动关闭方发送关闭请求(FIN)
在TCP的四次挥手过程中,首先由主动关闭方(通常是客户端)发起关闭请求。主动关闭方发送一个TCP报文,其中包含FIN(Finish)标志位,表示主动关闭方不再有数据要发送了。此时,主动关闭方进入FIN_WAIT_1状态,等待被动关闭方的确认。
第二次挥手:被动关闭方回应确认(ACK)
被动关闭方(通常是服务器)接收到主动关闭方的FIN后,发送一个确认(ACK)报文,表示已经收到了关闭请求。此时,被动关闭方进入CLOSE_WAIT状态,表示服务器端的应用程序已经等待关闭了,但仍可以接收来自客户端的数据。
第三次挥手:被动关闭方发送关闭请求(FIN)
在完成自己的数据发送后,被动关闭方也发送一个带有FIN标志的TCP报文,表示被动关闭方也没有数据要发送了。此时,被动关闭方进入LAST_ACK状态,等待主动关闭方的确认。
第四次挥手:主动关闭方回应确认(ACK)
主动关闭方接收到被动关闭方的FIN后,发送一个确认(ACK)报文,表示已经收到了关闭请求。此时,主动关闭方进入TIME_WAIT状态,等待一段时间,以确保被动关闭方收到了确认,并防止可能存在的延迟报文导致连接混乱。一旦等待时间过去,主动关闭方进入CLOSED状态,表示连接已经完全关闭。
为什么挥手需要四次
TCP的四次挥手是为了确保数据流的正确和可靠地关闭。这需要双方都明确地确认关闭请求,并确保所有的数据包都已接收或处理。通过四次握手,每一方都可以确认对方已经完成了数据传输和接收的准备工作。
为什么TIME_WAIT等待的时间是2MSL
TIME_WAIT状态是TCP四次挥手中的一个重要状态,其持续时间通常是2MSL(最大段生存期)。这是为了确保在网络中的所有数据包都已过期并被丢弃,防止出现旧的数据包在网络中循环并导致连接错误关闭的问题。TIME_WAIT状态也提供了一个机会让发送方等待一段时间,以确保接收方已经收到了关闭连接的请求。
等待2MSL的意义
等待2MSL是为了确保在网络中的所有数据包都已过期并被丢弃。如果发送方在发送完最后一个ACK报文段后立即关闭连接,而网络中仍然存在未过期或未被丢弃的数据包,那么这些数据包可能会在网络中循环并导致连接错误关闭。通过等待2MSL的时间,发送方可以确保这些数据包已经过期并被丢弃,从而避免这种情况的发生。此外,等待2MSL的时间也为接收方提供了一个机会,以确保它已经收到了关闭连接的请求并完成了所有必要的处理工作。
如何解决TIME_WAIT状态过多
最好的办法是尽量让客户端主动断开连接,除非遇到一些异常情况,如客户端协议错误、客户端超时等。
打开系统的TIME_WAIT重用和快速回收。
在Linux系统可以修改以下参数:
1.打开TCP对时间戳的支持,保持服务器与客户端时间同步
net.ipv4.tcp_timestamps=1(默认即为 1)
2.修改net.ipv4.tcp_tw_reuse = 1,允许对处于TIME_WAIT的socket用于建立新的连接
net.ipv4.tcp_tw_reuse = 1 (默认为0)
修改TIME_WAIT连接状态的上限值,超过上限值,处于TIME_WAIT状态的socket将立刻被清除并打印警告信息。
net.ipv4.tcp_max_tw_buckets = 18000,表示系统同时保持处于TIME_WAIT状态的socket的最大数量,默认为18000。
可修改为更小值。
net.ipv4.tcp_max_tw_buckets = 6666
结语
TCP的四次挥手是一个复杂但必要的过程,用于确保可靠地关闭TCP连接。通过四次握手和TIME_WAIT状态,TCP协议可以确保数据流的正确和可靠地关闭,并避免在网络中循环的数据包导致连接错误关闭的问题。了解TCP的四次挥手过程和状态变迁对于理解TCP协议的工作原理和实现可靠的网络通信至关重要。