大致流程:
将ICMP头和时间数据设置好后,通过创建好的原始套接字socket发出去。目的主机计算效验和后会将数据原样返回,用当前时间和返回的数据结算时间差,计算出rtt。
其中:
1.我们以ping www.baidu.com为例,但我们并没有计算平均rtt
2.我们并没有手动创建IP头,而是交给了内核去帮我们处理
3.中间设计到的函数需要查相关资料
代码实现:
1 /* 2 ============================================================================ 3 Name : myping.c 4 Author : huh 5 Version : 0.01 6 Copyright : Your copyright notice 7 Description : Hello World in C, Ansi-style 8 ============================================================================ 9 */ 10 11 #include <sys/types.h> 12 #include <sys/select.h> 13 #include <stdio.h> 14 #include <time.h> 15 #include <string.h> 16 #include <netdb.h> 17 #include <arpa/inet.h> 18 #include <unistd.h> 19 #include <signal.h> 20 #include <sys/time.h> 21 #include <sys/socket.h> 22 #include <netinet/ip_icmp.h> 23 24 #define PACKET_SIZE 1024*4 25 26 int pid; 27 int sockfd; 28 int datalen = 56; 29 int nsent = 1, nrecv = 1; 30 char sendbuf[PACKET_SIZE]; 31 char recvbuf[PACKET_SIZE]; 32 struct sockaddr_in dest_addr; //socket目的地址 33 struct sockaddr_in src_addr; 34 struct timeval tvrecv; 35 36 void send_packet(); 37 void un_packet(int); 38 void tv_sub(struct timeval *out, struct timeval *in); 39 unsigned short cal_chksum(unsigned short *addr, int len); 40 41 int main() 42 { 43 pid = getpid(); 44 char str[20]; 45 unsigned int inaddr; 46 struct hostent *host; 47 int size = 1024 * 25; 48 strcpy(str,"www.baidu.com"); 49 //IP地址(域名)到无符号整数的转换 50 inaddr = inet_addr(str); 51 if (inaddr == INADDR_NONE) 52 { 53 host = gethostbyname(str); 54 if (host == NULL) 55 { 56 printf("参数格式不正确,请重新输入!\n"); 57 return 0; 58 } 59 memcpy((char*) &inaddr, host->h_addr, sizeof(dest_addr.sin_addr)); 60 } 61 //设置套接字地址 62 dest_addr.sin_family = AF_INET; 63 memcpy((char *)&dest_addr.sin_addr, (char *)&inaddr,sizeof(inaddr)); 64 //创建套接字 65 sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 66 //改变socket缓冲区大小 67 setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); 68 69 printf("PING %s (%s) %d(84) bytes of data.\n",str, inet_ntoa(dest_addr.sin_addr), datalen); 70 //不停的发送和接受ICMP数据包 71 while (1) 72 { 73 send_packet(); 74 int src_addr_len = sizeof(struct sockaddr_in); 75 //接收数据包,一直阻塞到有数据包到达为止 76 int len = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *) &src_addr, (socklen_t *) &src_addr_len); 77 if(len < 0) 78 printf("recvfrom error!\n"); 79 un_packet(len); 80 sleep(1); //间隔一秒,后面会用信号实现每秒发送一个ICMP包。 81 } 82 return 0; 83 } 84 85 //解包 86 void un_packet(int len) 87 { 88 int hlen1; 89 double rtt; 90 struct ip *ip; 91 struct icmp *icmp; 92 struct timeval *tvsend; 93 94 ip = (struct ip *) recvbuf; 95 hlen1 = ip->ip_hl << 2; 96 if (ip->ip_p != IPPROTO_ICMP) 97 return ; 98 99 icmp = (struct icmp *) (recvbuf + hlen1); 100 len -= hlen1; 101 102 if ((icmp->icmp_type == ICMP_ECHOREPLY)) 103 { 104 if (icmp->icmp_id != pid) 105 return; 106 tvsend = (struct timeval *) icmp->icmp_data; //发送时间 107 gettimeofday(&tvrecv, NULL); //得到当前时间 108 109 tv_sub(&tvrecv, tvsend); //计算接收和发送的时间差 110 rtt = tvrecv.tv_sec * 1000.0 + tvrecv.tv_usec / 1000.0; //以毫秒单位计算rtt 111 printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3fms\n", len, inet_ntoa(src_addr.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt); 112 nrecv++; 113 } 114 } 115 116 //手动构建数据包,并通过原始套接字发送 117 void send_packet() 118 { 119 int len; 120 struct icmp *icmp; 121 icmp = (struct icmp *) (sendbuf); 122 icmp->icmp_type = ICMP_ECHO; //拼接icmp 123 icmp->icmp_code = 0; 124 icmp->icmp_id = pid; //2字节 125 icmp->icmp_seq = nsent++; //2字节 126 memset(icmp->icmp_data, 0xa5, datalen); 127 gettimeofday((struct timeval *) icmp->icmp_data, NULL); //将发送时间作为数据传递过去 128 129 len = datalen + 8; 130 icmp->icmp_cksum = 0; //校验和需要先置0 131 icmp->icmp_cksum = cal_chksum((unsigned short *) icmp, len); //计算效验和 132 133 sendto(sockfd, sendbuf, len, 0, (struct sockaddr *) &dest_addr, 134 sizeof(dest_addr)); //将包发出去 135 //printf("package have sent!\n"); 136 } 137 138 //计算效验和 139 unsigned short cal_chksum(unsigned short *addr, int len) 140 { 141 int nleft = len; 142 int sum = 0; 143 unsigned short *w = addr; 144 unsigned short answer = 0; 145 //把ICMP报头二进制数据以2字节为单位累加起来 146 while (nleft > 1) 147 { 148 sum += *w++; 149 nleft -= 2; 150 } 151 if (nleft == 1) 152 { 153 *(unsigned char *) (&answer) = *(unsigned char *) w; 154 sum += answer; 155 } 156 sum = (sum >> 16) + (sum & 0xffff); 157 sum += (sum >> 16); 158 answer = ~sum; 159 return answer; 160 } 161 162 //计算时间差 163 void tv_sub(struct timeval *out, struct timeval *in) 164 { 165 if ((out->tv_usec -= in->tv_usec) < 0) 166 { 167 --out->tv_sec; 168 out->tv_usec += 1000000; 169 } 170 out->tv_sec -= in->tv_sec; 171 }
时间: 2024-10-18 17:53:08