zoukankan      html  css  js  c++  java
  • ping 源码,详细解释

    #include "stdio.h"
    #include "fcntl.h"
    #include "errno.h"
    #include "signal.h"
    #include "sys/types.h"
    #include "sys/socket.h"
    #include "sys/time.h"
    #include "netinet/in.h"
    #include "arpa/inet.h"
    #include "netdb.h"

    #define ICMP_ECHO 8
    #define ICMP_ECHOREPLY 0
    #define ICMP_HEADSIZE   8
    #define IP_HEADSIZE 20
    #define MAX(a, b) ((a) > (b)) ? (a) : (b)
    #define MIN(a, b) ((a) > (b)) ? (b) : (a)


    char *host;
    char *prog;
    extern errno;
    long lSendTime;
    u_short seq;
    int iTimeOut;
    int sock, sent, recvd, max, min, total;
    u_long lHostIp;
    struct sockaddr_in it; //套接字地址信息
    struct timeval now;     //用于计算时间,timeval为系统内定义结构体
    int ping();
    void stat();

    typedef struct tagIpHead   //定义IP报头结构
    {
        u_char ip_verlen; //header length
        u_char ip_tos;    //type of service
        u_char ip_len;    //total length
        u_short ip_id;    //identification
        u_short ip_fragoff; //fragment offset field
        u_char ip_ttl;    //time to live
        u_char ip_proto; //protocol
        u_short ip_chksum; //IP checksum
        u_long ip_src_addr; //source address
        u_long ip_dst_addr; //dest address
    }IPHEAD;

    typedef struct tagIcmpHead //定义Icmp报头结构
    {
        u_char icmp_type; //type of message
        u_char icmp_code; //type sub code
        u_short icmp_chksum; //ones complement checksum of struct
        u_short icmp_id;   //标示符
        u_short icmp_seq; //顺序号
        u_char icmp_data[1];
    }ICMPHEAD;

    /*校验和算法
    原理:把被校验的数据16位进行累加,然后取反码,若数据字节长度为奇数,则数据尾部补一个字节的0以凑成偶数.
    接收方在计算过程中包含了发送方存在首部中的校验和,因此如果在传输过程中没发生任何差错,那么接收方计算的校验和结果应全为1。如果不全为1(即校验和错误),那么IP就丢弃收到的数据报。
    */
    u_short ChkSum(u_short *pIcmpData, int iDataLen)
    {
        u_short iSum;
        u_short iOddByte;
        iSum = 0;

        /*把ICMP报头二进制数据以2字节为单位累加起来*/
        while(iDataLen > 1)
        {
            iSum ^= *(pIcmpData++); //^表示异或运算
            iDataLen -= 2;
        }

        /*若ICMP报头为奇数个字节,会剩下最后一字节.把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加*/
        if(iDataLen == 1)
        {
            iOddByte = 0;
            *((u_char *) & iOddByte) = *(u_char *)pIcmpData;
            iSum ^= iOddByte;
        }
        iSum^0xffff;   //继续累加
        return(iSum);
    }

    /*获取当前时间*/
    long time_now()
    {
        struct timeval now;    
        long lPassed;
        gettimeofday(&now, 0);
        
        /* tv_sec 秒; tv_usec 微秒 */
        lPassed = now.tv_sec * 1000000 + now.tv_usec; //采用微秒精度
        return lPassed;
    }



    main(int argc, char **argv)
    {
        struct hostent *h;
        char buf[200];
        char dst_host[32];
        int i, namelen;
        IPHEAD *pIpHead;    
        ICMPHEAD *pIcmpHead;
        int normalSize = 0;
        int times = 0;
        if(argc < 2) //输入参数不合要求
        {
            printf("usage: %s [-timeout] host | IP\n", argv[0]);
            exit(0);
        }
        //printf("argc: %d argv: %s", argc, *(argv + 1));
        prog = argv[0];
        host = argc == 2 ? argv[1] : argv[2];
        iTimeOut = argc == 2 ? 1 : atoi(argv[1]);
        
        /*数据发送之前要建立一个套接字(socket),系统调用socket()返回一个套接口描述符,如果出错,则返回-1。*/
        if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
        {
            perror("socket");
            exit(2);
        }
        
        /*初始化套接字it*/
        bzero(&it, sizeof(it)); //置字节字符串it的前sizeof(it)个字节为零
        it.sin_family = AF_INET; //地址族
        
        /*inet_addr(host)
        每个ip对应一个32位的数,inet_addr把他们倒序过来,再转换为十进制数即为结果.
        */

        if((lHostIp = inet_addr(host)) != INADDR_NONE) //host为32位IP地址,lHostIp为十进制数表示
        {
            it.sin_addr.s_addr = lHostIp; //获取主机IP地址
            strcpy(dst_host, host);   //拷贝二进制四段IP
        }
        else if(h = gethostbyname(host))
        {    
            bcopy(h -> h_addr, &it.sin_addr, h -> h_length);
            //bcopy()函数,将h->h_addr的前h->h_length个字节复制到&it.sin_addr中
            sprintf(dst_host, "%s(%s)", host, inet_ntoa(it.sin_addr));    
        }
        else //IP或主机名错误
        {
            fprintf(stderr, "bad IP or host\n");
            exit(3);
        }


        namelen = sizeof(it);
        i = IP_HEADSIZE + ICMP_HEADSIZE + sizeof(long);
        printf("\npinging %s, send %d bytes\n\n", dst_host, i);
        seq = 0;
        sigset(SIGINT, stat);   //sigset用于修改信号定位
        sigset(SIGALRM, ping);
        alarm(iTimeOut); //alarm()用来设置信号在经过iTimeOut后传送给目前的进程

        ping();

        for(;;)
        {
            register size;
            register u_char ttl;
            register delta;
            register iIpHeadLen;
            size = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&it, &namelen); //recvfrom()从(已连接)套接口上接收数据,并捕获数据发送源的地址,it为装有源地址的缓冲区.若无错误发生,recvfrom()返回读入的字节数。如果连接已中止,返回0。否则的话,返回SOCKET_ERROR错误
            if(size == -1)
            {
                //printf("recvrrom failed, size is %d\n", size);
                continue;
            }
            delta = (int)((time_now() - lSendTime)); //计算时间差,即PING所需时间
            pIpHead = (IPHEAD *)buf;
            iIpHeadLen = (int)((pIpHead -> ip_verlen&0xf) << 2);
            normalSize = iIpHeadLen + ICMP_HEADSIZE;
            if(size < normalSize)
            {
            //printf("sizeof iIpHeadLen + ICMP_HEADSIZE error:size is %\n", size);
            continue;
            }
            ttl = pIpHead -> ip_ttl;
            pIcmpHead = (ICMPHEAD *)(buf + iIpHeadLen);
            if(pIcmpHead -> icmp_type != ICMP_ECHOREPLY)
            {
                //printf("ICMP_ECHOREPLY error\n");
                continue;
            }
            if(pIcmpHead -> icmp_id != seq || pIcmpHead -> icmp_seq != seq)
            {
                //printf("icmp_id != seq\n");
                continue;
            }
            sprintf(buf, "icmp_seq = %u bytes = %d ttl = %d",pIcmpHead -> icmp_seq, seq, size, ttl);
            printf("myping reply from %s: %s time=%.3f ms\n",host ,buf, (float)delta / 1000);
            max = MAX(delta,max);
            min = min < delta ? MAX(delta, min) : delta;
            total += delta;
            ++recvd;
            ++seq;
            if(times == 3) //设置ping的次数
            {
                stat();
                break;
            }
            times++;
        }

    }

    ping()
    {
        char buf[200];  
        int iPacketSize;
        int tag = -1;
        ICMPHEAD *pIcmpHead = (ICMPHEAD *)buf;
        pIcmpHead -> icmp_type = ICMP_ECHO;
        pIcmpHead -> icmp_code = 0;
        pIcmpHead -> icmp_id = seq;
        pIcmpHead -> icmp_seq = seq;
        pIcmpHead -> icmp_chksum = 0;
        *((long *)pIcmpHead -> icmp_data) = time_now();
        iPacketSize = ICMP_HEADSIZE + 4;
        pIcmpHead -> icmp_chksum = ChkSum((u_short *)pIcmpHead, iPacketSize); //校验和算法
        lSendTime = time_now();
        tag = sendto(sock, buf, iPacketSize, 0, (struct sockaddr *) & it, sizeof(it)); //发送数据
        if(tag < 0) //发送失败
        {
            printf("\nmy_ping send failed\n");
            exit(6);
        }
        ++sent;
        alarm(iTimeOut);
    }


    /*显示工作状态*/
    void stat()
    {
        if(sent)
        {
            printf("\n%s ping statistics summerized by 网络四班-彭华^-^\n", host);
            printf("%d packets sent, %d packets received, %.2f%% lost\n", sent, recvd, (float)(sent-recvd) / (float)sent * 100);
        }    
        if(recvd)
        {
            printf("round_trip min/avg/max: %d/%d/%d ms\n\n", min, total/recvd, max);
        }    
        exit(0);
    }
  • 相关阅读:
    js读取解析json数据
    dot.js简单入门
    免费API接口整理
    综合方式输出通信数据
    redis常用
    数据库主从复制
    数据库存储过程
    数据库触发器
    局域网访问本地wampserver调试环境
    远程数据同步工具cwRsync
  • 原文地址:https://www.cnblogs.com/mokliu/p/2138278.html
Copyright © 2011-2022 走看看