C语言ping的实现-其它函数

本节是对前面 C语言ping的实现-主函数 里面一些功能函数的相关描述

https://mp.csdn.net/console/editor/html/107043229

/*提示输入信息*/
static void icmp_usage(){
    /*ping 加IP地址或者域名*/
    printf("ping aaa.bbb.ccc.ddd \n");
}
/*终端信号处理函数*/
static void icmp_sigint(int signo){
    alive = 0;                                  /*告诉接收和发送线程结束程序*/
    gettimeofday(&tv_end , NULL);               /*读取程序结束时间*/
    tv_interval = icmp_tvsub(tv_end , tv_begin);/*计算总共所用时间*/
}
/*发送请求*/
static void *icmp_send(void *argv){
    /*保存程序开始发送数据时间*/
    gettimeofday(&tv_begin , NULL);
    while(alive){
        int size = 0;
        struct timeval tv;
        gettimeofday(&tv , NULL); /*当前发包时间*/
        /*在发送包状态数组中找一个空闲位置*/
        pingm_pakcet *packet = icmp_findpacket(-1);
        if(packet){
           packet->seq = packet_send; /*设置*/
           packet->flag = 1; /*已经使用*/
           gettimeofday(&packet->tv_begin , NULL); /*发送时间*/ 
        }
        /*打包数据*/
        icmp_pack((struct icmp*)send_buff , packet_send , &tv , 64 );
        size = sendto(rawsock , send_buff , 64 , 0 , (struct sockaddr*)&dest , sizeof(dest));
        if(size < 0){
            perror("sendto error");
            continue;
        }
        packet_send++; /*计数增加*/
        /*每隔1s , 发送一个ICMP回显请求包*/
        sleep(1);
    }

}
/*查找一个合适的包位置
当seq为-1时,表示查找空包
其他值表示查找seq对应的包
*/
static pingm_pakcet *icmp_findpacket(int seq){
    int i = 0;
    pingm_pakcet *found = NULL;
    /*查找包的位置*/
    if(seq == -1){
        for(i = 0 ; i <128 ; i++){
            if(pingpacket[i].flag == 0){
               found = &pingpacket[i];
               break;     
            }    
        }
    }else if(seq >= 0){
        for(i = 0 ; i < 128 ; i++){
            if(pingpacket[i].seq == seq){
                found = &pingpacket[i];
                break;
            }
        }
    }
    return found;
}
/*设置ICMP报头*/
static void *icmp_pack(struct icmp *icmph , int seq , struct timeval *tv , int length){
    unsigned char i = 0;
    /*设置报头*/
    icmph->icmp_type = ICMP_ECHO; /*ICMP回显请求*/
    icmph->icmp_code = 0;         /*code值为0*/
    icmph->icmp_cksum = 0;        /*先将cksum值1填写为0,便于之后的cksum计算*/
    icmph->icmp_seq = seq;        /*本报的序列号 0*/  
    icmph->icmp_id = pid & 0xffff;/*填写PID*/
    for(i = 0 ; i < length ; i++){
        icmph->icmp_data[i] = i;
    }
    icmph->icmp_cksum = icmp_cksum((unsigned char*)icmph , length);
}
/*CRC16校验和计算icmp_cksum
参数:
    data:数据
    len:数据长度
返回值:
    计算结果,short类型
*/
static unsigned short icmp_cksum(unsigned char *data , int len){
    int sum = 0;    /*计算结果*/
    int odd = len & 0x01;
    /*将数据按照2字节为单位累加起来*/
    while(len & 0xfffe){
        sum += *(unsigned short*)data;
        data +=2;
        len -= 2;
    }
    /*判断是否为奇数个数据,若ICMP报头为奇数个字节,会剩下最后一个字节*/
    if(odd){
        unsigned short tmp = ((*data) << 8) & 0xff00;
        sum += tmp;
    }
    sum = (sum >> 16) + (sum & 0xffff); /*高低位相加*/
    sum += (sum >> 16);                 /*将溢出位加入*/
    return ~sum;                        /*返回取反值*/

}

