From 1d720b10bc9897fe21194b38724a02e8141b7362 Mon Sep 17 00:00:00 2001 From: donghualin Date: Tue, 23 Jun 2026 15:51:25 +0800 Subject: [PATCH] fix: resolve network detection failure and ping-pong switching in multi-NIC scenario Two issues are fixed: 1. UB in HttpManager::get(url, timeoutSec): pass std::string to variadic function curl_easy_setopt instead of const char*, causing all HTTP requests to fail with "URL using bad/illegal format or missing URL". This made StatusChecker always report Limited and triggered the ping-pong switching loop. 2. Race condition in StatusChecker::realStartCheck(): when the check blocks for DNS timeout (~5s),InternetChecker may switch the primary connection in another thread. The stale result (based on old route) would incorrectly trigger network switching, creating a ping-pong loop between NICs. Fix: (1) use urlStd.c_str() in curl_easy_setopt; (2) snapshot the primary connection UUID before check and discard result if it changed. PMS:BUG-351163 --- config/org.deepin.dde.network.json | 8 ++-- .../src/system/connectivitychecker.cpp | 47 ++++++++++++++----- .../src/system/connectivitychecker.h | 1 + .../src/system/internetchecker.cpp | 3 +- .../src/utils/httpmanager.cpp | 2 +- 5 files changed, 43 insertions(+), 18 deletions(-) diff --git a/config/org.deepin.dde.network.json b/config/org.deepin.dde.network.json index ab6ee6cd..4139cf4c 100644 --- a/config/org.deepin.dde.network.json +++ b/config/org.deepin.dde.network.json @@ -307,23 +307,23 @@ "visibility": "private" }, "httpRequestTimeout":{ - "value": 15, + "value": 10, "serial": 0, "flags":["global"], "name":"httpRequestTimeout", "name[zh_CN]":"HTTP请求超时时间", - "description[zh_CN]":"HTTP请求超时时间(单位:秒),默认15秒", + "description[zh_CN]":"HTTP请求超时时间(单位:秒),默认10秒", "description":"HTTP request timeout in seconds, default 15 seconds", "permissions":"readwrite", "visibility":"private" }, "httpConnectTimeout":{ - "value": 10, + "value": 8, "serial": 0, "flags":["global"], "name":"httpConnectTimeout", "name[zh_CN]":"HTTP连接超时时间", - "description[zh_CN]":"HTTP连接超时时间(单位:秒),默认10秒", + "description[zh_CN]":"HTTP连接超时时间(单位:秒),默认8秒", "description":"HTTP connection timeout in seconds, default 10 seconds", "permissions":"readwrite", "visibility":"private" diff --git a/network-service-plugin/src/system/connectivitychecker.cpp b/network-service-plugin/src/system/connectivitychecker.cpp index 2ae831ed..f22a7937 100755 --- a/network-service-plugin/src/system/connectivitychecker.cpp +++ b/network-service-plugin/src/system/connectivitychecker.cpp @@ -264,34 +264,57 @@ void StatusChecker::realStartCheck() NetworkManager::ActiveConnection::Ptr pConnection = NetworkManager::primaryConnection(); if (!pConnection.isNull()) { m_primaryId = pConnection->connection()->uuid(); + m_checkStartPrimaryId = m_primaryId; + } else { + m_checkStartPrimaryId.clear(); } + int httpTimeout = SettingConfig::instance()->httpRequestTimeout(); bool networkIsOk = false; + network::service::Connectivity detectedConnectivity = m_connectivity; + QString detectedPortalUrl; for (const QString &url : m_checkUrls) { network::service::HttpManager http; - network::service::HttpReply *httpReply = http.get(url); + network::service::HttpReply *httpReply = http.get(url, httpTimeout); if (m_isStop) { qCDebug(DSM) << "Stop check connectivity"; break; } + if (httpReply->isTimeout()) { + qCWarning(DSM) << "check network time out, network is unavaibled "; + detectedConnectivity = network::service::Connectivity::Limited; + break; + } int httpCode = httpReply->httpCode(); + QString portalUrl = httpReply->portal(); if (httpCode == 0) { qCWarning(DSM) << "Nework is unreachabel:" << url << httpReply->errorMessage(); + detectedConnectivity = network::service::Connectivity::Limited; continue; } - - QString portalUrl = httpReply->portal(); networkIsOk = true; qCDebug(DSM) << "Http reply code:" << httpCode << ", portal url:" << portalUrl; - if (portalUrl.isEmpty()) { - // if the portal is empty, I think it ok - setConnectivity(network::service::Connectivity::Full); - } else { - setConnectivity(network::service::Connectivity::Portal); - } - setPortalUrl(portalUrl); + detectedPortalUrl = portalUrl; + detectedConnectivity = portalUrl.isEmpty() ? network::service::Connectivity::Full : network::service::Connectivity::Portal; break; } - if (!m_isStop && !networkIsOk) { + if (m_isStop) + return; + + // 主连接在检查期间可能已被 InternetChecker 切换,此时检测结果基于旧路由,必须丢弃 + NetworkManager::ActiveConnection::Ptr currentPrimary = NetworkManager::primaryConnection(); + QString currentPrimaryId; + if (!currentPrimary.isNull()) + currentPrimaryId = currentPrimary->connection()->uuid(); + if (m_checkStartPrimaryId != currentPrimaryId) { + qCInfo(DSM) << "Primary connection changed during check, discarding stale result" + << "check:" << m_checkStartPrimaryId << "current:" << currentPrimaryId; + return; + } + + if (networkIsOk) { + setConnectivity(detectedConnectivity); + setPortalUrl(detectedPortalUrl); + } else { NetworkManager::Device::List devices = NetworkManager::networkInterfaces(); int disconnectCount = 0; for (NetworkManager::Device::Ptr device : devices) { @@ -301,13 +324,13 @@ void StatusChecker::realStartCheck() } } qCDebug(DSM) << "Network is unreachabel, disconnect count:" << disconnectCount; - setPortalUrl(QString()); if (disconnectCount == devices.size()) { // 如果所有的网络设备都断开了,就默认让其变为断开的状态 setConnectivity(network::service::Connectivity::Noconnectivity); } else { setConnectivity(network::service::Connectivity::Limited); } + setPortalUrl(QString()); } } diff --git a/network-service-plugin/src/system/connectivitychecker.h b/network-service-plugin/src/system/connectivitychecker.h index bdb45828..0e5b73e4 100755 --- a/network-service-plugin/src/system/connectivitychecker.h +++ b/network-service-plugin/src/system/connectivitychecker.h @@ -113,6 +113,7 @@ private slots: QStringList m_checkUrls; bool m_isStop; QString m_primaryId; + QString m_checkStartPrimaryId; }; class NMConnectionvityChecker : public ConnectivityChecker diff --git a/network-service-plugin/src/system/internetchecker.cpp b/network-service-plugin/src/system/internetchecker.cpp index 67a2f015..ed3fb4ec 100644 --- a/network-service-plugin/src/system/internetchecker.cpp +++ b/network-service-plugin/src/system/internetchecker.cpp @@ -129,6 +129,7 @@ bool InternetChecker::checkInternetAccessible(int timeoutSec, bool &timedOut) co bool InternetChecker::checkInternetAccessibleWithRetry(int maxRetry) const { + int httpTimeout = SettingConfig::instance()->httpRequestTimeout(); for (int i = 0; i < maxRetry; ++i) { // 每次检测前等待1秒,让NM路由表和DHCP先稳定 QThread::sleep(1); @@ -136,7 +137,7 @@ bool InternetChecker::checkInternetAccessibleWithRetry(int maxRetry) const // 首次使用20秒超时,避免在无网线路由器的环境中长时间阻塞 QElapsedTimer timer; timer.start(); - if (checkInternetAccessible((i == 0) ? 20 : 0, timedOut)) { + if (checkInternetAccessible((i == 0) ? httpTimeout : 0, timedOut)) { return true; } if (timedOut) { diff --git a/network-service-plugin/src/utils/httpmanager.cpp b/network-service-plugin/src/utils/httpmanager.cpp index 4ea11f79..bd4d9d8a 100755 --- a/network-service-plugin/src/utils/httpmanager.cpp +++ b/network-service-plugin/src/utils/httpmanager.cpp @@ -236,7 +236,7 @@ HttpReply *HttpManager::get(const QString &url, int timeoutSec) } std::string urlStd = url.toStdString(); - curl_easy_setopt(curl, CURLOPT_URL, urlStd); + curl_easy_setopt(curl, CURLOPT_URL, urlStd.c_str()); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);