WIFI后端功能问题解决

问题一:WiFi名显示重复

在前端页面使用WiFi网络扫描功能,会产生多个重复的UUID,用NetworkManager工具并未产生类似情况

问题定位

寻找接口:networkmanage/wifiInfo

cpp 复制代码
int Handler::getWifi(const HttpContextPtr &ctx) {
    hv::Json j;
    j["msg"] = "success";
    j["code"] = HTTP_STATUS_OK;
    string info;
    int ret = selectDeployCode(dbCodeToString(SysDeployCode::WIFI_INFO), info);
    if (ret < 0) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "select error");
    }
    j["data"]["wifiInfo"] = hv::Json::parse(info);
    ret = selectDeployCode(dbCodeToString(SysDeployCode::ONLINE), info);
    if (ret < 0) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "select error");
    }
    j["data"]["online"] = stringToBool(info);
    return ctx->sendJson(j);
}

wifi功能实现代码:

cpp 复制代码
int Wifi::scanWifi() {
    string ssids;
    string scanCmd("nmcli -f SIGNAL,SSID d wifi list");
    if (!myPopen(scanCmd, ssids)) {
        hloge("failed to scan ssid");
        return -1;
    }
    stringstream ss;
    ss << ssids;
    string item;
    getline(ss, item, '\n');
    while(getline(ss, item, '\n')) {
        int pos = item.find_first_of(' ');
        if (pos == string::npos) {
            hloge("wifi scan result error");
            ssidVec.clear();
            return -1;
        }
        string signal = item.substr(0, pos);
        int sig = 0;
        if(stringIsNumber(signal))
            sig = stoi(signal);
        int sigLevel = 0;
        if (sig <= 20) {
            sigLevel = 0;
        } else if (sig > 20 && sig <= 40) {
            sigLevel = 1;
        } else if (sig > 40 && sig <= 60) {
            sigLevel = 2;
        } else if (sig > 60 && sig <= 80) {
            sigLevel = 3;
        } else {
            sigLevel = 4;
        }

        while(item[pos] == ' ') {
            pos++;
        }

        string itemTrim = hv::rtrim(item, " ");
        string ssid = itemTrim.substr(pos);
        if (ssid == "--") {
            continue;
        }
        string ssidUtf8Encoded = convert_escape_sequences(ssid);
        ssidVec.emplace_back(sigLevel, ssid);
    }
    hlogi("scan end %d", ssidVec.size());
    return 0;
}

罪魁祸首:

cpp 复制代码
        ssidVec.emplace_back(sigLevel, ssid);

也就是说:

  • ssidVecWifi 类的成员变量

  • getWifiInfo() 每调用一次

  • scanWifi() 就往 ssidVec继续 push

在整个 scanWifi() 函数里,没有任何地方清空 ssidVec

导致:

SSID 重复

UUID(如果在 WifiInfo::toJson 里生成)也会重复/变化

每次"前端扫描"数量越来越多

NM 工具不会这样(它每次 scan 都是 fresh list)

修复点:

1.在 scanWifi() 一开始清空 ssidVec

