/*
* 编写于2010-9-22晚,姓名:ringk
* ping程序:用于测试网络连接状况
* 参考书籍《Linux网络编程》
* 如有问题,邮箱:gisyhy@163.com, QQ:986418764
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h> //sockaddr_in
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netdb.h>
#include <signal.h>
#include <pthread.h>
//#include "fun.h"
#define SENDBUF 256 //发送时间缓存
#define RECVBUFSIZE 1024 //接受缓存
#define SENDBUFSIZE 72 //发送缓存
typedef struct icmpsend
{
struct timeval sendtime;
int seq;
}icmpsend;
static struct icmpsend psd[SENDBUF];
static struct sockaddr_in dest_addr;
static struct timeval t_sat,t_end; //ping到总时间
static char dest_str[60]; //地址别名
static int alive; //线程保时
static int pid; //本进程id
static int sock;
static int seq_send,seq_recv;//发送和接受到ICMP包个数
static pthread_t psendid,precvid;//发送和接受线程
short icmp_cksum(char *sendbuf,int len)//检验和
{
int sum=0;
int odd= len & 0x1;
char *value =sendbuf;
while(len & 0xfffe){
sum += *((short *)value);
value += 2;
len -=2;
}
if(odd)sum += (short)(((*value)<<8) & 0xff00);//奇偶判断
sum = (sum>>16) + (sum & 0xffff);//注意求和
sum += (sum>>16);
return (~sum) & 0xffff;//注意取反
}
void icmp_pack(struct icmp *picmp,int len)//打包
{
picmp->icmp_type =ICMP_ECHO;//回显请求
picmp->icmp_code=0;
picmp->icmp_cksum=0;
picmp->icmp_id=pid;
picmp->icmp_seq = seq_send & 0xffff;
int i=0;
for(;i<len;i++)
picmp->icmp_data[i]=i;
picmp->icmp_cksum = icmp_cksum((char *)picmp,len);
}
double icmp_sub(struct timeval begin,struct timeval end)//时间计算
{
double sec = end.tv_sec-begin.tv_sec;
double usec = end.tv_usec-begin.tv_usec;
//printf("%.1lf/n",usec);
if(usec <0){
sec -=1.0;
usec += 1000000.0;
}
return sec*1000+usec/1000;
}
icmpsend * findsend(int seq)//在发送时间缓存里找到序列号匹配
{
int i;
if(seq ==-1){
for(i=0;i<SENDBUF;i++)
if(psd[i].seq==0)
return (psd+i);
}
else if(seq >0){
for(i=0;i<SENDBUF;i++)
if(psd[i].seq ==seq)
return (psd+i);
}
return 0;
}
void icmp_unpack(char *rbuf,int len,struct timeval tr)//解包,注意接受到的为IP包
{
struct ip *pip=(struct ip *)rbuf;
struct icmp *picmp= (struct icmp *)(rbuf +pip->ip_hl*4);
if(len-pip->ip_hl*4 <8){
printf("recv data less than 8/n");
return ;
}
if(picmp->icmp_type == ICMP_ECHOREPLY && picmp->icmp_id==pid){
//printf("test goto icmp/n");
seq_recv ++;
struct icmpsend *pcur = findsend(picmp->icmp_seq);
if(pcur==0)return ;
struct timeval ts = pcur->sendtime;
double rtt = icmp_sub(ts,tr);
printf("%d bytes from %s: icmp_seq=%d ttl=%d rtt=%.1lf ms/n",
len-pip->ip_hl*4,
inet_ntoa(pip->ip_src),
pcur->seq,
pip->ip_ttl,
rtt);
pcur->seq =0;
}
}
void icmp_send(void *argv)//发送线程
{
printf("PING %s(%s) 64 bytes of data./n",
inet_ntoa(dest_addr.sin_addr.s_addr),
dest_str);
gettimeofday(&t_sat,0);
struct timeval ts;
char send_buf[SENDBUFSIZE];
int addr_len= sizeof(struct sockaddr_in);
while(alive){
memset(send_buf,0,SENDBUFSIZE);
icmp_pack((struct icmp *)send_buf,64);
gettimeofday(&ts,0);
//printf("test send data/n");
//注意发送的包为ICMP包
if(sendto(sock,send_buf,64,0,(struct sockaddr *)&dest_addr,addr_len)==-1){
printf("send failed/n");
alive=0;
break;
}
icmpsend * psend= findsend(-1);//找到一个空余位置,存放发送到时间
if(psend ==0){
printf("size to max/n");
alive=0;
break;
}
else {
psend->sendtime = ts;
psend->seq = seq_send++;
}
sleep(5);
}
}
void icmp_recv(void *argv)//接受线程
{
char recv_buf[RECVBUFSIZE];
struct timeval tr;
struct timeval timeout;
fd_set readfd;
int sel;
while(alive){
FD_ZERO(&readfd);
FD_SET(sock,&readfd);
timeout.tv_sec=5;
timeout.tv_usec =0;
if((sel=select(sock+1,&readfd,0,0,&timeout)) >0){
//printf("test recv data/n");
//注意接受的包为IP包
int cb =recv(sock,recv_buf,RECVBUFSIZE,0);
//printf("test recv size:%d/n",cb);
gettimeofday(&tr,0);
if(cb <0){
printf("recv error/n");
alive=0;
break;
}
icmp_unpack(recv_buf,cb,tr);
//showpacket(recv_buf,cb);
}
//else if(sel==0)printf("timeout/n");
}
}
void signo(int sig)//SIGINT信号
{
alive =0;
pthread_cancel(psendid);
pthread_cancel(precvid);
}
int main(int argc,char *argv[])
{
char dest[30];
if(argc <2)strcpy(dest,"127.0.0.1");
else strcpy(dest,argv[1]);
memset(psd,0,sizeof(psd));
memset(&dest_addr,0,sizeof(dest_addr));
alive=1;
sock=-1;
seq_send=1;
seq_send =0;
psendid=0;
precvid =0;
pid =getpid();
signal(SIGINT,signo);
struct hostent *hst=0;
int inaddr=inet_addr(dest);
if(inaddr ==INADDR_NONE){
strcpy(dest_str,dest);
hst=gethostbyname(dest);
if(hst ==0){
printf("gethostbyname error/n");
return 0;
}
dest_addr.sin_addr.s_addr= *((int*)*(hst->h_addr_list));
}
else {
//hst=gethostbyaddr((void*)&inaddr,sizeof(inaddr),AF_INET);
//if(hst)strcpy(dest_str,*(hst->h_aliases));
strcpy(dest_str,dest);
dest_addr.sin_addr.s_addr= inaddr;
}
dest_addr.sin_family = AF_INET;
sock=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);//注意协议类型
if(sock <0){
printf("socket error/n");
return 0;
}
if(pthread_create(&psendid,0,&icmp_send,0) <0){
printf("create send thread error/n");
return 0;
}
if(pthread_create(&precvid,0,&icmp_recv,0)<0){
printf("create recv thread error/n");
return 0;
}
pthread_join(psendid,0);
pthread_join(precvid,0);
gettimeofday(&t_end,0);
printf("/n--- %s ping statistics ---/n",inet_ntoa(dest_addr.sin_addr.s_addr));
printf("%d packets transmitted,%d received,%.2lf%c packets loss,time %.0lf ms./n",
seq_send--,
seq_recv,
1.0*(seq_send-seq_recv)/seq_send,
'%',
icmp_sub(t_sat,t_end));
close(sock);
return 0;
}