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

时间: 2024-11-03 21:37:21

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

TCP/IP 网络编程 (抄书笔记 5) -- select 和 IO 复用

TCP/IP 网络编程 (抄书笔记 5) – select 和 IO 复用 TCP/IP 网络编程 (抄书笔记 5) – select 和 IO 复用 利用 fork() 生成子进程 可以达到 服务器端可以同时响应多个 客户端的请求, 但是这样做有缺点: 需要大量的运算和内存空间, 每个进程都要有独立的内存空间, 数据交换也很麻烦 (IPC, 如管道) IO 复用: 以太网的总线结构也是采用了 复用技术, 如果不采用, 那么两两之间就要直接通信 网络知识 int server_sock; int

IO复用之——select

一. select 前面提到Linux下的五种IO模型中有一个是IO复用模型,这种IO模型是可以调用一个特殊的函数同时监听多个IO事件,当多个IO事件中有至少一个就绪的时候,被调用的函数就会返回通知用户进程来处理已经ready事件的数据,这样通过同时等待IO事件来代替单一等待一个IO窗口数据的方式,可以大大提高系统的等待数据的效率:而接下来,就要讨论在Linux系统中提供的一个用来进行IO多路等待的函数--select: 二. select函数的用法 首先在使用select之前,要分清在IO事件

[Z] linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO

原文链接:http://blog.csdn.net/colzer/article/details/8169075 IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据

linux基础编程:IO模型:阻塞/非阻塞/IO复用 同步/异步 Select/Epoll/AIO(转载)

IO概念 Linux的内核将所有外部设备都可以看做一个文件来操作.那么我们对与外部设备的操作都可以看做对文件进行操作.我们对一个文件的读写,都通过调用内核提供的系统调用:内核给我们返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有相应的描述符,称为socketfd(socket描述符).描述符就是一个数字,指向内核中一个结构体(文件路径,数据区,等一些属性).那么我们的应用程序对文件的读写就通过对描述符的读写完成. linux将内存分为内核区,用户区.l

查询指定网段Ping通的IP和主机名

$path="C:\Users\username\Desktop\" $ping = New-Object system.net.networkinformation.ping  70..80 | % {$ping.send("10.0.9.$_") | select address,status} | out-file -filepath "$path ping.txt" $IP= Get-Content "$path ping.tx

Fedora-20上ping本机IP地址时的问题

在Fedora 20上写了一个简单的ping程序,创建原始套接字后没有设置任何选项,ping其他主机(NetBSD-1.0)的时候运行正常: [[email protected] network]# ./ping 192.168.1.114 PING 192.168.1.114: send packet(icmp): 08 00 cc 95 0d 4c 00 00 a5 a5 .....L.... a5 a5 a5 a5 a5 a5 a5 a5 a5 a5 .......... a5 a5 a5

ping主机的ip地址,判断以及自动安装sysstat软件包 等脚本

ping主机的ip地址.返回结果 vi pinghost.sh #!/bin/bash ping -c 3 -i 0.2 -W 3 $1 &> /dev/null if [ $? -eq 0 ] then echo "Host $1 is up." else echo "Host $1 is down." fi sh pinghost.sh ip地址 判断分数所在的区间,给出不同的分档结果 #!/bin/bash read -p "请输入您的

windows与linux ping 显示的ip不一样

DNS修改了一下域名对应的IP后,域名不能访问了,我在windows下ping一下域名,IP没有变过来,还是老的IP.我在linux下又ping了一下域名,是换过了的.这个问题是由windows下的本地DNS缓存刷新时间过长引起的. 先解释一下windows的dns本地缓存是怎么回事. 你访问一个域名,并不是每一次都要DNS服务器来解析的.访问一次后,DNS的解析会在本地缓存起来,根据TTL,每隔一段时间会刷新一次.这个TTL的时间,不是固定的,刷新时新TTL会替换老的TTL. C:UsersA

一次读懂 Select、Poll、Epoll IO复用技术

我们之前采用的多进程方式实现的服务器端,一次创建多个工作子进程来给客户端提供服务.其实这种方式是存在问题的. 可以打个比方:如果我们先前创建的几个进程承载不了目前快速发展的业务的话,是不是还得增加进程数?我们都知道系统创建进程是需要消耗大量资源的,所以这样就会导致系统资源不足的情况. 那么有没有一种方式可以让一个进程同时为多个客户端端提供服务? 接下来要讲的IO复用技术就是对于上述问题的最好解答. 对于IO复用,我们可以通过一个例子来很好的理解它.(例子来自于<TCP/IP网络编程>) 某教室