GetAdaptersAddresses使用小結

這幾天想複習下Windows C socket網絡編程,網上查閱了一些資料,大部分資料還是比較老的,介紹的都是舊接口函數,而且,絕大多數書上的內容都沒有介紹API中支持ipv6的接口。

 

我在寫一個獲得本地網卡信息的函數的時候,本來想用GetAdapterInfo,由於很久沒用過win32 api,於是查了下MSDN,發現這個接口已經很舊了,而且不支持ipv6,在文檔中明確推薦使用GetAdaptersAddresses接口,查了下,除了支持IPV6,使用方式跟GetAdapterInfo差別倒是不大,但是數據結構複雜很多,特別是獲得網卡信息的句柄結構體,挺複雜的,google了一下,想找幾個sample,搜了半天就只有msdn上那一個不太好的sample而已,在CSDN上搜了一下,資料也非常有限,有的人甚至說用這個函數無法獲得DNS地址,因此仍然推薦老的GetAdapterInfo,我自己試了下,還是可以獲得地址信息的,無論是硬件地址還是網絡地址,只不過麻煩一些,但都可以得到,而且還提供了很多額外信息,比GetAdapterInfo更全面的信息,因此這個接口函數在將來應該是會很有用。

 

這個函數的接口聲明是這樣的:

ULONG WINAPIGetAdaptersAddresses(

  __in     ULONGFamily,

  __in     ULONGFlags,

  __in     PVOIDReserved,

  __inout  PIP_ADAPTER_ADDRESSESAdapterAddresses,

  __inout  PULONG SizePointer

);

第一個參數Family是網絡協議族,用戶可以指定ipv6和ipv4,這是它和GetAdapterInfo接口區別最大的地方。第二個參數是指定地址類型的,可以指定單播、多播、ipv6、DNS等,用戶可以根據需求傳不同的參數得到不同的地址。第三個是保留參數,補足位用的。第四個參數AdapterAddresses是一個指向網卡信息結構體的指針,該指針類型是PIP_ADAPTER_ADDRESSES類型的,關於這個變量後面再詳細介紹。第五個參數是AdapterAddresses所需數據大小的值。

 

這幾個變量裏,AdapterAddresses是最核心的變量,裏面存儲着用戶所需要的信息,該變量定義非常長,結構體裏面有34個數據成員,涵蓋了網卡的全部信息,由於信息衆多,因此不一一介紹,只說幾個常用的數據成員。


FirstUnicastAddress,FirstAnycastAddress, FirstMulticastAddress; FirstDnsServerAddress,這幾個數據成員分別代表單播地址、任播地址、多播地址、DNS服務器地址。其中的anycast是隻有ipv6纔有的數據傳輸方式,而單播和多播則無協議限制,這幾種傳輸方式的區別可以在任意一本網絡教材上查到,這裏不做多餘敘述。這幾個變量全都是一個鏈表的頭結點,這是處於多網卡計算機的考量,通過頭結點,用戶可以枚舉出所有網卡的地址信息,網上有人說只能枚舉出三個網卡的信息,但我在win7上試過,枚舉出所有網卡是沒問題的,可能跟操作系統有關。

另一個比較常用的數據成員是PhysicalAddress,它是一個數組,該數據成員存儲了網卡的mac地址,而數組大小由PhysicalAddressLength指定。一般來說,該數組大小是6。

 

Description是一個PCHAR類型的變量,存儲着網卡的描述,比如11b/g/n。

 

OperStatus是描述網卡狀態的變量,是個枚舉類型IF_OPER_STATUS,該類型包含7種網卡的狀態,比如測試、激活、等待等。

 

除了以上信息,AdapterAddresses還提供了其他很多有用的信息,如果有興趣可以msdn上搜索一下。

 

對於熟悉使用GetAdapterInfo的程序員來說,GetAdaptersAddresses函數的使用並不複雜,下面是一個我個人寫的示例程序,演示該函數的基本功能:


#include<WinSock2.h>
#include<WS2tcpip.h>
#include<iostream>
#include<IPHlpApi.h>
using namespace std;

