浅谈TCP keepalive

    在上一篇文章中曾经提到过,当server端收到来自client的FIN后却一直不关闭连接,TCP连接将进入CLOSE_WAIT状态,并一直维持该状态,实际上这时client端早已退出,这无疑是一种资源的浪费。在实际的生产环境中,由于掉电、系统崩溃、网络异常等原因,这种情况是很有可能发生的,于是我们需要一种方法来进行连接的探测和保活,及时判断出异常情况的发生,释放连接资源。

    TCP的keepalive机制就是为这项任务而生的,详细的描述见http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/

    默认情况下,TCP的keepalive功能是关闭的,所以在前面的例子中server端的CLOSE_WAIT连接会一直保持,可以通过下面的方式打开该功能:

int keepalive = 1;/*on*/
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));

系统默认的keepalive参数可以通过/proc或sysctl获取:

如果希望将keepalive机制引入程序中,并修改相关参数,可以通过两种方法:

  • 通过/proc或sysctl修改全局配置,也就是上图中的内容;

  • 通过套接字选项修改特定于套接字的配置;

由于我们不想改动全局配置,因此选择第二种方法,修改套接字选项:

int keepalive_idle = 120;
setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &keepalive_idle, sizeof(keepalive_idle));

int keepalive_intvl = 30;
setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &keepalive_intvl, sizeof(keepalive_intvl));

int keepalive_cnt = 5;
setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &keepalive_cnt, sizeof(keepalive_cnt));

注意:这里我们希望在server端增加保活探测机制,所以上述代码都是在server端的修改。

重新运行修改后的程序,并在server端使用netstat观察:


可以看到,keepalive的timer开始运行起来了,一段时间后CLOSE_WAIT的连接将被释放:

下面通过实际的抓包看看到底发生了什么?

可以看到,在数据流量结束120s(TCP_KEEPIDLE)后,服务端发送了一个keepalive报文,该报文是一个长度为0的"duplicate-ack",且序列号比上一个ACK少1。

此时,由于client早已退出,因此协议栈会回复一个RST,server端也就此立即退出了,后续的探测也就不会再进行了。



你可能感兴趣的:(浅谈TCP keepalive)