您的位置:首页 > 理论基础 > 计算机网络

keep-alive 和 TCP存活检测

2010-12-24 17:35 344 查看
什么是keep-alive?

  顾名思义即可,注意它只适用于TCP连接。系统会替你维护一个timer,时间到了,就会向remote peer发送一个probe package,当然里面是没有数据的,对方就会返回一个应答,这时你就知道这个通道保持正常。

有什么用呢?
  考虑下面这个场景,端点A和端B开始连接,三次握手,建立好了一个稳定的双向通道。然后双方发送完初始的数据后,进入等待状态。这时候,拔掉B的电源插头,B死掉了,它不可能有任何机会向A发送FIN包,或是其他数据来说明自己挂掉,需要终止连接。因此A只是简单的继续等待。然后重启机器和应用程序,B又恢复原始的状态,可是原本的连接已经没有了,之后就可能有三种情况,双方继续傻等,或是B向A发起新的请求,最可能的还是A向B发送新的数据,这时B因为原有的socket已经死了,就返回一个RST,A关闭连接。看下面的图:

_____                                                     _____
|     |                                                   |     |
|  A  |                                                   |  B  |
|_____|                                                   |_____|
^                                                         ^
|--->--->--->-------------- SYN -------------->--->--->---|
|---<---<---<------------ SYN/ACK ------------<---<---<---|
|--->--->--->-------------- ACK -------------->--->--->---|
|                                                         |
|                                       system crash ---> X
|
|                                     system restart ---> ^
|                                                         |
|--->--->--->-------------- PSH -------------->--->--->---|
|---<---<---<-------------- RST --------------<---<---<---|
|                                                         |


  上面的场景就提出了一个需求,作为服务器端,可不能老是傻等是不是,当然timeout也是一个方案了,不过如果是IM这种类型的服务器,总不能用户一段时间没反应,就把它给踢了吧。

 

  keep-alive还有另外一个有点trick的作用,考虑另外一个场景,一般的终端总是位于防火墙或是NAT后面,而这些设备限制于硬件,不可能处理任意数量的连接,在内部队列满的时候,总是要把最不活跃的连接给砍了的。这个时候keep-alive就可以发挥最用了。看下面的图:

_____           _____                                     _____
|     |         |     |                                   |     |
|  A  |         | NAT |                                   |  B  |
|_____|         |_____|                                   |_____|
^               ^                                         ^
|--->--->--->---|----------- SYN ------------->--->--->---|
|---<---<---<---|--------- SYN/ACK -----------<---<---<---|
|--->--->--->---|----------- ACK ------------->--->--->---|
|               |                                         |
|               | <--- connection deleted from table      |
|               |                                         |
|--->- PSH ->---| <--- invalid connection                 |
|               |                                         |


程序上如何使用呢?

  无论是windows还是linux都实现了这个功能,你只需要设置socket的属性即可,当然你也可自己实现这个功能,也就是定期发送心跳包.

  先说linux平台:

  一般的实现如下,也就是说每过7200s空闲(两个小时),就会发送一个探测包,如果没有应答,就隔75s,再次发送,连续9次没有反应,就判定该连接死了。

  # cat /proc/sys/net/ipv4/tcp_keepalive_time

  7200

  # cat /proc/sys/net/ipv4/tcp_keepalive_intvl

  75

  # cat /proc/sys/net/ipv4/tcp_keepalive_probes

  9

  如何改呢,可以用直接该系统默认值,则全局都会有效,这似乎不大合适。 如果是程序中,则只要设置socket的属性就行了,调用setsockopt,首先打开keep-alive功能:

  int optval = 1;

  socklen_t optlen = sizeof(optval);

  if(setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) {

     perror("setsockopt()");

     close(s);

     exit(EXIT_FAILURE);

  }

  然后设定要改的值,像这样:

  setsockopt (fd, SOL_TCP, TCP_KEEPIDLE, &newValue, optlen);

      TCP_KEEPCNT: 代表 tcp_keepalive_probes

      TCP_KEEPIDLE: 代表 tcp_keepalive_time

      TCP_KEEPINTVL: 代表 tcp_keepalive_intvl

 windows平台上也是差不多:

windows中也是可以做出系统级别的调整的, 对于Win2K/XP/2003,可以从下面的注册表项找到影响整个系统所有连接的参数:

        [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]

        “KeepAliveTime”=dword:006ddd00

        “KeepAliveInterval”=dword:000003e8

        “MaxDataRetries”=”5″

在程序中来设定的话,首先先打开keep-alive,跟在linux中是一样的,

        BOOL bKeepAlive = TRUE;

        int nRet = ::setsockopt(socket_handle, SOL_SOCKET, SO_KEEPALIVE, (char*)&bKeepAlive, sizeof(bKeepAlive));

然后调整具体的参数,需要调用WSAIoctl

        tcp_keepalive alive_in = {0};

        tcp_keepalive alive_out = {0};

        alive_in.keepalivetime = 5000;

        alive_in.keepaliveinterval = 1000;

        alive_in.onoff = TRUE;

        unsigned long ulBytesReturn = 0;

        nRet = WSAIoctl(socket_handle, SIO_KEEPALIVE_VALS, &alive_in, sizeof(alive_in), &alive_out, sizeof(alive_out), &ulBytesReturn, NULL, NULL);

开启Keepalive选项之后,对于使用IOCP模型的服务器端程序来说,一旦检测到连接断开,GetQueuedCompletionStatus函数将立即返回FALSE,使得服务器端能及时清除该连接、释放该连接相关的资源。对于使用 select模型的客户端来说,连接断开被探测到时,以recv目的阻塞在socket上的select方法将立即返回SOCKET_ERROR,从而得知连接已失效,客户端程序便有机会及时执行清除工作、提醒用户或重新连接。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: