问题一:WiFi名显示重复
在前端页面使用WiFi网络扫描功能,会产生多个重复的UUID,用NetworkManager工具并未产生类似情况
问题定位
寻找接口:networkmanage/wifiInfo
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功能实现代码:
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; }罪魁祸首:
ssidVec.emplace_back(sigLevel, ssid);也就是说:
ssidVec是Wifi类的成员变量getWifiInfo()每调用一次scanWifi()就往ssidVec里继续 push
在整个scanWifi()函数里,没有任何地方清空ssidVec。
导致:
SSID 重复
UUID(如果在 WifiInfo::toJson 里生成)也会重复/变化
每次“前端扫描”数量越来越多
NM 工具不会这样(它每次 scan 都是 fresh list)
修复点:
1.在scanWifi()一开始清空ssidVec
int Wifi::scanWifi() { ssidVec.clear(); // ← 每次扫描必须清空历史结果2.nmcli -f SIGNAL,SSID d wifi list修改为:
nmcli -t -f SSID,BSSID,FREQ,SIGNAL dev wifi listnmcli -f SIGNAL,SSID d wifi list会丢失BSSID/FREQ信息,导致没办法区分是同一个AP还是不同的AP
程序如下:
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”
所以看到的:
COMNOVA_Staff COMNOVA_Staff COMNOVA_Staff解决问题
按SSID聚合后代码:
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里并不包含信号强度:
"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
WifiInfo(int signal, const std::string &name) : name(name), signal(signal) { status = false; setting = false; autoConnect = true; }一开始以为是initWifiInfoCompareList覆盖掉了:
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。
所以添加代码验证:
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; }打印出来为:
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:
string ssid = fields[0]; string signalStr = fields[3];为
string ssid = fields[0]; string signalStr = fields.back();完整代码:
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; }检查:
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
输入密码,点确定,后台检查:
nmcli -t -f active,ssid dev wifi no:W51-Ch62-5G no:W51-Ch62 yes:萱萱超棒但是前端显示500,连接失败。
问题定位
定位到前端调用的对应功能函数:
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"); }/** * 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; }解决问题
看代码立刻发现问题:网卡名没改过来
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 真正连上
看日志:
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"来自:
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 infofailed 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。
更新代码:
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:
/** * 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:
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:
/** * 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:
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:
#include "base/wifi.h" ............. WifiEnv::init();调用:
const std::string& interface = WifiEnv::iface(); if (interface.empty()) return false;