Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1423685
  • 博文数量: 122
  • 博客积分: 340
  • 博客等级: 一等列兵
  • 技术积分: 2967
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-01 11:50
个人简介

说点什么呢

文章分类

全部博文(122)

文章存档

2018年(2)

2017年(1)

2015年(2)

2014年(30)

2013年(81)

2011年(5)

2009年(1)

分类: C/C++

2011-02-17 16:56:12

 

  1. /************************************************************
  2. ******c program of ping ************************************
  3. ******Author: Wayne Wang ***********************************
  4. ******Website: **************************
  5. ******Version:V1.0 *****************************************
  6. ******Modified:Feb 17 2011 *********************************
  7. ************************************************************/


  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <signal.h>
  12. #include <arpa/inet.h>
  13. #include <sys/types.h>
  14. #include <sys/socket.h>
  15. #include <unistd.h>
  16. #include <netinet/in.h>
  17. #include <netinet/ip.h>
  18. #include <netinet/ip_icmp.h>
  19. #include <netdb.h>
  20. #include <setjmp.h>
  21. #include <errno.h>
  22. #include <sys/time.h>

  23. #define bool int
  24. #define PACKET_SIZE 4096
  25. #define MAX_WAIT_TIME 3
  26. #define MAX_NO_PACKETS 3
  27. #define _DEBUG_ 0
  28.                                                                                       
  29. char sendpacket[PACKET_SIZE];
  30. char recvpacket[PACKET_SIZE];
  31. int sockfd, datalen = 8;
  32. int nsend = 0, nreceived = 0;
  33. struct sockaddr_in dest_addr;
  34. pid_t pid;
  35. struct sockaddr_in from;
  36. struct timeval tvrecv;
  37. int recvflag = 1;
  38.                                                                                       
  39. void statistics(int signo);
  40. void requesttimeout(int signo);
  41. unsigned short cal_chksum(unsigned short *addr,int len);
  42. int pack(int pack_no);
  43. void send_packet(void);
  44. void recv_packet(void);
  45. int unpack(char *buf,int len);
  46. int pingcount = 0, pinginterval = 0, timeout = 0 ;
  47. void tv_sub(struct timeval *out,struct timeval *in);

  48. void requesttimeout(int signo)
  49. {
  50.     recvflag = 0;
  51.     alarm(0);
  52.     printf("Request time out\n");     
  53.     return ;
  54. }
  55.      
  56. void statistics(int signo)
  57. {
  58.     float lostrate = (float)(nsend - nreceived) / nsend*100;
  59.     printf("\n--------------------PING statistics-------------------\n");
  60.     //printf("%d packets transmitted, %d received , %%%f lost\n", nsend, nreceived, (nsend-nreceived)/nsend*100);

  61.     printf(" packets transmitted: %d, received: %d , lost: %d (%.2f%%)\n", nsend, nreceived,(nsend - nreceived), lostrate);
  62.     close(sockfd);
  63.     exit(1);
  64. }
  65.                                                                                       
  66. /*校验和算法*/
  67.                                                                                       
  68. unsigned short cal_chksum(unsigned short *addr,int len)
  69. {
  70.     int nleft = len;
  71.     int sum = 0;
  72.     unsigned short *w = addr;
  73.     unsigned short answer = 0;
  74.     /*把ICMP报头二进制数据以2字节为单位累加起来*/
  75.     while (nleft > 1)
  76.     {
  77.         sum += *w++;
  78.         nleft -= 2;
  79.     }
  80.     /*若ICMP报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的低字节?*/ if (nleft == 1)
  81.     {
  82.         //*(unsigned char *)(&answer)=*(unsigned char *)w;

  83.         //answer = (*(unsigned char *)w) << 8;

  84.         //printf("answer = %x\n", answer);

  85.         sum += *(unsigned char *)w;
  86.         printf("sum = %x\n", sum);
  87.     }
  88.     sum = (sum>>16) + (sum&0xffff);
  89.     sum += (sum>>16);
  90.     answer =~ sum;
  91.     return answer;
  92. }
  93.                                                                                      
  94. /*设置ICMP报头*/
  95.                                                                                       
  96. int pack(int pack_no)
  97. {
  98.     int packsize;
  99.     struct icmp *icmp;
  100.     struct timeval *tval;
  101.     icmp = (struct icmp*)sendpacket;
  102.     icmp->icmp_type = ICMP_ECHO;
  103.     icmp->icmp_code = 0;
  104.     icmp->icmp_cksum = 0;
  105.     icmp->icmp_seq = pack_no;
  106.     icmp->icmp_id = pid;
  107.     packsize = 8 + datalen;
  108.     tval = (struct timeval *)icmp->icmp_data;
  109.     gettimeofday(tval, NULL); /*记录发送时间*/
  110.     #if _DEBUG_
  111.     printf("sec = %ld, usec = %ld\n", tval->tv_sec, tval->tv_usec);
  112.     #endif
  113.     icmp->icmp_cksum = cal_chksum( (unsigned short *)icmp,packsize); /*校验算法*/
  114.     #if _DEBUG_
  115.     printf("cksum = 0x%x\n", icmp->icmp_cksum);
  116.     #endif
  117.     return packsize;
  118. }
  119.                                                                                       
  120. /*发送三个ICMP报文*/
  121.                                                                                       
  122. void send_packet()
  123. {
  124.     int packetsize;
  125.     {
  126.         nsend++;
  127.         packetsize = pack(nsend); /*设置ICMP报头*/
  128.         #if _DEBUG_
  129.             printf("nsend = %d, packetsize = %d\n", nsend, packetsize);
  130.         #endif
  131.         if (sendto(sockfd, sendpacket, packetsize, 0,
  132.                 (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0)
  133.         {
  134.             perror("sendto error");
  135.             return;
  136.         }
  137.         //usleep(1000); /*每隔一秒发送一个ICMP报文*/

  138.     }
  139. }
  140.                                                                                       
  141. /*接收所有ICMP报文*/
  142.                                                                                       
  143. void recv_packet()
  144. {
  145.     int n, count = 0;
  146.     socklen_t fromlen;
  147.     extern int errno;
  148.     recvflag = 1;
  149.     fd_set rfds;
  150.     struct timeval tv;
  151.     struct timeval starttv;
  152.     struct timeval nowtv;
  153.     double betime;
  154.     int retval = 0, unpack_status = 0;
  155.     gettimeofday(&starttv, NULL);
  156.     tv.tv_sec = timeout;
  157.     tv.tv_usec = 0;
  158.     while (recvflag == 1)
  159.     {
  160.         fromlen = sizeof(from);
  161.         FD_ZERO(&rfds);
  162.         FD_SET(sockfd, &rfds);
  163.         retval = select((sockfd + 1),&rfds, NULL,NULL,&tv);
  164.         if(retval)
  165.         {
  166.             if ((n = recvfrom(sockfd, recvpacket, sizeof(recvpacket),MSG_DONTWAIT,
  167.                 (struct sockaddr *)&from, &fromlen)) < 0)
  168.             {
  169.                 if (errno == EINTR)
  170.                 {
  171.                     printf("errno == EINTR\n");
  172.                 }
  173.                 #if _DEBUG_
  174.                     printf("n is:%d\n",n);
  175.                 #endif
  176.                 return ;
  177.             }
  178.         unpack_status = unpack(recvpacket, n);
  179.         if(unpack_status == 1)
  180.         {
  181.             nreceived++;
  182.             return ;
  183.         }
  184.         else if(unpack_status == -3)
  185.         {
  186.             return ;
  187.         }
  188.         else if(unpack_status == -2)
  189.         {
  190.             gettimeofday(&nowtv, NULL);
  191.             tv_sub(&nowtv, &starttv);
  192.             betime = (nowtv.tv_sec*1000 + (double)nowtv.tv_usec/1000)/1000;
  193.             if(betime >= timeout)
  194.             {
  195.                 printf("Request time out \n");
  196.                 return;
  197.             }
  198.             else
  199.             {
  200.                 //tv_sub(&tv, nowtv);

  201.                 continue;
  202.             }
  203.         }
  204.         else
  205.         {
  206.             printf("Protocol data error\n");
  207.             recvflag = 0;
  208.             return;
  209.         }
  210.      }
  211.      else
  212.      {
  213.          printf("Request time out \n");
  214.          return ;
  215.      }
  216.     }
  217.     return;
  218. }
  219.                                                                                       
  220. /*剥去ICMP报头*/
  221.                                                                                       
  222. int unpack(char *buf,int len)
  223. {
  224.     int iphdrlen;
  225.     struct ip *ip;
  226.     struct icmp *icmp;
  227.     struct timeval *tvsend;
  228.     double rtt;
  229.     ip = (struct ip *)buf;
  230.     iphdrlen = ip->ip_hl<<2; /* ip_hl is the words of ip header*/
  231.     icmp = (struct icmp *)(buf+iphdrlen); /*越过ip报头,指向ICMP报头*/
  232.     len -= iphdrlen; /*ICMP报头及ICMP数据报的总长度*/
  233.     if (len < 8) /*小于ICMP报头长度则不合理*/
  234.     {
  235.         printf("ICMP packets\'s length is less than 8\n");
  236.         return -1;
  237.     }
  238.     /*确保所接收的是我所发的的ICMP的回应*/
  239.     //printf("%d, icmp_id = %d\n", ICMP_ECHOREPLY, icmp->icmp_id);

  240.     if ((icmp->icmp_type == ICMP_ECHOREPLY) && (icmp->icmp_id == pid))
  241.     {
  242.         gettimeofday(&tvrecv, NULL);
  243.         #if _DEBUG_
  244.             printf("sec = %ld, usec = %ld\n", tvrecv.tv_sec, tvrecv.tv_usec);
  245.         #endif
  246.         tvsend = (struct timeval *)icmp->icmp_data;
  247.         tv_sub(&tvrecv, tvsend); /*接收和发送的时间差*/
  248.         rtt = tvrecv.tv_sec*1000 + (double)tvrecv.tv_usec/1000; /*以毫秒为单位计算rtt*/
  249.         /*显示相关信息*/
  250.         printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",
  251.         len, inet_ntoa(from.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
  252.         return 1;
  253.     }
  254.     else if( icmp->icmp_type == 3)
  255.     {
  256.      printf("From:%s Destination host unreachable \n",inet_ntoa(from.sin_addr));
  257.         return -3;
  258.     }
  259.     else if (icmp->icmp_id != pid )
  260.     {
  261.         return -2;
  262.     }
  263.     else
  264.     {
  265.         return -1;
  266.     }
  267. }
  268.                                                                                      
  269. int main(int argc,char *argv[])
  270. {
  271.     struct hostent *host;
  272.     struct protoent *protocol;
  273.     unsigned long inaddr = 0l;
  274.     int i;
  275.     char *desthost;
  276.     bool runflag=1;
  277.     memset(sendpacket, 'a', sizeof(sendpacket)); // set sendpacket's content to "aaaaaaa"

  278.     int size = 50*1024;
  279.     if (argc < 2)
  280.     {
  281.         printf("usage:%s [-c count] [-i interval] [-t time of timeout] [-h show help message] destination(hostname/IP address)\n",argv[0]);
  282.         exit(1);
  283.     }
  284.     desthost=argv[argc-1];
  285.     for(i=1; i<argc; i++)
  286.     {
  287.         if( !strcmp(argv[i], "-c")){
  288.              pingcount = atoi(argv[(i+1)]);
  289.             // printf("cccccc:%d\n",pcount);

  290.         }
  291.         if( !strcmp(argv[i], "-i")) pinginterval = atoi(argv[(i+1)]);
  292.         if( !strcmp(argv[i], "-t")) timeout = atoi(argv[(i+1)]);
  293.         if( !strcmp(argv[i], "-h"))
  294.         {
  295.              printf("This is a ping tool\n");
  296.              printf("usage:%s [-c count] [-i interval] [-t time of timeout] [-h show help message] destination(hostname/IP address)\n",argv[0]);
  297.              exit(1);
  298.         }
  299.     }
  300.     //pcount,pinterval,ptimeout

  301.     if(pinginterval == 0) pinginterval = 1;
  302.     if(timeout == 0) timeout = MAX_WAIT_TIME;
  303.     if ((protocol = getprotobyname("icmp")) == NULL)
  304.     {
  305.         perror("getprotobyname");
  306.         exit(1);
  307.     }
  308.                                                                                       
  309.     /*生成使用ICMP的原始套接字,这种套接字只有root才能生成*/
  310.     #if _DEBUG_
  311.         printf("the protocol number used is %d\n", protocol->p_proto);
  312.     #endif
  313.     if ((sockfd = socket(AF_INET, SOCK_RAW, protocol->p_proto)) < 0)
  314.     {
  315.         perror("socket error");
  316.         exit(1);
  317.     }
  318.     /* 回收root权限,设置当前用户权限*/
  319.     //setuid(getuid());

  320.     /*扩大套接字接收缓冲区到50K这样做主要为了减小接收缓冲区溢出的
  321.     的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答*/
  322.     setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
  323.     bzero(&dest_addr,sizeof(dest_addr));
  324.     dest_addr.sin_family = AF_INET;
  325.     /*判断是主机名还是ip地址*/
  326.     //if ((inaddr = inet_addr(argv[1])) == INADDR_NONE)

  327.     if ((inaddr = inet_addr(desthost)) == INADDR_NONE)
  328.     {
  329.         if ((host = gethostbyname(desthost)) == NULL) /*是主机名*/
  330.         {
  331.             perror("gethostbyname error");
  332.             exit(1);
  333.         }
  334.         memcpy( (char *)&dest_addr.sin_addr, host->h_addr, host->h_length);
  335.     }
  336.     else /*是ip地址*/
  337.     {
  338.         memcpy( (char *)&dest_addr.sin_addr.s_addr, (char *)&inaddr, sizeof(inaddr));
  339.     }
  340.                                                                                       
  341.     /*获取main的进程id,用于设置ICMP的标志符*/
  342.     pid = getpid();
  343.     //printf("main pid:%d\n",pid);

  344.     printf("PING %s(%s): %d bytes data in ICMP packets.\n",desthost,
  345.         inet_ntoa(dest_addr.sin_addr),datalen);
  346.     // signal(SIGALRM, requesttimeout);

  347.     signal(SIGINT, statistics);
  348.     //while (nsend < MAX_NO_PACKETS)

  349.     while (runflag)
  350.     {
  351.         send_packet(); /*发送所有ICMP报文*/
  352.     //    printf("aaaaaaaaaa\n");

  353.         recv_packet(); /*接收所有ICMP报文*/
  354.         sleep(pinginterval);
  355.         if((pingcount != 0) && (nsend >= pingcount)) runflag = 0;
  356.     }
  357.     statistics(SIGALRM); /*进行统计*/
  358.     return 0;
  359. }
  360.                                                                                       
  361. /*两个timeval结构相减*/
  362.                                                                                       
  363. void tv_sub(struct timeval *out, struct timeval *in)
  364. {
  365.   if ((out->tv_usec -= in->tv_usec) < 0)
  366.   {
  367.     --out->tv_sec;
  368.     out->tv_usec += 1000000;
  369.   }
  370.   out->tv_sec -= in->tv_sec;
  371. }
阅读(2359) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~