cpp 复制代码
int Wifi::scanWifi() {
    ssidVec.clear();  // ← 每次扫描必须清空历史结果

2.nmcli -f SIGNAL,SSID d wifi list修改为:

cpp 复制代码
nmcli -t -f SSID,BSSID,FREQ,SIGNAL dev wifi list

nmcli -f SIGNAL,SSID d wifi list会丢失BSSID/FREQ信息,导致没办法区分是同一个AP还是不同的AP

程序如下:

cpp 复制代码
int Wifi::scanWifi() {
    ssidVec.clear();   // ★ 必须清空

    string ssids;
    string scanCmd("nmcli -t -f SSID,BSSID,FREQ,SIGNAL dev wifi list");
    if (!myPopen(scanCmd, ssids)) {
        hloge("failed to scan ssid");
        return -1;
    }

    stringstream ss(ssids);
    string line;

    while (getline(ss, line)) {
        // 格式:SSID:BSSID:FREQ:SIGNAL
        hv::StringList fields = hv::split(line, ':');


        if (fields.size() < 4) continue;

        string ssid = fields[0];
        string bssid = fields[1];
        string freq = fields[2];
        string signalStr = fields[3];

        if (ssid.empty() || ssid == "--") continue;

        int sig = 0;
        if (stringIsNumber(signalStr)) {
            sig = stoi(signalStr);
        }

        int sigLevel = 0;
        if (sig <= 20) sigLevel = 0;
        else if (sig <= 40) sigLevel = 1;
        else if (sig <= 60) sigLevel = 2;
        else if (sig <= 80) sigLevel = 3;
        else sigLevel = 4;

        // ★ 关键:不要把 BSSID / FREQ 拼进 name
        ssidVec.emplace_back(sigLevel, ssid /* 这里只放纯 SSID */);
    }

    hlogi("scan end %d", ssidVec.size());
    return 0;
}

但是出来的名字还是有重复的,定位问题是因为:

只做了【解析】和【清空历史】,没有做【按 SSID 聚合】

  • nmcli 扫描结果本身就会返回:

    • 同一个 SSID

    • 多个 BSSID

    • 多个频段(2.4G / 5G / 5G-2)

  • 你现在是 "一条扫描结果 → 一条 WifiInfo"

所以看到的:

cpp 复制代码
COMNOVA_Staff
COMNOVA_Staff
COMNOVA_Staff

解决问题

按SSID聚合后代码:

cs 复制代码
int Wifi::scanWifi() {
    ssidVec.clear();

    string ssids;
    string scanCmd("nmcli -t -f SSID,BSSID,FREQ,SIGNAL dev wifi list");
    if (!myPopen(scanCmd, ssids)) {
        hloge("failed to scan ssid");
        return -1;
    }

    // 用 map 按 SSID 聚合,只保留信号最强的
    unordered_map<string, int> bestSignal;   // SSID -> sigLevel

    stringstream ss(ssids);
    string line;

    while (getline(ss, line)) {
        hv::StringList fields = hv::split(line, ':');
        if (fields.size() < 4) continue;

        string ssid = fields[0];
        string signalStr = fields[3];

        if (ssid.empty() || ssid == "--") continue;

        int sig = 0;
        if (stringIsNumber(signalStr)) {
            sig = stoi(signalStr);
        }

        int sigLevel = 0;
        if (sig <= 20) sigLevel = 0;
        else if (sig <= 40) sigLevel = 1;
        else if (sig <= 60) sigLevel = 2;
        else if (sig <= 80) sigLevel = 3;
        else sigLevel = 4;

        auto it = bestSignal.find(ssid);
        if (it == bestSignal.end() || sigLevel > it->second) {
            bestSignal[ssid] = sigLevel;
        }
    }

    // 转成 ssidVec
    for (const auto& kv : bestSignal) {
        ssidVec.emplace_back(kv.second, kv.first);
    }

    hlogi("scan end %d (after dedup)", ssidVec.size());
    return 0;
}

问题二:信号不显示

发的json里并不包含信号强度:

cs 复制代码
        "auto_connect": true,
        "name": "DIRECT-7t-客厅电视",
        "password": "",
        "setting": false,
        "signal": 0,
        "status": false
      },
      {
        "auto_connect": true,
        "name": "DIRECT-xx-客厅电视",
        "password": "",
        "setting": false,
        "signal": 0,
        "status": false
      },
      {
        "auto_connect": true,
        "name": "W51-Ch62-5G",
        "password": "",
        "setting": false,
        "signal": 0,
        "status": false
      },

问题定位

先看一下wifi.h

cs 复制代码
WifiInfo(int signal, const std::string &name)
    : name(name), signal(signal) {
    status = false;
    setting = false;
    autoConnect = true;
}

一开始以为是initWifiInfoCompareList覆盖掉了:

