简单ping实现-凯发app官方网站

凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 752958
  • 博文数量: 144
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1150
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-17 14:32
个人简介

小公司研发总监,既当司令也当兵!

文章分类

全部博文(144)

相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: linux

2015-06-04 12:32:06

icmp(internet control message,网际控制报文协议)是为网关和目标主机而提供的一种差错控制机制,使它们在遇到差错时能把错误报告给报文源发方。icmp协议是ip层的一个协议,但是由于差错报告在发送给报文源发方时可能也要经过若干子网,因此牵涉到路由选择等问题,所以icmp报文需通过ip协议来发送。icmp数据报的数据发送前需要两级封装:首先添加icmp报头形成icmp报文,再添加ip报头形成ip数据报。
ip报头格式

由于ip层协议是一种点对点的协议,而非端对端的协议,它提供无连接的数据报服务,没有端口的概念,因此很少使用bind()和connect() 函数,若有使用也只是用于设置ip地址。发送数据使用sendto()函数,接收数据使用recvfrom()函数。ip报头格式如下图:

 
其中ping程序只使用以下数据:

  • ip报头长度ihl(internet header length)?d?d以4字节为一个单位来记录ip报头的长度,是上述ip数据结构的ip_hl变量。
  • 生存时间ttl(time to live)?d?d以秒为单位,指出ip数据报能在网络上停留的最长时间,其值由发送方设定,并在经过路由的每一个节点时减一,当该值为0时,数据报将被丢弃,是上述ip数据结构的ip_ttl变量。

icmp报头格式

icmp报文分为两种,一是错误报告报文,二是查询报文。每个icmp报头均包含类型、编码和校验和这三项内容,长度为8位,8位和16位,其余选项则随icmp的功能不同而不同。

ping命令只使用众多icmp报文中的两种:"请求回送'(icmp_echo)和"请求回应'(icmp_echoreply)。在linux中定义如下:

 #define icmp_echo   0
#define icmp_echoreply  8


这两种icmp类型报头格式如下:

 


下面是自己实现的一个ping的演示程序,该程序需要root权限运行(raw socket, 系统的ping工具为何不需要root权限:因为ping工具的所有者是root,且其带有-s标记,所以使用时不需要加root权限):


