本程序可以获得网卡的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;
}
代码在我的机器(win7 , vs2008上运行正常)(本机上有3个网卡:一个teamview软件的网卡,一个真实网卡,还有个virtualbox host only 的网卡
最后得到了一个正确的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字符串格式