1.思路
使用Windows的API函数来获取适配器信息,并遍历适配器列表,找到非回环类型的适配器,并且其IP地址不为0的适配器。然后,它会遍历该适配器的IP地址列表,找到与网关地址相匹配的IP地址,并将其作为本地IP地址返回。
主要步骤如下:
- 创建一个适配器信息的结构体实例。
- 获取适配器信息的大小,如果获取到的大小超过了初始分配的缓冲区大小,则重新分配更大的缓冲区。
- 获取适配器信息,并遍历适配器列表。
- 对于每个非回环类型且IP地址不为0的适配器,获取其网关地址,并将其转换为整数形式。
- 遍历该适配器的IP地址列表,对于每个IP地址,将其转换为整数形式,并与网关地址进行比较。
- 如果找到与网关地址匹配的IP地址,则将其作为本地IP地址返回。
2. api简介
DWORD GetAdaptersInfo(
PIP_ADAPTER_INFO pAdapterInfo,
PULONG pOutBufLen
);
GetAdaptersInfo 是一个 Windows API 函数,用于获取系统中所有适配器的信息。它定义在 Windows 的 IPHlpApi.h 头文件中。
参数说明:
- pAdapterInfo:一个指向 PIP_ADAPTER_INFO 结构体的指针,用于存储获取到的适配器信息。
- pOutBufLen:一个指向变量的指针,用于传递 pAdapterInfo 缓冲区的大小。在调用函数之前,需要将其设置为 pAdapterInfo 缓冲区的大小。函数返回时,它将包含实际写入到 pAdapterInfo 缓冲区的字节数。
函数返回一个 DWORD 类型的错误码,如果函数执行成功,返回值为 ERROR_SUCCESS。
GetAdaptersInfo 函数通过遍历系统中的适配器列表,将每个适配器的信息填充到 pAdapterInfo 缓冲区中。通过多次调用该函数,可以获取系统中所有适配器的信息。
注意:这里将IP地址前三位与网关前三位相同的IP认为是首选IP地址
typedef struct _IP_ADAPTER_INFO {
struct _IP_ADAPTER_INFO* Next;
DWORD ComboIndex;
char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
UINT AddressLength;
BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
DWORD Index;
UINT Type;
UINT DhcpEnabled;
PIP_ADDR_STRING CurrentIpAddress;
IP_ADDR_STRING IpAddressList;
IP_ADDR_STRING GatewayList;
IP_ADDR_STRING DhcpServer;
BOOL HaveWins;
IP_ADDR_STRING PrimaryWinsServer;
IP_ADDR_STRING SecondaryWinsServer;
time_t LeaseObtained;
time_t LeaseExpires;
} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
该结构体定义了适配器的信息,包括适配器的名称、描述、物理地址、IP 地址等。具体成员的含义如下:
- Next:指向下一个适配器信息结构体的指针,用于遍历适配器列表。
- ComboIndex:适配器的组合索引。
- AdapterName:适配器的名称。
- Description:适配器的描述。
- AddressLength:适配器地址的长度。
- Address:适配器的物理地址。
- Index:适配器的索引。
- Type:适配器的类型。
- DhcpEnabled:指示适配器是否启用了 DHCP。
- CurrentIpAddress:当前适配器的 IP 地址。
- IpAddressList:适配器的 IP 地址列表。
- GatewayList:适配器的网关地址列表。
- DhcpServer:DHCP 服务器的 IP 地址。
- HaveWins:指示适配器是否有 WINS 服务器。
- PrimaryWinsServer:主要 WINS 服务器的 IP 地址。
- SecondaryWinsServer:次要 WINS 服务器的 IP 地址。
- LeaseObtained:租约获取时间。
- LeaseExpires:租约到期时间。
通过遍历适配器列表,可以获取系统中所有适配器的信息,从而实现对网络连接的管理和配置。
2.代码
#include <string>
#include <iphlpapi.h>
#include <iostream>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int AIUtils::GetLocalIp(std::string &ip)
{
PIP_ADAPTER_INFO adapterInfo;
ULONG bufferSize = sizeof(IP_ADAPTER_INFO);
adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new char[bufferSize]);
if (GetAdaptersInfo(adapterInfo, &bufferSize) == ERROR_BUFFER_OVERFLOW) {
delete[] reinterpret_cast<char*>(adapterInfo);
adapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(new char[bufferSize]);
}
if (GetAdaptersInfo(adapterInfo, &bufferSize) == NO_ERROR) {
IP_ADAPTER_INFO* adapter = adapterInfo;
while (adapter) {
if (adapter->Type != MIB_IF_TYPE_LOOPBACK && adapter->IpAddressList.IpAddress.String[0] != '0') {
std::string gateway_addr = adapter->GatewayList.IpAddress.String;
int a, b, c, d;
sscanf(gateway_addr.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d);
IP_ADDR_STRING *addr = &adapter->IpAddressList;
while(addr) {
std::cout << "Primary IP address: " << addr->IpAddress.String << std::endl;
int a1, b1, c1, d1;
std::string ip_addr(addr->IpAddress.String);
sscanf(ip_addr.c_str(), "%d.%d.%d.%d", &a1, &b1, &c1, &d1);
if (a == a1 && b == b1 && c == c1) {
ip = ip_addr;
break;
}
addr = addr->Next;
}
break;
}
adapter = adapter->Next;
}
}
if (adapterInfo) {
delete[] reinterpret_cast<char*>(adapterInfo);
}
return 0;
}