C++ 中通过GetAdaptersInfo获取网卡配置和Ip地址信息

出处:https://www.cnblogs.com/L-hq815/archive/2012/08/04/2622829.html



一台机器上可能不只有一个网卡,但每一个网卡只有一个MAC地址,而每一个网卡可能配置有多个IP地址;如平常的笔记本电脑中,就会有无线网卡和有线网卡(网线接口)两种;因此,如果要获得本机所有网卡的IPMAC地址信息,则必须顺序获得每个网卡,再依次获取其信息等;在windows sdk中,用IP_ADAPTER_INFO结构体存储网卡信息,包括网卡名、网卡描述、网卡MAC地址、网卡IP等,该结构体的主要描述如下所示:

复制代码
typedef struct _IP_ADAPTER_INFO {
  struct _IP_ADAPTER_INFO* Next;//指向链表中下一个适配器信息的指针
  DWORD ComboIndex;//预留值
  char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];//使用ANSI字符串表示的适配器名称
  char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];//使用ANSI字符串表示的适配器描述
  UINT AddressLength;//适配器硬件地址以字节计算的长度
  BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];//硬件地址以BYTE数组所表示
  DWORD Index;//适配器索引
    UINT Type;//适配器类型,主要有以下几种:
    /*
     *   MIB_IF_TYPE_OTHER     1
     *   MIB_IF_TYPE_ETHERNET     6
     *   MIB_IF_TYPE_TOKENRING     9
     *   MIB_IF_TYPE_FDDI     15
     *   MIB_IF_TYPE_PPP     23
     *   MIB_IF_TYPE_LOOPBACK      24
     *   MIB_IF_TYPE_SLIP      28
     */
  UINT DhcpEnabled;//指定这个适配器是否开启DHCP
  PIP_ADDR_STRING CurrentIpAddress;//预留值
  IP_ADDR_STRING IpAddressList;//该适配器的IPv4地址链表
  IP_ADDR_STRING GatewayList;//该适配器的网关IPv4地址链表
  IP_ADDR_STRING DhcpServer;//该适配器的DHCP服务器的IPv4 地址链表
  BOOL HaveWins;
  IP_ADDR_STRING PrimaryWinsServer;
  IP_ADDR_STRING SecondaryWinsServer;
  time_t LeaseObtained;
  time_t LeaseExpires;
  } IP_ADAPTER_INFO,*PIP_ADAPTER_INFO;
复制代码

由于可能有多个网卡,因此struct _IP_ADAPTER_INFO* Next字段为一个链表结构指针,由于一个网卡可能有多个IP,因此IP_ADDR_STRING字段应该也是一个链表结构,其信息如下所示:

复制代码
typedef struct _IP_ADDR_STRING
{
    struct _IP_ADDR_STRING* Next;  //指向同类型节点,即下一个IP(如果有多IP的话)
    IP_ADDRESS_STRING IpAddress;  //IP地址信息
    IP_MASK_STRING IpMask; //IP子网掩码
    DWORD Context;// 网络表入口。这个值对应着AddIPAddredd和DeleteIPAddress函数中的NTEContext参数
} IP_ADDR_STRING;
复制代码

在基本了解以上信息后,就可以调用GetAdaptersInfo函数来获取相关网卡信息了,其通用的代码如下所示:

复制代码
#include <WinSock2.h>
#include <Iphlpapi.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"Iphlpapi.lib") //需要添加Iphlpapi.lib库

