GetAdaptersAddresses()将以IP_ADAPTER_UNICAST_ADDRESS格式获取地址,其格式定义为:
typedef struct _IP_ADAPTER_UNICAST_ADDRESS {
union {
struct {
ULONG Length;
DWORD Flags;
} ;
} ;
struct _IP_ADAPTER_UNICAST_ADDRESS *Next;
SOCKET_ADDRESS Address;
IP_PREFIX_ORIGIN PrefixOrigin;
IP_SUFFIX_ORIGIN SuffixOrigin;
IP_DAD_STATE DadState;
ULONG ValidLifetime;
ULONG PreferredLifetime;
ULONG LeaseLifetime;
UINT8 OnLinkPrefixLength;
} IP_ADAPTER_UNICAST_ADDRESS, *PIP_ADAPTER_UNICAST_ADDRESS;
似乎建议人类可读IP地址字符串的唯一字段是Address,它是一个SOCKET_ADDRESS结构,定义如下:
typedef struct _SOCKET_ADDRESS {
LPSOCKADDR lpSockaddr;
INT iSockaddrLength;
} SOCKET_ADDRESS, *PSOCKET_ADDRESS;
反过来,它使用另一个结构SOCKADDR,定义为:
很抱歉,这里的帖子很复杂,因为它取决于IPv4与IPv6以及Windows版本......所以这里是定义的链接:
http://msdn.microsoft.com/en-us/library/ms740496%28v=VS.85%29.aspx
如果你还没有像我那样头晕目眩并且遵循这个定义的迷宫,你可能会注意到检索IP地址的旧的虚线字符串样式是一场噩梦,因为它曾经更容易使用GetAdaptersInfo()
我的问题是:是否有真正的 IP Helper功能可以将IP_ADAPTER_UNICAST_ADDRESS转换为IPv4点分支(或IPv6字符串)?
答案 0 :(得分:6)
您可以使用GetIpAddrTable - returned data structure包含作为IPv4地址的DWORD dwAddr
。第一个链接上的示例代码应该显示您想要的内容。摘要向您展示我的意思:
if ( (dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 )) != NO_ERROR ) {
printf("GetIpAddrTable failed with error %d\n", dwRetVal);
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) & lpMsgBuf, 0, NULL)) {
printf("\tError: %s", lpMsgBuf);
LocalFree(lpMsgBuf);
}
exit(1);
}
printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
IP_ADAPTER_UNICAST_ADDRESS
在SOCKET_ADDRESS
中包含Address
,而{1}}在lpSockAddr中包含LPSOCKADDR
- 您可以使用{{3}将其转换为ipv4字符串形式}。
答案 1 :(得分:4)
查看SOCKADDR的文档。这引出了我们SOCKADDR_STORAGE的文档,它是IPv4和IPv6的辅助结构。
来自sockaddr文档的引用:
使用sockaddr的Winsock函数不是严格解释 指向sockaddr结构的指针。结构被解释 在不同地址族的背景下有所不同。
对于ipv4,您可以将sockaddr指针强制转换为sockaddr_in指针,然后从那里访问IPv4地址信息。然后,您可以使用您喜欢的字符串生成器来生成点分四格式的字符串。
sockaddr_in* address = (sockaddr_in*) temp->Address.lpSockaddr;
uint32_t ipv4 = address->sin_addr.S_un.S_addr;
// First octet: address->sin_addr.S_un.S_un_b.s_b1
// Second octet: address->sin_addr.S_un.S_un_b.s_b2
// Third octet: address->sin_addr.S_un.S_un_b.s_b3
// Fourth octet: address->sin_addr.S_un.S_un_b.s_b4
我想你也可以在给定结构定义(下面复制)的情况下以类似的方式为ipv6转换地址。
struct sockaddr {
ushort sa_family;
char sa_data[14];
};
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr_in6 {
short sin6_family;
u_short sin6_port;
u_long sin6_flowinfo;
struct in6_addr sin6_addr;
u_long sin6_scope_id;
};
答案 2 :(得分:0)
您只需将_SOCKET_ADDRESS.lpSockaddr
和_SOCKET_ADDRESS.iSockaddrLength
作为lpsaAddress
和dwAddressLength
参数传递给WSAAddressToString
函数即可。 WSAAddressToString
将为您进行必要的转换,无需深入挖掘。
function SOCKET_ADDRESS_ToString(const Addr: SOCKET_ADDRESS): String;
var
Len: DWORD;
begin
if (Addr.lpSockaddr = nil) or (Addr.iSockaddrLength <= 0) then
begin
Result := '';
Exit;
end;
Len := 0;
WSAAddressToString(Addr.lpSockaddr, Addr.iSockaddrLength, nil, nil, Len);
SetLength(Result, Len);
if WSAAddressToString(Addr.lpSockaddr, Addr.iSockaddrLength, nil, PChar(Result), Len) = 0 then
SetLength(Result, Len - 1)
else
Result := '';
end;