Linux编程之PING的实现(2)

/*校验和算法*/ 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; }

 

(3) ICMP包的解包

知道怎么封装包,那解包就也不难了,注意的是,收到一个ICMP包,我们不要就认为这个包就是我们发出去的ICMP回送回答包,我们需要加一层代码来判断该ICMP报文的id和seq字段是否符合我们发送的ICMP报文的设置,来验证ICMP回复包的正确性。

int icmp_unpack(char* buf, int len) { int iphdr_len; struct timeval begin_time, recv_time, offset_time; int rtt; //round trip time struct 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; //icmp包长度 if(len < 8) //判断长度是否为ICMP包长度 { fprintf(stderr, "Invalid icmp packet.Its length is less than 8\n"); return -1; } //判断该包是ICMP回送回答包且该包是我们发出去的 if((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)) { if((icmp->icmp_seq < 0) || (icmp->icmp_seq > PACKET_SEND_MAX_NUM)) { fprintf(stderr, "icmp packet seq is out of range!\n"); 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("%d byte from %s: icmp_seq=%u ttl=%d rtt=%d ms\n", len, inet_ntoa(ip_hdr->ip_src), icmp->icmp_seq, ip_hdr->ip_ttl, rtt); } else { fprintf(stderr, "Invalid ICMP packet! Its id is not matched!\n"); 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); //记录第一个ping包发出的时间 while(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++; //记录发出ping包的数量 if(size < 0) { fprintf(stderr, "send icmp packet fail!\n"); continue; } sleep(1); } } 

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/f1758934fb4a5bf95095a42c0fa42c83.html