点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <arpa/inet.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <unistd.h>
  7. #include <netinet/in.h>
  8. #include <netinet/ip.h>
  9. #include <netinet/ip_icmp.h>
  10. #include <netdb.h>
  11. #include <setjmp.h>
  12. #include <errno.h>

  13. #define packet_size 4096
  14. #define max_wait_time 5
  15. #define max_no_packets 4

  16. char g_sendpacketbuf[packet_size];
  17. char g_recvpacket[packet_size];
  18. int g_sockfd;
  19. int g_datalen = 56;
  20. int g_nsend = 0;
  21. int g_nreceived = 0;
  22. struct sockaddr_in dest_addr;
  23. pid_t pid;
  24. struct sockaddr_in from;
  25. struct timeval tvrecv;

  26. void statistics(int signo);
  27. unsigned short cal_chksum(unsigned short *addr, int len);
  28. int pack(int pack_no);
  29. void send_packet(void);
  30. void recv_packet(void);
  31. int unpack(char *buf, int len);
  32. void tv_sub(struct timeval *out, struct timeval *in);

  33. void statistics(int signo)
  34. {
  35.     printf("/n--------------------ping statistics-------------------\n");
  36.     printf("%d packets transmitted, %d received , %%%d lost\n", g_nsend, g_nreceived,
  37.            (g_nsend - g_nreceived) / g_nsend * 100);
  38.     close(g_sockfd);
  39.     exit(1);
  40. }

  41. /*校验和算法*/
  42. unsigned short cal_chksum(unsigned short *addr, int len)
  43. {
  44.     int nleft = len;
  45.     int sum = 0;
  46.     unsigned short *w = addr;
  47.     unsigned short answer = 0;

  48.     /*把icmp报头二进制数据以2字节为单位累加起来 */
  49.     while (nleft > 1)
  50.     {
  51.         sum = *w;
  52.         nleft -= 2;
  53.     }
  54.     /*若icmp报头为奇数个字节,会剩下最后一字节。把最后一个字节视为一个2字节数据的高字节,这个2字节数据的低字节为0,继续累加 */
  55.     if (nleft == 1)
  56.     {
  57.         *(unsigned char *)(&answer) = *(unsigned char *)w;
  58.         sum = answer;
  59.     }
  60.     sum = (sum >> 16) (sum & 0xffff);
  61.     sum = (sum >> 16);
  62.     answer = ~sum;
  63.     return answer;
  64. }

  65. /*设置icmp报头*/
  66. int pack(int pack_no)
  67. {
  68.     int i, packsize;
  69.     struct icmp *icmp;
  70.     struct timeval *tval;

  71.     icmp = (struct icmp *)g_sendpacketbuf;
  72.     icmp->icmp_type = icmp_echo;
  73.     icmp->icmp_code = 0;
  74.     icmp->icmp_cksum = 0;
  75.     icmp->icmp_seq = pack_no;
  76.     icmp->icmp_id = pid;
  77.     packsize = 8 g_datalen;
  78.     tval = (struct timeval *)icmp->icmp_data;
  79.     gettimeofday(tval, null); /*记录发送时间 */
  80.     icmp->icmp_cksum = cal_chksum((unsigned short *)icmp, packsize); /*校验算法 */
  81.     return packsize;
  82. }

  83. /*发送三个icmp报文*/
  84. void send_packet()
  85. {
  86.     int packetsize;

  87.     while (g_nsend < max_no_packets)
  88.     {
  89.         g_nsend;
  90.         packetsize = pack(g_nsend); /*设置icmp报头 */
  91.         if (sendto(g_sockfd, g_sendpacketbuf, packetsize, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0)
  92.         {
  93.             perror("sendto error");
  94.             continue;
  95.         }
  96.         sleep(1); /*每隔一秒发送一个icmp报文 */
  97.     }
  98. }

  99. /*接收所有icmp报文*/
  100. void recv_packet()
  101. {
  102.     int n, fromlen;
  103.     extern int errno;

  104.     signal(sigalrm, statistics);
  105.     fromlen = sizeof(from);
  106.     while (g_nreceived < g_nsend)
  107.     {
  108.         alarm(max_wait_time);
  109.         if ((n = recvfrom(g_sockfd, g_recvpacket, sizeof(g_recvpacket), 0, (struct sockaddr *)&from, &fromlen)) < 0)
  110.         {
  111.             if (errno == eintr)
  112.                 continue;
  113.             perror("recvfrom error");
  114.             continue;
  115.         }
  116.         gettimeofday(&tvrecv, null); /*记录接收时间 */
  117.         if (unpack(g_recvpacket, n) == -1)
  118.             continue;
  119.         g_nreceived;
  120.     }

  121. }

  122. /*剥去icmp报头*/
  123. int unpack(char *buf, int len)
  124. {
  125.     int i, iphdrlen;
  126.     struct ip *ip;
  127.     struct icmp *icmp;
  128.     struct timeval *tvsend;
  129.     double rtt;

  130.     ip = (struct ip *)buf;
  131.     iphdrlen = ip->ip_hl << 2; /*求ip报头长度,即ip报头的长度标志乘4 */
  132.     icmp = (struct icmp *)(buf iphdrlen); /*越过ip报头,指向icmp报头 */
  133.     len -= iphdrlen; /*icmp报头及icmp数据报的总长度 */
  134.     if (len < 8) /*小于icmp报头长度则不合理 */
  135.     {
  136.         printf("icmp packets/'s length is less than 8\n");
  137.         return -1;
  138.     }
  139.     /*确保所接收的是我所发的的icmp的回应 */
  140.     if ((icmp->icmp_type == icmp_echoreply) && (icmp->icmp_id == pid))
  141.     {
  142.         tvsend = (struct timeval *)icmp->icmp_data;
  143.         tv_sub(&tvrecv, tvsend); /*接收和发送的时间差 */
  144.         rtt = tvrecv.tv_sec * 1000 tvrecv.tv_usec / 1000; /*以毫秒为单位计算rtt */
  145.         /*显示相关信息 */
  146.         printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",
  147.                len, inet_ntoa(from.sin_addr), icmp->icmp_seq, ip->ip_ttl, rtt);
  148.     }
  149.     else
  150.         return -1;
  151. }

  152. int main(int argc, char *argv[])
  153. {
  154.     struct hostent *host;
  155.     struct protoent *protocol;
  156.     unsigned long inaddr = 0l;
  157.     int waittime = max_wait_time;
  158.     int size = 50 * 1024;

  159.     if (argc < 2)
  160.     {
  161.         printf("usage:%s hostname/ip address\n", argv[0]);
  162.         exit(1);
  163.     }

  164.     if ((protocol = getprotobyname("icmp")) == null)
  165.     {
  166.         perror("getprotobyname");
  167.         exit(1);
  168.     }
  169.     /*生成使用icmp的原始套接字,这种套接字只有root才能生成 */
  170.     if ((g_sockfd = socket(af_inet, sock_raw, protocol->p_proto)) < 0)
  171.     {
  172.         perror("socket error");
  173.         exit(1);
  174.     }

  175.     printf("user id :%d\n", getuid());
  176.     /* 回收root权限,设置当前用户权限 */
  177.     setuid(getuid());

  178.     printf("userid:%d,effect user:%d\n", getuid(), geteuid());

  179.     /*
  180.      * 扩大套接字接收缓冲区到50k这样做主要为了减小接收缓冲区溢出的
  181.      * 的可能性,若无意中ping一个广播地址或多播地址,将会引来大量应答
  182.      */
  183.     setsockopt(g_sockfd, sol_socket, so_rcvbuf, &size, sizeof(size));
  184.     bzero(&dest_addr, sizeof(dest_addr));
  185.     dest_addr.sin_family = af_inet;

  186.     /*判断是主机名还是ip地址 */
  187.     if ((inaddr = inet_addr(argv[1])) == inaddr_none)
  188.     {
  189.         if ((host = gethostbyname(argv[1])) == null) /*是主机名 */
  190.         {
  191.             perror("gethostbyname error");
  192.             exit(1);
  193.         }
  194.         memcpy((char *)&dest_addr.sin_addr, host->h_addr, host->h_length);
  195.     }
  196.     else /*是ip地址 */
  197.         dest_addr.sin_addr.s_addr = inaddr;

  198.     /*获取main的进程id,用于设置icmp的标志符 */
  199.     pid = getpid();
  200.     printf("ping %s(%s): %d bytes data in icmp packets.\n", argv[1], inet_ntoa(dest_addr.sin_addr), g_datalen);
  201.     send_packet(); /*发送所有icmp报文 */
  202.     recv_packet(); /*接收所有icmp报文 */
  203.     statistics(sigalrm); /*进行统计 */

  204.     return 0;

  205. }

  206. /*两个timeval结构相减*/
  207. void tv_sub(struct timeval *out, struct timeval *in)
  208. {
  209.     if ((out->tv_usec -= in->tv_usec) < 0)
  210.     {
  211.         --out->tv_sec;
  212.         out->tv_usec = 1000000;
  213.     }
  214.     out->tv_sec -= in->tv_sec;
  215. }

  216. /*------------- the end -----------*/



阅读(2869) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图