DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  4737 随笔 :: 2 文章 :: 542 评论 :: 1615万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

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


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((unsignedshort*)icmp,packsize);
return packsize;
}


unsignedshort cal_chksum(unsignedshort*addr,int len)
{
int nleft=len;
int sum=0;
unsignedshort*w=addr;
unsignedshort answer=0;
while(nleft>1)//把ICMP报头二进制数据以2字节为单位累加起来
{
sum+=*w++;
nleft-=2;
}
if( nleft==1)//若ICMP报头为奇数个字节,会剩下最后一字节.把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加
{
*(unsignedchar*)(&answer)=*(unsignedchar*)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;
return1;
}
}

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))
return0;
elsereturn-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;
}

posted on   DoubleLi  阅读(928)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2015-03-30 tcpdump 抓包让wireshark来分析
2015-03-30 wireshark怎么抓包、wireshark抓包详细图文教程
2015-03-30 Linux调试工具
2013-03-30 js操作DOM入门
2012-03-30 HTTP 错误 404 - 文件或目录未找到的最终解决方法
2012-03-30 CSS 有关Position = absolute (绝对定位 是相对于谁而言)
点击右上角即可分享
微信分享提示