int main(int argc, char* argv[])
{
    //PIP_ADAPTER_INFO结构体指针存储本机网卡信息
    PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();
    //得到结构体大小,用于GetAdaptersInfo参数
    unsigned long stSize = sizeof(IP_ADAPTER_INFO);
    //调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量;其中stSize参数既是一个输入量也是一个输出量
    int nRel = GetAdaptersInfo(pIpAdapterInfo,&stSize);
    //记录网卡数量
    int netCardNum = 0;
    //记录每张网卡上的IP地址数量
    int IPnumPerNetCard = 0;
    if (ERROR_BUFFER_OVERFLOW == nRel)
    {
        //如果函数返回的是ERROR_BUFFER_OVERFLOW
        //则说明GetAdaptersInfo参数传递的内存空间不够,同时其传出stSize,表示需要的空间大小
        //这也是说明为什么stSize既是一个输入量也是一个输出量
        //释放原来的内存空间
        delete pIpAdapterInfo;
        //重新申请内存空间用来存储所有网卡信息
        pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize];
        //再次调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量
        nRel=GetAdaptersInfo(pIpAdapterInfo,&stSize);    
    }
    if (ERROR_SUCCESS == nRel)
    {
        //输出网卡信息
         //可能有多网卡,因此通过循环去判断
    while (pIpAdapterInfo)
    {
        cout<<"网卡数量:"<<++netCardNum<<endl;
        cout<<"网卡名称:"<<pIpAdapterInfo->AdapterName<<endl;
        cout<<"网卡描述:"<<pIpAdapterInfo->Description<<endl;
        switch(pIpAdapterInfo->Type)
        {
        case MIB_IF_TYPE_OTHER:
            cout<<"网卡类型:"<<"OTHER"<<endl;
            break;
        case MIB_IF_TYPE_ETHERNET:
            cout<<"网卡类型:"<<"ETHERNET"<<endl;
            break;
        case MIB_IF_TYPE_TOKENRING:
            cout<<"网卡类型:"<<"TOKENRING"<<endl;
            break;
        case MIB_IF_TYPE_FDDI:
            cout<<"网卡类型:"<<"FDDI"<<endl;
            break;
        case MIB_IF_TYPE_PPP:
            printf("PP\n");
            cout<<"网卡类型:"<<"PPP"<<endl;
            break;
        case MIB_IF_TYPE_LOOPBACK:
            cout<<"网卡类型:"<<"LOOPBACK"<<endl;
            break;
        case MIB_IF_TYPE_SLIP:
            cout<<"网卡类型:"<<"SLIP"<<endl;
            break;
        default:

            break;
        }
        cout<<"网卡MAC地址:";
        for (DWORD i = 0; i < pIpAdapterInfo->AddressLength; i++)
            if (i < pIpAdapterInfo->AddressLength-1)
            {
                printf("%02X-", pIpAdapterInfo->Address[i]);
            }
            else
            {
                printf("%02X\n", pIpAdapterInfo->Address[i]);
            }
            cout<<"网卡IP地址如下:"<<endl;
            //可能网卡有多IP,因此通过循环去判断
            IP_ADDR_STRING *pIpAddrString =&(pIpAdapterInfo->IpAddressList);
            do 
            {
                cout<<"该网卡上的IP数量:"<<++IPnumPerNetCard<<endl;
                cout<<"IP 地址:"<<pIpAddrString->IpAddress.String<<endl;
                cout<<"子网地址:"<<pIpAddrString->IpMask.String<<endl;
                cout<<"网关地址:"<<pIpAdapterInfo->GatewayList.IpAddress.String<<endl;
                pIpAddrString=pIpAddrString->Next;
            } while (pIpAddrString);
            pIpAdapterInfo = pIpAdapterInfo->Next;
            cout<<"--------------------------------------------------------------------"<<endl;
    }
    
    }
    //释放内存空间
    if (pIpAdapterInfo)
    {
        delete pIpAdapterInfo;
    }
 
    return 0;
}
复制代码

执行结果:








出处:http://blog.sina.com.cn/s/blog_93b1c45d0101g4wk.html

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


 

[cpp]   view plain copy
  1. #include  
  2. #include  
  3. #include  
  4. #include  
  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类型的数据以便用户操作,因此把原始格式转化成用户可见的格式需要以上两步操作。



MIB_IFTABLE结构,获取网卡数据流量

出处:http://blog.sina.com.cn/s/blog_77538e140101b7fb.html

#include
#pragma comment ( lib, "iphlpapi.lib")

使用GetIfTable()获取各个端口信息的时候用到的 _MIB_IFTABLE结构:

typedef struct _MIB_IFTABLE {
DWORD dwNumEntries; //得到的端口个数
MIB_IFROW table[ANY_SIZE]; //得到的各个端口的信息,这个结构才是重点
} MIB_IFTABLE, *PMIB_IFTABLE;

 
typedef struct _MIB_IFROW { WCHAR wszName[MAX_INTERFACE_NAME_LEN]; DWORD dwIndex; DWORD dwType; DWORD dwMtu; DWORD dwSpeed; DWORD dwPhysAddrLen; BYTE bPhysAddr[MAXLEN_PHYSADDR]; DWORD dwAdminStatus; DWORD dwOperStatus; DWORD dwLastChange; DWORD dwInOctets; DWORD dwInUcastPkts; DWORD dwInNUcastPkts; DWORD dwInDiscards; DWORD dwInErrors; DWORD dwInUnknownProtos; DWORD dwOutOctets; DWORD dwOutUcastPkts; DWORD dwOutNUcastPkts; DWORD dwOutDiscards; DWORD dwOutErrors; DWORD dwOutQLen; DWORD dwDescrLen; BYTE bDescr[MAXLEN_IFDESCR]; } MIB_IFROW, *PMIB_IFROW;
 
