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

博客 动态
0 254
优雅殿下
优雅殿下 2022-03-29 11:57:17
悬赏:0 积分 收藏

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 //效验算法(百度下有注释,但是还是看不太明白)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)    {        sum += *w++;        nleft -= 2;    }        if( nleft == 1)    {               *(unsigned char *)(&answer) = *(unsigned char *)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_in from;      // 设定Ip信息      bzero(&addr,sizeof(addr));      addr.sin_family = AF_INET;      addr.sin_addr.s_addr = inet_addr(ips);  #if 1    int 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((unsigned short *)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;          }      } #endif    return SUCCESS;}    int main(){#if 1    char cPing[16];    printf("Please input ping IP:");    scanf("%s",cPing);#else    char *cPing = "192.168.1.200";    #endif    if(ping(cPing,10000))    {        printf("Ping succeed!\n");    }    else    {        printf("Ping wrong!\n");    }    return 0;    }

 

实际效果:

 

补充说明:

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

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

 

posted @ 2022-03-29 11:17 xcywt 阅读(22) 评论(0) 编辑 收藏 举报
回帖
    优雅殿下

    优雅殿下 (王者 段位)

    2018 积分 (2)粉丝 (47)源码

    小小码农,大大世界

     

    温馨提示

    亦奇源码

    最新会员