CLOSE_WAIT
在被动关闭连接(对方关闭)情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。出现大量close_wait的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下errno,如果不是AGAIN,就断开连接。
解决方法:
基本的思想就是要检测出对方已经关闭的socket,然后关闭它。
- 代码需要判断socket,一旦read返回0,断开连接,read返回负,检查一下errno,如果不是AGAIN,也断开连接。(注:在UNP 7.5节的图7.6中,可以看到使用select能够检测出对方发送了FIN,再根据这条规则就可以处理CLOSE_WAIT的连接)
- 给每一个socket设置一个时间戳last_update,每接收或者是发送成功数据,就用当前时间更新这个时间戳。定期检查所有的时间戳,如果时间戳与当前时间差值超过一定的阈值,就关闭这个socket。
- 使用一个Heart-Beat线程,定期向socket发送指定格式的心跳数据包,如果接收到对方的RST报文,说明对方已经关闭了socket,那么我们也关闭这个socket。
- 设置SO_KEEPALIVE选项,并修改内核参数,下面会详细介绍12a. 参数设置查看相关的参数,这是当前线上环境的配置
sysctl -a|grep tcp_keepalive
net.ipv4.tcp_keepalive_intvl = 300
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 75
tcp_keepalive_intvl: 在TCP保活打开的情况下,最后一次数据交换到TCP发送第一个保活探测包的间隔,即允许的持续空闲时长,或者说每次正常发送心跳的周期。
tcp_keepalive_probes: 在tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包次数,默认值为9(次)
tcp_keepalive_time: tcp_keepalive_time之后,没有接收到对方确认,继续发送保活探测包的发送频率,默认值为75s。
sysctl -w net.ipv4.tcp_keepalive_time = 75
也可以直接打开/etc/sysctl.conf
加入net.ipv4.tcp_keepalive_time = 75
``
让参数生效
sysctl -p
(tcp_keepalive_time + tcp_keepalive_intvl * tcp_keepalive_probes)时间内可检测到连接失效与否。
b. 开启keepalive属性
int keepAlive = 1;
setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));
c. 系统调用设置
这样只会影响单个连接,上面修改内核参数会影响所有设置keepalive属性的连接
#include
#include
#include
int keepAlive = 1; // 开启keepalive属性
int keepIdle = 1800; // 如该连接在1800秒内没有任何数据往来,则进行探测
int keepInterval = 3; // 探测时发包的时间间隔为3秒
int keepCount = 2; // 探测尝试的次数.如果第1次探测包就收到响应了,则后几次的不再发.
setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, (void)&keepAlive, sizeof(keepAlive));
setsockopt(client_fd, SOL_TCP, TCP_KEEPIDLE, (void )&keepIdle, sizeof(keepIdle));
setsockopt(client_fd, SOL_TCP,TCP_KEEPINTVL, (void )&keepInterval, sizeof(keepInterval));
setsockopt(client_fd, SOL_TCP, TCP_KEEPCNT, (void )&keepCount, sizeof(keepCount));
sysctl -w “net.ipv4.ip_local_port_range=1024 65535”
sysctl -w net.ipv4.tcp_timestamps=1
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -p
sysctl -w net.ipv4.tcp_timestamps=1
sysctl -w net.ipv4.tcp_tw_recycle=1
sysctl -p
sysctl -w “net.ipv4.ip_local_port_range=20000 20000”
./tcp_server05 127.0.0.1
./tcp_client01 127.0.0.1
chris@ubuntu:~/myspace/test/network$ ./tcp_client01 127.0.0.1
connect: Cannot assign requested address
sysctl -w net.ipv4.tcp_tw_reuse=1
```
- 重复3、4,可以看到可以成功建立连接
参数说明
- tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)
Enable fast recycling of TIME_WAIT sockets. Enabling this option is not recommended since this causes problems when working with NAT (Network Address Translation). - tcp_tw_reuse (Boolean; default: disabled; since Linux 2.4.19/2.6)
Allow to reuse TIME_WAIT sockets for new connections when it is safe from protocol viewpoint. It should not be changed without advice/request of technical experts.