ping 命令的实现

#include<unistd.h>
#include<stdio.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netdb.h>
#include<signal.h>
#include<arpa/inet.h>
#include<netinet/ip.h>
#include<netinet/in.h>
#include<netinet/ip_icmp.h>
#include<setjmp.h>
#include<error.h>
#include<string.h>
#include<sys/types.h>

#define MAX_NO_PACKETS  100000		/*最多发送的报文数*/
#define PACKET_SIZE 4096		/*发送和接受端的缓存大小*/ 

char send_buf[PACKET_SIZE] ;		/* 发送 */
char recv_buf[PACKET_SIZE] ;		/* 接收 */ 
char addr[200] ;
double temp_rtt[MAX_NO_PACKETS] ;
double rtt ,all_time;
double min ,max ,avg ,mdev ;
int size = 50 * 1024 ;
int sockfd , datalen = 56 ;
int nsend = 0 ,nreceived = 0 ;

pid_t pid ;

struct sockaddr_in dest_addr ;
struct sockaddr_in from ; 
struct timeval tvrecv,cost ;

void computer_rtt()
{
	double sum_avg = 0 ;
	int i ;
	min = max = temp_rtt[0] ;
	avg = all_time / nreceived ;
	for(i=0;i<nreceived;++i)
	{
		if(min > temp_rtt[i])
			min = temp_rtt[i] ;
		if(max < temp_rtt[i])
			max = temp_rtt[i] ;
		if(temp_rtt[i] > avg)	
			sum_avg += (temp_rtt[i] - avg) ;
		if(temp_rtt[i] < avg)
			sum_avg += (avg - temp_rtt[i]) ;
	}
	mdev = sum_avg / nreceived ;
}

void statistics(int sig)
{
	computer_rtt();
	printf("\n---------%s ping statistics--------\n",addr);
	printf("%d packets transmitted,%d received,%d%% packet loss,time %.f ms\n",
	        nsend,nreceived,(nsend-nreceived)/nsend*100,all_time);
	printf("rtt  min/avg/max/mdev = %.3f/%.3f/%.3f/%.3f ms\n",min,avg,max,mdev);
	close(sockfd);
	exit(1);
}

unsigned short cal_chksum(unsigned short *addr ,int len)
{
	int sum = 0 ;
	int nleft = len ;
	unsigned short *w = addr ;
	unsigned short check_sum = 0 ;
	while(nleft > 1)
	{
		sum += *w ;
		nleft -= 2 ;
		w ++ ;
	}	
	if(nleft == 1)
	{	
		sum += *(unsigned char *)(w) ;	
	}
	sum = (sum >> 16 ) + (sum & 0xFFFF) ;
	sum += (sum >> 16) ;
	check_sum = ~sum ;
	return check_sum ;
}


int pack(int pack_no)
{
	int packsize ;
	struct icmp *icmp ;
	struct timeval *tval ;
	icmp = (struct icmp *)send_buf ;
	icmp->icmp_type = ICMP_ECHO ;
	icmp->icmp_code = 0 ;
	icmp->icmp_cksum = 0 ;
	icmp->icmp_seq = pack_no ;
	icmp->icmp_id = pid ;
	packsize = 64 ;
	tval = (struct timeval *)icmp->icmp_data ;
	gettimeofday(tval ,NULL);
	icmp->icmp_cksum = cal_chksum((unsigned short *)icmp , packsize);	
	return packsize ;
}

void send_packet()
{
	int packsize ;
	if(nsend < MAX_NO_PACKETS)
	{
		nsend++ ;
		packsize = pack(nsend);
		if(sendto(sockfd , send_buf,packsize ,0 ,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0)
		{
			printf("sendto error.\n");
			exit(1) ;
		}
	}	
}

void tv_sub(struct timeval *send ,struct timeval *recv , struct timeval *cost)
{
	long sec;
	long usec ;
	sec = recv->tv_sec - send->tv_sec ;
	usec = recv->tv_usec - send->tv_usec ;
	if(usec >= 0)
	{
		cost->tv_sec = sec ;
		cost->tv_usec = usec ;
	}
	else{
		cost->tv_sec = sec - 1 ;
		cost->tv_usec = usec + 1e6 ;
	}
}

void unpack(char *buf, int len)
{
	int iphdrlen ;	
	struct ip *ip ;
	struct icmp *icmp ;
	struct timeval *tvsend ;
	ip = (struct ip *)buf ;
	iphdrlen = ip->ip_hl << 2 ;
	icmp = (struct icmp *)(buf + iphdrlen) ;
	len -= iphdrlen ;
	if(len < 8)
	{
		printf("ICMP packet's length is less than 8.\n");
		return ;
	}
	if( (icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid))
	{
		tvsend = (struct timeval *)(icmp->icmp_data);
		tv_sub(tvsend,&tvrecv,&cost);
		rtt = cost.tv_sec * 1000 + cost.tv_usec/1000 ;    /*以ms为单位。*/
		temp_rtt[nreceived] =  rtt ;
		all_time += rtt ;
		printf("%d bytes from %s: icmp_seq=%u ttl=%d time=%.1lf ms\n",len,inet_ntoa(from.sin_addr),icmp->icmp_seq,ip->ip_ttl,rtt);
	}
}

void recv_packet()
{
	int n;
	int fromlen = sizeof(from) ;
	if(nreceived < nsend)
	{
		if( (n=recvfrom(sockfd,recv_buf,sizeof(recv_buf),0,(struct sockaddr *)&from,&fromlen)) < 0)
		{
			printf("recvfrom error.\n");
			exit(1) ;
		}
		gettimeofday(&tvrecv,NULL);
		unpack(recv_buf,n);
		nreceived ++ ;	
	}

}

int main(int argc,char* argv[]){
	struct protoent *protocol ;
	struct hostent *hp ;
	memcpy(addr,argv[0],sizeof(argv[0]));
	if(argc < 2)
	{
		printf("usage:%s hostname/IP\n",argv[0]);
		exit(1) ;	
	}
	if( ( protocol = getprotobyname("icmp") ) == NULL )
	{
		printf("getprotobyname error.\n");	
		exit(1) ;	
	}		
	if( ( sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto) ) < 0)
	{
		printf("socket error.\n");
		exit(1) ;
	}
	setuid(getpid());
	setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size, sizeof(size));
	memset(&dest_addr, 0 , sizeof(&dest_addr));
	dest_addr.sin_family = AF_INET ;
	if( !inet_aton(argv[1] , &dest_addr.sin_addr) )
	{
		hp = gethostbyname(argv[1]) ;
		if( hp == NULL )
		{	
			printf("gethostbyname error.\n");
			exit(1) ;	
		}
		dest_addr.sin_addr = *(struct in_addr *)hp->h_addr ;
	}
	pid = getpid() ;
	printf("PING %s(%s):%d bytes of data.\n",argv[1],inet_ntoa(dest_addr.sin_addr),datalen);
	signal(SIGINT,statistics);
	while(nsend < MAX_NO_PACKETS)
	{
		sleep(1) ;
		send_packet() ;
		recv_packet() ;	
	}	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值