本节是对前面 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);/*时间*/
}