cpp 复制代码
hv::Json Wifi::initWifiInfoCompareList(const string &wifiList, bool shouldConnect) {
    string ssidOnline;
    if (!shouldConnect) {
        // 刷新时找到在线的wifi mac
        ssidOnline = getOnlineWifiName();
    }

    bool isConnected = false;
    // 根据wifi_list中的信息,更新搜索到的WifiInfo
    hv::Json wifiListJson = hv::Json::parse(wifiList);
    for (auto &listItem : wifiListJson) {
        WifiList wList = WifiList::fromJson(listItem);
        string ssidName = wList.getSsid();
        for (auto &info : ssidVec) {
            if (ssidName != info.getName()) {
                continue;
            }

            string pwd = wList.getPassword();
            bool autoConnect = wList.getAutoConnect();
            info.setPassword(pwd);
            info.setAutoConnect(autoConnect);
            info.setSetting(true);
            if (!shouldConnect) {
                if (ssidOnline == info.getName()) {
                    info.setStatus(true);
                }
            } else {
                if (!isConnected && autoConnect) {
                    setSsid(ssidName);
                    setPassword(pwd);
                    setAutoConnect(autoConnect);
		    hlogi("will auto connect %s", ssidName.c_str());
                    if (connect()) {
                        isConnected = true;
                        info.setStatus(true);
                    }
                }
            }
	    break;
        }
    }

    hv::Json jsn = hv::Json::array();
    for (const auto &item: ssidVec) {
        jsn.emplace_back(item.toJson());
    }
    return jsn;
}

只更新了 :password,autoConnect,setting,status,没有任何地方再更新 signal

所以添加代码验证:

cpp 复制代码
int Wifi::getWifiInfo(hv::Json &resp, bool tryConnect) {
    if (scanWifi() < 0) {
        hloge("failed to scan wifi");
        return -1;
    }
    for (auto &w : ssidVec) {
        hlogi("[SCAN] %s signal=%d", w.getName().c_str(), w.getSignal());
    }
    // compare wifi list, connect if auto connect is true
    string wifiList;
    selectDeployCode(dbCodeToString(SysDeployCode::WIFI_LIST), wifiList);
    hv::Json &&wifiJson = initWifiInfoCompareList(wifiList, tryConnect);
    int ret = updateDatabaseWithEscape(dbCodeToString(SysDeployCode::WIFI_INFO), wifiJson);
    if (ret < 0) {
        hloge("failed to update wifi info");
        return ret;
    }

    if (!tryConnect) {
        resp["data"]["wifiInfo"] = wifiJson;
    } else if (isConnected()) {
        ret = updateDeployCode(dbCodeToString(SysDeployCode::ONLINE), "true");
        if (ret < 0) {
            hloge("failed to update wifi switch");
            return ret;
        }
    }
    for (auto &w : ssidVec) {
        hlogi("[FINAL] %s signal=%d", w.getName().c_str(), w.getSignal());
    }
    return 0;
}

打印出来为:

cpp 复制代码
1970-01-05 06:53:41.586 INFO  [SCAN] DIRECT-7t-客厅电视 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] DIRECT-xx-客厅电视 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] W51-Ch62-5G signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] 萱萱超棒 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] CCORE_WIRELESS_2.4G signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] OrayBox-2.4G-4D28 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] COMNOVA_Staff signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] QL480_98797 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.586 INFO  [SCAN] CCORE_WIRELESS signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.587 INFO  [SCAN] HONOR 90 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.587 INFO  [SCAN] test1 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.587 INFO  [SCAN] Hyper_Link signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.587 INFO  [SCAN] 3F-R11-Display signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.587 INFO  [SCAN] W51-Ch62 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.587 INFO  [SCAN] DIRECT-SU-tcl_mt5879_cn signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 06:53:41.637 INFO  [FINAL] DIRECT-7t-客厅电视 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.637 INFO  [FINAL] DIRECT-xx-客厅电视 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] W51-Ch62-5G signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] 萱萱超棒 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] CCORE_WIRELESS_2.4G signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] OrayBox-2.4G-4D28 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] COMNOVA_Staff signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] QL480_98797 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] CCORE_WIRELESS signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] HONOR 90 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] test1 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] Hyper_Link signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] 3F-R11-Display signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] W51-Ch62 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 06:53:41.638 INFO  [FINAL] DIRECT-SU-tcl_mt5879_cn signal=0 [wifi.cpp:72:getWifiInfo]

