TIME_WAIT状态:

  在TCP连接中,主动关闭链接的一方会进入TIME_WAIT状态,它会保持长达2MSL周期的时间,为什么会保持这么长的时间上一篇博客中已经提到过了。如果server的最后一次ACKclient没有收到,client会重新发送一次fin这个时间是在2ML周期之间的,这个时候server还保持着TIME_WAIT的状态所以它会再次发送ACK确认,而如果不等待这么长时间的话,server就会是CLOSED状态,收到来自client的fin时回复的便是rst(强制关闭TCP链接)了。

服务器中保持大量的TIME_WAIT是很不好的事情,它可能会导致服务器暂时处理不了新的链接,操作系统中提供的文件描述符是在一定数量的,多出来的请求连接,是无法分配的。

在linux下使用下面的命令可以查看当时系统中所维持的TCP状态信息。

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 

如何避免TIME_WAIT所占用的大量时间?


方式一

C库中提供了一个对TCP链接很有用的函数。

 int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);

sockfd表示一个socket描述字。

level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。

optname:需设置的选项。

optval:指针,指向存放选项待设置的新值的缓冲区。

optlen:optval缓冲区长度。


setsockopt()支持下列选项。其中“类型”表明optval所指数据的类型。

选项      类型 意义

SO_BROADCAST BOOL 允许套接口传送广播信息。

SO_DEBUG   BOOL 记录调试信息。

SO_DONTLINER BOOL 不要因为数据未发送就阻塞关闭操作。设置本选项相当于将SO_LINGER的l_onoff元素置为零。

SO_DONTROUTE BOOL 禁止选径;直接传送。

SO_KEEPALIVE BOOL 发送“保持活动”包。

SO_LINGER struct linger FAR* 如关闭时有未发送数据,则逗留。

SO_OOBINLINE BOOL 在常规数据流中接收带外数据。

SO_RCVBUF int 为接收确定缓冲区大小。

SO_REUSEADDR BOOL 允许套接口和一个已在使用中的地址捆绑。

SO_SNDBUF int 指定发送缓冲区大小。

TCP_NODELAY BOOL 禁止发送合并的Nagle算法。

   

使用setsockopt()让TIME_WAIT改变。

1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用
closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));

2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历
TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));


setsockopt()函数的功能很强大,这里只说了它对于TIME_WAIT的帮助。



方式二、

修改 /etc/sysctl.conf文件;

  1. #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间   

  2. net.ipv4.tcp_syn_retries=2  

  3. #net.ipv4.tcp_synack_retries=2  

  4. #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒  

  5. net.ipv4.tcp_keepalive_time=1200  

  6. net.ipv4.tcp_orphan_retries=3  

  7. #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间  

  8. net.ipv4.tcp_fin_timeout=30    

  9. #表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。  

  10. net.ipv4.tcp_max_syn_backlog = 4096  

  11. #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN***,默认为0,表示关闭  

  12. net.ipv4.tcp_syncookies = 1  

  13.   

  14. #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭  

  15. net.ipv4.tcp_tw_reuse = 1  

  16. #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭  

  17. net.ipv4.tcp_tw_recycle = 1  

  18.   

  19. ##减少超时前的探测次数   

  20. net.ipv4.tcp_keepalive_probes=5   

  21. ##优化网络设备接收队列   

  22. net.core.netdev_max_backlog=3000   

  

修改完之后执行/sbin/sysctl -p让参数生效。

这里头主要注意到的是net.ipv4.tcp_tw_reuse

net.ipv4.tcp_tw_recycle
net.ipv4.tcp_fin_timeout
net.ipv4.tcp_keepalive_*

这几个参数。

 

net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。

net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。

net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。

关于keepalive的用途可以参考:http://hi.baidu.com/tantea/blog/item/580b9d0218f981793812bb7b.html