【问题标题】:getaddrinfo and IPv6getaddrinfo 和 IPv6
【发布时间】:2011-08-22 19:50:39
【问题描述】:

我试图了解 getaddrinfo 函数返回什么:

#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>

int main (int argc, char *argv[])
{


struct addrinfo *res = 0 ;

  getaddrinfo("localhost", NULL ,NULL,&res);
  printf("ai_flags -> %i\n", res->ai_flags) ;
  printf("ai_family -> %i\n", res->ai_family) ;
  printf("ai_socktype -> %i\n", res->ai_socktype) ;
  printf("ai_protocol -> %i\n", res->ai_protocol) ;
  printf("ai_addrlen -> %i\n", res->ai_addrlen) ;
  struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
  printf("ai_addr hostname ->  %s\n", inet_ntoa(saddr->sin_addr));

  freeaddrinfo(res);

  return 0 ;
}

结果:

ai_flags -> 40
ai_family -> 2
ai_socktype -> 1
ai_protocol -> 6
ai_addrlen -> 16
ai_addr hostname ->  127.0.0.1

在 /etc/hosts 中,我有:

127.0.0.1 localhost    
::1     localhost

Getaddrinfo 只返回 127.0.0.1 而不是 ::1 ?我不明白为什么?

第二个问题是我在哪里可以找到这些整数 (40,2,1,6 等) 的含义?我读过这个人,但没有任何内容。

我还想知道是否可以提供 IPv6 地址(例如 ::1)并且函数返回名称:localhost?

非常感谢!!

【问题讨论】:

  • 根据手册页,getaddrinfo 分配空间并填充从 &res 开始的链表。我认为它会返回多个条目,您需要通过 res->ainext 遍历链表

标签: c network-programming ipv6 ipv4 getaddrinfo


【解决方案1】:

@jwodder 和 @onteria_ 很好地涵盖了 IPv6 部分,所以我将只处理 numbers 部分:

ai_flags -> 40

这可能是/usr/include/netdb.h中以下两个的总和:

# define AI_V4MAPPED    0x0008  /* IPv4 mapped addresses are acceptable.  */
# define AI_ADDRCONFIG  0x0020  /* Use configuration of this host to choose

这是协议族、inet、inet6、apx、unix等:

ai_family -> 2

bits/socket.h:78:#define    PF_INET     2   /* IP protocol family.  */
bits/socket.h:119:#define   AF_INET     PF_INET

这是socket类型,stream,dgram,packet,rdm,seqpacket:

ai_socktype -> 1

bits/socket.h:42:  SOCK_STREAM = 1,     /* Sequenced, reliable, connection-based

高层协议,TCP、UDP、TCP6、UDP6、UDPlite、ospf、icmp等:

ai_protocol -> 6

很有趣,在/etc/protocols

tcp 6   TCP     # transmission control protocol

struct sockaddr 的大小。 (因地址族而异!呃。)

ai_addrlen -> 16

这是因为你得到了一个struct sockaddr_in,见linux/in.h

#define __SOCK_SIZE__   16      /* sizeof(struct sockaddr)  */
struct sockaddr_in {
  sa_family_t       sin_family; /* Address family       */
  __be16        sin_port;   /* Port number          */
  struct in_addr    sin_addr;   /* Internet address     */

  /* Pad to size of `struct sockaddr'. */
  unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -
            sizeof(unsigned short int) - sizeof(struct in_addr)];
};

最后一个,来自/etc/hosts :)

ai_addr hostname ->  127.0.0.1

【讨论】:

    【解决方案2】:

    res 还包含一个字段struct addrinfo *ai_next;,它是指向getaddrinfo 找到的其他条目的指针,如果没有其他条目,则为NULL。如果您检查 res-&gt;ai_next,您应该会找到 IPv6 条目。

    至于struct addrinfo 中的整数字段,它们对应于具有实现定义值的预定义常量,而整数值本身并不被普遍关注。如果您想知道给定字段的含义,请将其与可以分配给该字段的常量进行比较(ai_socktypeSOCK_STREAMSOCK_DGRAM 等;IPPROTO_TCPIPPROTO_UDPai_protocol; 等等),或者对于ai_flags,测试对应于预定义常量的每个位(例如,if (res-&gt;ai_flags &amp; AI_NUMERICHOST) {printf("ai_flags has AI_NUMERICHOST\n"); })。

    【讨论】:

      【解决方案3】:
      extern struct sockaddr_in6 create_socket6(int port, const char * address) {
      
          struct addrinfo hints, *res, *resalloc;
          struct sockaddr_in6 input_socket6;
          int errcode;
      
          /* 0 out our structs to be on the safe side */
          memset (&hints, 0, sizeof (hints));
          memset (&input_socket6, 0, sizeof(struct sockaddr_in6));
      
          /* We only care about IPV6 results */
          hints.ai_family = AF_INET6;
          hints.ai_socktype = SOCK_STREAM;
          hints.ai_flags = AI_DEFAULT;
      
          errcode = getaddrinfo (address, NULL, &hints, &res);
          if (errcode != 0)
          {
           perror ("[ERROR] getaddrinfo ");
           return input_socket6;
          }
      
          resalloc = res;
      
          while (res)
          {
              /* Check to make sure we have a valid AF_INET6 address */
              if(res->ai_family == AF_INET6) {
                      /* Use memcpy since we're going to free the res variable later */
                              memcpy (&input_socket6, res->ai_addr, res->ai_addrlen);
      
                             /* Here we convert the port to network byte order */
                              input_socket6.sin6_port = htons (port);
                              input_socket6.sin6_family = AF_INET6;
                     break;
              }
      
              res = res->ai_next;
          }
      
          freeaddrinfo(resalloc);
      
          return input_socket6;
      }
      

      这里有一些解释它的代码。基本上,除非你给 getaddrinfo 一些提示来告诉它只适用于 IPV6,否则它也会给出 IPV4 结果。这就是为什么你必须循环显示结果。

      【讨论】:

        【解决方案4】:

        大多数部分都给出了其他答案,但要回答最后一部分:

        我还想知道是否可以提供 IPv6 地址(例如 ::1)并且函数返回名称:localhost?

        你想要的功能是getnameinfo();给定一个套接字地址,它返回一个字符串名称。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-02-03
          • 1970-01-01
          • 2018-02-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-12-16
          • 2012-06-01
          相关资源
          最近更新 更多