说明不是initWifiInfoCompareList的问题,是Scanwifi的问题,排查发现fields[3] 根本不是 SIGNAL。

现在用的是:

nmcli -t -f SSID,BSSID,FREQ,SIGNAL dev wifi list

-t模式的输出规则是

SSID:BSSID:FREQ:SIGNAL

nmcli 会对 SSID 里的冒号 : 做转义,显示为 \:

SIGNAL 根本不是 fields[3],而是 fields.back()

解决问题

修正scanwifi:

cpp 复制代码
string ssid = fields[0];
string signalStr = fields[3];

cpp 复制代码
string ssid = fields[0];
string signalStr = fields.back();

完整代码:

cpp 复制代码
int Wifi::scanWifi() {
    ssidVec.clear();

    string ssids;
    string scanCmd("nmcli -t -f SSID,BSSID,FREQ,SIGNAL dev wifi list");
    if (!myPopen(scanCmd, ssids)) {
        hloge("failed to scan ssid");
        return -1;
    }

    // 用 map 按 SSID 聚合,只保留信号最强的
    unordered_map<string, int> bestSignal;   // SSID -> sigLevel

    stringstream ss(ssids);
    string line;

    while (getline(ss, line)) {
        hv::StringList fields = hv::split(line, ':');
        if (fields.size() < 4) continue;

        string ssid = fields[0];
        string signalStr = fields.back();

        if (ssid.empty() || ssid == "--") continue;

        int sig = 0;
        if (stringIsNumber(signalStr)) {
            sig = stoi(signalStr);
        }

        int sigLevel = 0;
        if (sig <= 20) sigLevel = 0;
        else if (sig <= 40) sigLevel = 1;
        else if (sig <= 60) sigLevel = 2;
        else if (sig <= 80) sigLevel = 3;
        else sigLevel = 4;

        auto it = bestSignal.find(ssid);
        if (it == bestSignal.end() || sigLevel > it->second) {
            bestSignal[ssid] = sigLevel;
        }
    }

    // 转成 ssidVec
    for (const auto& kv : bestSignal) {
        ssidVec.emplace_back(kv.second, kv.first);
    }

    hlogi("scan end %d (after dedup)", ssidVec.size());
    return 0;
}

检查:

cpp 复制代码
1970-01-05 07:01:48.869 INFO  [SCAN] DIRECT-7t-客厅电视 signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] DIRECT-SU-tcl_mt5879_cn signal=0 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] W51-Ch62 signal=4 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] CCORE_WIRELESS_2.4G signal=3 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] OrayBox-2.4G-4D28 signal=2 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] COMNOVA_Staff signal=3 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] QL480_98797 signal=2 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] CCORE_WIRELESS signal=2 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] HONOR 90 signal=3 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] test1 signal=2 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] 3F-R11-Display signal=1 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] W51-Ch62-5G signal=4 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] 萱萱超棒 signal=4 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.869 INFO  [SCAN] DIRECT-xx-客厅电视 signal=1 [wifi.cpp:50:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] DIRECT-7t-客厅电视 signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] DIRECT-SU-tcl_mt5879_cn signal=0 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] W51-Ch62 signal=4 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] CCORE_WIRELESS_2.4G signal=3 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] OrayBox-2.4G-4D28 signal=2 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] COMNOVA_Staff signal=3 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] QL480_98797 signal=2 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] CCORE_WIRELESS signal=2 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] HONOR 90 signal=3 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] test1 signal=2 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] 3F-R11-Display signal=1 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] W51-Ch62-5G signal=4 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] 萱萱超棒 signal=4 [wifi.cpp:72:getWifiInfo]
1970-01-05 07:01:48.923 INFO  [FINAL] DIRECT-xx-客厅电视 signal=1 [wifi.cpp:72:getWifiInfo]

