这几天想复习下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类型的数据以便用户操作,因此把原始格式转化成用户可见的格式需要以上两步操作。
转载: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)
{
}
====================================================
用 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,
}
// Make a second call to GetAdapters Addresses to get the
// actual data we want
if ((dwRetVal = GetAdaptersAddresses(AF_INET,
}
else {
}
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