Linux 网络编程中之心跳机制应用

来源:互联网 发布:淘宝修改为没有发货 编辑:程序博客网 时间:2024/04/28 07:17
Linux 网络编程中之心跳机制应用


一、问题:Linux TCP 中如何判断非正常连接的断开

二、方法分析:
      面向连接的TCP连接,在实际的应用中经常需要检测连接是否断开。而在实际的连接过程中,连接断开分为两种情况:
      1、客户端正常关闭。即客户端通过调用close,shutdown来正常关闭socket。此时服务端通过read和write的返回值来判断客户端是否正常
   关闭0表示,-1表示读写错误。均可以表示连接正常断开。
      2、客户端非正常关闭。比如说网络断开,wifi连接失效断开等的非正常断开。此时,不能通过read和write判断,这两个函数将不能判断
    socket已经失效。需要通过其他方式来判断连接是否断开。判断方式有以下两种:
      1、TCP通信的双方定时发送数据包,通过判断是否收到对方的数据包以检测对方是否在线。
      2、利用Socket的KEEP_ALIVE 机制,来定时给对方发送一个"探测存活数据包",当超过一定时间对方没有回复ACK,则视为连接断开,此时
    调用read或者write时,将返回错误信息。我们可以通过判断错误信息来检测链接已经断开。

两者的优劣:
方法一:需要客户端与服务端协商好双方的通信协议,需要双方的应用层程序通信作出修改,比较不灵活。
方法二:默认状态下,KEEP_ALIVE 机制判断tcp链接是否断开需要发送心跳包来检测,由于发送心跳包的工作由tcp得协议栈来做,应用层只需要
        对链接两端的TCP socket 设置keep alive 机制,进行监听即可。这种方法实现较为方便。

三、注意事项:
       KEEP_ALIVE 机制默认发送心跳包的首次空闲时间为:2小时(7200s),但在实际应用中,我们需要实时监听对方是否在线,因此需要对KEEP_ALIVE 相关参数做出调整。并且当检测到链接断开时,keep_alive 不会自动通知上层,需要应用层通过read 和 write 的返回值确定。0
或-1均表示,链接已断开。
            
四、相关参数设置:
      Linux内核包含对keepalive的支持。其中使用了三个参数:
tcp_keepalive_time(开启keepalive的闲置时长/首次探测空闲时间),表示tcp连接双方没有数据交换的空闲时间。
tcp_keepalive_intvl(keepalive探测包的发送间隔) 
tcp_keepalive_probes (如果对方不予应答,探测包的发送次数);
对于异常关闭如网络崩溃、主机宕机等,可通过设置SO_KEEPALIVE设置保活,协议会按照设定间隔自动发送探测分节。该选项分为设置无数据首次探测时间、探测间隔、探测次数控制TCP是否出错。如果你设置首次探测在10秒之后、探测间隔3s,探测次数3次,则最多在10+3*3=19秒之后(最少在3*3=9秒),将给应用层返回一个对方非正常关闭的异常,此时可通过获得errno得到对应错误,read/recv返回为-1。

     修改方法有以下两种:    
     1、系统配置:分别修改以下三个参数
# 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
    2、应用编程:见下方例子(仅列出主要头文件)

/* 仅列出主要头文件 */#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <unistd.h>/* 调用方法  * sg_socketfd 为服务端的监听socket */anetKeepAlive(NULL, sg_socketfd, 6);/* TCP 心跳包检测 *//* Set TCP keep alive option to detect dead peers. The interval option * is only used for Linux as we are using Linux-specific APIs to set * the probe send time, interval, and count. */int ANET_ERR = -1;int anetKeepAlive(char *err, int fd, int interval){    int val = 1;    /* 开启keepalive机制 */    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)    {        perror("setsockopt SO_KEEPALIVE:");        return ANET_ERR;    }    /* Default settings are more or less garbage, with the keepalive time     * set to 7200 by default on Linux. Modify settings to make the feature     * actually useful. */    /* 开始首次KeepAlive探测前的TCP空闲时间     * Send first probe after interval. */    val = interval;    if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {    perror("setsockopt TCP_KEEPIDLE: \n");        return ANET_ERR;    }    /* 两次KeepAlive探测间的时间间隔     * Send next probes after the specified interval. Note that we set the     * delay as interval / 3, as we send three probes before detecting     * an error (see the next setsockopt call). */     val = interval/3;     if (val == 0) val = 1;     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {    perror("setsockopt TCP_KEEPINTVL: \n");        return ANET_ERR;     }     /* 判定断开前的KeepAlive探测次数     * Consider the socket in error state after three we send three ACK     * probes without getting a reply. */     val = 3;     if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {    perror("setsockopt TCP_KEEPCNT: \n");        return ANET_ERR;     }     return 0;}



0 0