很多TIME_WAIT连接导致Cannot assign requested address的解决办法 客户端connect服务器,执行一定时间后,接口返回-99的错误 查看错误信息 OS error code 99: Cannot assign requested address 猜想端口被用光的原因,网络搜索得到一个解释:客户端频繁的连服务器,由于每次连接都在很短的时间内结束,导致很多的TIME_WAIT,以至于用光了可用的端口号,所以新的连接没办法绑定端口,即“Cannot assign requested address” 通过netstat,的确看到很多TIME_WAIT状态的连接 因为是调用API,所以无法对连接进行opt设置,只能通过设置系统配置得以解决 解决办法: 执行命令修改如下2个内核参数 sysctl -w net.ipv4.tcp_timestamps=1 开启对于TCP时间戳的支持,若该项设置为0,则下面一项设置不起作用 sysctl -w net.ipv4.tcp_tw_recycle=1 表示开启TCP连接中TIME-WAIT sockets的快速回收 经常检查apache的连接数,会发现很多无用的time_wait连接。有人说这是正常的,是因为一个请求中途中断造成的;还有人说微软的IE连接时产生的Time_wait会比用Firefox连接时多。个人认为有一定的Time_wait是正常的,如果超过了连接数的比例就不是很正常,所以还是找来方法解决一下。 先检查一下time wait的值: [root@aaa1 ~]#sysctl -a | grep time | grep wait net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait=120 net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait =60 net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait =120 这里解决问题的关键是如何能够重复利用time_wait的值,检查net.ipv4.tcp_tw当前值: [root@aaa1 ~]# sysctl -a|grep net.ipv4.tcp_tw net.ipv4.tcp_tw_reuse= 0 net.ipv4.tcp_tw_recycle = 0 增加或修改net.ipv4.tcp_tw值,将当前的值更改为1分钟(reuse是表示是否允许重新应用处于TIME-WAIT状态的socket用于新的TCP连接; recycle是加速TIME-WAIT sockets回收): [root@aaa1 ~]# vi /etc/sysctl.conf net.ipv4.tcp_tw_reuse=1 net.ipv4.tcp_tw_recycle =1 使内核参数生效: [root@aaa1 ~]# sysctl -p 用netstat再观察时会发现已经恢复正常。 结合DDOS和TIME_WAIT过多,建议增加如下参数设置: # Use TCP syncookies when needed net.ipv4.tcp_syncookies=1 net.ipv4.tcp_synack_retries=3 net.ipv4.tcp_syn_retries=3 net.ipv4.tcp_max_syn_backlog=2048 # Enable TCP window scaling net.ipv4.tcp_window_scaling:=1 # Increase TCP max buffer size net.core.rmem_max=16777216 net.core.wmem_max =16777216 # Increase Linux autotuning TCP buffer limits net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem =4096 65536 16777216 # Increase number of ports available net.ipv4.tcp_fin_timeout=30 net.ipv4.tcp_keepalive_time =300 net.ipv4.tcp_tw_reuse =1 net.ipv4.tcp_tw_recycle =1 net.ipv4.ip_local_port_range =5000 65000 附:查看当前的连接数状况 netstat -nat|awk '{print awk $NF}'|sort|uniq -c|sort -n 1 established 1 State 2 LAST_ACK 4 CLOSING 4 FIN_WAIT2 9 LISTEN 17 FIN_WAIT1 18 SYN_RECV 27 ESTABLISHED 811 TIME_WAIT 上面的命令可以帮助分析哪种tcp状态数量异常。其中的SYN_RECV表示正在等待处理的请求数;ESTABLISHED表示正常数据传输状态;TIME_WAIT表示处理完毕,等待超时结束的请求数。 附:查看IP连接数状况 netstat -nat|grep ":80"|awk '{print $5}' |awk -F: '{print $1}' | sort| uniq -c|sort -n 发现异常的,可以封了这个IP =============== TCP/IP TIME_WAIT状态原理 TIME_WAIT状态原理 ---------------------------- 通信双方建立TCP连接后,主动关闭连接的一方就会进入TIME_WAIT状态。 客户端主动关闭连接时,会发送最后一个ack后,然后会进入TIME_WAIT状态,再停留2个MSL时间(后有MSL的解释),进入CLOSED状态。 下图是以客户端主动关闭连接为例,说明这一过程的。 TIME_WAIT状态存在的理由 ---------------------------- TCP/IP协议就是这样设计的,是不可避免的。主要有两个原因: 1)可靠地实现TCP全双工连接的终止 TCP协议在关闭连接的四次握手过程中,最终的ACK是由主动关闭连接的一端(后面统称A端)发出的,如果这个ACK丢失,对方(后面统称B端)将重发出最终的FIN,因此A端必须维护状态信息(TIME_WAIT)允许它重发最终的ACK。如果A端不维持TIME_WAIT状态,而是处于CLOSED 状态,那么A端将响应RST分节,B端收到后将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。 因而,要实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,主动关闭连接的A端必须维持TIME_WAIT状态 。 2)允许老的重复分节在网络中消逝 TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个迟到的迷途分节到达时可能会引起问题。在关闭“前一个连接”之后,马上又重新建立起一个相同的IP和端口之间的“新连接”,“前一个连接”的迷途重复分组在“前一个连接”终止后到达,而被“新连接”收到了。为了避免这个情况,TCP协议不允许处于TIME_WAIT状态的连接启动一个新的可用连接,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个新TCP连接的时候,来自旧连接重复分组已经在网络中消逝。 MSL时间 ---------------------------- MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间IP数据包将在网络中消失 。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒。 TIME_WAIT状态维持时间 ---------------------------- TIME_WAIT状态维持时间是两个MSL时间长度,也就是在1-4分钟。Windows操作系统就是4分钟。 用于统计当前各种状态的连接的数量的命令 --------------------------- #netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 返回结果如下: LAST_ACK 14 SYN_RECV 348 ESTABLISHED 70 FIN_WAIT1 229 FIN_WAIT2 30 CLOSING 33 TIME_WAIT 18122 对上述结果的解释: CLOSED:无连接是活动的或正在进行 LISTEN:服务器在等待进入呼叫 SYN_RECV:一个连接请求已经到达,等待确认 SYN_SENT:应用已经开始,打开一个连接 ESTABLISHED:正常数据传输状态 FIN_WAIT1:应用说它已经完成 FIN_WAIT2:另一边已同意释放 ITMED_WAIT:等待所有分组死掉 CLOSING:两边同时尝试关闭 TIME_WAIT:另一边已初始化一个释放 LAST_ACK:等待所有分组死掉 进一步论述这个问题: ===============================--------------客户端主动关闭连接----------------------- 注意一个问题,进入TIME_WAIT状态的一般情况下是客户端。 大多数服务器端一般执行被动关闭,服务器不会进入TIME_WAIT状态。 当在服务器端关闭某个服务再重新启动时,服务器是会进入TIME_WAIT状态的。 举例: 1.客户端连接服务器的80服务,这时客户端会启用一个本地的端口访问服务器的80,访问完成后关闭此连接,立刻再次访问服务器的 80,这时客户端会启用另一个本地的端口,而不是刚才使用的那个本地端口。原因就是刚才的那个连接还处于TIME_WAIT状态。 2.客户端连接服务器的80服务,这时服务器关闭80端口,立即再次重启80端口的服务,这时可能不会成功启动,原因也是服务器的连 接还处于TIME_WAIT状态。 服务端提供服务时,一般监听一个端口就够了。例如Apach监听80端口。 客户端则是使用一个本地的空闲端口(大于1024),与服务端的Apache的80端口建立连接。 当通信时使用短连接,并由客户端主动关闭连接时,主动关闭连接的客户端会产生TIME_WAIT状态的连接,一个TIME_WAIT状态的连接就占用了一个本地端口。这样在TIME_WAIT状态结束之前,本地最多就能承受6万个TIME_WAIT状态的连接,就无端口可用了。 客户端与服务端进行短连接的TCP通信,如果在同一台机器上进行压力测试模拟上万的客户请求,并且循环与服务端进行短连接通信,那么这台机器将产生4000个左右的TIME_WAIT socket,后续的短连接就会产生address already in use : connect的异常。 关闭的时候使用RST的方式,不进入 TIME_WAIT状态,是否可行? --------------服务端主动关闭连接------------------------------ 服务端提供在服务时,一般监听一个端口就够了。例如Apach监听80端口。 客户端则是使用一个本地的空闲端口(大于1024),与服务端的Apache的80端口建立连接。 当通信时使用短连接,并由服务端主动关闭连接时,主动关闭连接的服务端会产生TIME_WAIT状态的连接。 由于都连接到服务端80端口,服务端的TIME_WAIT状态的连接会有很多个。 假如server一秒钟处理1000个请求,那么就会积压240秒*1000=24万个TIME_WAIT的记录,服务有能力维护这24万个记录。 大多数服务器端一般执行被动关闭,服务器不会进入TIME_WAIT状态。 服务端为了解决这个TIME_WAIT问题,可选择的方式有三种: 保证由客户端主动发起关闭(即做为B端) 关闭的时候使用RST的方式 对处于TIME_WAIT状态的TCP允许重用 一般Apache的配置是: Timeout 30 KeepAlive On #表示服务器端不会主动关闭链接 MaxKeepAliveRequests 100 KeepAliveTimeout 180 表示:Apache不会主动关闭链接, 两种情况下Apache会主动关闭连接: 1、Apache收到了http协议头中有客户端要求Apache关闭连接信息,如setRequestHeader("Connection", "close"); 2、连接保持时间达到了180秒的超时时间,将关闭。 如果配置如下: KeepAlive Off #表示服务器端会响应完数据后主动关闭链接