003.同时Ping多个IP(select实现IO复用,信号计时),ping程序升级版

写这个的目的主要是为了以后的方便:

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.h

/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!\n");
 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.\n");
 59 
 60     if ((setitimer(ITIMER_REAL, &val_alarm, NULL)) == -1) /*定时函数*/
 61         printf("setitimer fails.\n");
 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个包\n", nsent, nrecv);
 86             printf("timeout:程序结束!\n");
 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遍!\n", 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!\n");
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("参数格式不正确,请重新输入!\n");
150                 continue;
151             }
152             memcpy((char*) &name, host->h_addr, 4);
153         }
154         addr[ping_num] = name;
155         printf("%s:%s\n", str, intip_to_ipv4(name));
156         ping_num++;
157     }
158     printf("程序将 ping %d 个IP.\n", ping_num);
159 }
/src/ping_project.c

/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---\n",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!\n");    return 0; }
84 
85     fprintf(out,"inet addr:%s 's packet have sent!\n",intip_to_ipv4(ip_num));
86     return 0;
87 }
/src/send.c

/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\n",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\n", 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/recv.c

/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
/src/file/a.in

 

posted @ 2015-11-19 20:46  ruo_yu  阅读(693)  评论(0编辑  收藏  举报