对于heartbeat检测,常用的是在通迅协议上面做,比如pgpool,一台机器会定时向另一台新建一个联接,写和读一些特定字符,之后关闭这个检测用的连接,能连上表明对方没有挂机

 

对于通过TCP/IP本身这样做,网上win平台下面的例子比较多,相对LINUX,MS确实增加了一些特定的函数来检测状态,LINUX下面就没有特定的函数这样做了

 

下面这部分代码就是通过TCP/IP本身客户端检测服务器是否挂掉,挂掉的意义就是拔网线或断电,测试时要在两台不同的机器上,或是两台虚拟机上,同一台的话,测不出来

 

代码目的是 使用TCP/IP本身 SO_KEEPALIVE 来检测,不需要辅助协议的支持 当服务器意外挂掉后,客户端能检测出来

 代码是演示原理用的,实际使用请自已封装,

 

主要参考了

http://www.tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/


 

服务器端代码,这个没什么可说的

#include<stdio.h> 
#include<stdlib.h> 
#include<sys/socket.h> 
#include<error.h> 
#include<string.h> 
#include<sys/types.h> 
#include<netinet/in.h> 
#include<sys/wait.h> 
#include<arpa/inet.h> 
#include<unistd.h> 

#define SERVPORT 6123 //设定服务器服务端口
#define MAX_LISTEN_SOCK_NUM 20 //设定可监听套接字的最大个数为20 
int main() 
{ 
//sockfd为本地监听套接字标识符,client_fd为客户端套接字标识符 
int sockfd,client_fd; 
struct sockaddr_in my_addr; 
struct sockaddr_in client_addr; 
int i;
//创建本地监听套接字 
if((sockfd=socket(/*AF_INET,SOCK_STREAM,0*/PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1){ 
perror("套接字创建失败!/n"); 
exit(1); 
} 

//设置套接字的属性使它能够在计算机重启的时候可以再次使用套接字的端口和IP 
int err,sock_reuse=1; 
//err=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&sock_reuse,sizeof(sock_reuse)); 
//if(err!=0){ 
//printf("套接字可重用设置失败!/n"); 
//exit(1); 
//} 


int opt = 1; 
socklen_t len=sizeof(int);


my_addr.sin_family=AF_INET; 
my_addr.sin_port=htons(SERVPORT); 
my_addr.sin_addr.s_addr=INADDR_ANY; 
bzero(&(my_addr.sin_zero),8); 
//绑定套接字 
if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1){ 
perror("绑定失败!/n"); 
exit(1); 
} 
//设置监听 
if((listen(sockfd,MAX_LISTEN_SOCK_NUM))==-1){ 
perror("设置监听失败!/n"); 
exit(1); 
} 
printf("套接字进入监听状态,等待请求连接:/n"); 


while(1){ 

//有连接请求时进行连接 
socklen_t sin_size=sizeof(struct sockaddr_in); 
if((client_fd=accept(sockfd,(struct sockaddr *)&client_addr,&sin_size))==-1){ 
perror("接受连接失败!/n"); 
continue; 
} 


 
if((getsockopt(client_fd,SOL_SOCKET,SO_KEEPALIVE,(char*)&opt,&len))==0){ 
printf("SO_KEEPALIVE Value: %d/n", opt); 
} 
printf("接到一个来自%s的连接/n",inet_ntoa(client_addr.sin_addr)); 
//创建子进程来处理已连接的客户端套接字 

for(i=0; i<3; i++){

	if(send(client_fd,"您好,您已经连接成功!/n",50,0)==-1){ 
	perror("发送通知信息失败!/n"); 
	exit(0); 
	}
	else
		{
			printf("成功: %d/n", i);
			}
	sleep(10);
}

while(1)
{
	if(send(client_fd,"abcdef",6,0)==-1){ 
	perror("发送通知信息失败!/n"); 
	exit(0); 
	}
	else
		{
			printf("成功: %d/n", i);
		}
		i++;
} 
	 
close(client_fd); 
}

return 0; 
}

 

 

客户端代码,和一般的客户端不一样

#include<stdio.h> 
#include<stdlib.h> 
#include<error.h> 
#include<string.h> 
#include<netdb.h> 
#include<sys/types.h> 
#include<netinet/in.h> 
#include<sys/socket.h> 
#include<unistd.h> 
#include<arpa/inet.h> 
#include <netinet/tcp.h>

#include <errno.h>


#define MAXDATASIZE 100 
int main() 
{ 
int sockfd,nbytes,serv_port; 
char buf_serv_ip[16],buf[260]; 
struct sockaddr_in serv_addr; 



if((sockfd=socket(/*AF_INET,SOCK_STREAM,0*/PF_INET, SOCK_STREAM, IPPROTO_TCP))==-1){ 
perror("创建套接字失败!/n"); 
exit(1); 
} 


//创建套接字成功后设置其可重用的属性 
int KeepAlive=1; 
socklen_t KPlen=sizeof(int); 




if(setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char *)&KeepAlive,KPlen)!=0){ 
perror("设置周期测试连接是否仍存活失败!/n"); 
exit(1); 
} 

