Linux C++ 实现一个简易版的ping (也就是ICMP协议)

首页 / 新闻资讯 / 正文

想实现一个在没外网的时候就自动重启路由器的功能。

又不想用ping命令,因为在代码里调用system("ping"); 可能会比较耗时,得单开线程。于是找了个实现ICMP协议的代码。

参考:https://blog.csdn.net/qivan/article/details/7237051

#include <stdio.h> #include<sys/socket.h> #include<netinet/in.h> #include<netinet/ip.h> #include<netinet/ip_icmp.h> #include<arpa/inet.h> #include<sys/types.h> #include<sys/time.h> #include<unistd.h> #include<netdb.h> #include<string.h>#define PACKET_SIZE     4096#define ERROR           0#define SUCCESS         1//效验算法(百度下有注释,但是还是看不太明白) unsignedshort cal_chksum(unsignedshort *addr,int len) {int nleft=len;int sum=0;     unsignedshort *w=addr;     unsignedshort answer=0;while(nleft >1)     {         sum+= *w++;         nleft-=2;     }if( nleft ==1)     {*(unsignedchar *)(&answer) = *(unsignedchar *)w;         sum+= answer;     }          sum= (sum >>16) + (sum &0xffff);     sum+= (sum >>16);     answer= ~sum;return answer; }// Ping函数int ping(char *ips,int timeout)   {struct timeval *tval;int maxfds =0;       fd_set readfds;struct sockaddr_in addr;struct sockaddr_infrom;// 设定Ip信息     bzero(&addr,sizeof(addr));       addr.sin_family= AF_INET;        addr.sin_addr.s_addr= inet_addr(ips);#if 1int sockfd;// 取得socket  。  如果没加sudo 这里会报错     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);if (sockfd <0)       {           printf("ip:%s,socket error\n",ips);return ERROR;       }struct timeval timeo;// 设定TimeOut时间     timeo.tv_sec = timeout /1000;       timeo.tv_usec= timeout %1000;if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeo,sizeof(timeo)) == -1)       {           printf("ip:%s,setsockopt error\n",ips);return ERROR;       }char sendpacket[PACKET_SIZE];char recvpacket[PACKET_SIZE];// 设定Ping包     memset(sendpacket,0,sizeof(sendpacket));            pid_t pid;// 取得PID,作为Ping的Sequence ID     pid=getpid();struct ip *iph;struct icmp *icmp;               icmp=(struct icmp*)sendpacket;       icmp->icmp_type=ICMP_ECHO;//回显请求     icmp->icmp_code=0;       icmp->icmp_cksum=0;       icmp->icmp_seq=0;       icmp->icmp_id=pid;      tval= (struct timeval *)icmp->icmp_data;       gettimeofday(tval,NULL);       icmp->icmp_cksum=cal_chksum((unsignedshort *)icmp,sizeof(struct icmp));//校验int n;// 发包 。可以把这个发包挪到循环里面去。     n = sendto(sockfd, (char *)&sendpacket,sizeof(struct icmp),0, (struct sockaddr *)&addr,sizeof(addr));if (n <1)       {           printf("ip:%s,sendto error\n",ips);return ERROR;       }// 接受// 由于可能接受到其他Ping的应答消息,所以这里要用循环while(1)       {// 设定TimeOut时间,这次才是真正起作用的         FD_ZERO(&readfds);           FD_SET(sockfd,&readfds);           maxfds= sockfd +1;           n=select(maxfds, &readfds, NULL, NULL, &timeo);if (n <=0)           {                       printf("ip:%s,Time out error\n",ips);               close(sockfd);return ERROR;           }// 接受         memset(recvpacket,0,sizeof(recvpacket));int fromlen =sizeof(from);           n= recvfrom(sockfd, recvpacket,sizeof(recvpacket),0, (struct sockaddr *)&from, (socklen_t *)&fromlen);       printf("recvfrom Len:%d\n",n);if (n <1)      {return ERROR;           }char *from_ip = (char *)inet_ntoa(from.sin_addr);// 判断是否是自己Ping的回复if (strcmp(from_ip,ips) !=0)           {               printf("NowPingip:%s Fromip:%s NowPingip is not same to Fromip,so ping wrong!\n",ips,from_ip);return ERROR;         }                    iph= (struct ip *)recvpacket;                    icmp=(struct icmp *)(recvpacket + (iph->ip_hl<<2));                    printf("ip:%s,icmp->icmp_type:%d,icmp->icmp_id:%d\n",ips,icmp->icmp_type,icmp->icmp_id);// 判断Ping回复包的状态if (icmp->icmp_type == ICMP_ECHOREPLY && icmp->icmp_id == pid)//ICMP_ECHOREPLY回显应答        {// 正常就退出循环         printf("icmp succecss .............  \n");break;           }else           {// 否则继续等continue;           }       }#endifreturn SUCCESS; }int main() {#if 1char cPing[16];     printf("Please input ping IP:");     scanf("%s",cPing);#elsechar *cPing ="192.168.1.200";#endifif(ping(cPing,10000))     {         printf("Ping succeed!\n");     }else     {         printf("Ping wrong!\n");     }return0;     }

Linux  C++  实现一个简易版的ping (也就是ICMP协议)

0)直接用参考链接上的代码时编译不过,不知道是不是因为我用的是cpp,没太深究。

1)实际使用的时候需要加上sudo,不然在创建套接字那个地方会报错。我还没想好怎么在代码里用sudo,(因为实际项目运行起来是不需要加sudo的)。