这几天想复习下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
#include
#include
#include
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;
}