wzsName:包含了该接口的名字(多字节的),具体也不知道哈意思,就是一串数字,有懂的和我说一下
dwIndex:该接口的索引值,比如有多个网卡的时候,每个网卡都有一个索引值,是会随着网卡正在被使用的个数变化的
dwType:该接口的类型,这个类型是被IANA(是个什么协会吧)定义的,有以下几种:
其中24是网络回路的网卡(我自己是这样叫的),就是127.0.0.1那个,应该是每个机子都有的吧 
一般我们用的是6. 
dwMtu:百度一下MTU就知道了,就是该接口的最大传输单元,理解为该通信协议的某一层上面能通过的最大的数据包的大小(以字节为单位) 
dwSpeed:该接口最大的传输速率,可是看成是这个接口每秒最多传多大的数据的一个规格,我刚开始的时候以为这是该接口的即时传输速度呢,郁闷 
dwPhysAddrLen: bPhysAddr指向的地址的长度 
bPhysAddr:指向该接口地址的指针 
dwAdminStatus:该接口的管理状态,按我的理解就是人为设定的那个状态:启用/禁用 
dwOperStatus:该接口的操作状态,它可以取以下的值,看了下面的值就知道是什么意思了 
0 MIB_IF_OPER_STATUS_NON_OPERATIONAL 网络适配器被禁止的状态; 
1 MIB_IF_OPER_STATUS_UNREACHABLE 没有连接的状态; 
2 MIB_IF_OPER_STATUS_DISCONNECTED 电缆未连接的状态; 
3 MIB_IF_OPER_STATUS_CONNECTING 广域网适配器连接中的状态; 
4 MIB_IF_OPER_STATUS_CONNECTED 广域网适配器连接上远程对等点的状态; 
5 MIB_IF_OPER_STATUS_OPERATIONAL 局域网适配器默认的连接状态;

dwLastChange: 适配器状态最后一次改变的时间; 
dwInOctets: 该接口总的收到的数据大小; 
dwInUcastPkts As Long '总共收到(unicast包)
dwInNUcastPkts As Long '总共收到(non-unicast包),包括广播包和多点传送包dwInDiscards As Long '收到后丢弃包总数(即使没有错误)
dwInErrors As Long '收到出错包总数
dwInUnknownProtos As Long '收到后因协议不明而丢弃的包总数
dwOutOctets As Long '总共发送(字节)
dwOutUcastPkts As Long '总共发送(unicast包)
dwOutNUcastPkts As Long '总共发送(non-unicast包),包括广播包和多点传送包
dwOutDiscards As Long '发送丢弃包总数(即使没有错误)
dwOutErrors As Long '发送出错包总数
dwOutQLen As Long '发送队列长度
dwDescrLen As Long ' bDescr部分有效长度
bDescr(0 To 255) As Byte '接口描述 也就是在设备管理器上看的到名字 


用一秒钟前后得到的dwInOctets数据相减,就是这一秒中该接口的流量,用这个方法就可以计算机子的即时流量了 

但是当机子有多个网卡时,怎么判断目前机子正在用的是哪一个,我也不知道,在网上看好多例子,当有多网卡时,都是直接用第一个来计算流量的,难道默认得到的结构数组第一个就是正在用的? 好像是这样的,如果有更详细的,留下说明吧
ValueMeaning
IF_TYPE_OTHER 1

Some other type of network interface.

IF_TYPE_ETHERNET_CSMACD 6

An Ethernet network interface.

IF_TYPE_ISO88025_TOKENRING 9

A token ring network interface.

IF_TYPE_PPP 23

A PPP network interface.

IF_TYPE_SOFTWARE_LOOPBACK 24

A software loopback network interface.

IF_TYPE_ATM 37

An ATM network interface.

IF_TYPE_IEEE80211 71

An IEEE 802.11 wireless network interface.

IF_TYPE_TUNNEL 131

A tunnel type encapsulation network interface.

IF_TYPE_IEEE1394 144

An IEEE 1394 (Firewire) high performance serial bus network interface.





  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值