写这个的目的主要是为了以后的方便:
1.信号计时函数的使用
2.ip头的构建和icmp头的构建
3.selec函数t的用法
代码实现:
/src/ping.h
1 /* 2 * ping.h 3 * 4 * Created on: 2015年11月6日 5 * Author: root 6 */ 7 8 #ifndef PING_H_ 9 #define PING_H_ 10 11 #endif /* PING_H_ */ 12 13 #include <sys/types.h> 14 #include <sys/select.h> 15 #include <stdio.h> 16 #include <string.h> 17 #include <netdb.h> 18 #include <arpa/inet.h> 19 #include <unistd.h> 20 #include <signal.h> 21 #include <sys/time.h> 22 #include <sys/socket.h> 23 #include <netinet/ip_icmp.h> 24 #include<errno.h> /*sys/types.h中文名称为基本系统数据类型*/ 25 26 #define N 200005 27 #define PACKET_SIZE 1024*4 28 29 extern FILE *out; 30 extern int nsent; 31 extern int nrecv; 32 33 extern char * intip_to_ipv4(unsigned int ip); 34 extern void recv_all_packet(int sockfd); 35 extern int send_one_packet(int sockfd, unsigned int ip_num, int pid);
/src/ping_project.c
1 /* 2 ============================================================================ 3 Name : ping_project.c 4 Author : huh 5 Version : 6 Copyright : ---notice--- 7 Description : Hello World in C, Ansi-style 8 ============================================================================ 9 */ 10 11 #include "ping.h" 12 13 FILE *out; 14 15 int pid; 16 int the_number_of_ping = 0; 17 int send_sockfd, recv_sockfd; 18 int ping_num = 0; //ip地址数量 19 unsigned int addr[N]; //ip地址数组 20 struct sigaction act_alarm; 21 struct timeval timeout; 22 struct itimerval val_alarm ={ 23 .it_interval.tv_sec = 1, 24 .it_interval.tv_usec = 0, 25 .it_value.tv_sec = 0, 26 .it_value.tv_usec = 1 }; 27 fd_set init_recv_sockets, recv_sockets; 28 fd_set init_send_sockets, send_sockets, init_send_sockets_2; 29 30 int size = 1024 * 50; 31 32 void init_ip(); 33 void sig_alrm(int singo); 34 35 int main(void) 36 { 37 init_ip(); 38 out = fopen("./src/file/a.out", "w"); 39 if (out == NULL) 40 { 41 perror("stdout error! "); 42 } 43 int flag1, flag2; 44 pid = getpid(); 45 send_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 46 //if(send_sockfd < 0) { perror("send_sockfd error:"); return 0; } 47 flag1 = setsockopt(send_sockfd, IPPROTO_IP, IP_HDRINCL, &size, 48 sizeof(size)); 49 //if(flag1<0) { perror("setsockopt error:"); return 0; } 50 recv_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 51 //if(recv_sockfd < 0) { perror("recv_sockfd error:"); return 0; } 52 flag2 = setsockopt(recv_sockfd, IPPROTO_IP, IP_HDRINCL, &size, 53 sizeof(size)); 54 //if(flag2<0) { perror("setsockopt error:"); return 0; } 55 56 act_alarm.sa_handler = sig_alrm; 57 if (sigaction(SIGALRM, &act_alarm, NULL) == -1) 58 printf("SIGALRM handler setting fails. "); 59 60 if ((setitimer(ITIMER_REAL, &val_alarm, NULL)) == -1) /*定时函数*/ 61 printf("setitimer fails. "); 62 63 int result; 64 int maxfdp1; 65 int k = 0; 66 67 FD_ZERO(&init_recv_sockets); 68 FD_SET(recv_sockfd, &init_recv_sockets); 69 70 FD_ZERO(&init_send_sockets); 71 FD_SET(send_sockfd, &init_send_sockets); 72 init_send_sockets_2 = init_send_sockets; 73 74 maxfdp1 = recv_sockfd + 1; 75 while (1) //无限循环,接受包 76 { 77 recv_sockets = init_recv_sockets; 78 send_sockets = init_send_sockets_2; 79 timeout.tv_sec = 1; 80 timeout.tv_usec = 500000; 81 result = select(maxfdp1, &recv_sockets, &send_sockets, NULL, &timeout); 82 switch (result) 83 { 84 case 0: 85 printf("共发送了%d个包, 收到了%d个包 ", nsent, nrecv); 86 printf("timeout:程序结束! "); 87 return 0; 88 break; 89 case -1: 90 if (errno == EINTR) 91 continue; 92 perror("select:"); 93 return 0; 94 break; 95 default: 96 if (FD_ISSET(recv_sockfd, &recv_sockets)) 97 recv_all_packet(recv_sockfd); 98 if (FD_ISSET(send_sockfd, &send_sockets)) 99 { 100 send_one_packet(send_sockfd, addr[k], pid); 101 k = (k + 1) % ping_num; 102 if (k == 0) 103 FD_ZERO(&init_send_sockets_2); 104 } 105 break; 106 } 107 } 108 return 0; 109 } 110 111 void sig_alrm(int singo) 112 { 113 the_number_of_ping++; 114 if (the_number_of_ping <= 30) 115 { 116 printf("ping了第%d遍! ", the_number_of_ping); 117 init_send_sockets_2 = init_send_sockets; 118 } else 119 { 120 val_alarm.it_interval.tv_sec = 0; 121 val_alarm.it_interval.tv_usec = 0; 122 val_alarm.it_value = val_alarm.it_interval; 123 setitimer(ITIMER_REAL, &val_alarm, NULL); 124 } 125 } 126 127 void init_ip() //将要访问的主机全部变成32位无符号的ip。 128 { 129 FILE *in; 130 unsigned int name; 131 char str[55]; 132 struct hostent *host; 133 134 in = freopen("./src/file/a.in", "r", stdin); 135 if (in == NULL) 136 { 137 printf("stdin error! "); 138 } 139 140 while (!feof(in)) 141 { 142 scanf("%s", str); //ip地址或域名 143 name = inet_addr(str); //将一个点分十进制IP转化为长整数 144 if (name == INADDR_NONE) 145 { 146 host = gethostbyname(str); 147 if (host == NULL) 148 { 149 printf("参数格式不正确,请重新输入! "); 150 continue; 151 } 152 memcpy((char*) &name, host->h_addr, 4); 153 } 154 addr[ping_num] = name; 155 printf("%s:%s ", str, intip_to_ipv4(name)); 156 ping_num++; 157 } 158 printf("程序将 ping %d 个IP. ", ping_num); 159 }
/src/send.c
1 /* 2 * send.c 3 * 4 * Created on: 2015年11月6日 5 * Author: root 6 */ 7 8 #include "ping.h" 9 10 int icmp_len; 11 int flag; 12 int nsent=0; 13 int datalen = 56; 14 struct icmp *icmp; 15 char sendbuf[PACKET_SIZE]; 16 struct sockaddr_in dest_addr; //socket目的地址 17 18 unsigned short cal_chksum(unsigned short *addr, int len) 19 { 20 int nleft = len; 21 int sum = 0; 22 unsigned short *w = addr; 23 unsigned short answer = 0; 24 //把ICMP报头二进制数据以2字节为单位累加起来 25 while (nleft > 1) 26 { 27 sum += *w++; 28 nleft -= 2; 29 } 30 if (nleft == 1) 31 { 32 *(unsigned char *)(&answer) = *(unsigned char *)w; 33 sum += answer; 34 } 35 sum = (sum>>16) + (sum&0xffff); 36 sum += (sum>>16); 37 answer = ~sum; 38 return answer; 39 } 40 41 char * intip_to_ipv4(unsigned int ip) 42 { 43 char *str; 44 struct in_addr des; 45 des.s_addr = ip; 46 str = inet_ntoa(des); 47 //printf("---%s--- ",str); 48 return str; 49 } 50 51 int send_one_packet(int sockfd, unsigned int ip_num, int pid) 52 { 53 struct iphdr *ip; 54 ip = (struct iphdr *)sendbuf; 55 ip->ihl = sizeof(struct iphdr) >> 2; //首部长度 56 ip->version = 4; //ip协议版本 57 ip->tos = 0; //服务类型字段 58 ip->tot_len = 84; //总长度 59 ip->id = 0; 60 ip->frag_off = 0; 61 ip->ttl = 255; 62 ip->protocol = IPPROTO_ICMP; 63 ip->check = 0; //让内核算 64 65 dest_addr.sin_family = AF_INET; 66 memcpy((char *)&dest_addr.sin_addr, (char *)&ip_num,sizeof(ip_num)); 67 //ip->saddr = src_addr.sin_addr.s_addr; 68 ip->daddr = dest_addr.sin_addr.s_addr; 69 70 icmp = (struct icmp *)(sendbuf + sizeof(struct iphdr)); 71 icmp->icmp_type = ICMP_ECHO; //拼接icmp 72 icmp->icmp_code = 0; 73 icmp->icmp_id = pid; //2字节 74 icmp->icmp_seq = ++nsent; //2字节 75 memset(icmp->icmp_data, 0xa5, datalen); 76 gettimeofday((struct timeval *)icmp->icmp_data, NULL); //将发送时间作为数据传递过去 77 78 icmp_len = datalen + 8; 79 icmp->icmp_cksum = 0; 80 icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, icmp_len); 81 82 flag = sendto(sockfd, sendbuf, ip->tot_len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); //将包发出去 83 //if(flag < 0) { printf("sendto error! "); return 0; } 84 85 fprintf(out,"inet addr:%s 's packet have sent! ",intip_to_ipv4(ip_num)); 86 return 0; 87 }
/src/recv.c
1 /* 2 * recv.c 3 * 4 * Created on: 2015年11月6日 5 * Author: root 6 */ 7 8 #include "ping.h" 9 10 int nrecv=0; 11 struct timeval tvrecv; 12 char recvbuf[PACKET_SIZE]; 13 struct sockaddr_in src_addr; //socket源地址 14 15 int len; //统计收到的包的长度 16 int src_addr_len = sizeof(struct sockaddr_in); 17 18 void tv_sub(struct timeval *out,struct timeval *in) 19 { 20 if ((out->tv_usec-=in->tv_usec) < 0) 21 { 22 --out->tv_sec; 23 out->tv_usec += 1000000; 24 } 25 out->tv_sec -= in->tv_sec; 26 } 27 28 void unpacket(int sockfd, int len,int pid) 29 { 30 //int len; 31 double rtt; 32 int iphdrlen; 33 struct ip *ip; 34 struct icmp *icmp; 35 struct timeval *tvsend; 36 37 gettimeofday(&tvrecv,NULL); 38 ip = (struct ip *)recvbuf; 39 iphdrlen = ip->ip_hl<<2; 40 //printf("%d ",iphdrlen); 41 icmp = (struct icmp *)(recvbuf+iphdrlen); 42 len -= iphdrlen; 43 44 if((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid)) 45 { 46 tvsend = (struct timeval *)icmp->icmp_data; 47 tv_sub(&tvrecv, tvsend);//接收和发送的时间差 48 rtt = tvrecv.tv_sec*1000.0 + (1.0*tvrecv.tv_usec)*0.001;//以毫秒单位计算rtt 49 fprintf(out,"%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms ", len, inet_ntoa(ip->ip_src), icmp->icmp_seq, ip->ip_ttl, rtt); 50 nrecv++; 51 } 52 //sleep(1); 53 } 54 55 void recv_all_packet(int recv_sockfd) 56 { 57 len = recvfrom(recv_sockfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&src_addr, (socklen_t *)&src_addr_len); 58 if(len < 0) 59 perror("recvfrom:"); 60 unpacket(recv_sockfd, len, getpid()); 61 return ; 62 }
/src/file/a.in
1 www.baidu.com 2 www.qq.com 3 www.jd.com 4 www.aminglinux.com 5 map.baidu.com 6 music.baidu.com 7 image.baidu.com 8 zhidao.baidu.com 9 www.tmall.com 10 127.0.0.1