Linux下實(shí)時(shí)獲取WiFi與熱點(diǎn)狀態(tài)的方法詳解
一、引言:為什么需要網(wǎng)絡(luò)狀態(tài)檢測(cè)?
1.1 典型應(yīng)用場(chǎng)景
在物聯(lián)網(wǎng)和智能設(shè)備開發(fā)中,網(wǎng)絡(luò)狀態(tài)檢測(cè)是基礎(chǔ)而關(guān)鍵的功能。想象以下場(chǎng)景:
- 智能家居:當(dāng)家庭WiFi斷開時(shí),智能音箱自動(dòng)開啟熱點(diǎn)模式,讓用戶通過手機(jī)直連配置
- 工業(yè)設(shè)備:生產(chǎn)線設(shè)備在檢測(cè)到網(wǎng)絡(luò)異常時(shí),自動(dòng)記錄狀態(tài)并開啟維護(hù)通道
- 移動(dòng)終端:平板電腦在不同網(wǎng)絡(luò)環(huán)境下自動(dòng)調(diào)整同步策略,節(jié)省電量
1.2 技術(shù)實(shí)現(xiàn)目標(biāo)
實(shí)現(xiàn)以下核心功能:
功能 | 描述 | 技術(shù)指標(biāo) |
---|---|---|
WiFi連接檢測(cè) | 判斷設(shè)備是否連接到無線網(wǎng)絡(luò) | 響應(yīng)時(shí)間<1s |
WiFi名稱獲取 | 獲取當(dāng)前連接的無線網(wǎng)絡(luò)SSID | 支持特殊字符 |
熱點(diǎn)狀態(tài)檢測(cè) | 判斷設(shè)備是否處于熱點(diǎn)模式 | 準(zhǔn)確率100% |
熱點(diǎn)名稱獲取 | 獲取設(shè)備熱點(diǎn)的SSID | 多編碼支持 |
1.3 技術(shù)原理概述
Linux系統(tǒng)通過NetworkManager服務(wù)管理網(wǎng)絡(luò)連接,提供了豐富的命令行工具:
- iwgetid:查詢無線接口連接狀態(tài)
- nmcli:NetworkManager的命令行接口
- hostapd:熱點(diǎn)管理服務(wù)
Qt的QProcess
類可以無縫調(diào)用這些系統(tǒng)命令,并通過解析輸出來獲取網(wǎng)絡(luò)狀態(tài)信息。
二、核心思路:結(jié)合Qt與Linux命令
2.1 技術(shù)架構(gòu)設(shè)計(jì)
2.2 關(guān)鍵命令分析
檢測(cè)WiFi連接狀態(tài)
# 返回當(dāng)前連接的SSID(無連接則返回空) iwgetid -r # 示例輸出: # MyHomeWiFi
檢測(cè)熱點(diǎn)狀態(tài)
# 查看活動(dòng)連接中的熱點(diǎn) nmcli connection show --active | grep wifi | grep ap # 查看hostapd進(jìn)程 pgrep hostapd
獲取熱點(diǎn)名稱
# 通過nmcli獲取 nmcli device wifi show | grep SSID # 通過hostapd配置獲取 grep ssid= /etc/hostapd/hostapd.conf
2.3 性能考量
- 命令執(zhí)行時(shí)間:各命令在樹莓派4上的平均執(zhí)行時(shí)間
iwgetid
:50-100msnmcli
:200-300mspgrep
:10-20ms
- 優(yōu)化策略:
- 緩存結(jié)果,減少命令調(diào)用
- 異步執(zhí)行,避免阻塞UI
- 合理設(shè)置檢測(cè)間隔(建議1-5秒)
三、詳細(xì)實(shí)現(xiàn):
3.1 核心類實(shí)現(xiàn)
NetworkTool.h
#ifndef NETWORKTOOL_H #define NETWORKTOOL_H #include <QObject> #include <QProcess> #include <QTimer> class NetworkTool : public QObject { Q_OBJECT public: explicit NetworkTool(QObject *parent = nullptr); // 基礎(chǔ)檢測(cè)功能 Q_INVOKABLE bool isWifiConnected(); Q_INVOKABLE QString wifiName(); Q_INVOKABLE bool isHotspotActive(); Q_INVOKABLE QString hotspotName(); // 高級(jí)功能 Q_INVOKABLE void startAutoRefresh(int interval = 3000); Q_INVOKABLE void stopAutoRefresh(); signals: void wifiStatusChanged(bool connected, const QString &name); void hotspotStatusChanged(bool active, const QString &name); private slots: void refreshStatus(); private: QString executeCommand(const QString &cmd, const QStringList &args = {}); QString parseWifiName(const QString &output); QString parseHotspotName(const QString &output); QTimer m_refreshTimer; bool m_lastWifiState = false; QString m_lastWifiName; bool m_lastHotspotState = false; QString m_lastHotspotName; }; #endif // NETWORKTOOL_H
NetworkTool.cpp
#include "NetworkTool.h" #include <QDebug> #include <QFile> NetworkTool::NetworkTool(QObject *parent) : QObject(parent) { m_refreshTimer.setSingleShot(false); connect(&m_refreshTimer, &QTimer::timeout, this, &NetworkTool::refreshStatus); } bool NetworkTool::isWifiConnected() { return !wifiName().isEmpty(); } QString NetworkTool::wifiName() { QString output = executeCommand("iwgetid", {"-r"}); return parseWifiName(output); } bool NetworkTool::isHotspotActive() { // 方法1:使用nmcli檢測(cè) QString output = executeCommand("nmcli", {"connection", "show", "--active"}); if (output.contains("wifi") && output.contains("ap")) { return true; } // 方法2:檢測(cè)hostapd進(jìn)程 output = executeCommand("pgrep", {"hostapd"}); return !output.isEmpty(); } QString NetworkTool::hotspotName() { // 嘗試通過nmcli獲取 QString output = executeCommand("nmcli", {"device", "wifi", "show"}); QString name = parseHotspotName(output); if (!name.isEmpty()) return name; // 回退到讀取hostapd配置 QFile config("/etc/hostapd/hostapd.conf"); if (config.open(QIODevice::ReadOnly)) { while (!config.atEnd()) { QByteArray line = config.readLine().trimmed(); if (line.startsWith("ssid=")) { return QString::fromUtf8(line.mid(5)); } } } return "Unknown"; } void NetworkTool::startAutoRefresh(int interval) { m_refreshTimer.start(interval); } void NetworkTool::stopAutoRefresh() { m_refreshTimer.stop(); } void NetworkTool::refreshStatus() { // 獲取當(dāng)前狀態(tài) bool wifiConnected = isWifiConnected(); QString currentWifiName = wifiName(); bool hotspotActive = isHotspotActive(); QString currentHotspotName = hotspotName(); // 檢查狀態(tài)變化 if (wifiConnected != m_lastWifiState || currentWifiName != m_lastWifiName) { m_lastWifiState = wifiConnected; m_lastWifiName = currentWifiName; emit wifiStatusChanged(wifiConnected, currentWifiName); } if (hotspotActive != m_lastHotspotState || currentHotspotName != m_lastHotspotName) { m_lastHotspotState = hotspotActive; m_lastHotspotName = currentHotspotName; emit hotspotStatusChanged(hotspotActive, currentHotspotName); } } QString NetworkTool::executeCommand(const QString &cmd, const QStringList &args) { QProcess process; process.start(cmd, args); if (!process.waitForFinished(1000)) { qWarning() << "Command timeout:" << cmd << args; return ""; } return QString::fromUtf8(process.readAllStandardOutput()).trimmed(); } QString NetworkTool::parseWifiName(const QString &output) { // iwgetid -r 直接返回SSID或空 return output; } QString NetworkTool::parseHotspotName(const QString &output) { // 解析nmcli輸出中的SSID QStringList lines = output.split('\n'); for (const QString &line : lines) { if (line.trimmed().startsWith("SSID:")) { return line.mid(5).trimmed(); } } return ""; }
3.2 UI集成示例
Qt Widgets版本
// MainWindow.h #include <QMainWindow> #include "NetworkTool.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void onWifiStatusChanged(bool connected, const QString &name); void onHotspotStatusChanged(bool active, const QString &name); private: Ui::MainWindow *ui; NetworkTool m_networkTool; }; // MainWindow.cpp #include "MainWindow.h" #include "ui_MainWindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); // 連接信號(hào) connect(&m_networkTool, &NetworkTool::wifiStatusChanged, this, &MainWindow::onWifiStatusChanged); connect(&m_networkTool, &NetworkTool::hotspotStatusChanged, this, &MainWindow::onHotspotStatusChanged); // 啟動(dòng)自動(dòng)刷新 m_networkTool.startAutoRefresh(); // 初始化狀態(tài) onWifiStatusChanged(m_networkTool.isWifiConnected(), m_networkTool.wifiName()); onHotspotStatusChanged(m_networkTool.isHotspotActive(), m_networkTool.hotspotName()); } void MainWindow::onWifiStatusChanged(bool connected, const QString &name) { ui->wifiStatusLabel->setText(connected ? "已連接" : "未連接"); ui->wifiNameLabel->setText(connected ? name : "N/A"); ui->wifiIcon->setPixmap(QPixmap(connected ? ":/icons/wifi-on.png" : ":/icons/wifi-off.png")); } void MainWindow::onHotspotStatusChanged(bool active, const QString &name) { ui->hotspotStatusLabel->setText(active ? "已開啟" : "未開啟"); ui->hotspotNameLabel->setText(active ? name : "N/A"); ui->hotspotIcon->setPixmap(QPixmap(active ? ":/icons/hotspot-on.png" : ":/icons/hotspot-off.png")); }
QML版本
// main.qml import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 ApplicationWindow { id: window width: 400 height: 300 visible: true title: "網(wǎng)絡(luò)狀態(tài)監(jiān)測(cè)" NetworkTool { id: networkTool onWifiStatusChanged: { wifiStatusText.text = connected ? "已連接" : "未連接" wifiNameText.text = name || "N/A" } onHotspotStatusChanged: { hotspotStatusText.text = active ? "已開啟" : "未開啟" hotspotNameText.text = name || "N/A" } Component.onCompleted: startAutoRefresh() } ColumnLayout { anchors.fill: parent anchors.margins: 20 spacing: 15 GroupBox { title: "WiFi狀態(tài)" Layout.fillWidth: true GridLayout { columns: 2 width: parent.width Label { text: "狀態(tài):" } Label { id: wifiStatusText } Label { text: "名稱:" } Label { id: wifiNameText } } } GroupBox { title: "熱點(diǎn)狀態(tài)" Layout.fillWidth: true GridLayout { columns: 2 width: parent.width Label { text: "狀態(tài):" } Label { id: hotspotStatusText } Label { text: "名稱:" } Label { id: hotspotNameText } } } Button { text: "手動(dòng)刷新" Layout.alignment: Qt.AlignHCenter onClicked: networkTool.refreshStatus() } } }
四、注意事項(xiàng):避坑指南
4.1 權(quán)限問題解決
常見權(quán)限錯(cuò)誤:
nmcli
報(bào)錯(cuò):“權(quán)限不足”iwgetid
無法獲取信息
解決方案:
Polkit規(guī)則配置(推薦):
sudo nano /etc/polkit-1/rules.d/10-network-info.rules
添加內(nèi)容:
polkit.addRule(function(action, subject) { if (action.id.indexOf("org.freedesktop.NetworkManager.") == 0 && subject.isInGroup("users")) { return polkit.Result.YES; } });
用戶組配置:
sudo usermod -aG netdev,network $USER
sudo免密碼(開發(fā)測(cè)試用):
echo "$USER ALL=(ALL) NOPASSWD: /usr/bin/nmcli, /usr/bin/iwgetid" | sudo tee /etc/sudoers.d/network
4.2 系統(tǒng)兼容性處理
不同發(fā)行版適配:
發(fā)行版 | 檢測(cè)命令 | 配置文件路徑 |
---|---|---|
Ubuntu/Debian | nmcli/iwgetid | /etc/NetworkManager/ |
CentOS/RHEL | nmcli/iw | /etc/sysconfig/network-scripts/ |
Arch Linux | iw/wpa_cli | /etc/netctl/ |
兼容性代碼改進(jìn):
QString NetworkTool::wifiName() { // 嘗試iwgetid QString output = executeCommand("iwgetid", {"-r"}); if (!output.isEmpty()) return output; // 回退到iw命令 output = executeCommand("iw", {"dev", "wlan0", "link"}); QRegularExpression regex("SSID: (.+)"); QRegularExpressionMatch match = regex.match(output); if (match.hasMatch()) { return match.captured(1); } return ""; }
4.3 性能優(yōu)化進(jìn)階
優(yōu)化策略:
- 命令執(zhí)行優(yōu)化:
// 異步執(zhí)行命令 void NetworkTool::executeCommandAsync( const QString &cmd, const QStringList &args, std::function<void(QString)> callback) { QProcess *process = new QProcess(this); connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [=](int exitCode, QProcess::ExitStatus exitStatus){ if (exitStatus == QProcess::NormalExit) { callback(QString::fromUtf8(process->readAllStandardOutput())); } process->deleteLater(); }); process->start(cmd, args); }
- 智能刷新機(jī)制:
void NetworkTool::refreshStatus() { // 僅當(dāng)界面可見時(shí)刷新 if (!m_windowVisible) return; // 根據(jù)網(wǎng)絡(luò)狀態(tài)動(dòng)態(tài)調(diào)整間隔 if (m_lastWifiState) { m_refreshTimer.setInterval(5000); // 連接狀態(tài)穩(wěn)定時(shí)降低頻率 } else { m_refreshTimer.setInterval(1000); // 未連接時(shí)提高檢測(cè)頻率 } // ...原有刷新邏輯... }
- 結(jié)果緩存:
struct NetworkCache { bool wifiConnected; QString wifiName; bool hotspotActive; QString hotspotName; QDateTime lastUpdated; }; NetworkCache m_cache; void NetworkTool::refreshCache() { if (m_cache.lastUpdated.secsTo(QDateTime::currentDateTime()) < 2) { return; // 2秒內(nèi)不重復(fù)更新 } // ...更新緩存... }
五、擴(kuò)展
智能網(wǎng)絡(luò)切換:
void autoSwitchNetwork() { if (!m_networkTool.isWifiConnected() && !m_networkTool.isHotspotActive()) { // WiFi斷開且熱點(diǎn)未開啟時(shí),自動(dòng)開啟熱點(diǎn) QProcess::startDetached("nmcli", { "device", "wifi", "hotspot", "ssid", "RescueHotspot", "password", "12345678" }); } }
網(wǎng)絡(luò)質(zhì)量監(jiān)測(cè):
int getWifiSignalStrength() { QString output = executeCommand("iwconfig", {"wlan0"}); QRegularExpression regex("Signal level=(-?\\d+) dBm"); QRegularExpressionMatch match = regex.match(output); if (match.hasMatch()) { return match.captured(1).toInt(); } return 0; }
歷史狀態(tài)記錄:
void logNetworkStatus() { QFile logFile("network_status.log"); if (logFile.open(QIODevice::Append)) { QString log = QString("%1|%2|%3|%4\n") .arg(QDateTime::currentDateTime().toString()) .arg(m_lastWifiState) .arg(m_lastWifiName) .arg(m_lastHotspotState); logFile.write(log.toUtf8()); } }
以上就是Linux下實(shí)時(shí)獲取WiFi與熱點(diǎn)狀態(tài)的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Linux獲取WiFi與熱點(diǎn)狀態(tài)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
初識(shí)centos7與centos6的區(qū)別整理(內(nèi)核、命令等)
這篇文章主要介紹了初識(shí)centos7與centos6的區(qū)別整理,需要的朋友可以參考下2017-08-08linux grep查找的結(jié)果中顯示匹配行的上下行內(nèi)容方式
這篇文章主要介紹了linux grep查找的結(jié)果中顯示匹配行的上下行內(nèi)容方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-112018值得選用的五個(gè)Linux服務(wù)器發(fā)行版
Linux發(fā)行版很少互相復(fù)制。從以下5個(gè)最流行的Linux服務(wù)器發(fā)行版來看,這一點(diǎn)很明顯,它們各有不同的優(yōu)缺點(diǎn),這篇文章給大家?guī)砹?018值得選用的五個(gè)Linux服務(wù)器發(fā)行版,需要的朋友參考下吧2018-01-01apache的commons-pool2原理與使用實(shí)踐記錄
Apache?Commons?Pool2是一個(gè)高效的對(duì)象池化框架,通過復(fù)用昂貴資源(如數(shù)據(jù)庫連接、線程、網(wǎng)絡(luò)連接)優(yōu)化系統(tǒng)性能,這篇文章主要介紹了apache的commons-pool2原理與使用詳解,需要的朋友可以參考下2025-05-05Linux網(wǎng)絡(luò)DNS域名如何解析服務(wù)
詳解DNS系統(tǒng)的作用、分布式數(shù)據(jù)結(jié)構(gòu)、系統(tǒng)類型、查詢類型及原理,介紹如何配置DNS正向解析,包括環(huán)境搭建、修改配置文件、啟動(dòng)服務(wù)等2024-09-09Windows 10 下安裝 Apache 2.4.41的教程
這篇文章主要介紹了Windows 10 下安裝 Apache 2.4.41的教程,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01Apache No space left on device的解決辦法
[Fri Aug 15 10:54:31 2008] [emerg] (28)No space left on device: Couldn't create accept lockdf一下發(fā)現(xiàn)不是磁盤空間的問題。Google了一下就找到了解決方案,原來是系統(tǒng)的信號(hào)量(?)不夠用了。2008-08-08