2016-11-24 45 views
2

我想創建一個簡單的程序,獲取IP地址給予一定的主機名:爲什麼getaddrinfo有多個結果?

我剪斷代碼附加如下:

#include<stdio.h> 
#include<stdlib.h> 
#include<stdio.h> 
#include<netdb.h> 
#include<sys/socket.h> 
#include<errno.h> 
#include<arpa/inet.h> 
#include<string.h> 
#include<unistd.h> 

int main(int argc, char *argv[]) { 
    if(argc<2){ 
     printf("Please provide a hostname.\n"); 
     exit(1); 
    } 

    char *hostname = argv[1]; 
    char ip[100]; 
    get_ip(hostname,ip); 
    printf("%s resolved to %s\n",hostname,ip); 
} 

int get_ip(char *hostname,char *ip){ 
    struct sockaddr_in *h; 
    int sockfd; 
    struct addrinfo hints, *servinfo,*res; 
    struct addrinfo *iter; 
    int rv; 

    memset(&hints,0,sizeof hints); 
    hints.ai_family = AF_UNSPEC; 
    hints.ai_socktype = SOCK_STREAM; 
    if((rv = getaddrinfo(hostname,"http",&hints, &res))!=0){ 
     fprintf(stderr, "getaddrinfo %s\n",gai_strerror(rv)); 
     return 1; 
    } 
    for(iter=res;iter != NULL;iter=iter->ai_next){ 
     printf("%p\n",iter->ai_next); 

     h=(struct sockaddr_in *)iter->ai_addr; 
     strcpy(ip,inet_ntoa(h->sin_addr)); 
     printf("%s\n",ip); 
    } 
    freeaddrinfo(res); 
    return 0; 
} 

我在下面的參數輸入:

gcc get_ip_addr.c -o get_ip_addr; 
./get_ip_addr google-public-dns-b.google.com 

這導致:

0x2475330 
8.8.4.4 
(nil) 
0.0.0.0 

當我刪除了「http」和&提示,並將它們設置爲NULL我得到如下結果:

0x1b63310 
8.8.4.4 
0x1b63360 
8.8.4.4 
0x1b633b0 
8.8.4.4 
0x1b63410 
0.0.0.0 
0x1b63470 
0.0.0.0 
(nil) 
0.0.0.0 

所以,當我設置的服務,並提示爲NULL的getaddrinfo返回多個可能的結果,我不明白爲什麼我得到多個IP地址而不是隻獲得一個IP地址? 任何幫助將不勝感激!謝謝!

+0

答案@fluter的摘要如下:主機名可以有多個IP地址。 –

+0

一個IP地址可以有不同的配置,'getaddrinfo'返回地址配置而不是IP地址。 – fluter

回答

6

servicehints字段設置爲NULL,你問getaddrinfo返回所有可能的地址,其中包括IP和IPv6,與不同的插座類型,輸出是什麼只是IP地址的圓點表示,而這僅僅整個地址結構的一部分返回getaddrinfo。如果仔細查看返回的地址,您會注意到每個地址實際上是不同的。例如,下面的程序輸出6個地址總數:

int main(int argc, char *argv[]) 
{ 
    struct addrinfo *result, *rp; 
    int s; 

    s = getaddrinfo("google-public-dns-b.google.com", NULL, NULL, &result); 
    if (s != 0) { 
      fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s)); 
      exit(EXIT_FAILURE); 
    } 

    char addr[1024]; 
    for (rp = result; rp != NULL; rp = rp->ai_next) { 
      printf("flags: 0x%x\tfamily: %d\tsocktype: %d\tprotocol: %d\n", 
          rp->ai_flags, 
          rp->ai_family, 
          rp->ai_socktype, 
          rp->ai_protocol); 
      s = getnameinfo(rp->ai_addr, rp->ai_addrlen, addr, sizeof addr, NULL, 0, NI_NUMERICHOST); 
      if (s != 0) { 
        printf("getnameinfo error:%d\n", s); 
        continue; 
      } 
      printf("addr: %s\n", addr); 
    } 

    freeaddrinfo(result); 
} 


$ ./a.out 
flags: 0x28 family: 2  socktype: 1  protocol: 6 
addr: 8.8.4.4 
flags: 0x28 family: 2  socktype: 2  protocol: 17 
addr: 8.8.4.4 
flags: 0x28 family: 2  socktype: 3  protocol: 0 
addr: 8.8.4.4 
flags: 0x28 family: 10  socktype: 1  protocol: 6 
addr: 2001:4860:4860::8844 
flags: 0x28 family: 10  socktype: 2  protocol: 17 
addr: 2001:4860:4860::8844 
flags: 0x28 family: 10  socktype: 3  protocol: 0 
addr: 2001:4860:4860::8844 

正如你可以看到,返回的每個地址是不同的,在地址,家庭,socktype和協議的組合而言。 如果您想返回一種特定類型的地址,請使用hints將返回的地址限制爲您想要的值。 每個數據字段解釋說:

家庭:

#define PF_INET   2  /* IP protocol family. */ 
#define PF_INET6  10  /* IP version 6. */ 
#define AF_INET   PF_INET 
#define AF_INET6  PF_INET6 

Socktypes:

enum __socket_type 
{ 
    SOCK_STREAM = 1,    /* Sequenced, reliable, connection-based 
           byte streams. */ 
    SOCK_DGRAM = 2,    /* Connectionless, unreliable datagrams 
           of fixed maximum length. */ 
    SOCK_RAW = 3,     /* Raw protocol interface. */ 
.... 
} 

協議:

enum { 
    IPPROTO_IP = 0,    /* Dummy protocol for TCP    */ 

    IPPROTO_TCP = 6,    /* Transmission Control Protocol  */ 

    IPPROTO_UDP = 17,    /* User Datagram Protocol    */ 
} 

另外一個問題,你看 「0.0.0.0」 在你的輸出,因爲您在IPv6地址上使用inet_ntoa,而此功能只適用於pport IPv4,因此它已被棄用,您應該考慮使用支持兩個地址系列的inet_ntopgetnameinfo

+0

我試過你的代碼,但我得到的地址是2.0.0.0。我現在明白爲什麼我打印出套接字和協議後得到不同的地址等等。但是運行你的代碼我得不到和你一樣的輸出,看起來你的協議應該輸出十六進制,但是你的輸出是小數點。根據輸出的差異,我猜測在翻譯中丟失了一些東西?你能幫忙的話,我會很高興。如果需要,我可以粘貼我看到的輸出。 – ahat

+0

我得到的地址是2.0.0。0你輸出的地方8.8.4.4(我知道這是正確的IP)。 – ahat

+0

沒問題!我認爲這是小事!現在它起作用了(並且是合理的),我可以按照getnameinfo的做法進行操作!謝謝! :) – ahat