首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

Linux系统停的设置TCP心跳机制Keepalive为什么总是无效果

2013-08-09 
Linux系统下的设置TCP心跳机制Keepalive为什么总是无效果最近在Linux系统下的研究心跳包机制,遍了一个简单

Linux系统下的设置TCP心跳机制Keepalive为什么总是无效果
最近在Linux系统下的研究心跳包机制,遍了一个简单的在服务器端测试客户端连接的例子,具体内容是客户端在for循环里发送字符串给服务器端,服务端接收后,再将字符串发送给客户端,由客户端显示出来。可不知怎么的拔掉网线后,没有效果。再插上网线后程序有时候继续运行,求这方面的高手解答帮忙。
服务端代码如下:


#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<netinet/tcp.h>

#define SERVER_PORT 3333
#define LENGTH_OF_LISTEN_QUEUE 20
#define MAXBUF 200
using namespace std;
int EstablishServer(struct sockaddr_in * addr,socklen_t addrLen,int port);
void * HeartService(void * arg);
void setnonblocking(int sock);
void socketkeepalive(int sockfd);
int main()
{
    struct sockaddr_in server_addr,client_addr;
    int serverFd,clientFd;
    socklen_t clientLen=sizeof(struct sockaddr);
    //建立socket连接
    serverFd=EstablishServer(&server_addr,sizeof(server_addr),SERVER_PORT);
    if(serverFd==-1)
    {
        cout<<"serverFd create failed!"<<endl;
        exit(1);
    }
    for(;;)
    {
        //等待连接的请求
        if((clientFd=accept(serverFd,(struct sockaddr *)&client_addr,&clientLen))<0)
        {
            perror("accept wrong !:");
            exit(1);
        }
        cout<<"----------------------------"<<endl;
        printf("accept from %s:%d\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port);
        pthread_t clientTid;
        int pthrst=pthread_create(&clientTid,NULL,HeartService,(void *)&clientFd);
        if(pthrst!=0)
        {
            perror("pthread_create wrong ! :");


        }
        
    }
    return 0;
}
//建立服务端socket的通信,绑定端口
int EstablishServer(struct sockaddr_in * addr,socklen_t addrLen,int port)
{
    int listenfd=socket(AF_INET,SOCK_STREAM,0);
    if(listenfd<0)
    {
        perror("listenfd socket error");
        return -1;
    }
    bzero(addr,addrLen);
    addr->sin_family=AF_INET;
    addr->sin_addr.s_addr=htonl(INADDR_ANY);
    addr->sin_port=htons(port);
    if(bind(listenfd,(struct sockaddr *)addr,addrLen)<0)
    {
        perror("bind error");
        return -1;
    }
    if(listen(listenfd,LENGTH_OF_LISTEN_QUEUE)<0)
    {
        perror("listen error");
        return -1;
    }
    return listenfd;
}
//心跳机制线程
void *HeartService(void * arg)
{
    //获得自身的线程号
    pthread_t tid=pthread_self();
    //获得传送过来的clientFd
    int clientFd=*(int *)arg;
    cout<<"New thread create,the tid is "<<tid<<" the clientFd is "<<clientFd<<endl;
    char buf[MAXBUF];
    int n,m,count=0;
    socketkeepalive(clientFd);
    for(;;)
    {
                if((n=recv(clientFd,buf,MAXBUF,0))<0)
                {
                    if(errno==ECONNRESET)//表示连接被重置了,已经无效了,关闭它,删除它
                    {
                        printf("It is ECONNRESET:%s\n",strerror(ECONNRESET));


                        close(clientFd);
                    }
                    else if(errno==ETIMEDOUT)
                    {
                        cout<<"Ha Ha you want ETIMEDOUT"<<endl;
                        close(clientFd);
                    }
                    else
                    {
                        perror("read error!");  //其他错误
                       close(clientFd);
       exit(1);
                    }     
                   
                }
                else if(n==0)//有读事件触发,但是read返回0,所以是对面已经关闭sockfd了
                {
                    cout<<"n=0 ,clientFd is "<<clientFd<<" and tid is "<<tid<<endl;
                    close(clientFd);
                }
                else
                {


                    buf[n]='\0';
                    cout<<"1: received success,buf: "<<buf<<endl;
                }
                m=strlen(buf);
                if(write(clientFd,buf,m)<0)
                {
                    cout<<"write wrong ! and sockfd is:"<<clientFd<<endl;
                }
                else
                {
                    cout<<"2: send success,buf:  "<<buf<<endl;
                }
    }   
}
void socketkeepalive(int sockfd)
{
    int keepAlive=1;//开启keepalive属性
    int keepIdle=3;//如该连接在5秒内没有任何数据往来,则进行探测
    int keepInterval=2;//探测时发包的时间间隔为2秒
    int keepCount=3;//探测尝试的次数。如果第1次探测包就收到响应了,则后2次的不再发送
    if(setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(void *)&keepAlive,sizeof(keepAlive))!=0)//若无错误发生,setsockopt()返回值为0
    {
        cout<<"It is SO_KEEPALIVE WRONG!"<<endl;
        exit(1);
    }
    if(setsockopt(sockfd,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle))!=0)
    {
        cout<<"It is TCP_KEEPIDLE WRONG!"<<endl;
        exit(1);
    }
    if(setsockopt(sockfd,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval))!=0)
    {
        cout<<"It is TCP_KEEPIDLE WRONG!"<<endl;


        exit(1);
    }
    if(setsockopt(sockfd,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount))!=0)
    {
        cout<<"It is TCP_KEEPCNT WRONG!"<<endl;
        exit(1);
    }
}


