本文改写自网上的一个程序,原始程序中为阻塞式调用,而且有进程创建的过程,非常不利于集成到自己程序中,因此对原始程序进行改造,使其可以完成发送一个imcp包的方式来判断网络连通,只需要调用改进后的

 bool NetIsOK()

函数即可,该函数返回true即表示网络状态良好,否则表示网络状态不连同,本程序中只发送了一个icmp包,在实际应用中可以根据需要改进为发送多个imcp包。

修改之后的程序为:只需要调用函数NetIsOK()即可。源码如下所示:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netdb.h>
#include <errno.h>
#define MAX_WAIT_TIME   1
#define MAX_NO_PACKETS  1
#define ICMP_HEADSIZE 8 
#define PACKET_SIZE     4096
struct timeval tvsend,tvrecv;	
struct sockaddr_in dest_addr,recv_addr;
int sockfd;
pid_t pid;
char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
 
//函数定义
void timeout(int signo);
unsigned short cal_chksum(unsigned short *addr,int len);
int pack(int pkt_no,char *sendpacket);
int send_packet(int pkt_no,char *sendpacket);
int recv_packet(int pkt_no,char *recvpacket);
int unpack(int cur_seq,char *buf,int len);
void tv_sub(struct timeval *out,struct timeval *in);
void _CloseSocket();
 
bool NetIsOk()
{     
		
	double rtt;
	struct hostent *host;
	struct protoent *protocol;
	int i,recv_status;
 
#ifdef _USE_DNS //如果定义该宏,则可以使用域名进行判断网络连接,例如www.baidu.com
	/* 设置目的地址信息 */
	char hostname[32];
	sprintf(hostname,"%s","www.baidu.com")
	bzero(&dest_addr, sizeof(dest_addr));
	dest_addr.sin_family = AF_INET;	
 
	if((host=gethostbyname(hostname))==NULL) 
	{
		printf("[NetStatus]  error : Can't get serverhost info!\n");
		return false;
	}
 
	bcopy((char*)host->h_addr,(char*)&dest_addr.sin_addr,host->h_length);
#else //如果不使用域名,则只能用ip地址直接发送icmp包,例如谷歌的地址:8.8.8.8
	dest_addr.sin_addr.s_addr = inet_addr("8.8.8.8");
#endif
	
 
	if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) 
	{	/* 创建原始ICMP套接字 */
		printf("[NetStatus]  error : socket");
		return false;
	}
 
	int iFlag;
	if(iFlag = fcntl(sockfd,F_GETFL,0)<0)
	{
		printf("[NetStatus]  error : fcntl(sockfd,F_GETFL,0)");
		_CloseSocket();
		return false;
	}
	iFlag |= O_NONBLOCK;
	if(iFlag = fcntl(sockfd,F_SETFL,iFlag)<0)
	{
		printf("[NetStatus]  error : fcntl(sockfd,F_SETFL,iFlag )");
		_CloseSocket();
		return false;
	}
 
	pid=getpid();
	for(i=0;i<MAX_NO_PACKETS;i++)
	{		
	
		if(send_packet(i,sendpacket)<0)
		{
			printf("[NetStatus]  error : send_packet");
			_CloseSocket();
			return false;
		}	
 
		if(recv_packet(i,recvpacket)>0)
		{
			_CloseSocket();
			return true;
		}
		
	} 
	_CloseSocket();     	
	return false;
}
 
 
 
int send_packet(int pkt_no,char *sendpacket)
{    
	int packetsize;       
	packetsize=pack(pkt_no,sendpacket); 
	gettimeofday(&tvsend,NULL);    
	if(sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr) )<0)
	{      
		printf("[NetStatus]  error : sendto error");
		return -1;
	}
	return 1;
}
 
 
int pack(int pkt_no,char*sendpacket)
{       
	int i,packsize;
	struct icmp *icmp;
	struct timeval *tval;
	icmp=(struct icmp*)sendpacket;
	icmp->icmp_type=ICMP_ECHO;   //设置类型为ICMP请求报文
	icmp->icmp_code=0;
	icmp->icmp_cksum=0;
	icmp->icmp_seq=pkt_no;
	icmp->icmp_id=pid;			//设置当前进程ID为ICMP标示符
	packsize=ICMP_HEADSIZE+sizeof(struct timeval);
	tval= (struct timeval *)icmp->icmp_data;
	gettimeofday(tval,NULL);
	icmp->icmp_cksum=cal_chksum( (unsigned short *)icmp,packsize); 
	return packsize;
}
 
 
unsigned short cal_chksum(unsigned short *addr,int len)
{       
	int nleft=len;
	int sum=0;
	unsigned short *w=addr;
	unsigned short answer=0;
	while(nleft>1)		//把ICMP报头二进制数据以2字节为单位累加起来
	{       
		sum+=*w++;
		nleft-=2;
	}
	if( nleft==1)		//若ICMP报头为奇数个字节,会剩下最后一字节.把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加
	{
		*(unsigned char *)(&answer)=*(unsigned char *)w;
		sum+=answer;
	}
	sum=(sum>>16)+(sum&0xffff);
	sum+=(sum>>16);
	answer=~sum;
	return answer;
}
 
 
int recv_packet(int pkt_no,char *recvpacket)
{       	
	int n,fromlen;
	fd_set rfds;
	FD_ZERO(&rfds);
	FD_SET(sockfd,&rfds);
	signal(SIGALRM,timeout);
	fromlen=sizeof(recv_addr);
	alarm(MAX_WAIT_TIME);
	while(1)
	{
		select(sockfd+1, &rfds, NULL, NULL, NULL);
		if (FD_ISSET(sockfd,&rfds))
		{  
			if( (n=recvfrom(sockfd,recvpacket,PACKET_SIZE,0,(struct sockaddr *)&recv_addr,&fromlen)) <0)
    		{   
			if(errno==EINTR)
				return -1;
				perror("recvfrom error");
				return -2;
      		}
		}
		gettimeofday(&tvrecv,NULL); 
		if(unpack(pkt_no,recvpacket,n)==-1)
			continue;
		return 1;
	}
}
 
int unpack(int cur_seq,char *buf,int len)
{    
	int iphdrlen;
	struct ip *ip;
	struct icmp *icmp;
	ip=(struct ip *)buf;
	iphdrlen=ip->ip_hl<<2;		//求ip报头长度,即ip报头的长度标志乘4
	icmp=(struct icmp *)(buf+iphdrlen);		//越过ip报头,指向ICMP报头
	len-=iphdrlen;		//ICMP报头及ICMP数据报的总长度
	if( len<8)
		return -1;       
	if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) && (icmp->icmp_seq==cur_seq))
		return 0;	
	else return -1;
}
 
 
void timeout(int signo)
{
	printf("Request Timed Out\n");
}
 
void tv_sub(struct timeval *out,struct timeval *in)
{       
	if( (out->tv_usec-=in->tv_usec)<0)
	{       
		--out->tv_sec;
		out->tv_usec+=1000000;
	}
	out->tv_sec-=in->tv_sec;
}
 
void _CloseSocket()
{
	close(sockfd);
	sockfd = 0;
}

 

Logo

更多推荐