实现ping命令的代码2015-05-03
#include<stdio.h>#include<sys/time.h>#include<signal.h>#include<arpa/inet.h>#include<sys/types.h>#include<sys/socket.h>#include<unistd.h>#include<netinet/in.h>#include<netinet/ip.h>#include<netdb.h>#include<setjmp.h>#include<errno.h>#include<netinet/ip_icmp.h>#include<string.h>#include<stdlib.h>#definePACKET_SIZE 4096/**/#define MAX_WAIT_TIME 5#define MAX_NO_PACKETS 3char sendpacket[PACKET_SIZE];char recvpacket[PACKET_SIZE];int sockfd,datalen=56;int nsend=0,nreceived=0;struct sockaddr_in dest_addr;pid_t pid;struct sockaddr_in from;struct timeval tvrecv;void statistics(int signo);unsigned short cal_chksum(unsigned short *addr,int len);int pack(int pack_no);void send_packet(void);void recv_packet(void);int unpack(char* buf,int len);void tv_sub(struct timeval *out,struct timeval* in);void statistics(int signo){ printf("
-----------------PING statistics----------------
"); printf("%d packet transmitted,%d received,%%%d lost
",nsend,nreceived,(nsend-nreceived)/nsend*100); close(sockfd); exit(1);}/*校验和算法*/unsigned short cal_chksum(unsigned short *addr,int len){ int nleft=len; int sum=0; unsigned short *w=addr; unsigned short answer=0; /*把ICMP报头二进制数据以2字节为单位累加起来*/ while(nleft>1) { sum+=*w++; nleft-=2; } /*若ICMP报头为奇数个字节,会剩下最后一个字节。把最后一个字节视为2字节数据的高字节,这个2字节数据的低字节为0,继续累加*/ if(nleft==1) { *(unsigned char *)(&answer)=*(unsigned char *)w; sum+=answer; } sum=(sum>>16)+(sum&0xffff); sum+=(sum>>16); answer=~sum; return answer;}/*设置ICMP报头*/int pack(int pack_no){ int packsize; struct icmp *icmpp; struct timeval *tval; icmpp=(struct icmp *)sendpacket; icmpp->icmp_type=ICMP_ECHO; icmpp->icmp_code=0; icmpp->icmp_cksum=0; icmpp->icmp_seq=pack_no; icmpp->icmp_id=pid; packsize=8+datalen; tval=(struct timeval *)icmpp->icmp_data; gettimeofday(tval,NULL);/*记录发送时间*/ icmpp->icmp_cksum=cal_chksum((unsigned short*)icmpp,packsize);/*校验算法*/ return packsize;}/*发送3个ICMP报文*/void send_packet(){ int packetsize; while(nsend<MAX_NO_PACKETS) { nsend++; packetsize=pack(nsend);/*设置ICMP报文*/ if(sendto(sockfd,sendpacket,packetsize,0,(struct sockaddr *)&dest_addr,sizeof(dest_addr))<0) { perror("sendto error"); continue; } sleep(1);/*每隔一秒发送一个ICMP报文*/ }}/*接收所有ICMP报文*/void recv_packet(){ int n,fromlen; extern int errno;/**/ signal(SIGALRM,statistics); fromlen=sizeof(from); while(nreceived<10) { alarm(MAX_WAIT_TIME); if((n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),0,(struct sockaddr *)&from,&fromlen))<0) { if(errno==EINTR) { continue; } perror("recvfrom error
"); continue; } gettimeofday(&tvrecv,NULL); /*记录接收时间*/ if(unpack(recvpacket,n)==-1) continue; nreceived++; }}/*剥去ICMP报头*/int unpack(char *buf,int len){ int iphdrlen; struct ip *ipp; struct icmp *icmpp; struct timeval *tvsend; int rtt; ipp=(struct ip *)buf; iphdrlen=(ipp->ip_hl)*4;/*求IP报头长度,即IP报头的长度标志乘4*/ icmpp=(struct icmp*)(buf+iphdrlen);/*超过IP报头,指向ICMP报头*/ len-=iphdrlen;/*ICMP报头及ICMP数据报的总长度*/ if(len<8)/*小于ICMP报头长度则不合理*/ { printf("ICMP packets"s length is less than 8
"); return -1; } /*确保所接收的是用户所发的ICMP的回应*/ if((icmpp->icmp_type==ICMP_ECHOREPLY)&&(icmpp->icmp_id==pid)) { tvsend=(struct timeval *)icmpp->icmp_data; tv_sub(&tvrecv,tvsend); /*接收和发送的时间差*/ rtt=tvrecv.tv_sec*1000+tvrecv.tv_usec/1000;/*以毫秒为单位计算rtt*/ /*显示相关信息*/ printf("%d byte from %s:icmp_seq=%u ttl=%d rtt=%dms
", len,inet_ntoa(from.sin_addr),icmpp->icmp_seq,ipp->ip_ttl,rtt); } else return -1;}void tv_sub(struct timeval *out,struct timeval *in){ if((out->tv_usec-=in->tv_usec)<0) { out->tv_sec-=1; out->tv_usec+=1000000; } out->tv_sec-=in->tv_sec;}int main(int argc,char **argv){ struct hostent *host; struct protoent *protocol; int size=50*1024; if(argc<2) { printf("usage : %s hostname/IP address
",argv[0]); exit(1); } if((protocol=getprotobyname("icmp"))==NULL) { perror("getprotobyname
"); exit(1); } /*生成使用ICMP的原始套接字,这种套接字只是root才能生成*/ if((sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto))<0) { perror("socket error
"); exit(1); } /*回收root权限,设置当前用户权限*/ setuid(getuid()); /*扩大套接字接收缓冲区到50K,这样做主要为了减少接收缓冲区溢出的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答*/ setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&size,sizeof(size)); bzero(&dest_addr,sizeof(dest_addr)); dest_addr.sin_family=AF_INET; if((host=gethostbyname(argv[1]))==NULL) { perror("gethostbyname error
"); exit(1); } dest_addr.sin_addr=*((struct in_addr *)host->h_addr); pid=getpid(); printf("PING %s(%s): %d bytes data in ICMP packets.
",argv[1],inet_ntoa(dest_addr.sin_addr),datalen); send_packet();/*发送所有ICMP报文*/ recv_packet();/*接收所有ICMP报文*/ statistics(SIGALRM);/*进行统计*/ return 0;}
本文出自 “好好活着” 博客,请务必保留此出处http://wolfword.blog.51cto.com/4892126/1225685