本文改寫自網上的一個程序,原始程序中爲阻塞式調用,而且有進程創建的過程,非常不利於集成到自己程序中,因此對原始程序進行改造,使其可以完成發送一個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;
}