现在就有wifi信号啦!

问题三:接口连上了WiFi但是报错500

输入密码,点确定,后台检查:

cpp 复制代码
nmcli -t -f active,ssid dev wifi
no:W51-Ch62-5G
no:W51-Ch62
yes:萱萱超棒

但是前端显示500,连接失败。

问题定位

定位到前端调用的对应功能函数:

cpp 复制代码
int Handler::wifiPwdSet(const HttpContextPtr& ctx) {
    hv::Json req = ctx->json();
    Wifi wifi(req["password"], req["auto_connect"], req["name"]);
    int ret = wifi.requestConnect();
    if (ret == -1 || !wifi.isConnected()) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "wifi连接失败");
    }
    if (ret == -2) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "failed to update database");
    }
    return response_status(ctx, HTTP_STATUS_OK, "success");
}
cpp 复制代码
/**
 * only invoked when wifi item with ssid/password is clicked in front UI
 * @return 0 on success, while -1 on failure
*/
int Wifi::updateDatabaseOnConnect() {
    if (updateWifiInfoOnConnect() < 0) {
        hloge("failed to update wifi info");
        return -1;
    }
    if (updateWifiListOnConnect() < 0) {
        hloge("failed to update wifi list");
        return -1;
    }
    return updateDeployCode(dbCodeToString(SysDeployCode::ONLINE), "true");
}

/**
 * set wifi info status to be false when wifi disconnected
 * @return 0 on success, while -1 on failure
*/
int Wifi::updateWifiInfoOnDisconnect() {
    string info;
    string dbInfoStr = dbCodeToString(SysDeployCode::WIFI_INFO);
    selectDeployCode(dbInfoStr, info);
    hv::Json wifiJson = hv::Json::parse(info);
    for (auto &item: wifiJson) {
        item["status"] = false;
    }
    return updateDatabaseWithEscape(dbInfoStr, wifiJson);
}

/**
 *  set auto_connect/password/setting/status when wifi connected
 * @return 0 on success, while -1 on failure
*/
int Wifi::updateWifiInfoOnConnect() {
    string wifiInfo;
    string dbInfoStr = dbCodeToString(SysDeployCode::WIFI_INFO);
    selectDeployCode(dbInfoStr, wifiInfo);
    hv::Json wifiJson = hv::Json::parse(wifiInfo);
    bool found = false;
    for (auto &item: wifiJson) {
        if (found) {
            item["status"] = false;
            continue;
        }
        string ssid = item["name"];
        if (ssid == getSsid()) {
            item["auto_connect"] = isAutoConnect();
            item["password"] = getPassword();
            item["setting"] = true;
            item["status"] = true;
            found = true;
        }
    }
    return updateDatabaseWithEscape(dbInfoStr, wifiJson);
}

/**
 * update wifiList when wifi connected
 * @return 0 on success, while -1 on failure
*/
int Wifi::updateWifiListOnConnect() {
    string wifiList;
    string dbInfoStr = dbCodeToString(SysDeployCode::WIFI_LIST);
    selectDeployCode(dbInfoStr, wifiList);
    hv::Json listJson = hv::Json::parse(wifiList);
    list<WifiList> objWifiList;
    for (auto &item: listJson) {
        WifiList obj = WifiList::fromJson(item);
        objWifiList.push_back(obj);
    }
    string pwd = getPassword();
    string onlineSsid = getSsid();
    bool isAutoConn = isAutoConnect();
    int i = 0;
    for (auto it = objWifiList.begin(); it != objWifiList.end(); ++it) {
        if (it->getSsid() == onlineSsid) {
            it->setPassword(pwd);
            it->setAutoConnect(isAutoConn);
            objWifiList.splice(objWifiList.cbegin(), objWifiList, it);
            break;
        }
        i++;
    }

    if (i == objWifiList.size()) {
        objWifiList.emplace_front(pwd, ssid, isAutoConn);
    }
    listJson.clear();
    for (auto &item: objWifiList) {
        listJson.push_back(item.toJson());
    }
    return updateDatabaseWithEscape(dbInfoStr, listJson);
}

