⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 myping.c

📁 用标准C写的一个MYPing小程序.ping 是向目的地址发送一个icmp回显请求报文
💻 C
字号:
#include <stdio.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/types.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 <setjmp.h>
#include <errno.h>

#define PACKET_SIZE     4096
#define MAX_WAIT_TIME   5
#define MAX_NO_PACKETS  3

char sendpacket[PACKET_SIZE];
char recvpacket[PACKET_SIZE];
int sockfd,datalen=56;
int nsend=0,nreceived=0;
struct sockaddr_in dest_addr;
pid_t pid;
struct sockaddr_in from;
struct timeval tvrecv;

void statistics(int signo);
unsigned short cal_chksum(unsigned short *addr,int len);
int pack(int pack_no);
void send_packet(void);
void recv_packet(void);
int unpack(char *buf,int len);
void tv_sub(struct timeval *out,struct timeval *in);

/**//*
 *ping 是向目的地址发送一个icmp回显请求报文,然后等待回显应答报文
 *并在超时打印超时信息,本代码没有实现超时检测
 *icmp回显请求报文或回显应答报文格式
 *|   类型(8/0) |  代码(0)   |                 校验和              |
 *|          标志符                  |                序号                     |
 *|                                  选项数据                                     |
 *其中标志符是本进程号,用于区别收到的其他报文,这样可以允许多个ping程序同时运行
 *序号是从0开始,每次都加1
 *有时第一个往返时间可能比后面的长,因为第一次可能要查找ARP
 */

void statistics(int signo)
{ 
printf(" --------------------PINGstatistics------------------- \n");
printf("%d packets transmitted, %d received , %%%d lost\n ",nsend,nreceived,
        (nsend-nreceived)/nsend*100);
         close(sockfd);
         exit(1);
}
/**//*校验和算法*/   /*发送之前计算校验和*/
unsigned short cal_chksum(unsigned short *addr,int len)
{    int nleft=len;
        int sum=0;
        unsigned short *w=addr;
        unsigned short answer=0;
 /**//*把ICMP报头二进制数据以2字节为单位累加起来*/
        while(nleft>1)
  {       sum+=*w++;
                   nleft-=2;
        }
         /**//*若ICMP报头为奇数个字节,会剩下最后一字节。*/
          /*把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加*/
        if( nleft==1)
{       *(unsigned char *)(&answer)=*(unsigned char *)w;
                sum+=answer;                                       /*最后一个字节加上*/
        }
        sum=(sum>>16)+(sum&0xffff);                    /*第一次溢出*/
        sum+=(sum>>16);                      /*如果有溢出,把溢出位移到低位加上*/
        answer=~sum;
        return answer;
}
/**//*设置ICMP报头*/
int pack(int pack_no)      /*由发送函数调用*/
{    int i,packsize;
        struct icmp *icmp;
        struct timeval *tval;

        icmp=(struct icmp*)sendpacket;     //源代码里找不到,库文件或者包在udp中的,自己看协议栈或自己定义
        icmp->icmp_type=ICMP_ECHO;
        icmp->icmp_code=0;
        icmp->icmp_cksum=0;
        icmp->icmp_seq=pack_no;
        icmp->icmp_id=pid;
        packsize=8+datalen;         /*icmp的头8bytes,数据56bytes,所以字节总数64,(若加上ip头20bytes,则是84bytes)*/
        tval= (struct timeval *)icmp->icmp_data;       /*icmp的data中存放的是时间,报文的数据部分插入发送时间,所以能算往返时间*/
        gettimeofday(tval,NULL);    /**//*记录发送时间*/
        icmp->icmp_cksum=cal_chksum((unsigned short *)icmp,packsize); /**//*校验算法*/
        return packsize;
}

/**//*发送三个ICMP报文*/
void send_packet()
{       int packetsize;
        while( nsend<MAX_NO_PACKETS)
        {       nsend++;
                packetsize=pack(nsend); /**//*设置ICMP报头*/
                if( sendto(sockfd,sendpacket,packetsize,0,                                    /*socket的发送函数*/                           (struct sockaddr *)&dest_addr,sizeof(dest_addr) )<0  )
               {       perror("sendto error");
                        continue;                    }
                sleep(1); /**//*每隔一秒发送一个ICMP报文*/
        }
}

/**//*接收所有ICMP报文*/
void recv_packet()
{       int n,fromlen;
        extern int errno;

        signal(SIGALRM,statistics);      
        fromlen=sizeof(from);
        while( nreceived<nsend)
        {
                alarm(MAX_WAIT_TIME);           
                if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,             /*socket中的接收函数*/
                                (struct sockaddr *)&from,&fromlen)) <0)
                {       if(errno==EINTR)continue;
                        perror("recvfrom error");
                        continue;
                }
                gettimeofday(&tvrecv,NULL);  /**//*记录接收时间*/  
                if(unpack(recvpacket,n)==-1)continue;
                nreceived++;
        }
}

