本文改写自网上的一个程序,原始程序中为阻塞式调用,而且有进程创建的过程,非常不利于集成到自己程序中,因此对原始程序进行改造,使其可以完成发送一个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; }