📜  在C中ping

📅  最后修改于: 2021-05-28 02:14:17             🧑  作者: Mango

先决条件:ICMP |原始插座|互联网校验和|域名解析

Ping是Internet调试的必要条件。
Ping是一种基本的Internet工具,它使用户可以使用其他工具来验证特定IP地址是否存在并可以接受请求。

Ping通过打开RAW套接字发送ICMP数据包,该套接字与TCP和UDP分开。

由于IP没有任何内置的机制来发送错误和控制消息。提供错误控制取决于Internet控制消息协议(ICMP)。它用于报告错误和管理查询。

Ubuntu Ping示例

ping www.google.com
PING www.google.com (172.217.194.105) 56(84) bytes of data.
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=1 ttl=46 time=116 ms
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=2 ttl=46 time=102 ms
64 bytes from 172.217.194.105 (172.217.194.105): icmp_seq=3 ttl=46 time=119 ms
^C
--- www.google.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 3110ms

工作机制
Internet Ping程序的工作原理类似于声纳回声定位,将包含ICMP ECHO_REQUEST的一小包信息发送到指定的计算机,然后由该计算机发送ECHO_REPLY包作为回报。
数据包具有TTL(生存时间)值,该值确定路由器跃点的最大数量。

如果数据包没有到达,则发回给发件人并显示错误信息。

错误有以下几种类型:

  • TTL在运输中过期
  • 目标主机不可达
  • 请求超时,即无回复
  • 未知主机

