现在的位置: 首页 > 综合 > 正文

GetAdaptersAddresses使用小结

2018年07月17日 ⁄ 综合 ⁄ 共 9593字 ⁄ 字号 评论关闭

这几天想复习下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函数的使用并不复杂,下面是一个我个人写的示例程序,演示该函数的基本功能:


  1. #include<WinSock2.h>  
  2. #include<WS2tcpip.h>  
  3. #include<iostream>  
  4. #include<IPHlpApi.h>  
  5. using namespace std;  
  6.   
  7. int main()  
  8. {  
  9.     PIP_ADAPTER_ADDRESSES pAddresses = nullptr;  
  10.     IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = nullptr;  
  11.     ULONG outBufLen = 0;  
  12.     DWORD dwRetVal = 0;  
  13.     char buff[100];  
  14.     DWORD bufflen=100;  
  15.     int i;  
  16.   
  17.     GetAdaptersAddresses(AF_UNSPEC,0, NULL, pAddresses,&outBufLen);  
  18.   
  19.     pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen);  
  20.   
  21.     if ((dwRetVal = GetAdaptersAddresses(AF_INET,GAA_FLAG_SKIP_ANYCAST,NULL,pAddresses,&outBufLen)) == NO_ERROR) {  
  22.   
  23.             while (pAddresses) {  
  24.                 printf("%S, %.2x-%.2x-%.2x-%.2x-%.2x-%.2x: \n",  
  25.                     pAddresses->FriendlyName,  
  26.                     pAddresses->PhysicalAddress[0],pAddresses->PhysicalAddress[1],  
  27.                     pAddresses->PhysicalAddress[2],pAddresses->PhysicalAddress[3],  
  28.                     pAddresses->PhysicalAddress[4],pAddresses->PhysicalAddress[5]);  
  29.       
  30.                 PIP_ADAPTER_UNICAST_ADDRESS pUnicast = pAddresses->FirstUnicastAddress;  
  31.                 pDnServer = pAddresses->FirstDnsServerAddress;  
  32.       
  33.                 if(pDnServer)  
  34.                 {  
  35.                     sockaddr_in *sa_in = (sockaddr_in *)pDnServer->Address.lpSockaddr;  
  36.                     printf("DNS:%s\n",inet_ntop(AF_INET,&(sa_in->sin_addr),buff,bufflen));  
  37.                 }  
  38.                 if (pAddresses->OperStatus == IfOperStatusUp)  
  39.                 {  
  40.                     printf("Status: active\n");  
  41.                 }  
  42.                 else  
  43.                 {  
  44.                     printf("Status: deactive\n");  
  45.                 }  
  46.       
  47.                   
  48.                 for (i = 0; pUnicast != NULL; i++)  
  49.                 {  
  50.                     if (pUnicast->Address.lpSockaddr->sa_family == AF_INET)  
  51.                     {  
  52.                         sockaddr_in *sa_in = (sockaddr_in *)pUnicast->Address.lpSockaddr;  
  53.                         printf("IPV4 Unicast Address:%s\n",inet_ntop(AF_INET,&(sa_in->sin_addr),buff,bufflen));  
  54.                     }  
  55.                     else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6)  
  56.                     {  
  57.                         sockaddr_in6 *sa_in6 = (sockaddr_in6 *)pUnicast->Address.lpSockaddr;  
  58.                         printf("IPV6:%s\n",inet_ntop(AF_INET6,&(sa_in6->sin6_addr),buff,bufflen));  
  59.                     }  
  60.                     else  
  61.                     {  
  62.                         printf("\tUNSPEC");  
  63.                     }  
  64.                     pUnicast = pUnicast->Next;  
  65.                 }  
  66.                 printf("Number of Unicast Addresses: %d\n", i);  
  67.                 pAddresses = pAddresses->Next;  
  68.             }  
  69.     }  
  70.     else {  
  71.         LPVOID lpMsgBuf;  
  72.         printf("Call to GetAdaptersAddresses failed.\n");  
  73.         if (FormatMessage(  
  74.             FORMAT_MESSAGE_ALLOCATE_BUFFER |  
  75.             FORMAT_MESSAGE_FROM_SYSTEM |  
  76.             FORMAT_MESSAGE_IGNORE_INSERTS,  
  77.             NULL,  
  78.             dwRetVal,  
  79.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),   
  80.             (LPTSTR) &lpMsgBuf,  
  81.             0,  
  82.             NULL )) {  
  83.                 printf("\tError: %s", lpMsgBuf);  
  84.         }  
  85.         LocalFree( lpMsgBuf );  
  86.     }  
  87.     free(pAddresses);  
  88.   
  89.     return 0;  
  90. }  