/**
 * disconnect wifi
 * @return true on success, while false on failure
*/
bool Wifi::disconnect() {
    if (!isConnected()) {
        return true;
    }
    string interface("wlan0");
    string cmd = "nmcli d disconnect " + interface;
    if (!mySystem(cmd)) {
        hloge("failed to disconnect wifi");
        return false;
    }

    return updateDeployCode(dbCodeToString(SysDeployCode::ONLINE), boolToString(false));
}

/**
 * check wifi connected or not by /sys/class/net/mlan0/operstate
 * @return true if content is up in the file, while false if down
*/
bool Wifi::isConnected() {
    const std::string interfaceName = "wlan0";
    std::ifstream file("/sys/class/net/" + interfaceName + "/operstate");
    bool res = false;
    if (file.is_open()) {
        std::string state;
        file >> state;
        file.close();

        if (state == "up") {
            res = true;
        }
    }

    hlogi("check wifi connected: %d", res);
    return res;
}

解决问题

看代码立刻发现问题:网卡名没改过来

cpp 复制代码
bool Wifi::isConnected() {
    const std::string interfaceName = "wlP1p1s0"; //❗
    std::ifstream file("/sys/class/net/" + interfaceName + "/operstate");
    bool res = false;
    if (file.is_open()) {
        std::string state;
        file >> state;
        file.close();

        if (state == "up") {
            res = true;
        }
    }

    hlogi("check wifi connected: %d", res);
    return res;
}

但是改了还是有可能报500

猜想问题点:

1.在程序里,连接成功和后续状态/数据库更新失败被当成了一回事。

可能connet成功了,但updateDatabaseOnConnect某一步失败了。

如果是这样,要不解决updateDatabaseOnConnect的bug,要不连接成功了就返回0,数据库更新失败不返回。

2.可能wifi连接有延迟,HTTP 请求已经结束(500),NetworkManager 还在后台继续,几秒后 Wi-Fi 真正连上

看日志:

cpp 复制代码
1970-01-05 07:11:33.650 INFO  [106289-106289][127.0.0.1:58332][GET /networkmanage/wifiScan]=>[200 OK] [HttpServer.cpp:175:on_recv]
1970-01-05 07:11:38.222 INFO  check wifi connected: 0 [wifi.cpp:395:isConnected]
1970-01-05 07:11:38.222 INFO  [106289-106289][127.0.0.1:58352][POST /networkmanage/wifiPassSet]=>[500 Internal Server Error] [HttpServer.cpp:175:on_recv]
1970-01-05 07:11:46.973 INFO  check wifi connected: 0 [wifi.cpp:395:isConnected]
1970-01-05 07:11:46.973 INFO  [106289-106289][127.0.0.1:58336][POST /networkmanage/wifiPassSet]=>[500 Internal Server Error] [HttpServer.cpp:175:on_recv]
1970-01-05 07:11:53.834 INFO  check wifi connected: 0 [wifi.cpp:395:isConnected]
1970-01-05 07:11:53.834 INFO  [106289-106289][127.0.0.1:58334][POST /networkmanage/wifiPassSet]=>[500 Internal Server Error] [HttpServer.cpp:175:on_recv]
1970-01-05 07:12:07.128 INFO  check wifi connected: 0 [wifi.cpp:395:isConnected]
1970-01-05 07:12:07.129 INFO  [106289-106289][127.0.0.1:58366][POST /networkmanage/wifiPassSet]=>[500 Internal Server Error] [HttpServer.cpp:175:on_recv]
1970-01-05 07:12:13.744 INFO  check wifi connected: 0 [wifi.cpp:395:isConnected]
1970-01-05 07:12:13.745 INFO  [106289-106289][127.0.0.1:58356][POST /networkmanage/wifiPassSet]=>[500 Internal Server Error] [HttpServer.cpp:175:on_recv]