int keepIdle = 10;//每次检测的相隔时间
int keepInterval = 1;//检测前的空间时间
int keepCount = 3;//最大检测次数
setsockopt(sockfd,SOL_TCP,TCP_KEEPINTVL,(void *)&keepIdle,sizeof(keepIdle));
setsockopt(sockfd,SOL_TCP,TCP_KEEPIDLE,(void *)&keepInterval,sizeof(keepInterval));
setsockopt(sockfd,SOL_TCP,TCP_KEEPCNT,(void *)&keepCount,sizeof(keepCount)); 



printf("请输入要连接主机的IP地址:/n"); 
scanf("%s",buf_serv_ip); 
printf("请输入要连接主机的端口号:/n"); 
scanf("%d",&serv_port); 


serv_addr.sin_family=AF_INET; 
serv_addr.sin_addr.s_addr=inet_addr(buf_serv_ip); 
serv_addr.sin_port=htons(serv_port); 
bzero(&(serv_addr.sin_zero),8); 
if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){ 
perror("连接服务器失败!/n"); 
exit(1); 
} 



if((getsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char*)&KeepAlive,&KPlen))==0){ 
printf("SO_KEEPALIVE Value: %d/n", KeepAlive); 
} 



printf("连接服务器成功!/n"); 
//在此处可以先接受判断将要接受数据的长度再创建数组 


int i =0;
int res = 0;
int errcode;
fd_set	readmask;
struct timeval	timeout;

while(1){
	
	FD_ZERO(&readmask);
		FD_SET(sockfd, &readmask);
		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
	
	
	if ((res = select(sockfd + 1, &readmask, NULL, NULL, &timeout)) < 0)
		{
			printf("the socket is error/n");
			exit(1); 
		}
		
		printf("select return %d/n", res);
		
		if(res == 0)
		{//检测连接是否超时,可能服务器长时间没发数据
			int sockfdtemp;
			errno = 0;
			printf("连接超时/n");
			
			/*
			if((sockfdtemp=socket(AF_INET,SOCK_STREAM,0))==-1){ 
				perror("超时接字失败!/n"); 
				exit(1); 
			}
			
			if(connect(sockfdtemp,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){ 
				printf("超时连接服务器失败!/n"); 
				 errcode = errno;u
         //close(sockfdtemp);
          if (errcode != ENOENT && errcode != ECONNREFUSED) {
              printf("Failed to establish connection: %d/n", errcode);
              
          }
				//continue;
				//exit(1); 
			}else
				{
					printf("服务器没挂!/n"); 
         //close(sockfdtemp);
					
					}  
         close(sockfdtemp);	
         */
       
         continue;
				
		}
		

		/* 处理Slave端连接请求 */
		if( (sockfd != -1 ) && FD_ISSET(sockfd, &readmask) )
		{
				printf("the socket is ok/n");
				memset(buf, 0, 260);
				errno = 0;
				if((nbytes=recv(sockfd,buf,260,0))==-1){ 
				printf("接受数据失败!/n"); 
				errcode = errno;
				if(errcode == ETIMEDOUT)
				{//服务器真正的挂掉
					printf("******服务器挂掉 %d : %d /n", errno, ETIMEDOUT);
					exit(1);
				} 
				}
				if (nbytes == 0)
				{
					printf("数据为空: %d/n", i);
				}
				else
				{
					buf[nbytes]='/0'; 
					printf("接受的数据为:%d/n", i);
				}
				
		}else
		{
				printf("the socket is not OK/n");
				exit(1); 
				
			}
	
	
	i++; 
}

close(sockfd); 
return 0; 
}