1. 程式人生 > 其它 >Qt獲取ip地址

Qt獲取ip地址

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



轉載於:Qt 5.7 獲取本機IP地址-阿里雲開發者社群 (aliyun.com)