"check wifi connected: %d"来自:

cpp 复制代码
bool Wifi::isConnected() {
    const std::string interfaceName = "wlan0";
    std::ifstream file("/sys/class/net/" + interfaceName + "/operstate");
    bool res = false;
    if (file.is_open()) {
        std::string state;
        file >> state;
        file.close();

        if (state == "up") {
            res = true;
        }
    }

    hlogi("check wifi connected: %d", res);
    return res;
}

没有出现
failed to update wifi info
failed to update wifi list

所以500 不是 updateDatabaseOnConnect() 里抛出来的。

视角 含义
nmcli d wifi connect 命令已下发,NetworkManager 接受
/sys/class/net/wlan0/operstate 内核网卡是否 UP
IP 是否拿到 DHCP 是否完成
UI 感觉 Wi-Fi 图标亮了

这些都不是同步发生,但是我检查用的指令nmcli d wifi connect 是异步的。

NetworkManager:

  • 先关联 AP

  • 再 WPA 握手

  • 再 DHCP

  • 最后内核接口才 UP

好的,知道原因了,是我查太早了。

现在有两个修改方案:

方案1.wifiPwdSet只判断是否下发成功

方案2.等待连接

最佳方案为方案1,为了尽量不影响逻辑,先尝试用方案2。

更新代码:

cpp 复制代码
int Handler::wifiPwdSet(const HttpContextPtr& ctx) {
    hv::Json req = ctx->json();
    Wifi wifi(req["password"], req["auto_connect"], req["name"]);

    int ret = wifi.requestConnect();
    if (ret == -1) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "wifi连接命令失败");
    }
    if (ret == -2) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "failed to update database");
    }

    // 等待最多 2 次,每次 1 秒
    const int retry = 2;
    for (int i = 0; i < retry; ++i) {
        if (Wifi::isConnected()) {
            return response_status(ctx, HTTP_STATUS_OK, "success");
        }
        sleep(3);
    }

    // 两次都没连上,判定失败
    return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "wifi连接失败");
}

完美解决!

问题四:硬编码问题

发现此问题的来源:

点击忘记网络,wifi依然连接,但界面上显示无wifi连接

问题定位

handler.cpp:

cpp 复制代码
/**
 * ignore wifi: disconnect it and delete it in the wifi_list
 */
int Handler::wifiIgnore(const HttpContextPtr &ctx) {
    hv::Json req = ctx->json();
    string ssidName = req["name"];
    Wifi wifi;
    if (wifi.getWifiInfoFromDb() < 0) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "操作失败");
    }
    hv::Json resp;
    if (wifi.ignore(ssidName, resp["data"]["wifiInfo"]) < 0) {
        return response_status(ctx, HTTP_STATUS_INTERNAL_SERVER_ERROR, "操作失败");
    }

    resp["msg"] = "success";
    resp["code"] = HTTP_STATUS_OK;
    return ctx->sendJson(resp);
}

wifi.cpp:

