GetAdaptersAddresses使用小结

来源:互联网 发布:最新个人业务网站源码 编辑:程序博客网 时间:2024/04/26 03:42

这几天想复习下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类型的数据以便用户操作,因此把原始格式转化成用户可见的格式需要以上两步操作。

原创粉丝点击