
1、主动关闭方发送FIN包,进入FIN-WAIT1
状态
2.1、被动关闭方收到FIN包后,回复ACK包,进入CLOSE_WAIT
状态
2.2、主动关闭方收到ACK包后,等待对方关闭,进入FIN_WAIT2
状态
3、被动关闭方在完成所有数据发送后,调用close()操作;此时,协议层发送FIN包给主动关闭方,等待对方的ACK,被动关闭的一方进入LAST_ACK
状态;
4、主动关闭方收到FIN包,协议层回复ACK;此时,主动关闭方,进入TIME_WAIT
状态;而被动关闭方收到ACK后,进入CLOSED
状态 ;
5、主动关闭方等待2MSL(max segment lifetime)时间后,结束TIME_WAIT,进入CLOSED状态 ;
这里有个问题,就是主动关闭方发送最后一个ACK包后,为什么要进入
TIME-WAIT
等待2MSL而不是进入CLOSED
状态,2MSL是一个发送加一个回复的最大时间,如果直到2MSL后主动关闭方没有再次收到FIN,那么可以推断出ACK已被成功接收,结束TCP连接。而主动关闭方为什么会在发出ACK后又收到FIN包,这是因为被动关闭方在发出最后一个ACK包后也会等待2MSL的时间,如果在这个时间内没有收到主动关闭方回复的ACK后,可能是由于被动关闭方发送的FIN包没有被收到,也可能是主动关闭方回复的ACK包由于网络等原因没有发送成功,此时被动关闭方会再次发送一个FIN包给主动关闭方。
TIME_WAIT有什么用?
TIME_WAIT的出现,是为了解决网络的丢包和网络不稳定所带来的其他问题
1)**防止前一个连接上延迟的数据包或者丢失重传的数据包,被后面复用的连接错误地接收。**如果没有TIME_WAIT,或者TIME_WAIT时间非常端,那么连接关闭后源端口可能马上被服用,此时,前面一个连接上的数据被后面的一个连接错误的接收。
2)确保连接方能在时间范围内,关闭自己的连接。 如果主动关闭的一方回复的最后一个ACK丢失,而被动关闭的一方还继续停留在LAST_ACK状态,如果没有TIME_WAIT的存在,或者说,停留在TIME_WAIT上的时间很短,则主动关闭的一方很快就进入了CLOSED状态,也即是说,如果此时新建一个连接,源随机端口如果被复用,在新连接上发送SYN包后,由于被动方仍认为这条原先的连接还在等待ACK,但是却收到了SYN,则被动方会回复RST,造成新建立的连接无法成功。
服务器异常解决方案
如果服务器出了异常,百分之八九十都是下面两种情况:
1.服务器保持了大量TIME_WAIT状态
基于TCP的HTTP协议,关闭连接的不是客户端,而是服务器,所以web服务器也是会出现大量的TIME_WAIT的情况的。解决思路就是让服务器能够快速回收和重用那些TIME_WAIT的资源,可以修改对应的配置文件。
/etc/sysctl.conf
- #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间
- net.ipv4.tcp_syn_retries=2
- #net.ipv4.tcp_synack_retries=2
- #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
- net.ipv4.tcp_keepalive_time=1200
- net.ipv4.tcp_orphan_retries=3
- #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
- net.ipv4.tcp_fin_timeout=30
- #表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
- net.ipv4.tcp_max_syn_backlog = 4096
- #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
- net.ipv4.tcp_syncookies = 1
- #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
- net.ipv4.tcp_tw_reuse = 1
- #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
- net.ipv4.tcp_tw_recycle = 1
- ##减少超时前的探测次数
- net.ipv4.tcp_keepalive_probes=5
- ##优化网络设备接收队列
- net.core.netdev_max_backlog=3000
2.服务器保持了大量CLOSE_WAIT状态
TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。
但 是CLOSE_WAIT就不一样了,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。所以解决方法只能是检查代码。