cpp 复制代码
int Wifi::ignore(const std::string &name, hv::Json &wifiJson) {
    wifiJson = hv::Json::array();
    for(auto &it : ssidVec) {
        if (name == it.getName()) {
            if (it.getStatus()) {
                disconnect();
            }
            it.setAutoConnect(true);
            it.setPassword("");
            it.setSetting(false);
            it.setStatus(false);
        }
        wifiJson.emplace_back(it.toJson());
    }
    updateDatabaseWithEscape(dbCodeToString(SysDeployCode::WIFI_INFO), wifiJson);

    // 从wifi_list中删除此wifi
    string wifiList;
    selectDeployCode(dbCodeToString(SysDeployCode::WIFI_LIST), wifiList);
    hv::Json listJson = hv::Json::parse(wifiList);
    for (auto it = listJson.begin(); it != listJson.end(); ++it) {
        WifiList item = WifiList::fromJson(*it);
        if (item.getSsid() == name) {
            listJson.erase(it);
            break;
        }
    }
    return updateDatabaseWithEscape(dbCodeToString(SysDeployCode::WIFI_LIST), listJson);
}
bool Wifi::disconnect() {
    if (!isConnected()) {
        return true;
    }
    string interface("wlan0");  // ❗
    string cmd = "nmcli d disconnect " + interface;
    if (!mySystem(cmd)) {
        hloge("failed to disconnect wifi");
        return false;
    }

    return updateDeployCode(dbCodeToString(SysDeployCode::ONLINE), boolToString(false));
}

????这里怎么又有一个网卡名要更改?

一搜有一堆硬编码:

解决方案:

可不可以程序初始化的时候获取wifi接口,后面把网卡名存在一个变量里,要用的地方就调用这个变量,外部不允许可以修改这个变量,当然肯定不用全局string

解决问题

直接贴代码:

wifi.h:

cpp 复制代码
/**
 * Wi-Fi runtime environment
 * Initialized once at program startup
 */
class WifiEnv {
public:
    // 程序启动时调用一次
    static bool init();

    // 只读获取 Wi-Fi 接口名
    static const std::string& iface();

private:
    WifiEnv() = delete;
    static std::string wifi_iface_;
    static constexpr const char* DEFAULT_WIFI_IFACE = "wlP1p1s0";
};

wifi.cpp:

cpp 复制代码
std::string WifiEnv::wifi_iface_;

bool WifiEnv::init() {
    std::string out;
    std::string cmd =
        "nmcli -t -f DEVICE,TYPE,STATE d | "
        "awk -F: '$2==\"wifi\" && $3!=\"unavailable\" {print $1; exit}'";

    if (myPopen(cmd, out)) {
        out.erase(std::remove(out.begin(), out.end(), '\n'), out.end());
    }

    if (!out.empty()) {
        wifi_iface_ = out;
        hlogi("wifi interface detected: %s", wifi_iface_.c_str());
        return true;
    }

    // fallback:使用默认接口名
    wifi_iface_ = DEFAULT_WIFI_IFACE;
    hloge("wifi interface detect failed, fallback to default: %s",
          wifi_iface_.c_str());

    return true;
}


const std::string& WifiEnv::iface() {
    return wifi_iface_;
}

main.cpp:

cpp 复制代码
#include "base/wifi.h"
.............
WifiEnv::init();

调用:

cpp 复制代码
    const std::string& interface = WifiEnv::iface();
    if (interface.empty()) return false;
相关推荐
yimengsama2 小时前
VMWare虚拟机如何连接U盘
linux·运维·服务器·网络·windows·经验分享·远程工作
Xの哲學2 小时前
Linux NAT 深度剖析: 从设计哲学到实现细节
linux·服务器·网络·架构·边缘计算
鲨莎分不晴3 小时前
强化学习第七课 —— 策略网络设计指南:赋予 Agent“大脑”的艺术
网络·人工智能·机器学习
胡闹543 小时前
海康和大华厂商的RTSP取流地址格式进行拉流直播
java·网络
2501_938810113 小时前
动态IP的使用方法
网络·网络协议·tcp/ip
Neolnfra4 小时前
陇剑杯2021-wifi题目解析
网络·安全·web安全·网络安全·系统安全·密码学·csrf
一周困⁸天.4 小时前
K8S-CoreDNS组件
网络·kubernetes
无限大.4 小时前
为什么网站需要“域名“?——从 IP 地址到网址的演进
网络·网络协议·tcp/ip
sdszoe49224 小时前
思科DHCP服务1
网络·思科网络·dhcp服务基础