执行
一个简单的ping程序遵循的步骤是:

  1. 主机名作为输入
  2. 进行DNS查找

    可以使用gethostbyname()完成DNS查找。 gethostbyname()函数转换普通的人类可读网站,并返回hostent类型的结构,该结构包含二进制点符号形式的IP地址以及地址类型。

  3. 一些ping程序(如ubuntu附带的ping程序)支持反向DNS查找
    反向DNS查找使用getnameinfo()进行,并将点表示法IP地址转换为主机名。

    例如,对google.com进行ping操作经常会给出一个奇怪的地址:
    bom07s18-in-f14.1e100.net

    这是反向DNS查找的结果。

  4. 使用SOCK_RAW(协议为IPPROTO_ICMP)打开Raw套接字
    注意:原始套接字需要超级用户权限,因此您必须使用sudo运行此代码
  5. 按下crtl + C时,ping将给出报告。该中断被中断处理程序捕获
    这只是将我们的ping循环条件设置为false。
  6. 这是主要的ping发送循环。
    我们必须:
    1. ttl选项设置为套接字中的值
      设置TTL值以限制数据包可以进行的跳数。
    2. 设置recv函数的超时时间
      如果未设置超时,则recv将永远等待,从而停止循环。
    3. 填满icmp数据包
      如下:
      1. 将报文头类型设置为ICMP_ECHO。
      2. 将ID设置为进程的pid
      3. 随机填充味精部分。
      4. 计算校验和并将其填写在校验和字段中。
    4. 发送数据包
    5. 等待它被接收
      这里的主要问题是接收到的数据包并不意味着目的地正在运行。
      回声表示目的地确定。回声答复是从目标OS内核发送的。

      这是所有类型和代码的列表。
      这里的问题是,如果一切正确,程序将显示类型69和代码0,而不是代表echo_reply的0。

      // C program to Implement Ping
        
      // compile as -o ping
      // run as sudo ./ping 
        
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
      #include 
        
      // Define the Packet Constants
      // ping packet size
      #define PING_PKT_S 64
         
      // Automatic port number
      #define PORT_NO 0 
        
      // Automatic port number
      #define PING_SLEEP_RATE 1000000 x
        
      // Gives the timeout delay for receiving packets
      // in seconds
      #define RECV_TIMEOUT 1 
        
      // Define the Ping Loop
      int pingloop=1;
        
        
      // ping packet structure
      struct ping_pkt
      {
          struct icmphdr hdr;
          char msg[PING_PKT_S-sizeof(struct icmphdr)];
      };
        
      // Calculating the Check Sum
      unsigned short checksum(void *b, int len)
      {    unsigned short *buf = b;
          unsigned int sum=0;
          unsigned short result;
        
          for ( sum = 0; len > 1; len -= 2 )
              sum += *buf++;
          if ( len == 1 )
              sum += *(unsigned char*)buf;
          sum = (sum >> 16) + (sum & 0xFFFF);
          sum += (sum >> 16);
          result = ~sum;
          return result;
      }
        
        
      // Interrupt handler
      void intHandler(int dummy)
      {
          pingloop=0;
      }
        
      // Performs a DNS lookup 
      char *dns_lookup(char *addr_host, struct sockaddr_in *addr_con)
      {
          printf("\nResolving DNS..\n");
          struct hostent *host_entity;
          char *ip=(char*)malloc(NI_MAXHOST*sizeof(char));
          int i;
        
          if ((host_entity = gethostbyname(addr_host)) == NULL)
          {
              // No ip found for hostname
              return NULL;
          }
            
          //filling up address structure
          strcpy(ip, inet_ntoa(*(struct in_addr *)
                                host_entity->h_addr));
        
          (*addr_con).sin_family = host_entity->h_addrtype;
          (*addr_con).sin_port = htons (PORT_NO);
          (*addr_con).sin_addr.s_addr  = *(long*)host_entity->h_addr;
        
          return ip;
            
      }
        
      // Resolves the reverse lookup of the hostname
      char* reverse_dns_lookup(char *ip_addr)
      {
          struct sockaddr_in temp_addr;    
          socklen_t len;
          char buf[NI_MAXHOST], *ret_buf;
        
          temp_addr.sin_family = AF_INET;
          temp_addr.sin_addr.s_addr = inet_addr(ip_addr);
          len = sizeof(struct sockaddr_in);
        
          if (getnameinfo((struct sockaddr *) &temp_addr, len, buf, 
                          sizeof(buf), NULL, 0, NI_NAMEREQD)) 
          {
              printf("Could not resolve reverse lookup of hostname\n");
              return NULL;
          }
          ret_buf = (char*)malloc((strlen(buf) +1)*sizeof(char) );
          strcpy(ret_buf, buf);
          return ret_buf;
      }
        
      // make a ping request
      void send_ping(int ping_sockfd, struct sockaddr_in *ping_addr,
                      char *ping_dom, char *ping_ip, char *rev_host)
      {
          int ttl_val=64, msg_count=0, i, addr_len, flag=1,
                     msg_received_count=0;
            
          struct ping_pkt pckt;
          struct sockaddr_in r_addr;
          struct timespec time_start, time_end, tfs, tfe;
          long double rtt_msec=0, total_msec=0;
          struct timeval tv_out;
          tv_out.tv_sec = RECV_TIMEOUT;
          tv_out.tv_usec = 0;
        
          clock_gettime(CLOCK_MONOTONIC, &tfs);
        
            
          // set socket options at ip to TTL and value to 64,
          // change to what you want by setting ttl_val
          if (setsockopt(ping_sockfd, SOL_IP, IP_TTL, 
                     &ttl_val, sizeof(ttl_val)) != 0)
          {
              printf("\nSetting socket options 
                       to TTL failed!\n");
              return;
          }
        
          else
          {
              printf("\nSocket set to TTL..\n");
          }
        
          // setting timeout of recv setting
          setsockopt(ping_sockfd, SOL_SOCKET, SO_RCVTIMEO,
                         (const char*)&tv_out, sizeof tv_out);
        
          // send icmp packet in an infinite loop
          while(pingloop)
          {
              // flag is whether packet was sent or not
              flag=1;
             
              //filling packet
              bzero(&pckt, sizeof(pckt));
                
              pckt.hdr.type = ICMP_ECHO;
              pckt.hdr.un.echo.id = getpid();
                
              for ( i = 0; i < sizeof(pckt.msg)-1; i++ )
                  pckt.msg[i] = i+'0';
                
              pckt.msg[i] = 0;
              pckt.hdr.un.echo.sequence = msg_count++;
              pckt.hdr.checksum = checksum(&pckt, sizeof(pckt));
        
        
              usleep(PING_SLEEP_RATE);
        
              //send packet
              clock_gettime(CLOCK_MONOTONIC, &time_start);
              if ( sendto(ping_sockfd, &pckt, sizeof(pckt), 0, 
                 (struct sockaddr*) ping_addr, 
                  sizeof(*ping_addr)) <= 0)
              {
                  printf("\nPacket Sending Failed!\n");
                  flag=0;
              }
        
              //receive packet
              addr_len=sizeof(r_addr);
        
              if ( recvfrom(ping_sockfd, &pckt, sizeof(pckt), 0, 
                   (struct sockaddr*)&r_addr, &addr_len) <= 0
                    && msg_count>1) 
              {
                  printf("\nPacket receive failed!\n");
              }
        
              else
              {
                  clock_gettime(CLOCK_MONOTONIC, &time_end);
                    
                  double timeElapsed = ((double)(time_end.tv_nsec - 
                                       time_start.tv_nsec))/1000000.0
                  rtt_msec = (time_end.tv_sec-
                                time_start.tv_sec) * 1000.0
                              + timeElapsed;
                    
                  // if packet was not sent, don't receive
                  if(flag)
                  {
                      if(!(pckt.hdr.type ==69 && pckt.hdr.code==0)) 
                      {
                          printf("Error..Packet received with ICMP 
                                 type %d code %d\n", 
                                 pckt.hdr.type, pckt.hdr.code);
                      }
                      else
                      {
                          printf("%d bytes from %s (h: %s) 
                                (%s) msg_seq=%d ttl=%d 
                                rtt = %Lf ms.\n", 
                                PING_PKT_S, ping_dom, rev_host, 
                                ping_ip, msg_count,
                                ttl_val, rtt_msec);
        
                          msg_received_count++;
                      }
                  }
              }    
          }
          clock_gettime(CLOCK_MONOTONIC, &tfe);
          double timeElapsed = ((double)(tfe.tv_nsec - 
                                tfs.tv_nsec))/1000000.0;
            
          total_msec = (tfe.tv_sec-tfs.tv_sec)*1000.0+ 
                                timeElapsed
                           
          printf("\n===%s ping statistics===\n", ping_ip);
          printf("\n%d packets sent, %d packets received, %f percent
                 packet loss. Total time: %Lf ms.\n\n", 
                 msg_count, msg_received_count,
                 ((msg_count - msg_received_count)/msg_count) * 100.0,
                total_msec); 
      }
        
      // Driver Code
      int main(int argc, char *argv[])
      {
          int sockfd;
          char *ip_addr, *reverse_hostname;
          struct sockaddr_in addr_con;
          int addrlen = sizeof(addr_con);
          char net_buf[NI_MAXHOST];
        
          if(argc!=2)
          {
              printf("\nFormat %s 
      \n", argv[0]);         return 0;     }        ip_addr = dns_lookup(argv[1], &addr_con);     if(ip_addr==NULL)     {         printf("\nDNS lookup failed! Could                     not resolve hostname!\n");         return 0;     }        reverse_hostname = reverse_dns_lookup(ip_addr);     printf("\nTrying to connect to '%s' IP: %s\n",                                        argv[1], ip_addr);     printf("\nReverse Lookup domain: %s",                             reverse_hostname);        //socket()     sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);     if(sockfd<0)     {         printf("\nSocket file descriptor not received!!\n");         return 0;     }     else         printf("\nSocket file descriptor %d received\n", sockfd);        signal(SIGINT, intHandler);//catching interrupt        //send pings continuously     send_ping(sockfd, &addr_con, reverse_hostname,                                   ip_addr, argv[1]);            return 0; }

      输出示例:
      运行sudo ./ping google.com

      Resolving DNS..
      
      Trying to connect to 'google.com' IP: 172.217.27.206
      
      Reverse Lookup domain: bom07s15-in-f14.1e100.net
      Socket file descriptor 3 received
      
      Socket set to TTL..
      64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206)
                                      msg_seq=1 ttl=64 rtt = 57.320584 ms.
      
      64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206)
                                      msg_seq=2 ttl=64 rtt = 58.666775 ms.
      
      64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206)
                                      msg_seq=3 ttl=64 rtt = 58.081148 ms.
      
      64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) 
                                      msg_seq=4 ttl=64 rtt = 58.700630 ms.
      
      64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) 
                                      msg_seq=5 ttl=64 rtt = 58.281802 ms.
      
      64 bytes from bom07s15-in-f14.1e100.net (h: google.com) (172.217.27.206) 
                                      msg_seq=6 ttl=64 rtt = 58.360916 ms.
      
      ===172.217.27.206 ping statistics===
      
      6 packets sent, 6 packets received, 0.000000 percent packet loss. 
      Total time: 6295.187804 ms.
      

      想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。