程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> ping命令的C語言實現(linux, IPv4,簡單版)

ping命令的C語言實現(linux, IPv4,簡單版)

編輯:關於C語言

這個程序主要運用了ICMPv4協議(回顯請求)來測試本機到某服務器的網絡是否連通,因為其中用到了原始套接字,所以運行該程序需要管理員權限。   PS:本程序只支持一種輸入方式:./myping <hostname>,不支持其他參數。   思路: 1:根據hostname參數創建原始套接字。 2:每隔1秒鐘向服務器發送一個ICMP回顯請求。 3:循環接收從服務器返回的應答並處理其數據。   上代碼: [cpp]  #include <signal.h>   #include <stdio.h>   #include <errno.h>   #include <stdlib.h>      #include <netinet/in_systm.h>   #include <netinet/ip.h>   #include <netinet/ip_icmp.h>   #include <arpa/inet.h>   #include <netdb.h>   #include <sys/un.h>      //各種緩沖區的長度   #define BUFSIZE 1500   //ICMP回顯請求的長度   #define  DATA_LEN 56       struct proto    {       struct sockaddr *sasend; /* sockaddr{} for send, from getaddrinfo */       struct sockaddr *sarecv; /* sockaddr{} for receiving */       socklen_t salen; /* length of sockaddr{}s */       int icmpproto; /* IPPROTO_xxx value for ICMP */   };      //全局變量   pid_t g_pid;   int g_sockfd;   struct proto g_proto = { NULL, NULL, 0, IPPROTO_ICMP };      //處理服務器返回的ICMP回顯信息   void proc_msg(char *, ssize_t, struct msghdr *, struct timeval *);      //發送ICMP回顯請求   void send_msg(void);      //循環發送、接收信息   void readloop(void);      //定時器入門函數,每隔一秒一次發送ICMP請求   void sig_alrm(int);      //計算兩個時間之間的間隔   void tv_sub(struct timeval *, struct timeval *);      //獲取服務器的地址等信息   struct addrinfo *host_serv(const char *host,        const char *serv, int family, int socktype);      //根據服務器信息,得到服務器的IP地址   char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen);      //計算校驗和   uint16_t in_cksum(uint16_t *addr, int len);      //輸出錯誤信息,退出程序   void error_quit(const char *str);         int main(int argc, char **argv)   {        int c;       struct addrinfo *ai;       struct sockaddr_in *sin;       char *ip_address;       char *host;          //本程序只支持一種輸入方式:./myping <hostname>       if( argc != 2 )           error_quit("usage: myping <hostname>");          host = argv[1];       //將pid的高二位全置為0,ICMP的ID只有16位       g_pid = getpid() & 0xffff;           //設置定時器,每秒鐘向服務器發送一次請求       signal(SIGALRM, sig_alrm);          //獲取服務器的信息(addrinfo結構)       ai = host_serv(host, NULL, 0, 0);       ip_address = sock_ntop_host(ai->ai_addr, ai->ai_addrlen);          printf("PING %s (%s): %d data bytes\n",           ai->ai_canonname ? ai->ai_canonname : ip_address,           ip_address, DATA_LEN);          //如果返回的協議簇不是AF_INET(IPv4),則退出       if ( ai->ai_family != AF_INET )           error_quit("unknown address family");          //設置proto結構體       g_proto.sasend = ai->ai_addr;       g_proto.sarecv = calloc(1, ai->ai_addrlen);       g_proto.salen = ai->ai_addrlen;          //開始循環發送/接收請求       readloop();          return 0;   }      void readloop(void)   {       int size;       char recvbuf[BUFSIZE];       char controlbuf[BUFSIZE];       struct msghdr msg;       struct iovec iov;       ssize_t n;       struct timeval tval;          //創建一個IPv4的原始套接字       g_sockfd = socket(g_proto.sasend->sa_family, SOCK_RAW, g_proto.icmpproto);       if( -1 == g_sockfd )           error_quit("socket error");          //放棄管理員權限       //這個程序中,只用創建原始套接字時需要管理員權限       setuid(getuid());          //設置socket的接收緩沖區       size = 60 * 1024;       setsockopt(g_sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));          //發出第一個請求       sig_alrm(SIGALRM);          //為recvmsg調用設置msghdr結構       iov.iov_base = recvbuf;       iov.iov_len = sizeof(recvbuf);       msg.msg_name = g_proto.sarecv;       msg.msg_iov = &iov;       msg.msg_iovlen = 1;       msg.msg_control = controlbuf;          //開始死循環,不斷讀取和處理從服務器中返回的信息       while( 1 )       {           msg.msg_namelen = g_proto.salen;           msg.msg_controllen = sizeof(controlbuf);           n = recvmsg(g_sockfd, &msg, 0);           if (n < 0)           {               if (errno == EINTR)                   continue;               else                   error_quit("recvmsg error");           }              //分析返回內容,產生輸出           gettimeofday(&tval, NULL);           proc_msg(recvbuf, n, &msg, &tval);       }   }      void proc_msg(char *ptr, ssize_t len, struct msghdr *msg, struct timeval *tvrecv)   {       int hlen1, icmplen;       double rtt;       struct ip *ip;       struct icmp *icmp;       struct timeval *tvsend;          //將服務器返回的字符串強轉為ip結構       ip = (struct ip *) ptr;           //得到IP表頭的長度       hlen1 = ip->ip_hl << 2;           //如果不是ICMP的應答,則返回       if (ip->ip_p != IPPROTO_ICMP)           return;          icmp = (struct icmp *) (ptr + hlen1);        //長度不足,不是合法應答       if ( (icmplen = len - hlen1) < 8)           return;          //不是回顯應答,返回       if (icmp->icmp_type != ICMP_ECHOREPLY)            return;          //不是我們發出請求的應答,返回       if (icmp->icmp_id != g_pid)           return;        //長度不足,非法應答       if (icmplen < 16)           return;          //計算網絡延時       tvsend = (struct timeval *) icmp->icmp_data;       tv_sub(tvrecv, tvsend);       rtt = tvrecv->tv_sec * 1000.0 + tvrecv->tv_usec / 1000.0;          //輸出信息       printf("%d bytes from %s: seq=%u, ttl=%d, rtt=%.3f ms\n",           icmplen, sock_ntop_host(g_proto.sarecv, g_proto.salen),           icmp->icmp_seq, ip->ip_ttl, rtt);      }         void send_msg(void)   {       int len;       int res;       struct icmp *icmp;       char sendbuf[BUFSIZE];       static int nsent = 0;          //根據ICMPv4協議來設置發送信息       icmp = (struct icmp *) sendbuf;          //ICMP回顯請求       icmp->icmp_type = ICMP_ECHO;       icmp->icmp_code = 0;          //ICMP標識符字段為本進程的PID       icmp->icmp_id = g_pid;          //ICMP序列號字段為不斷遞增的全局變量nsent       icmp->icmp_seq = nsent++;          //ICMP數據字段為當前時間截,空白部分填充0xa5       memset(icmp->icmp_data, 0xa5, DATA_LEN);       gettimeofday((struct timeval *)icmp->icmp_data, NULL);          //計算並填充校驗和       len = 8 + DATA_LEN;       icmp->icmp_cksum = 0;       icmp->icmp_cksum = in_cksum((u_short *) icmp, len);          //發送數據       res = sendto(g_sockfd, sendbuf, len, 0, g_proto.sasend, g_proto.salen);       if( -1 == res )           error_quit("sendto error");   }         void sig_alrm(int signo)   {       send_msg();       alarm(1);   }      void tv_sub(struct timeval *out, struct timeval *in)   {       //將兩個時間相減,並把結果存入第一個參數中( out -= in )       if ( (out->tv_usec -= in->tv_usec) < 0)        {            --out->tv_sec;           out->tv_usec += 1000000;       }       out->tv_sec -= in->tv_sec;   }      struct addrinfo *host_serv(const char *host, const char *serv, int family, int socktype)   {       int n;       struct addrinfo hints, *res;          memset(&hints, 0, sizeof(struct addrinfo));       hints.ai_flags = AI_CANONNAME;       hints.ai_family = family;        hints.ai_socktype = socktype;          n = getaddrinfo(host, serv, &hints, &res);       if ( n != 0 )           error_quit("getaddrinfo error");          return res;   }      char *sock_ntop_host(const struct sockaddr *sa, socklen_t salen)   {       static char str[128];       struct sockaddr_in *sin = (struct sockaddr_in *) sa;          //本程序只支持IPv4協議       if( sa->sa_family != AF_INET )           error_quit("sock_ntop_host: the type must be AF_INET");          if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)           error_quit("inet_ntop error");          return str;   }      //《UNIX網絡編程》書上的源碼   uint16_t in_cksum(uint16_t *addr, int len)   {       int nleft = len;       uint32_t sum = 0;       uint16_t *w = addr;       uint16_t answer = 0;          /*      * Our algorithm is simple, using a 32 bit accumulator (sum), we add      * sequential 16 bit words to it, and at the end, fold back all the      * carry bits from the top 16 bits into the lower 16 bits.      */       while (nleft > 1)        {           sum += *w++;           nleft -= 2;       }          /* 4mop up an odd byte, if necessary */       if (nleft == 1) {           *(unsigned char *)(&answer) = *(unsigned char *)w ;           sum += answer;       }          /* 4add back carry outs from top 16 bits to low 16 bits */       sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */       sum += (sum >> 16); /* add carry */       answer = ~sum; /* truncate to 16 bits */       return(answer);   }      void error_quit(const char *str)   {       //輸出錯誤信息,退出程序       fprintf(stderr, "%s", str);          if( errno != 0 )               fprintf(stderr, " : %s", strerror(errno));           fprintf(stderr, "\n");               exit(1);    }   www.2cto.com   運行示例: qch@LinuxMint ~/program/tcode $ gcc myping.c -o myping qch@LinuxMint ~/program/tcode $ sudo ./myping www.baidu.com PING www.a.shifen.com (115.239.210.26): 56 data bytes 64 bytes from 115.239.210.26: seq=0, ttl=128, rtt=31.272 ms 64 bytes from 115.239.210.26: seq=1, ttl=128, rtt=34.722 ms 64 bytes from 115.239.210.26: seq=2, ttl=128, rtt=30.822 ms 64 bytes from 115.239.210.26: seq=3, ttl=128, rtt=31.273 ms 64 bytes from 115.239.210.26: seq=4, ttl=128, rtt=29.995 ms ...........................

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved