TCP协议是传输层协议,是一个面向连接的协议,为用户提供可靠全双工字节流。

TCP连接与断开

三次握手

建立一个TCP连接需要进行三次握手:

三次握手

三次握手是必要的。如果只靠前两步就建立连接,考虑这样一个场景:

  1. 客户端向服务器发送第一个分节请求SYN j1建立连接,但是这个分节由于网络阻塞,很久都没有到达服务器

  2. 客户端长时间没收到服务器的响应,认为第一个分节失效,根据TCP特性,重新发送第二个分节请求SYN j2建立连接

  3. 服务器响应第二个分节ACK j2,连接建立成功,客户端和服务器通信后,关闭连接

  4. 这时原来被认为失效的第一个分节到达了服务器端,服务器会以为又建立了一个新连接,发送响应ACK j1

  5. 服务器会等待客户端发送消息,但实际上并没有连接,所以造成了资源的浪费

所以,在进行前两步后,还需要额外再进行一次消息的确认,即三次握手,来确保两端的正常连接建立。

四次挥手

终止TCP连接的方式是四次挥手:

四次挥手

TCP协议是全双工的,所以每个方向都需要进行单独的关闭(close)。关闭了一端后,这一端就不能发送数据了,但还可以接收另一端的数据,这也称为半关闭。在客户端进行close后,服务器端响应一个ACK,但服务器端会等到数据都发送给客户端后,再进行close,这也就是终止TCP连接为什么需要进行四次挥手的原因。

TCP状态转换

对于TCP状态转换,有一张非常经典图:

TCP状态转换

使用时序图能更好的理解TCP的状态转换:

TCP状态转换

TIME_WAIT状态

在执行主动关闭的一端,四次挥手之后,会进入一个TIME_WAIT状态,这个状态的持续时间是2倍的MSL,MSL是一个TCP报文在网络中的最大生存时间。TIME_WAIT状态的存在有两个目的:

  • 实现可靠的TCP连接终止

    最后一个ACK如果被丢弃,那么被动关闭的一方就会重新发送FIN。让主动关闭一端维持2MSL的TIME_WAIT,就能确保在这种情况下重新发送ACK。1个MSL留给最后ACK到达对端,1个MSL留给重新发送的FIN。

  • 让老的重复报文在网络中过期消失,建立新连接的时将不会受影响

    在TCP连接关闭之后,如果同时又在相同的地址和端口建立新连接,这可以看做是原来连接的新化身。这就必须保证新的连接不能接收到原先连接的老重复报文。当处于TIME_WAIT状态时,是无法创建新的连接的,而TIME_WAIT状态持续时间足以让老的重复报文在网络中消失。

当重启服务器时,原服务器节点地址关闭后可能处于TIME_WAIT状态,这时重启后就会出现EADDRINUSE的错误(地址已使用),尽管有办法关闭TIME_WAIT状态,但是应该避免这么做,这会影响可靠性。一般在服务器端,可以使用SO_REUSEADDR套接字选项来避免这个问题。