客户端代码如下:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<netinet/in.h>
#include<string.h>
#include<netdb.h>
#include<unistd.h>
#include<time.h>
#include<errno.h>
#include<fcntl.h>
#include<netinet/tcp.h>
#include<string.h>
#define SERVPORT 3333
#define MAXDATASIZE 200
using namespace std;
void sockfdalive(int sockfd);
void setnonblocking(int sock);
int main(int argc,char *argv[]){
    int sockfd,recvbytes;
    struct hostent *host;
    struct sockaddr_in serv_addr;
    char str[MAXDATASIZE];
    char tempbuf[MAXDATASIZE];
    char num[12];
    int lenstr;
    int srcount=0;
    if(argc<2){
        fprintf(stderr,"Please enter the server's hostname!\n");
        exit(1);
    }
    if((host=gethostbyname(argv[1]))==NULL)
    {
        herror("gethostbyname wrong !");
        exit(1);
    }

    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socket create wrong !");
        exit(1);
    }
    bzero(&serv_addr,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(SERVPORT);
    serv_addr.sin_addr=*((struct in_addr *)host->h_addr);
    if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1)
    {
        perror("connect wrong !");


        exit(1);
    }
    sockfdalive(sockfd);//设置保活机制
    for(;;)
    {
        //sleep(1);
                srcount++;
                sprintf(tempbuf,"Hello heartactive! %d",srcount);
                lenstr=strlen(tempbuf);
                if(send(sockfd,tempbuf,lenstr,0)==-1)//发送
                {
                    perror("send wrong !");
                    exit(1);
                }
                cout<<"1 send: "<<tempbuf<<endl;
                recvbytes=recv(sockfd,str,MAXDATASIZE,0);//接收
                if(recvbytes<0)
                    {
                        if(errno==ECONNRESET)
                        {
                               printf("It is ECONNRESET:%s\n",strerror(ECONNRESET));
                               exit(1);
                         }
                        else if(errno==ETIMEDOUT)
                         {


                           cout<<"HAHA client is ETIMEDOUT!"<<endl;
                          exit(1);
                          }
                         else
                         {
                            perror("recv wrong !");
                           exit(1);
                         }     
                        
                    }
                  else if(recvbytes==0)
                    {
                        cout<<"recvbytes=0"<<endl;
                        exit(1);
                    }
                    else
                    {
                       str[recvbytes]='\0';
                       cout<<"2 received: "<<str<<endl;


                    }
        }
    close(sockfd);
}
void sockfdalive(int sockfd){
    int keepAlive=1;
    int keepIdle=3;
    int keepInterval=2;
    int keepCount=3;
    if(setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(void *)&keepAlive,sizeof(keepAlive))!=0)
    {
        cout<<"It is SO_KEEPALIVE WRONG!"<<endl;
        exit(1);
    }
    if(setsockopt(sockfd,SOL_TCP,TCP_KEEPIDLE,(void *)&keepIdle,sizeof(keepIdle))!=0)
    {
        cout<<"It is TCP_KEEPIDLE WRONG!"<<endl;
        exit(1);
    }
    if(setsockopt(sockfd,SOL_TCP,TCP_KEEPINTVL,(void *)&keepInterval,sizeof(keepInterval))!=0)
    {
        cout<<"It is TCP_KEEPIDLE WRONG!"<<endl;
        exit(1);
    }
    if(setsockopt(sockfd,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount))!=0)
    {
        cout<<"It is TCP_KEEPCNT WRONG!"<<endl;
        exit(1);
    }    
}


端口号是3333,求解答 Linux TCP/IP Keepalive心跳机制保活机制
[解决办法]
引用:
我先顶一下啊,按理说设置了Keepalive之后,拔掉网线后,服务端recv()函数返回值应该小于0并置errno==ETIMEDOUT。可是不是啊,客户端也不是。

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



看看这个
http://blog.csdn.net/ctthuangcheng/article/details/8596818
[解决办法]

引用:
Quote: 引用:

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

看看这个
http://blog.csdn.net/ctthuangcheng/article/details/8596818
额 可是当拔掉网线后,服务端在一定时间后就启用探测包,连续几次都没收到客户端回应,就将套接口的待处理错误置为ETIMEDOUT,套接口本身则被关闭。你看看这个对不对:http://blog.csdn.net/taolinke/article/details/6981345



如果一直在socket上有数据来往就不会触发keepalive,但是如果30秒一直没有数据往来,则keep alive开始工作:发送探测包,受到响应则认为网络,是好的,结束探测;如果没有相应就每隔1秒发探测包,一共发送3次,3次后仍没有相应,则发送RST包关闭连接,也就是从网络开始到你的socket能够意识到网络异常,最多花33秒。但是如果没有设置keep alive,可能你在你的socket(阻塞性)的上面,接收: recv会一直阻塞不能返回,除非对端主动关闭连接,因为recv不知道socket断了。发送:取决于数据量的大小,只要底层协议栈的buffer能放下你的发送数据,应用程序级别的send就会一直成功返回。 直到buffer满,甚至buffer满了还要阻塞一段时间试图等待buffer空闲。所以你对send的返回值的检查根本检测不到失败。开启了keep alive功能,你直接通过发送接收的函数返回值就可以知道网络是否异常
 正常情况下,连接的另一端主动调用colse关闭连接,tcp会通知,我们知道了该连接已经关闭。但是如果tcp连接的另一端突然掉线,或者重启断电,这个时候我们并不知道网络已经关闭。而此时,如果有发送数据失败,tcp会自动进行重传。重传包的优先级高于keepalive,那就意味着,我们的keepalive总是不能发送出去。 而此时,我们也并不知道该连接已经出错而中断。在较长时间的重传失败之后,我们才会知道。

热点排行