检测网络连接状态_转

1. 根据ifconfig中相应网口的RUNNING字段检查

int check_net(const char *eth)
{
    int ret = 1;
    char buf[256];
    FILE *fp;
    memset(buf, 0, 256);

    sprintf(buf, "ifconfig %s | grep 'RUNNING'", eth);
    fp = popen(buf, "r");
    if(fp == NULL)
    {
        printf("failed to popen %s
", buf);
        return 0;
    }

    memset(buf, 0, 256);
    fgets(buf, 256, fp);

    if(!strcmp(buf, ""))
        ret = 0;
    else
        ret = 1;

    pclose(fp);

}

 2. keepalive

一般服务器程序不会用到这个机制,而是把Keepalive放到业务层,自己实现Keepalive机制。这样做的好处是可以减少程序对特定协议的依赖,且发送给客户端的请求可以组合起来,在心跳包到来的时候一并返回给客户端。还有,开启Keepalive特性还有有一些其他方面的风险。
比如:
(1) cause perfectly good connections to break during transient Internet failures; 
(2)consume unnecessary bandwidth ("if no one is using the connection, who cares if it is still good?"); 
(3) cost money for an Internet path that charges for packets.  (RFC1122)
void try_connect_tcp(void)
{
    int ret;
    int opt = 1;
    int keep_intvl = 5;
    int keep_probes= 3;
    int keep_idletime = 5;
    int sd = 0;

    sd = socket(AF_INET, SOCK_STREAM, 0);
    if(sd < 0)
    {
        printf("failed to create socket(tcp)
");
        return;
    }

    setsockopt(sd,SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt));   
    setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    setsockopt(sd,SOL_TCP,TCP_KEEPCNT,&keep_probes,sizeof(int));
    setsockopt(sd,SOL_TCP,TCP_KEEPINTVL,&keep_intvl,sizeof(int));
    setsockopt(sd,SOL_TCP,TCP_KEEPIDLE,&keep_idletime,sizeof(int));

    fcntl(sd, F_SETFL, O_NONBLOCK | fcntl(sd, F_GETFL));

    connect(sd, (struct sockaddr *)addr, sizeof(struct sockaddr_in));

    ret = check_tcp_connect(sd, gtimeout);
    if(ret == 1)//connect success
    {
        printf("Tcp(%d) connect success!!!
", which);
     }
    else//connect failed
    {
        close(sd);
        printf("Connect to sd failed!
");
    }

}
int check_tcp_connect(int sd, int sec)
{
    int error = -1, len;
    int ret = 0;
    struct timeval tm;

    len = sizeof(int);
    fd_set set;
    tm.tv_sec = sec;
    tm.tv_usec = 0;
    FD_ZERO(&set);
    FD_SET(sd, &set);
    if(select(sd + 1, NULL, &set, NULL, &tm) > 0)
    {   
        getsockopt(sd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
        if(error == 0)
        {
            ret = 1;
        }
        else
        {
            printf("Warnning: Tcp connect error:%d
", error);
            ret = 0;
        }
    }
    else
    {
        printf("Warnning: Wait TCP connect timeout!
");
        ret = 0;
    }
    return ret;
}

SO_KEEPALIVE 保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换(即服务器没有发送数据或者接收数据),TCP就自动给对方发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导致以下三种情况:

  • 对方接收一切正常:以期望的ACK响应。
  • 2小时后,TCP将发出另一个探测分节。对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET(recv返回0,errno为ECONNRESET),套接字本身则被关闭。
  • 对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到一个响应。在发出第一个探测分节11分钟15秒(9*75s)后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭。如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为EHOSTUNREACH。

如果在keepalive时间内对端未回复,read()返回0。对于tcp连接,如果一直在socket上有数据来往就不会触发keepalive,但是如果30秒一直没有数据往来,则keep alive开始工作:发送探测包,受到响应则认为网络是好的,结束探测;如果没有响应就每隔1秒发探测包,一共发送3次,3次后仍没有相应,则发送RST包关闭连接,也就是从网络开始到你的socket能够意识到网络异常,最多花33秒。但是如果没有设置keepalive,可能你在你的socket(阻塞性)的上面,接收: recv会一直阻塞不能返回,除非对端主动关闭连接,因为recv不知道socket断了。发送:取决于数据量的大小,只要底层协议栈的buffer能放下你的发送数据,应用程序级别的send就会一直成功返回。 直到buffer满,甚至buffer满了还要阻塞一段时间试图等待buffer空闲。所以你对send的返回值的检查根本检测不到失败。开启了keep alive功能,你直接通过发送接收的函数返回值就可以知道网络是否异常。

 转自:http://blog.csdn.net/rainharder/article/details/47022903

TCP的keep-alive可以在不增加服务器处理逻辑的前提下,检测客户端连接是否中断

/proc/sys/net/ipv4/tcp_keepalive_time 开始首次KeepAlive探测前的TCP空闭时间 
/proc/sys/net/ipv4/tcp_keepalive_intvl 两次KeepAlive探测间的时间间隔 
/proc/sys/net/ipv4/tcp_keepalive_probes 判定断开前的KeepAlive探测次数

对 于一个已经建立的tcp连接。如果在keepalive_time时间内双方没有任何的数据包传输,则开启keepalive功能的一端将发送 keepalive数据包,若没有收到应答,则每隔keepalive_intvl时间再发送该数据包,发送keepalive_probes次。一直没有 收到应答,则发送rst包关闭连接。若收到应答,则将计时器清零。

谁想定期检查连接状况,谁就启用keep alive。另一端可以不起,只是被动地对探测包进行响应,这种响应是tcp协议的基本要求,跟keep alive无关。并不需要客户端和服务器端都开启keep alive。

Remember that keepalive support, even if configured in the kernel, is not the default behavior in Linux. Programs must request keepalive control for their sockets using the setsockopt interface.

虽然是全局配置,但想要生效,还得程序各自开启

int keepalive = 1; // 开启keepalive属性
int keepidle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测
int keepinterval = 5; // 探测时发包的时间间隔为5 秒
int keepcount = 3; // 探测尝试的次数。如果第1次探测包就收到响应了,则后2次的不再发。
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive));
// 可配置各socket自己的keepalive参数
setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle));
setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval));
setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount));

