TCP协议是传输层协议,是一个面向连接的协议,为用户提供可靠的全双工字节流。
TCP连接与断开
三次握手
建立一个TCP连接需要进行三次握手:
三次握手是必要的。如果只靠前两步就建立连接,考虑这样一个场景:
客户端向服务器发送第一个分节请求
SYN j1
建立连接,但是这个分节由于网络阻塞,很久都没有到达服务器客户端长时间没收到服务器的响应,认为第一个分节失效,根据TCP特性,重新发送第二个分节请求
SYN j2
建立连接服务器响应第二个分节
ACK j2
,连接建立成功,客户端和服务器通信后,关闭连接这时原来被认为失效的第一个分节到达了服务器端,服务器会以为又建立了一个新连接,发送响应
ACK j1
服务器会等待客户端发送消息,但实际上并没有连接,所以造成了资源的浪费
所以,在进行前两步后,还需要额外再进行一次消息的确认,即三次握手,来确保两端的正常连接建立。
四次挥手
终止TCP连接的方式是四次挥手:
TCP协议是全双工的,所以每个方向都需要进行单独的关闭(close)。关闭了一端后,这一端就不能发送数据了,但还可以接收另一端的数据,这也称为半关闭。在客户端进行close后,服务器端响应一个ACK,但服务器端会等到数据都发送给客户端后,再进行close,这也就是终止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套接字选项来避免这个问题。