int main()
{
	PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
	IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = nullptr;
	ULONG outBufLen = 0;
	DWORD dwRetVal = 0;
	char buff[100];
	DWORD bufflen=100;
	int i;

	GetAdaptersAddresses(AF_UNSPEC,0, NULL, pAddresses,&outBufLen);

	pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen);

	if ((dwRetVal = GetAdaptersAddresses(AF_INET,GAA_FLAG_SKIP_ANYCAST,NULL,pAddresses,&outBufLen)) == NO_ERROR) {

			while (pAddresses) {
				printf("%S, %.2x-%.2x-%.2x-%.2x-%.2x-%.2x: \n",
					pAddresses->FriendlyName,
					pAddresses->PhysicalAddress[0],pAddresses->PhysicalAddress[1],
					pAddresses->PhysicalAddress[2],pAddresses->PhysicalAddress[3],
					pAddresses->PhysicalAddress[4],pAddresses->PhysicalAddress[5]);
	
				PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress;
				pDnServer = pAddresses->FirstDnsServerAddress;
	
				if(pDnServer)
				{
					sockaddr_in *sa_in = (sockaddr_in *)pDnServer->Address.lpSockaddr;
					printf("DNS:%s\n",inet_ntop(AF_INET,&(sa_in->sin_addr),buff,bufflen));
				}
				if (pAddresses->OperStatus == IfOperStatusUp)
				{
					printf("Status: active\n");
				}
				else
				{
					printf("Status: deactive\n");
				}
	
				
				for (i = 0; pUnicast != NULL; i++)
				{
					if (pUnicast->Address.lpSockaddr->sa_family == AF_INET)
					{
						sockaddr_in *sa_in = (sockaddr_in *)pUnicast->Address.lpSockaddr;
						printf("IPV4 Unicast Address:%s\n",inet_ntop(AF_INET,&(sa_in->sin_addr),buff,bufflen));
					}
					else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6)
					{
						sockaddr_in6 *sa_in6 = (sockaddr_in6 *)pUnicast->Address.lpSockaddr;
						printf("IPV6:%s\n",inet_ntop(AF_INET6,&(sa_in6->sin6_addr),buff,bufflen));
					}
					else
					{
						printf("\tUNSPEC");
					}
					pUnicast = pUnicast->Next;
				}
				printf("Number of Unicast Addresses: %d\n", i);
				pAddresses = pAddresses->Next;
			}
	}
	else {
		LPVOID lpMsgBuf;
		printf("Call to GetAdaptersAddresses failed.\n");
		if (FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER |
			FORMAT_MESSAGE_FROM_SYSTEM |
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			dwRetVal,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
			(LPTSTR) &lpMsgBuf,
			0,
			NULL )) {
				printf("\tError: %s", lpMsgBuf);
		}
		LocalFree( lpMsgBuf );
	}
	free(pAddresses);

	return 0;
}

這個小程序沒什麼特別之處,可以輸出網卡描述,IP地址,MAC地址和DNS地址。但是,這個程序有幾處值得注意的地方,首先就是第一次調用GetAdaptersAddresses的地方,這個調用目的並不是獲得網卡信息句柄,而是獲得該結構體的大小值,在這裏也就是outBufLen,然後才能爲pAddresses分配內存空間,最後再再次調用GetAdaptersAddresses以獲得網卡信息的指針,這種使用方法有點怪異,但是我翻閱了一些資料,幾乎全是這麼幹的。還有一處是由FirstUnicastAddress獲得網絡地址信息的地方,由這個數據成員可以得到包含地址信息的值,在這裏就是lpSockaddr,這個變量是sockaddr類型的,我之前曾經試圖直接輸出這個變量來獲得地址信息,後來查閱了一些資料才知道,這個變量是操作系統數據類型,並不是給用戶用的,該數據之所以沒格式,是爲了適應不同操作系統而避免數據不通用。用戶想要初始化或者使用這個數據,必須把它轉化成XXX_in類型的數據,如果是ipv4地址,需要轉化成sockaddr_in格式數據,而ipv6類型的地址則需要轉化成sockaddr_in6格式的數據。轉化完之後,工作還沒完,還必須用InetNtop把數據轉化成string類型的數據以便用戶操作,因此把原始格式轉化成用戶可見的格式需要以上兩步操作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章