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

时间:2023-12-15 13:05:32

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

1.信号计时函数的使用

2.ip头的构建和icmp头的构建

3.selec函数t的用法

代码实现:

/src/ping.h

 /*
* ping.h
*
* Created on: 2015年11月6日
* Author: root
*/ #ifndef PING_H_
#define PING_H_ #endif /* PING_H_ */ #include <sys/types.h>
#include <sys/select.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/ip_icmp.h>
#include<errno.h> /*sys/types.h中文名称为基本系统数据类型*/ #define N 200005
#define PACKET_SIZE 1024*4 extern FILE *out;
extern int nsent;
extern int nrecv; extern char * intip_to_ipv4(unsigned int ip);
extern void recv_all_packet(int sockfd);
extern int send_one_packet(int sockfd, unsigned int ip_num, int pid);

/src/ping.h

/src/ping_project.c

 /*
============================================================================
Name : ping_project.c
Author : huh
Version :
Copyright : ---notice---
Description : Hello World in C, Ansi-style
============================================================================
*/ #include "ping.h" FILE *out; int pid;
int the_number_of_ping = ;
int send_sockfd, recv_sockfd;
int ping_num = ; //ip地址数量
unsigned int addr[N]; //ip地址数组
struct sigaction act_alarm;
struct timeval timeout;
struct itimerval val_alarm ={
.it_interval.tv_sec = ,
.it_interval.tv_usec = ,
.it_value.tv_sec = ,
.it_value.tv_usec = };
fd_set init_recv_sockets, recv_sockets;
fd_set init_send_sockets, send_sockets, init_send_sockets_2; int size = * ; void init_ip();
void sig_alrm(int singo); int main(void)
{
init_ip();
out = fopen("./src/file/a.out", "w");
if (out == NULL)
{
perror("stdout error!\n");
}
int flag1, flag2;
pid = getpid();
send_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
//if(send_sockfd < 0) { perror("send_sockfd error:"); return 0; }
flag1 = setsockopt(send_sockfd, IPPROTO_IP, IP_HDRINCL, &size,
sizeof(size));
//if(flag1<0) { perror("setsockopt error:"); return 0; }
recv_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
//if(recv_sockfd < 0) { perror("recv_sockfd error:"); return 0; }
flag2 = setsockopt(recv_sockfd, IPPROTO_IP, IP_HDRINCL, &size,
sizeof(size));
//if(flag2<0) { perror("setsockopt error:"); return 0; } act_alarm.sa_handler = sig_alrm;
if (sigaction(SIGALRM, &act_alarm, NULL) == -)
printf("SIGALRM handler setting fails.\n"); if ((setitimer(ITIMER_REAL, &val_alarm, NULL)) == -) /*定时函数*/
printf("setitimer fails.\n"); int result;
int maxfdp1;
int k = ; FD_ZERO(&init_recv_sockets);
FD_SET(recv_sockfd, &init_recv_sockets); FD_ZERO(&init_send_sockets);
FD_SET(send_sockfd, &init_send_sockets);
init_send_sockets_2 = init_send_sockets; maxfdp1 = recv_sockfd + ;
while () //无限循环,接受包
{
recv_sockets = init_recv_sockets;
send_sockets = init_send_sockets_2;
timeout.tv_sec = ;
timeout.tv_usec = ;
result = select(maxfdp1, &recv_sockets, &send_sockets, NULL, &timeout);
switch (result)
{
case :
printf("共发送了%d个包, 收到了%d个包\n", nsent, nrecv);
printf("timeout:程序结束!\n");
return ;
break;
case -:
if (errno == EINTR)
continue;
perror("select:");
return ;
break;
default:
if (FD_ISSET(recv_sockfd, &recv_sockets))
recv_all_packet(recv_sockfd);
if (FD_ISSET(send_sockfd, &send_sockets))
{
send_one_packet(send_sockfd, addr[k], pid);
k = (k + ) % ping_num;
if (k == )
FD_ZERO(&init_send_sockets_2);
}
break;
}
}
return ;
} void sig_alrm(int singo)
{
the_number_of_ping++;
if (the_number_of_ping <= )
{
printf("ping了第%d遍!\n", the_number_of_ping);
init_send_sockets_2 = init_send_sockets;
} else
{
val_alarm.it_interval.tv_sec = ;
val_alarm.it_interval.tv_usec = ;
val_alarm.it_value = val_alarm.it_interval;
setitimer(ITIMER_REAL, &val_alarm, NULL);
}
} void init_ip() //将要访问的主机全部变成32位无符号的ip。
{
FILE *in;
unsigned int name;
char str[];
struct hostent *host; in = freopen("./src/file/a.in", "r", stdin);
if (in == NULL)
{
printf("stdin error!\n");
} while (!feof(in))
{
scanf("%s", str); //ip地址或域名
name = inet_addr(str); //将一个点分十进制IP转化为长整数
if (name == INADDR_NONE)
{
host = gethostbyname(str);
if (host == NULL)
{
printf("参数格式不正确,请重新输入!\n");
continue;
}
memcpy((char*) &name, host->h_addr, );
}
addr[ping_num] = name;
printf("%s:%s\n", str, intip_to_ipv4(name));
ping_num++;
}
printf("程序将 ping %d 个IP.\n", ping_num);
}