/*接收ping目的主机的回复*/
static void *icmp_recv(void *argv){
    /*轮询等待时间*/
    struct timeval tv;
    tv.tv_usec = 200;
    tv.tv_sec = 0;
    fd_set readfd;
    /*当没有信号发出一直接收数据*/
    while(alive){
        int ret = 0;
        FD_ZERO(&readfd);
        FD_SET(rawsock , &readfd);
        ret = select(rawsock+1 , &readfd , NULL , NULL , &tv);
        switch(ret){
            case -1:
                /*错误发生*/    
                break;
            case 0:
                /*超时*/
                break;
            default:{
                /*收到一个包*/
                int fromlen = 0;
                struct sockaddr from;
                fromlen = sizeof(from);
                /*接收数据*/
                int size = recvfrom(rawsock , recv_buff , sizeof(recv_buff) , 0 , (struct sockaddr*)&from , &fromlen);
                /**/
                if(errno == EINTR){
                    perror("recvfrom error");
                }
                /*解包*/
                ret = icmp_unpack(recv_buff , size);
                if(ret == -1){
                    continue;
                }
            }
            break;
        }
    }

}

/*解压接收到的包,并打印文件*/
static int icmp_unpack(char *buff , int len){
    int i , iphdrlen;
    struct ip *ip = NULL;
    struct icmp *icmp = NULL;
    int rtt;
    ip = (struct ip*)buff; /*IP头部*/
    iphdrlen = ip->ip_hl*4;/*IP头部长度*/
    icmp = (struct icmp *)(buff + iphdrlen); /*ICMP段的地址*/
    len -= iphdrlen;
    /*判断长度是否为ICMP包*/
    if(len < 8){
        printf("ICMP packets \'s length is less than 8 \n");
        return -1;
    }
    /*ICMP类型为ICMP_ECHOREPLY并且为本进程的PID*/
    if((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)){
        struct timeval tv_internel , tv_recv , tv_send;
        /*在发送表格中查找已经发送的包,按照seq*/
        pingm_pakcet* packet = icmp_findpacket(icmp->icmp_seq);
        if(packet == NULL) return -1;
        packet->flag = 0; //取消标志
        tv_send = packet->tv_begin;/*获取本包发送时间*/
        gettimeofday(&tv_recv , NULL); /*读取此时间,计算时间差*/
        tv_internel = icmp_tvsub(tv_recv , tv_send);
        rtt = tv_internel.tv_sec * 1000 + tv_internel.tv_usec / 1000;
        /*打印结果,包含
        *ICMP长度
        *源IP地址
        *包的序列号
        *TTL
        *时间差
        */
        printf("%d type from %s:icmp_seq=%u ttl=%d rtt=%d ms \n",
            len,
            inet_ntoa(ip->ip_src),
            icmp->icmp_seq,
            ip->ip_ttl,
            rtt);
        packet_recv++;    
    }else{
        return -1;
    }
}
/*计算时间差time_sub
*参数:
*   end,接收到时间
*   begin,开始发送的时间
*返回值:
*   使用时间    
*/
static struct timeval icmp_tvsub(struct timeval end , struct timeval begin){
    struct timeval tv;
    /*计算差值*/
    tv.tv_sec = end.tv_sec - begin.tv_sec;
    tv.tv_usec = end.tv_usec - begin.tv_usec;
    /*如果接受时间的usec值小于发送时的usec值,从usec域借位*/
    if(tv.tv_usec < 0){
        tv.tv_sec--;
        tv.tv_usec += 1000000;
    }
    return tv;
}

/*打印全部ICMP发送接收统计结果*/
static void icmp_statistics(void){
    long time = (tv_interval.tv_sec * 1000) + (tv_interval.tv_usec/1000);
    printf("--- %s ping statistics ---\n" , dest_str); /*目的IP地址*/
    printf("%d packets transmitted , %d received , %d %c packet loss , time %d ms \n",
        packet_send,/*发送*/
        packet_recv,/*接收*/
        (packet_send - packet_recv) * 100 /packet_send,/*丢失百分比*/
        '%',
        time);/*时间*/

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值