首页程序设计LinuxC程序linux C 实现ping功能

linux C 实现ping功能

时间2023-09-17 15:13:56发布caterwang分类LinuxC程序浏览3653

核心代码引自:Linux C++ 实现一个简易版的ping (也就是ICMP协议)-腾讯云开发者社区-腾讯云 (tencent.com)

以下为我实际使用的代码

#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> 

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函数,timeout为超时时间,单位是ms,我实际用时默认10000
//成功:返回0,失败:返回1或-1
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);  

    int sockfd;  
    // 取得socket  。  如果没加sudo 这里会报错
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  
    if (sockfd < 0)  
    {  
        printf("ip:%s,socket error\n",ips);  
        return -1;  
    }  
    
    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); 
		close(sockfd);
        return -1;  
    }  
 
    char sendpacket[2048];  
    char recvpacket[2048];  
    // 设定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 = sendto(sockfd, (char *)&sendpacket, sizeof(struct icmp), 0, (struct sockaddr *)&addr, sizeof(addr));  
    if (n < 1)  
    {  
        printf("ip:%s,sendto error\n",ips); 
		close(sockfd);
        return -1;  
    }  
    
    // 接受  
    // 由于可能接受到其他Ping的应答消息,所以这里要用循环
    int cnt=0;
    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 -1;  
        }  
        
        // 接受  
        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) 
    	{  
    		close(sockfd);
        	return 1;  
        }          
     
        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);  
		   close(sockfd);
           return 1;
        }  
        
        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 if(cnt<3) 
        {  
            // 否则继续等
            cnt++;
            continue;  
        }
	else
	{
	    close(sockfd);
	    return -1;
	}
    } 
	close(sockfd);
    return 0;
}


凯特网版权声明:以上内容允许转载,但请注明出处,谢谢!

展开全文READ MORE
C++
PCB铜皮厚度线宽与电流能力的关系 基于DRV8825+STC15的四线两相步进电机项目应用

游客 回复需填写必要信息