/src/ping_project.c

/src/send.c

 /*
* send.c
*
* Created on: 2015年11月6日
* Author: root
*/ #include "ping.h" int icmp_len;
int flag;
int nsent=;
int datalen = ;
struct icmp *icmp;
char sendbuf[PACKET_SIZE];
struct sockaddr_in dest_addr; //socket目的地址 unsigned short cal_chksum(unsigned short *addr, int len)
{
int nleft = len;
int sum = ;
unsigned short *w = addr;
unsigned short answer = ;
//把ICMP报头二进制数据以2字节为单位累加起来
while (nleft > )
{
sum += *w++;
nleft -= ;
}
if (nleft == )
{
*(unsigned char *)(&answer) = *(unsigned char *)w;
sum += answer;
}
sum = (sum>>) + (sum&0xffff);
sum += (sum>>);
answer = ~sum;
return answer;
} char * intip_to_ipv4(unsigned int ip)
{
char *str;
struct in_addr des;
des.s_addr = ip;
str = inet_ntoa(des);
//printf("---%s---\n",str);
return str;
} int send_one_packet(int sockfd, unsigned int ip_num, int pid)
{
struct iphdr *ip;
ip = (struct iphdr *)sendbuf;
ip->ihl = sizeof(struct iphdr) >> ; //首部长度
ip->version = ; //ip协议版本
ip->tos = ; //服务类型字段
ip->tot_len = ; //总长度
ip->id = ;
ip->frag_off = ;
ip->ttl = ;
ip->protocol = IPPROTO_ICMP;
ip->check = ; //让内核算 dest_addr.sin_family = AF_INET;
memcpy((char *)&dest_addr.sin_addr, (char *)&ip_num,sizeof(ip_num));
//ip->saddr = src_addr.sin_addr.s_addr;
ip->daddr = dest_addr.sin_addr.s_addr; icmp = (struct icmp *)(sendbuf + sizeof(struct iphdr));
icmp->icmp_type = ICMP_ECHO; //拼接icmp
icmp->icmp_code = ;
icmp->icmp_id = pid; //2字节
icmp->icmp_seq = ++nsent; //2字节
memset(icmp->icmp_data, 0xa5, datalen);
gettimeofday((struct timeval *)icmp->icmp_data, NULL); //将发送时间作为数据传递过去 icmp_len = datalen + ;
icmp->icmp_cksum = ;
icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, icmp_len); flag = sendto(sockfd, sendbuf, ip->tot_len, , (struct sockaddr *)&dest_addr, sizeof(dest_addr)); //将包发出去
//if(flag < 0) { printf("sendto error!\n"); return 0; } fprintf(out,"inet addr:%s 's packet have sent!\n",intip_to_ipv4(ip_num));
return ;
}

/src/send.c

/src/recv.c

 /*
* recv.c
*
* Created on: 2015年11月6日
* Author: root
*/ #include "ping.h" int nrecv=;
struct timeval tvrecv;
char recvbuf[PACKET_SIZE];
struct sockaddr_in src_addr; //socket源地址 int len; //统计收到的包的长度
int src_addr_len = sizeof(struct sockaddr_in); void tv_sub(struct timeval *out,struct timeval *in)
{
if ((out->tv_usec-=in->tv_usec) < )
{
--out->tv_sec;
out->tv_usec += ;
}
out->tv_sec -= in->tv_sec;
} void unpacket(int sockfd, int len,int pid)
{
//int len;
double rtt;
int iphdrlen;
struct ip *ip;
struct icmp *icmp;
struct timeval *tvsend; gettimeofday(&tvrecv,NULL);
ip = (struct ip *)recvbuf;
iphdrlen = ip->ip_hl<<;
//printf("%d\n",iphdrlen);
icmp = (struct icmp *)(recvbuf+iphdrlen);
len -= iphdrlen; if((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid))
{
tvsend = (struct timeval *)icmp->icmp_data;
tv_sub(&tvrecv, tvsend);//接收和发送的时间差
rtt = tvrecv.tv_sec*1000.0 + (1.0*tvrecv.tv_usec)*0.001;//以毫秒单位计算rtt
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);
nrecv++;
}
//sleep(1);
} void recv_all_packet(int recv_sockfd)
{
len = recvfrom(recv_sockfd, recvbuf, sizeof(recvbuf), , (struct sockaddr *)&src_addr, (socklen_t *)&src_addr_len);
if(len < )
perror("recvfrom:");
unpacket(recv_sockfd, len, getpid());
return ;
}

/src/recv.c

/src/file/a.in

 www.baidu.com
www.qq.com
www.jd.com
www.aminglinux.com
map.baidu.com
music.baidu.com
image.baidu.com
zhidao.baidu.com
www.tmall.com
127.0.0.1

/src/file/a.in