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

2022年5月30日11:29:29

背景:

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

又不想用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的)。

  • 作者:xcywt
  • 原文链接:https://www.cnblogs.com/xcywt/p/16070814.html
    更新时间:2022年5月30日11:29:29 ,共 3237 字。