2017-08-26 62 views
0

我不明白为什么getaddrinfo没有返回一个有效的IPv6地址。getaddrinfo()与IPv6没有意义

在我的系统下面的代码打印22:B8:00:00:00:00:00:00:00:00:00:00:00:00,但我期望的01的地方,因为localhost应该下定决心::1

与此同时,sa_data只有14字节,而IPv6的地址是16个字节,如此看来,最后几个字节总是砍掉,并且功能不能返回IPv6地址?

有人可以解释发生了什么?我该如何在IPv6上使用这个功能?

#include <stdio.h> 
#include <WinSock2.h> 
#include <WS2TCPIP.h> 
#pragma comment(lib, "WS2_32") 

int main(int argc, char *argv[]) 
{ 
    WSADATA wsadata; 
    WSAStartup(0x0002, &wsadata); 
    addrinfo addr_hints = { 0, PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, *addrs_out; 
    getaddrinfo("localhost", "8888", &addr_hints, &addrs_out); 
    fprintf(stderr, 
     "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 0]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 1]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 2]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 3]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 4]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 5]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 6]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 7]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 8]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[ 9]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[10]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[11]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[12]), 
     static_cast<unsigned char>(addrs_out->ai_addr->sa_data[13])); 
    freeaddrinfo(addrs_out); 
    return 0; 
} 
+0

首先一定要检查的getaddrinfo的'的返回值()'。如果它失败,你的地址将是垃圾。 – Jeff

+0

@Jeff:我在我的实际代码中;这不是问题。 – Mehrdad

回答

6

sockaddr参考结构的定义:

struct sockaddr { 
    ushort sa_family; 
    char sa_data[14]; 
}; 


struct sockaddr_in6 { 
    short sin6_family; 
    u_short sin6_port; 
    u_long sin6_flowinfo; 
    struct in6_addr sin6_addr; 
    u_long sin6_scope_id; 
}; 

ai_family == AF_INET6ai_addr实际指向一个struct sockaddr_in6。您打印的前几个字节是sin6_portsin6_flowinfo。 IPv6地址在之后。

编辑补充:

你可以像bind()getnameinfo()功能直接使用ai_addr。您通常不需要深入了解结构定义的详细信息。例如,我会使用getnameinfo()NI_NUMERICHOST来获取可打印的地址。

+1

唉,我认为'sockaddr'足够容纳'sockaddr_in6',但它看起来就是'SOCKADDR_STORAGE'的意思!这终于有道理,谢谢! – Mehrdad

+0

旁边的问题,但你碰巧知道是否有一个地方列出每个标准地址家庭的长度? – Mehrdad

+0

@Mehrdad不是我所知道的。其中有40个,他们自己的sockaddr定义分散在各种头文件中。 – Jeff

1

sockaddr不严格地解释为指向sockaddr结构的指针 。该结构在不同地址系列的 上下文中有不同解释。

所以我们需要从addrinfosockaddrai_family第一检查sa_family(在此基础它必须等于),并从sockaddr(这等void*指针)在此基础上需要“重新解释”指针真实结构。说利用联盟这个:

addrinfo *addrs_out, *addr; 

if (getaddrinfo("localhost", "8888", 0, &addrs_out) == NOERROR) 
{ 
    addr = addrs_out; 

    CHAR buf[256], *sz, srv[128]; 
    ULONG n; 
    PUCHAR Byte; 

    do 
    { 
     union { 
      sockaddr* ai_addr; 
      SOCKADDR_IN* pa; 
      SOCKADDR_IN6* pa6; 
     }; 

     ai_addr = addr->ai_addr; 

     if (addr->ai_family != ai_addr->sa_family) 
     { 
      __debugbreak(); 
     } 

     switch (addr->ai_family) 
     { 
     case AF_INET6: 
      Byte = pa6->sin6_addr.u.Byte, n = RTL_NUMBER_OF(pa6->sin6_addr.u.Byte), sz = buf; 
      do 
      { 
       sz += sprintf(sz, "%02X:", *Byte++); 
      } while (--n); 

      sz[-1] = 0; 
      DbgPrint("AF_INET6: %s\n", buf); 
      break; 

     case AF_INET: 
      if (0 <= RtlIpv4AddressToStringExA(&pa->sin_addr.S_un.S_addr, pa->sin_port, buf, &(n = RTL_NUMBER_OF(buf)))) 
      { 
       DbgPrint("AF_INET: %s\n", buf); 
      } 
      break; 
     } 

     // alt print 
     if (getnameinfo(ai_addr, (socklen_t)addr->ai_addrlen, buf, RTL_NUMBER_OF(buf), srv, RTL_NUMBER_OF(srv), NI_NUMERICHOST) == NOERROR) 
     { 
      DbgPrint("%s:%s\n", buf, srv); 
     } 

    } while (addr = addr->ai_next); 

    freeaddrinfo(addrs_out); 
}