TCP连接的终止(四次握手释放)
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。
首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的,如图:
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
对应函数接口如图:
调用过程如下:
-
当client想要关闭它与server之间的连接。client(某个应用进程)首先调用close主动关闭连接,这时TCP发送一个FIN M;client端处于FIN_WAIT1状态。
-
当server端接收到FIN M之后,执行被动关闭。对这个FIN进行确认,返回给client ACK。
当server端返回给client ACK后,client处于FIN_WAIT2状态,server处于CLOSE_WAIT状态。它的接收也作为文件结束符传递给应用进程,因为FIN的接收 意味着应用进程在相应的连接上再也接收不到额外数据;
-
一段时间之后,当server端检测到client端的关闭操作(read返回为0)。接收到文件结束符的server端调用close关闭它的socket。这导致server端的TCP也发送一个FIN N;此时server的状态为LAST_ACK。
-
当client收到来自server的FIN后 。client端的套接字处于TIME_WAIT状态,它会向server端再发送一个ack确认,此时server端收到ack确认后,此套接字处于CLOSED状态。
这样每个方向上都有一个FIN和ACK。
1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;
但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样):
一方面是可靠的实现TCP全双工连接的终止,也就是当最后的ACK丢失后,被动关闭端会重发FIN,因此主动关闭端需要维持状态信息,以允许它重新发送最终的ACK。
另一方面,但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。
TCP在2MSL等待期间,定义这个连接(4元组)不能再使用,任何迟到的报文都会丢弃。设想如果没有2MSL的限制,恰好新到的连接正好满足原先的4元组,这时候连接就可能接收到网络上的延迟报文就可能干扰最新建立的连接。
3、发现系统存在大量TIME_WAIT状态的连接,可以通过调整内核参数解决:vi /etc/sysctl.conf 加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
然后执行 /sbin/sysctl -p 让参数生效。
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系统默认的 TIMEOUT 时间