返回列表 发帖

Linux使用KEEPALIVE判断对端网络状态

Linux使用KEEPALIVE判断对端网络状态

原文参考 http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/

TCP连接有很多定时器,其中一些与Keepalive有关。当这些定时器计数为0时,就会发送Keepalive探帧包(我记得在最早的BSD代码上该探帧包占一个序列号Sequence)。如果这些包没有得到对端的ACK回应,则可采取一定的控制策略,比如继续发送探帧包或认为对端已非正常中断连接。

Keepalive的用途主要有两个:检查对端是否死掉或者防止因网络无活动而连接中断。假设A和B建立了一个TCP连接,B突然死掉了,A会在一定时间后发送Keepalive探帧包,如果B重启后收到了探帧包,因TCP套接字对不上或TCP序列号对不上,会导致B给A发送RST包,此时连接马上中断。如果B一直没活过来,则发送一定数量的探帧包后A认为连接中断。这是有效检查对端死掉的情况,还有一种情况是通过NAT或代理上网,如果A和B之间隔着NAT或代理,这些NAT或者代理通常有一定的连接超时控制,发现A和B在一段时间后没有交换数据,就会切断二者的通信,此时用Keepalive可避免这种情况。

Linux下控制Keepalive的参数有三个:

tcp_keepalive_time  在连接多长时间没有数据交换时发送探帧包
tcp_keepalive_intvl 发送下一个探帧包距离发送本次探帧包的时间间隔
tcp_keepalive_probes 发送多少次没有回应的探帧包后认为对端死掉

对应在proc文件系统的位置是

/proc/sys/net/ipv4/tcp_keepalive_time
/proc/sys/net/ipv4/tcp_keepalive_intvl
/proc/sys/net/ipv4/tcp_keepalive_probes

注意:仅仅配置内核参数是不够的,还必须在编程的时候设置套接字的选项,调用函数是

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

选项为SO_KEEPALIVE,此外还可以设置TCP选项(设置的level为SOL_TCP而不是SOL_SOCKET)覆盖系统全局设置

TCP_KEEPCNT  与tcp_keepalive_probes对应
TCP_KEEPIDLE 与tcp_keepalive_time对应
TCP_KEEPINTVL 与tcp_keepalive_intvl对应

如果有些程序无法修改源码,可采用libkeepalive覆盖程序的socket调用,达到设置这些选项的目的,为socket调用提供覆盖的是ld的PRELOAD机制。假设待覆盖的程序为test,则

$ LD_PRELOAD=libkeepalive.so \
  KEEPCNT=20 \
  KEEPIDLE=180 \
  KEEPINTVL=60 \
  test

可修改test程序的socket选项。

关于libkeepalive项目可参考 http://libkeepalive.sourceforge.net/

示例代码:

  1. /*
  2. * Enable socket SO_KEEPALIVE, if the connection disconnected, any system call on socket
  3. * will return immediately and errno will be set to "WSAENOTCONN"
  4. *
  5. * keepalive is not program related, but socket related, * so if you have multiple sockets,
  6. * you can handle keepalive for each of them separately.
  7. *
  8. * Reference: http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
  9. */
  10. int cp_enable_sock_keepalive(CP_SOCK *sock)
  11. {
  12.     int  opt;

  13.     if(!sock)
  14.         return -1;


  15.     /* Enable the KEEPALIVE flag */
  16.     opt = 1;
  17.     if (setsockopt (sock->fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof (opt)))
  18.     {
  19.         sock->err_no = errno;
  20.         return -2;
  21.     }

  22.     if(sock->keepintvl || sock->keepcnt)
  23.     {
  24.         /*
  25.          *  The tcp_keepidle parameter specifies the interval between the last data packet sent
  26.          *  (simple ACKs are not considered data) and the first keepalive probe; after the
  27.          *  connection is marked to need keepalive, this counter is not used any further.
  28.          *  ~ >: cat /proc/sys/net/ipv4/tcp_keepalive_time
  29.          *  7200
  30.          */
  31.         opt = 3; /* 3 seconds  */
  32.         if (setsockopt (sock->fd, SOL_TCP, TCP_KEEPIDLE, (char *) &opt, sizeof (opt)))
  33.         {
  34.             sock->err_no = errno;
  35.             return -3;
  36.         }

  37.         if((opt=sock->keepintvl) > 0)
  38.         {
  39.             /*
  40.              * The tcp_keepintvl parameter specifies the interval between subsequential keepalive
  41.              * probes, regardless of what the connection has exchanged in the meantime.
  42.              * ~ >: cat /proc/sys/net/ipv4/tcp_keepalive_intvl
  43.              * 75
  44.              */
  45.             if (setsockopt (sock->fd, SOL_TCP, TCP_KEEPINTVL, (char *) &opt, sizeof (opt)))
  46.             {
  47.                 sock->err_no = errno;
  48.                 return -4;
  49.             }
  50.         }

  51.         if((opt=sock->keepcnt) > 0)
  52.         {
  53.             /*
  54.              * The TCP_KEEPCNT option specifies the maximum number of unacknowledged probes to
  55.              * send before considering the connection dead and notifying the application layer
  56.              * probes to be sent. The value of TCP_KEEPCNT is an integer value between 1 and n,
  57.              * where n is the value of the systemwide tcp_keepcnt parameter.
  58.              * ~ >: cat /proc/sys/net/ipv4/tcp_keepalive_probes
  59.              * 9
  60.              */
  61.             if (setsockopt (sock->fd, SOL_TCP, TCP_KEEPCNT, (char *) &opt, sizeof (opt)))
  62.             {
  63.                 sock->err_no = errno;
  64.                 return -5;
  65.             }
  66.         }
  67.     }


  68.     return 0;
  69. }
复制代码

返回列表
网页右侧QQ悬浮滚动在线客服
网页右侧QQ悬浮滚动在线客服