Remember that keepalive is not program-related, but socket-related, so if you have multiple sockets, you can handle keepalive for each of them separately.

从上面的接口也可以看出,keepalive设置是socket相关的。 
另外这些属性是sockt继承的,即listen的套接字设置该属性后,后面建立连接后的accept 套接字同样继承该属性(心跳属性)。

import socket

HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
s.setsockopt(socket.SOL_TCP, socket.SO_KEEPIDEL, 10)
s.setsockopt(socket.SOL_TCP, socket.SO_KEEPINTVL, 3)
s.setsockopt(socket.SOL_TCP, socket.SO_KEEPCNT, 2)

conn, addr = s.accept()
print 'Connected by', addr
while 1:
    data = conn.recv(1024)
    if not data: break
    conn.sendall(data)
conn.close()

但是,tcp自己的keepalive有这样的一个bug: 

正常情况下,连接的另一端主动调用colse关闭连接,tcp会通知,我们知道了该连接已经关闭。但是如果tcp连接的另一端突然掉线,或者重启断电,这个时候我们并不知道网络已经关闭。而此时,如果有发送数据失败,tcp会自动进行重传。重传包的优先级高于keepalive,那就意味着,我们的keepalive总是不能发送出去。 而此时,我们也并不知道该连接已经出错而中断。在较长时间的重传失败之后,我们才会知道。

参考

http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/ 
http://blog.csdn.net/ctthuangcheng/article/details/8596818 
http://blog.csdn.net/ctthuangcheng/article/details/9450087

RFC 1122 - Requirements for Internet Hosts

原文地址:https://www.cnblogs.com/embedded-linux/p/6084133.html