在Linux环境下使用TCP的keepalive机制

Linux内置支持keepalive机制,为了使用它,你须要使能TCP/IP网络,为了可以配置内核在执行时的參数。你还须要procfs和sysctl的支持。

这个过程涉及到keepalive使用的三个用户驱使的变量:

tcp_keepalive_time:表示的是近期一次数据包(简单的不含数据的ACKs包)发送与第一次keepalive探针发送之间的时间间隔;当连接被标记为keepalive之后。这个计数器就不会再使用。

tcp_keepalive_intvl:表示的是并发keepalive探针之间的时间间隔。

tcp_keepalive_probes:在确定连接已经断开而且通知应用层之前所发送的没有得到回复的探针数。

对于这三个參数能够在Linux系统的终端中查看和改动它们的缺省值:

查看三个參数的值:

[root@Server3 ~]# cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
[root@Server3 ~]# cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
[root@Server3 ~]# cat /proc/sys/net/ipv4/tcp_keepalive_probes
9

通过命令对这三个參数值进行改动(图中将三个參数值分别设为:600、60、20):

[root@Server3 ~]# echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time
[root@Server3 ~]# echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl
[root@Server3 ~]# echo 6 > /proc/sys/net/ipv4/tcp_keepalive_probes

这样的方式重置三个參数值,在系统重新启动后三个參数的值又会恢复到默认值,详细怎样让系统永远记住自己设置的值,可參考其它资料,我们如今关系的是怎样在程序中使用keepalive机制并设置这三个參数的值。

在程序中使用keepalive机制

想在程序中使用这样的机制,仅仅须要使用setsockopt()函数。

setsockopt()函数用于随意类型、随意状态套接口的设置选项值。虽然在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。

选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据能否够从套接口发送等等。

以下为setsockopt()函数的原型:

#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
參数:  
sock:将要被设置或者获取选项的套接字。
level:选项所在的协议层。


optname:须要訪问的选项名。
optval:对于getsockopt()。指向返回选项值的缓冲。对于setsockopt()。指向包括新选项值的缓冲。


optlen:对于getsockopt()。作为入口參数时,选项值的最大长度。作为出口參数时。选项值的实际长度。

对于setsockopt()。现选项的长度。


为了使用函数setsockopt()将某个特定的套接字的keepalive机制打开,參数s是一个socket文件描写叙述符。必需要在这之前使用socket()函数进行创建。參数level必须设置为SOL_SOCKET;第三个參数必须设置为SO_KEEPALIVE。optval參数必须是一个布尔型整型变量,表示想要使能这个选项;最后一个參数表示第四个參数的大小。

程序实现心跳包检測机制

首先要在备份机和源机之间建立一个专门的socket链路来进行心跳检測。

在源机端,在进行数据迁移之前,会建立一个socket来监听备份机的连接。并将这个socket和相应的处理函数放入原软件的io处理列表中。代码例如以下:

int listenfd;
    struct sockaddr_in server_sin;

    /* establish socket */
    listenfd=socket(AF_INET,SOCK_STREAM,0);
    server_sin.sin_family=AF_INET;
    server_sin.sin_addr.s_addr=htonl(INADDR_ANY);
    server_sin.sin_port=htons(PORT);
    bind(listenfd,(struct sockaddr *)&server_sin,sizeof(server_sin));
    /* establish end */

    listen(listenfd,1024);

    qemu_set_fd_handler2(listenfd, NULL, tcpkeepalive_server, NULL,
                         (void *)(intptr_t)listenfd);

该socket相应的处理函数例如以下:

static void tcpkeepalive_server(void *opaque)
{
    int connfd;
    struct sockaddr_in client_sin;
    socklen_t client_len=sizeof(client_sin);
    int listenfd = (intptr_t)opaque;

    connfd=accept(listenfd,(struct sockaddr *)&client_sin,&client_len);
}

在备份机端,当其開始作为备份机时,会建立socket连接源机的监听端。并设置相应的tcpkeepalive參数。然后将socket和相应的处理函数增加io处理列表。

我们建立的socket是一个心跳检測专用链路。其上不会有数据流动,仅仅有一种情况备份机端会收到数据,那就是源端出现了故障。tcpkeepalive机制会返回一个错误信息,所以捕捉到了这个信息,备份机就会跳转到相应的处理函数,接替源机開始执行。

相应代码例如以下:

int sockfd;
    struct sockaddr_in sin;

    int optval;
    socklen_t optlen = sizeof(optval);

    sockfd=socket(AF_INET,SOCK_STREAM,0);
    sin.sin_family=AF_INET;
    sin.sin_addr.s_addr=addr.sin_addr.s_addr;
    sin.sin_port=htons(PORT);


    optval = 1;
    setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen);

    optval = 5;
    setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &optval, optlen);

    optval = 1;
    setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen);

    optval = 1;
    setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, &optval, optlen);

    connect(sockfd,(struct sockaddr *)&sin,sizeof(sin));

    qemu_set_fd_handler2(sockfd, NULL, tcpkeepalive_vm_start, NULL,
                         (void *)(intptr_t)sockfd);

该socket相应的处理函数非常easy,就是让备份机開始执行:

static void tcpkeepalive_vm_start(void *opaque)
{
    vm_start();
}








posted @ 2017-07-13 16:36  yxysuanfa  阅读(8418)  评论(0编辑  收藏  举报