本程序可以获得网卡的IPV6地址(如果有)
排除tunnel adapter地址(隧道适配器)、环回地址和无法使用的IP
代码如下:
#include <WS2tcpip.h> #include <Iphlpapi.h> #include <assert.h> #include <stdio.h> #pragma comment(lib, "IPHLPAPI.lib") #pragma comment(lib, "ws2_32.lib") bool InitWSA(); int getLocalIPV6(char *IPAddr); int main() { bool bRetVal = InitWSA(); assert(bRetVal); char ipv6[512]; ZeroMemory(ipv6,512); int iRetVal = getLocalIPV6(ipv6); if(iRetVal != 0){ printf("error occur!\n"); exit(-1); } printf("%s",ipv6); WSACleanup(); } bool InitWSA() { WSADATA wsaData; int err; err = WSAStartup(MAKEWORD(2,2),&wsaData); if(err != 0){ printf("WSAStartup failed with error: %d\n",err); return false; } /* Confirm that the WinSock DLL supports 2.2.*/ /* Note that if the DLL supports versions greater */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we */ /* requested. */ if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2){ /* Tell the user that we could not find a usable */ /* WinSock DLL. */ printf("Could not find a usable version of Winsock.dll\n"); WSACleanup(); return false; } return true; } int getLocalIPV6(char *IPAddr) { PIP_ADAPTER_ADDRESSES pAddresses = NULL; PIP_ADAPTER_UNICAST_ADDRESS pCurrentUnicastAddr = NULL; DWORD dwRetVal = 0; const DWORD dwSize = 512; DWORD bufflen=dwSize; char buff[dwSize]; int addrLen = sizeof(SOCKADDR_STORAGE); int Flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER; //这里只需要unicast address(或者按各自的要求) ULONG outBufLen = sizeof(IP_ADAPTER_ADDRESSES); ULONG family = AF_INET6; ULONG Iteration = 3; do{ pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen); if(pAddresses == NULL){ printf("Memory allocation failed for IP_ADAPTER_ADDRESS struct\n"); exit(1); } dwRetVal = GetAdaptersAddresses(family,0,NULL,pAddresses,&outBufLen); if(dwRetVal == ERROR_BUFFER_OVERFLOW){ free(pAddresses); pAddresses = NULL; }else{ break; } Iteration --; }while((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iteration >= 0)); // 第二次调用GetAdaptersAddresses 获取实际想获取的信息. if (dwRetVal == NO_ERROR) { PIP_ADAPTER_ADDRESSES pCurrAddresses = pAddresses; while (pCurrAddresses) { //去除cluster类型的IP,具体的请在MSDN上找这个结构体的说明 if(pCurrAddresses->IfType != IF_TYPE_ETHERNET_CSMACD){ pCurrAddresses = pCurrAddresses->Next; continue; } pCurrentUnicastAddr = pCurrAddresses->FirstUnicastAddress; while(pCurrentUnicastAddr != NULL){ if( (pCurrentUnicastAddr->Flags != IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) || (pCurrentUnicastAddr->DadState == IpDadStateInvalid)){ pCurrentUnicastAddr = pCurrentUnicastAddr->Next; continue; } INT brtVal = WSAAddressToStringA(pCurrentUnicastAddr->Address.lpSockaddr, pCurrentUnicastAddr->Address.iSockaddrLength,NULL,buff,&bufflen); if(brtVal != 0){ int errorcode = WSAGetLastError(); switch (errorcode){ case WSAEFAULT: return 1; case WSAEINVAL :return 2; case WSANOTINITIALISED:return 3; case WSAENOBUFS: return 4; default: return 5; } } printf("%s\n",buff); //我为了显示所有符合条件的地址。 //若准备实际使用的话,把上面一行注释掉,下面的几行去掉注释 //if (strncmp(buff,"fe80",4)==0||strcmp(buff,"::1") ==0) //{ //} //else //{ // strcpy(IPAddr,buff); // return 0; //} pCurrentUnicastAddr = pCurrentUnicastAddr->Next; ZeroMemory(buff,dwSize); } pCurrAddresses = pCurrAddresses->Next; } } return 0; }
最后得到了一个正确的IPV6地址
代码中使用了
ULONG WINAPI GetAdaptersAddresses( __in ULONG Family, __in ULONG Flags, __in PVOID Reserved, __inout PIP_ADAPTER_ADDRESSES AdapterAddresses, __inout PULONG SizePointer );MSDN: http://msdn.microsoft.com/en-us/library/aa365915(VS.85).aspx
获得网卡信息,因为只需要IPV6单播地址,故Flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
| GAA_FLAG_SKIP_DNS_SERVER;
然后检查IP_ADAPTER_ADDRESSES 结构体:http://msdn.microsoft.com/en-us/library/aa366058(v=VS.85).aspx
IP_ADAPTER_ADDRESSES 结构体中只要真实的网卡IP,故检查IP_ADAPTER_ADDRESSES::IfType = IF_TYPE_ETHERNET_CSMACD (An Ethernet network interface.)
这里的话还可以改成排除IF_TYPE_TUNNEL类型(隧道适配器) (这里根据需要设定)
然后在得到IP_ADAPTER_ADDRESSES::FirstUnicastAddress
类型为PIP_ADAPTER_UNICAST_ADDRESS , MSDN:http://msdn.microsoft.com/en-us/library/windows/desktop/aa366066(v=vs.85).aspx
The DAD state is invalid.
The DAD state is tentative.
A duplicate IP address has been detected.
The IP address has been deprecated.
The IP address is the preferred address.
INT WSAAPI WSAAddressToString( __in LPSOCKADDR lpsaAddress, __in DWORD dwAddressLength, __in_opt LPWSAPROTOCOL_INFO lpProtocolInfo, __inout LPTSTR lpszAddressString, __inout LPDWORD lpdwAddressStringLength );将SOCKADDR转换成IPV6字符串格式