//打印ip报文的信息
void printIP(char *packet, int len)
{
    struct sockaddr_in addr_src,addr_dst;
    struct ip *pip = (struct ip*)packet;
    printf("ip head len:%d\n ",pip->ip_hl<<2);
    printf("ip len:%d\n ",ntohs(pip->ip_len));
    printf("ip pro id:%d\n ",pip->ip_p);
    printf("ip ttl:%d\n ",pip->ip_ttl);
    printf("ip offset:%d\n ",ntohs(pip->ip_off)&IP_OFFMASK);

    memset(&addr_src,0,sizeof(struct sockaddr_in));      
    memset(&addr_dst,0,sizeof(struct sockaddr_in));     
    memcpy(&addr_src,&pip->ip_src,sizeof(struct sockaddr_in)); memcpy(&addr_dst,&pip->ip_dst,sizeof(struct sockaddr_in));    
    printf("src ip:%x\n ",addr_src.sin_addr);
    printf("dst ip:%x\n ",addr_dst.sin_addr);
}

/**//*剥去ICMP报头*/
int unpack(char *buf,int len)
{       int i,iphdrlen;
        struct ip *ip;
        struct icmp *icmp;
        struct timeval *tvsend;
        double rtt;

        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)                /**//*小于ICMP报头长度则不合理*/
        {       printf("ICMP packets's length is less than 8\n ");
                return -1;
        }
        /**//*确保所接收的是我所发的的ICMP的回应*/
        if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) )
        {       tvsend=(struct timeval *)icmp->icmp_data;
                tv_sub(&tvrecv,tvsend);  /**//*接收和发送的时间差*/
                rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;  /**//*以毫秒为单位计算rtt*/ /*一个是微秒,一个是秒*/
                /**//*显示相关信息*/
                printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n ",
                        len,
                        inet_ntoa(from.sin_addr),     
                        icmp->icmp_seq,
                        ip->ip_ttl,
                        rtt);
        }
        else    return -1;
}

main(int argc,char *argv[])                     //argv[0]指向输入的程序路径及名称。 argv[1]指向参数para_1字符串。 
{       struct hostent *host;                   //int main(int argc,char *argv[]) 是 UNIX 和 Linux 中的标准写法,而 int main() 只是 UNIX 及 Linux 默许的用法;
        struct protoent *protocol;              //argc 是外部命令参数的个数,argv[] 存放各参数的内容
        unsigned long inaddr=0l;
        int waittime=MAX_WAIT_TIME;
        int size=50*1024;
  
        if(argc<2)                                     
        {       printf("usage:%s hostname/IP address ",argv[0]);
                exit(1);                    //exit   (0)   表示正常退出   ; exit   (1)   非零数,表示有错误,不正常退出
        }
        
    printf("step1\n ");
    
        if( (protocol=getprotobyname("icmp") )==NULL)
       {       perror("getprotobyname");                                          
                exit(1);
        }
      
        printf("step2\n "); 
        
    /**//*生成使用ICMP的原始套接字,这种套接字只有root才能生成*/
        if( (sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto) )<0)
      {       perror("socket error");
                exit(1);
        }
        
    /**//* 回收root权限,设置当前用户权限*/
        setuid(getuid());
        
    /**//*扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的
          的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答*/
        setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size) );                   
        bzero(&dest_addr,sizeof(dest_addr));
        dest_addr.sin_family=AF_INET;

        /**//*判断是主机名还是ip地址*/
        if( inaddr=inet_addr(argv[1])==INADDR_NONE)
      {
        if((host=gethostbyname(argv[1]) )==NULL) /**//*是主机名*/
               {       perror("gethostbyname error");
                        exit(1);
                }
                memcpy( (char *)&dest_addr.sin_addr,host->h_addr,host->h_length);
        }
        else    /**//*是ip地址*/ {
        dest_addr.sin_addr.s_addr = inaddr;
                //memcpy( (char *)&dest_addr,(char *)&inaddr,host->h_length);
        }
        /**//*获取main的进程id,用于设置ICMP的标志符*/        
        pid=getpid();
        printf("PING %s(%s): %d bytes data in ICMP packets.\n ",argv[1],
                        inet_ntoa(dest_addr.sin_addr),datalen);
        send_packet();  /**//*发送所有ICMP报文*/
        recv_packet();  /**//*接收所有ICMP报文*/
        statistics(SIGALRM); /**//*进行统计*/

        return 0;

}
/**//*两个timeval结构相减*/
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;
}
/**//*------------- The End -----------*/

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -