2017-08-26 98 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); 
}