这个小程序没什么特别之处,可以输出网卡描述,IP地址,MAC地址和DNS地址。但是,这个程序有几处值得注意的地方,首先就是第一次调用GetAdaptersAddresses的地方,这个调用目的并不是获得网卡信息句柄,而是获得该结构体的大小值,在这里也就是outBufLen,然后才能为pAddresses分配内存空间,最后再再次调用GetAdaptersAddresses以获得网卡信息的指针,这种使用方法有点怪异,但是我翻阅了一些资料,几乎全是这么干的。还有一处是由FirstUnicastAddress获得网络地址信息的地方,由这个数据成员可以得到包含地址信息的值,在这里就是lpSockaddr,这个变量是sockaddr类型的,我之前曾经试图直接输出这个变量来获得地址信息,后来查阅了一些资料才知道,这个变量是操作系统数据类型,并不是给用户用的,该数据之所以没格式,是为了适应不同操作系统而避免数据不通用。用户想要初始化或者使用这个数据,必须把它转化成XXX_in类型的数据,如果是ipv4地址,需要转化成sockaddr_in格式数据,而ipv6类型的地址则需要转化成sockaddr_in6格式的数据。转化完之后,工作还没完,还必须用InetNtop把数据转化成string类型的数据以便用户操作,因此把原始格式转化成用户可见的格式需要以上两步操作。

转载:http://blog.csdn.net/linuxtiger/article/details/7002896


GetAdaptersInfo

 

This function retrieves adapter information for the local computer. This function is deprecated in favor of GetAdaptersAddresses.

虽然 MSDN 上这么说:用 GetAdaptersAddresses替代 GetAdaptersInfo. 但是我在 winxp 上测试的结果,必须用 GetAdaptersInfo 才能得到

正确的结果,尤其是当你有多个网卡(含虚拟网卡)存在时。

以下的例子在我的机器上可以正常运行。用于枚举本机上所有的网卡。

 

void get_adapter_info(void)
{
    PIP_ADAPTER_INFO pAdapterInfo;
    PIP_ADAPTER_INFO pAdapter = NULL;
    DWORD dwRetVal = 0;
    ULONG ulOutBufLen;
    DWORD i;

    pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
    ulOutBufLen = sizeof(IP_ADAPTER_INFO);

    // Make an initial call to GetAdaptersInfo to get
    // the necessary size into the ulOutBufLen variable
    if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
      free(pAdapterInfo);
      pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen );
    }

    if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
      pAdapter = pAdapterInfo;
      while (pAdapter) {
        printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName);
        printf("\tAdapter Desc: \t%s\n", pAdapter->Description);
        //printf("\tAdapter Addr: \t%ld\n", pAdapter->Address);
        printf("\tIP Address: \t%s\n", pAdapter->IpAddressList.IpAddress.String);
        printf("\tIP Mask: \t%s\n", pAdapter->IpAddressList.IpMask.String);
        printf("\tGateway: \t%s\n", pAdapter->GatewayList.IpAddress.String);
        printf("\tMac: \t");
        for( i = 0; i < pAdapter->AddressLength; i++)
            printf("%.2x-",pAdapter->Address[i]);
        printf("\n");

        printf("\t***\n");
        if (pAdapter->DhcpEnabled) {
          printf("\tDHCP Enabled: Yes\n");
          printf("\t\tDHCP Server: \t%s\n", pAdapter->DhcpServer.IpAddress.String);
          printf("\tLease Obtained: %ld\n", pAdapter->LeaseObtained);
        }
        else
          printf("\tDHCP Enabled: No\n");
        if (pAdapter->HaveWins) {
          printf("\tHave Wins: Yes\n");
          printf("\t\tPrimary Wins Server: \t%s\n", pAdapter->PrimaryWinsServer.IpAddress.String);
          printf("\t\tSecondary Wins Server: \t%s\n", pAdapter->SecondaryWinsServer.IpAddress.String);
        }
        else
          printf("\tHave Wins: No\n");

        pAdapter = pAdapter->Next;
      }
    }
    else {
      printf("Call to GetAdaptersInfo failed.\n");
    }
    free(pAdapterInfo);
}

====================================================

用 GetAdaptersAddresses 不能得到正确的结果,它不能枚举所有的网卡。似乎只能枚举前3个。

void getmac(void)
{

PIP_ADAPTER_ADDRESSES pAddresses;

ULONG outBufLen = 0;
DWORD dwRetVal = 0;

pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(sizeof(IP_ADAPTER_ADDRESSES));

// Make an initial call to GetAdaptersAddresses to get the
// size needed into the outBufLen variable
if (GetAdaptersAddresses(AF_INET,
  0,
  NULL,
  pAddresses,
  &outBufLen) == ERROR_BUFFER_OVERFLOW) {
  free(pAddresses);
  pAddresses = (IP_ADAPTER_ADDRESSES*) malloc(outBufLen);
}

// Make a second call to GetAdapters Addresses to get the
// actual data we want
if ((dwRetVal = GetAdaptersAddresses(AF_INET,
  GAA_FLAG_SKIP_ANYCAST,
  NULL,
  pAddresses,
  &outBufLen)) == NO_ERROR) {
  // If successful, output some information from the data we received
  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]);
    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), // Default language
    (LPTSTR) &lpMsgBuf,
    0,
    NULL )) {
    printf("\tError: %s", lpMsgBuf);
  }
  LocalFree( lpMsgBuf );
}

free(pAddresses);
}

==============

ip = inet_addr(“192.168.1.1”);

serv_addr.sin_addr.s_addr = ip;

 

serv_addr.sin_addr.s_addr 的值必须是网络字节顺序。

注意,inet_addr 返回的值已经是网络字节顺序。所以可以直接给 serv_addr.sin_addr.s_addr  赋值。不需要用 htonl() 转换。

转载:http://blog.sina.com.cn/s/blog_730d14360100oj4f.html

抱歉!评论已关闭.