Qt 获取本机IP地址
最先想到的方案
QString get_local_ip()
{
QHostInfo info = QHostInfo::fromName(QHostInfo::localHostName());
// 找出一个IPv4地址即返回
foreach(QHostAddress address,info.addresses())
{
if(address.protocol() == QAbstractSocket::IPv4Protocol)
{
return address.toString();
}
}
return "0.0.0.0";
}
如果机器上仅有一块以太网卡,且没有安装虚拟机,这段代码似乎可以完成任务。不幸的是,机器上还有无线网卡,VMware和VirtualBox这两个虚拟机软件。因此要过滤掉虚拟网卡的信息。
修改后的方案
/**
* @brief 检测当前网卡是否是虚拟网卡(VMware/VirtualBox)或回环网卡
* @param str_card_name 网卡的描述信息
* @return 如果是虚拟网卡或回环网卡,返回true, 否则返回false
*/
bool is_virtual_network_card_or_loopback(QString str_card_name)
{
if (-1 != str_card_name.indexOf("VMware")
|| -1 != str_card_name.indexOf("Loopback")
|| -1 != str_card_name.indexOf("VirtualBox")
)
return true;
return false;
}
/**
* @brief 获取本机IP地址
*/
void print_local_ip()
{
// 1. 获取所有网络接口
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
QList<QNetworkAddressEntry> entry;
foreach(QNetworkInterface inter, interfaces)
{
// 过滤掉不需要的网卡信息
if (is_virtual_network_card_or_loopback(inter.humanReadableName()))
continue;
if (inter.flags() & (QNetworkInterface::IsUp | QNetworkInterface::IsRunning))
{
entry = inter.addressEntries();
// entry.at(0) 是IPv6信息
if (entry.at(1).ip().protocol() == QAbstractSocket::IPv4Protocol)
{
if (-1 != inter.name().indexOf("wireless"))
qDebug() << inter.humanReadableName() << inter.name() << " 无线网IP: " << entry.at(1).ip().toString();
else if (-1 != inter.name().indexOf("ethernet"))
qDebug() << inter.humanReadableName() << inter.name() << " 以太网IP: " << entry.at(1).ip().toString();
}
entry.clear();
}
}
}
这段代码仅能过滤VMware和VirtualBox的虚拟网卡,且只打印出了网卡上配置的第一个IPv4地址。通常,这应该能够满足获取IP地址需求了,但是,有时候一块网卡上配置了多个IP地址,为了能获取所有IP地址,对上述代码作如下修改:
/**
* @brief 获取本机IP地址
* @param map_ip 输出参数 IPv4列表
* QString ipv4地址
* int 网卡类型 取值为[0,1],0表示无线,1表示有线
*/
void get_ip(QMap<QString, int> & map_ip)
{
// 1. 获取所有网络接口
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
QList<QNetworkAddressEntry> entry;
foreach(QNetworkInterface inter, interfaces)
{
// 过滤掉vmware虚拟网卡和回环网卡
if (is_virtual_network_card_or_loopback(inter.humanReadableName()))
continue;
if (inter.flags() & (QNetworkInterface::IsUp | QNetworkInterface::IsRunning))
{
entry = inter.addressEntries();
int cnt = entry.size() - 1;
for (int i = 1; i <= cnt; ++i)
{
if (entry.at(i).ip().protocol() == QAbstractSocket::IPv4Protocol)
{
if (-1 != inter.name().indexOf("wireless"))
{
map_ip.insert(entry.at(i).ip().toString(), 0);
}
else if (-1 != inter.name().indexOf("ethernet"))
{
map_ip.insert(entry.at(i).ip().toString(), 1);
}
}
}
entry.clear();
}
}
}
// 测试
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, int> ipv4;
get_ip(ipv4);
QMapIterator<QString, int> it(ipv4);
while(it.hasNext())
{
it.next();
qDebug() << it.key() << " : " << it.value();
}
return a.exec();
}
运行结果如下:
"172.16.254.51" : 0
"192.168.10.30" : 1
"192.168.10.31" : 1
ipconfig查询结果:
现在已经解决了一个网卡多个IP的问题,但还有一个问题没有解决,那就是过滤虚拟网卡,以上仅能过滤VMware和VirtualBox的虚拟网卡。
windows系统,通过查看各个接口的设备描述信息,我们发现,这个信息中包含设备厂商相关内容,并且只有有线网卡的设备描述中含有"PCI"字样,通过查看windows相关API,发现结构体IP_ADAPTER_INFO中包含了网卡信息,并附带了示例,链接为https://msdn.microsoft.com/en-us/library/aa366062(VS.85).aspx
将示例稍作修改,以满足需求:
void get_ip_list()
{
DWORD dwRetVal = 0;
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
if(GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS)
{
GlobalFree(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
}
if((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
{
while (pAdapterInfo)
{
if(strstr(pAdapterInfo->Description,"PCI") > 0 || IF_TYPE_IEEE80211 == pAdapterInfo->Type)
{
qDebug() << "------------------------------------------------------------------------";
qDebug() << "Adapter Desc: " << QString::fromLocal8Bit(pAdapterInfo->Description);
qDebug() << "Adapter Type: " << pAdapterInfo->Type;
// 单个网卡上可能有多个IP
PIP_ADDR_STRING addr = &(pAdapterInfo->IpAddressList);
do
{
qDebug() << "IP Address: " << addr->IpAddress.String;
addr = addr->Next;
}
while (addr);
}
pAdapterInfo = pAdapterInfo->Next;
}
}
else
{
qDebug() << "GetAdaptersInfo failed with error: " << dwRetVal;
}
if(pAdapterInfo)
{
GlobalFree(pAdapterInfo);
}
}
我机器上的输出结果如下:
------------------------------------------------------------
Adapter Desc: "Microsoft Wi-Fi Direct 虚拟适配器"
Adapter Type: 71
IP Address: 0.0.0.0
------------------------------------------------------------
Adapter Desc: "Broadcom 802.11n 网络适配器"
Adapter Type: 71
IP Address: 172.16.254.51
------------------------------------------------------------
Adapter Desc: "Realtek PCIe GBE Family Controller"
Adapter Type: 6
IP Address: 192.168.10.30
IP Address: 192.168.10.128
IP Address: 192.168.10.130
显然,"Realtek PCIe GBE Family Controller"上配置了三个IP,且“Microsoft Wi-Fi Direct 虚拟适配器”不是我所需要的,排除之。
void get_ip_list(QMap<QString, int> & map_ip)
{
DWORD dwRetVal = 0;
PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof(IP_ADAPTER_INFO));
ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
if(GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) != ERROR_SUCCESS)
{
GlobalFree(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
}
if((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
{
while (pAdapterInfo)
{
if(strstr(pAdapterInfo->Description,"PCI") > 0
|| (IF_TYPE_IEEE80211 == pAdapterInfo->Type && 0 < strstr(pAdapterInfo->Description, "802")))
{
PIP_ADDR_STRING addr = &(pAdapterInfo->IpAddressList);
do
{
if (IF_TYPE_IEEE80211 == pAdapterInfo->Type)
map_ip.insert(addr->IpAddress.String, 0);
else
map_ip.insert(addr->IpAddress.String, 1);
addr = addr->Next;
}
while (addr);
}
pAdapterInfo = pAdapterInfo->Next;
}
}
else
{
qDebug() << "GetAdaptersInfo failed with error: " << dwRetVal;
}
if(pAdapterInfo)
{
GlobalFree(pAdapterInfo);
}
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QMap<QString, int> ipv4;
get_ip_list(ipv4);
QMapIterator<QString, int> it(ipv4);
while(it.hasNext())
{
it.next();
qDebug() << it.key() << " : " << it.value();
}
return a.exec();
}
输出如下:
"172.16.254.51" : 0
"192.168.10.128" : 1
"192.168.10.130" : 1
"192.168.10.30" : 1