int icmp_unpack(char* buf, int len) { int iphdr_len; struct timeval begin_time, recv_time, offset_time; ip* ip_hdr = (struct ip *)buf; iphdr_len = ip_hdr->ip_hl*4; struct icmp* icmp = (struct icmp*)(buf+iphdr_len); //使指针跳过IP头指向ICMP头 len-=iphdr_len; (len < 8) //判断长度是否为ICMP包长度 { fprintf(stderr, ); return -1; } ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == (pid & 0xffff))) { if((icmp->icmp_seq < 0) || (icmp->icmp_seq > PACKET_SEND_MAX_NUM)) { fprintf(stderr, ); return -1; } ping_packet[icmp->icmp_seq].flag = 0; begin_time = ping_packet[icmp->icmp_seq].begin_time; //去除该包的发出时间 gettimeofday(&recv_time, NULL); offset_time = cal_time_offset(begin_time, recv_time); rtt = offset_time.tv_sec*1000 + offset_time.tv_usec/1000; //毫秒为单位 printf(, len, inet_ntoa(ip_hdr->ip_src), icmp->icmp_seq, ip_hdr->ip_ttl, rtt); } else { fprintf(stderr, ); return -1; } return 0; }
二、发包线程的搭建
根据PING程序的框架,我们需要建立一个线程用于ping包的发送,我的想法是这样的:使用sendto进行发包,发包速率我们维持在1秒1发,我们需要用一个全局变量记录第一个ping包发出的时间,除此之外,我们还需要一个全局变量来记录我们发出的ping包到底有几个,这两个变量用于后来收到ping包回复后的数据计算。
void ping_send() { char send_buf[128]; memset(send_buf, 0, sizeof(send_buf)); gettimeofday(&start_time, NULL); (alive) { int size = 0; gettimeofday(&(ping_packet[send_count].begin_time), NULL); ping_packet[send_count].flag = 1; //将该标记为设置为该包已发送 icmp_pack((struct icmp*)send_buf, send_count, 64); //封装icmp包 size = sendto(rawsock, send_buf, 64, 0, (struct sockaddr*)&dest, sizeof(dest)); send_count++; (size < 0) { fprintf(stderr, ); continue; } sleep(1); } }
三、收包线程的搭建
我们同样建立一个接收包的线程,这里我们采用select函数进行收包,并为select函数设置超时时间为200us,若发生超时,则进行下一个循环。同样地,我们也需要一个全局变量来记录成功接收到的ping回复包的数量。
void ping_recv() { struct timeval tv; tv.tv_usec = 200; //设置select函数的超时时间为200us tv.tv_sec = 0; fd_set read_fd; char recv_buf[512]; memset(recv_buf, 0 ,sizeof(recv_buf)); while(alive) { int ret = 0; FD_ZERO(&read_fd); FD_SET(rawsock, &read_fd); ret = select(rawsock+1, &read_fd, NULL, NULL, &tv); switch(ret) { case -1: fprintf(stderr,); break; case 0: break; default: { int size = recv(rawsock, recv_buf, sizeof(recv_buf), 0); if(size < 0) { fprintf(stderr,); continue; } ret = icmp_unpack(recv_buf, size); (ret == -1) //不是属于自己的icmp包,丢弃不处理 { continue; } recv_count++; //接收包计数 } break; } } }
四、中断处理
我们规定了一次ping发送的包的最大值为64个,若超出该数值就停止发送。作为PING的使用者,我们一般只会发送若干个包,若有这几个包顺利返回,我们就crtl+c中断ping。这里的代码主要是为中断信号写一个中断处理函数,将alive这个全局变量设置为0,进而使发送ping包的循环停止而结束程序。
void icmp_sigint(int signo) { alive = 0; gettimeofday(&end_time, NULL); time_interval = cal_time_offset(start_time, end_time); } signal(SIGINT, icmp_sigint);
五、总体实现
各模块介绍完了,现在贴出完整代码。