仿ping程序(linux网络编程

/* * ===================================================================================== * * Filename: ping.c * * Description: * * Version: 1.0 * Created: 2012年02月25日 23时04分36秒 * Revision: none * Compiler: gcc * * Author: MaZheng (blog.csdn.net/mazheng1989), mazheng19891019@gmail.com * Company: Dalian University Of Technology * * ===================================================================================== */ #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> #define PACKET_SIZE 4096 /* */ #define MAX_WAIT_TIME 5 #define MAX_NO_PACKETS 3 char 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("\n-----------------PING statistics----------------\n"); printf("%d packet transmitted,%d received,%%%d lost \n",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\n"); 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\n"); 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=%d ms\n", 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 \n",argv[0]); exit(1); } if((protocol=getprotobyname("icmp"))==NULL) { perror("getprotobyname\n"); exit(1); } /*生成使用ICMP的原始套接字,这种套接字只是root才能生成*/ if((sockfd=socket(AF_INET,SOCK_RAW,protocol->p_proto))<0) { perror("socket error\n"); 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\n"); exit(1); } dest_addr.sin_addr=*((struct in_addr *)host->h_addr); pid=getpid(); printf("PING %s(%s): %d bytes data in ICMP packets.\n",argv[1],inet_ntoa(dest_addr.sin_addr),datalen); send_packet();/*发送所有ICMP报文*/ recv_packet();/*接收所有ICMP报文*/ statistics(SIGALRM);/*进行统计*/ return 0; }

编译:$:gcc -o ping ping.c

授权: $: sudo chown root ping

$:sudo chown u+s ping

运行: $./ping target(target为目标地址


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值