diff --git a/.gitignore b/.gitignore index 0845592f..11af80d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ # Some files -compile_commands.json .DS_Store *.pro.user *.user diff --git a/.gitmodules b/.gitmodules index 62a6f383..52fdd669 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,13 +19,3 @@ [submodule "3rdparty/uistyles"] path = 3rdparty/uistyles url = https://github.com/Qv2ray/QvUIStyles -[submodule "3rdparty/backward-cpp"] - path = 3rdparty/backward-cpp - url = https://github.com/bombela/backward-cpp -[submodule "libuv"] - path = 3rdparty/libuv - url = https://github.com/libuv/libuv.git -[submodule "3rdparty/uvw"] - path = 3rdparty/uvw - url = https://github.com/skypjack/uvw.git - diff --git a/3rdparty/libuv b/3rdparty/libuv deleted file mode 160000 index 1ab9ea37..00000000 --- a/3rdparty/libuv +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1ab9ea3790378f9f25c4e78e9e2b511c75f9c9ed diff --git a/3rdparty/uvw b/3rdparty/uvw deleted file mode 160000 index 52785475..00000000 --- a/3rdparty/uvw +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 52785475b9cb727f6912f8cc00ba65ff3bd44e67 diff --git a/CMakeLists.txt b/CMakeLists.txt index 74602be1..bc0a2bac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,7 @@ message(" ") if(WIN32) add_compile_options("/std:c++17") - add_definitions(-DUNICODE -D_UNICODE -DNOMINMAX) + add_definitions(-DUNICODE -D_UNICODE) add_definitions(-D_WIN32_WINNT=0x600 -D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS) set(GUI_TYPE WIN32) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) @@ -139,7 +139,6 @@ endif() # ================================================================================== # 3rdparty Sources # ================================================================================== -include(cmake/libuv.cmake) include(cmake/translations.cmake) include(cmake/qnodeeditor.cmake) if (ANDROID) @@ -235,7 +234,6 @@ target_link_libraries(qv2ray-baselib ${ZXING_LIBRARY} Threads::Threads ${QV2RAY_QT_LIBS} - ${LibUV_LIBRARIES} ) target_link_libraries(qv2ray @@ -259,7 +257,6 @@ target_include_directories(qv2ray-baselib PUBLIC ${CMAKE_BINARY_DIR} ${ZXING_INCLUDE_PATH} ${Protobuf_INCLUDE_DIRS} - ${LibUV_INCLUDE_DIR} ) if (BUILD_TESTING) diff --git a/cmake/components/qv2ray-lib.cmake b/cmake/components/qv2ray-lib.cmake index c9459623..5de59a99 100644 --- a/cmake/components/qv2ray-lib.cmake +++ b/cmake/components/qv2ray-lib.cmake @@ -37,7 +37,6 @@ set(QV2RAY_LIB_SOURCES ${CMAKE_SOURCE_DIR}/src/components/latency/TCPing.cpp ${CMAKE_SOURCE_DIR}/src/components/latency/TCPing.hpp ${CMAKE_SOURCE_DIR}/src/components/latency/win/ICMPPing.cpp - ${CMAKE_SOURCE_DIR}/src/components/latency/win/ICMPPingWork.cpp ${CMAKE_SOURCE_DIR}/src/components/latency/win/ICMPPing.hpp ${CMAKE_SOURCE_DIR}/src/components/latency/unix/ICMPPing.cpp ${CMAKE_SOURCE_DIR}/src/components/latency/unix/ICMPPing.hpp diff --git a/cmake/libuv.cmake b/cmake/libuv.cmake deleted file mode 100644 index 2f72119d..00000000 --- a/cmake/libuv.cmake +++ /dev/null @@ -1,35 +0,0 @@ -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libuv) -add_library(uv::uv-static ALIAS uv_a) -set_target_properties(uv_a PROPERTIES POSITION_INDEPENDENT_CODE 1) -set(UVW_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/async.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/check.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/dns.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/emitter.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/fs.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/fs_event.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/fs_poll.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/idle.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/lib.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/loop.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/pipe.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/poll.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/prepare.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/process.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/signal.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/stream.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/tcp.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/thread.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/timer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/tty.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/util.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/work.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src/uvw/udp.cpp -) -add_library(UVW_LIB ${UVW_SOURCES}) -target_compile_definitions(UVW_LIB PUBLIC UVW_AS_LIB) -target_link_libraries(UVW_LIB uv::uv-static) -set(LibUV_LIBRARIES uv::uv-static UVW_LIB) -set(LibUV_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/libuv/include - ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/uvw/src -) diff --git a/src/components/latency/LatencyTest.cpp b/src/components/latency/LatencyTest.cpp index f3a30aa0..6c60cc8c 100644 --- a/src/components/latency/LatencyTest.cpp +++ b/src/components/latency/LatencyTest.cpp @@ -1,97 +1,50 @@ -#include "uvw.hpp" #include "LatencyTest.hpp" #include "LatencyTestThread.hpp" #include "core/handler/ConfigHandler.hpp" +constexpr auto LATENCY_PROPERTY_KEY = "__QvLatencyTest__"; namespace Qv2ray::components::latency { - int getSockAddress(std::shared_ptr &loop, const char *host, int port, struct sockaddr_storage *storage, int ipv6first) - { - if (uv_ip4_addr(host, port, reinterpret_cast(storage)) == 0) - { - return 0; - } - if (uv_ip6_addr(host, port, reinterpret_cast(storage)) == 0) - { - return 0; - } - // not an ip - auto getAddrInfoReq = loop->resource(); - char digitBuffer[20] = { 0 }; - sprintf(digitBuffer, "%d", port); - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - auto dns_res = getAddrInfoReq->addrInfoSync(host, digitBuffer, &hints); - int prefer_af = ipv6first ? AF_INET6 : AF_INET; - if (dns_res.first) - { - struct addrinfo *rp = nullptr; - for (rp = dns_res.second.get(); rp != nullptr; rp = rp->ai_next) - if (rp->ai_family == prefer_af) - { - if (rp->ai_family == AF_INET) - memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); - else if (rp->ai_family == AF_INET6) - memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); - break; - } - if (rp == nullptr) - { - // fallback: if we can't find prefered AF, then we choose alternative. - for (rp = dns_res.second.get(); rp != nullptr; rp = rp->ai_next) - { - if (rp->ai_family == AF_INET) - memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in)); - else if (rp->ai_family == AF_INET6) - memcpy(storage, rp->ai_addr, sizeof(struct sockaddr_in6)); - break; - } - } - if (rp == nullptr) - { - return -1; // dns not resolved - } - return 0; - } - else - { - return -1; // dns not resolved - } - return -1; - } LatencyTestHost::LatencyTestHost(const int defaultCount, QObject *parent) : QObject(parent) { - qRegisterMetaType(); - qRegisterMetaType(); totalTestCount = defaultCount; - latencyThread = new LatencyTestThread(this); - latencyThread->start(); - } - - LatencyTestHost::~LatencyTestHost() - { - latencyThread->stopLatencyTest(); - latencyThread->wait(); } void LatencyTestHost::StopAllLatencyTest() { - latencyThread->stopLatencyTest(); - latencyThread->wait(); - latencyThread->start(); + for (const auto &thread : latencyThreads) + { + thread->terminate(); + } + latencyThreads.clear(); } - void LatencyTestHost::TestLatency(const ConnectionId &id, Qv2rayLatencyTestingMethod method) { - latencyThread->pushRequest(id, totalTestCount, method); + const auto &[protocol, host, port] = GetConnectionInfo(id); + auto thread = new LatencyTestThread(host, port, method, totalTestCount, this); + connect(thread, &QThread::finished, this, &LatencyTestHost::OnLatencyThreadProcessCompleted); + thread->setProperty(LATENCY_PROPERTY_KEY, QVariant::fromValue(id)); + latencyThreads.push_back(thread); + thread->start(); } - void LatencyTestHost::TestLatency(const QList &ids, Qv2rayLatencyTestingMethod method) + + void LatencyTestHost::OnLatencyThreadProcessCompleted() { - latencyThread->pushRequest(ids, totalTestCount, method); + const auto senderThread = qobject_cast(sender()); + latencyThreads.removeOne(senderThread); + auto result = senderThread->GetResult(); + + if (!result.errorMessage.isEmpty()) + { + LOG(MODULE_NETWORK, "Ping --> " + result.errorMessage) + result.avg = LATENCY_TEST_VALUE_ERROR; + result.best = LATENCY_TEST_VALUE_ERROR; + result.worst = LATENCY_TEST_VALUE_ERROR; + } + + emit OnLatencyTestCompleted(senderThread->property(LATENCY_PROPERTY_KEY).value(), result); } } // namespace Qv2ray::components::latency diff --git a/src/components/latency/LatencyTest.hpp b/src/components/latency/LatencyTest.hpp index 143208ae..fea763b6 100644 --- a/src/components/latency/LatencyTest.hpp +++ b/src/components/latency/LatencyTest.hpp @@ -1,10 +1,6 @@ #pragma once #include "base/Qv2rayBase.hpp" -namespace uvw -{ - class Loop; -} -struct sockaddr_storage; + namespace Qv2ray::components::latency { class LatencyTestThread; @@ -18,16 +14,6 @@ namespace Qv2ray::components::latency long avg = LATENCY_TEST_VALUE_ERROR; Qv2rayLatencyTestingMethod method; }; - struct LatencyTestRequest - { - ConnectionId id; - QString host; - int port; - int totalCount; - Qv2rayLatencyTestingMethod method; - }; - - int getSockAddress(std::shared_ptr &loop, const char *host, int port, struct sockaddr_storage *storage, int ipv6first); class LatencyTestHost : public QObject { @@ -35,22 +21,21 @@ namespace Qv2ray::components::latency public: explicit LatencyTestHost(const int defaultCount = 3, QObject *parent = nullptr); void TestLatency(const ConnectionId &connectionId, Qv2rayLatencyTestingMethod); - void TestLatency(const QList &connectionIds, Qv2rayLatencyTestingMethod); void StopAllLatencyTest(); - - ~LatencyTestHost() override; - + ~LatencyTestHost() + { + StopAllLatencyTest(); + } signals: - void OnLatencyTestCompleted(ConnectionId id, LatencyTestResult data); + void OnLatencyTestCompleted(const ConnectionId &id, const LatencyTestResult &data); + + private slots: + void OnLatencyThreadProcessCompleted(); private: int totalTestCount; - // we're not introduce multi latency test thread for now, - // cause it's easy to use a scheduler like round-robin scheme - // and libuv event loop is fast. - LatencyTestThread *latencyThread; + QList latencyThreads; }; } // namespace Qv2ray::components::latency using namespace Qv2ray::components::latency; -Q_DECLARE_METATYPE(LatencyTestResult) diff --git a/src/components/latency/LatencyTestThread.cpp b/src/components/latency/LatencyTestThread.cpp index d54dea10..efc48ea0 100644 --- a/src/components/latency/LatencyTestThread.cpp +++ b/src/components/latency/LatencyTestThread.cpp @@ -1,83 +1,84 @@ #include "LatencyTestThread.hpp" #include "TCPing.hpp" -#include "core/CoreUtils.hpp" #ifdef Q_OS_UNIX #include "unix/ICMPPing.hpp" #else #include "win/ICMPPing.hpp" #endif -#include "uvw.hpp" namespace Qv2ray::components::latency { - LatencyTestThread::LatencyTestThread(QObject *parent) : QThread(parent) + LatencyTestThread::LatencyTestThread(const QString &host, int port, Qv2rayLatencyTestingMethod method, int count, QObject *parent) + : QThread(parent) { + this->count = count; + this->host = host; + this->port = port; + this->method = method; + this->resultData = {}; + this->resultData.method = method; } - - void LatencyTestThread::pushRequest(const ConnectionId &id, int totalTestCount, Qv2rayLatencyTestingMethod method) - { - std::unique_lock lockGuard{ m }; - const auto &[protocol, host, port] = GetConnectionInfo(id); - requests.emplace_back(LatencyTestRequest{ id, host, port, totalTestCount, method }); - } - void LatencyTestThread::run() { - loop = uvw::Loop::create(); - stopTimer = loop->resource(); - stopTimer->on([this](auto &, auto &handle) { - if (isStop) + resultData.avg = 0; + resultData.best = 0; + resultData.worst = 0; + switch (method) + { + case ICMPING: { - handle.stop(); - handle.close(); - requests.clear(); - loop->clear(); - loop->close(); - loop->stop(); - } - else - { - std::unique_lock lockGuard{ m, std::defer_lock }; - if (!lockGuard.try_lock()) - return; - if (requests.empty()) - return; - auto parent = qobject_cast(this->parent()); - for (auto &req : requests) + icmping::ICMPPing pingHelper(30); + for (auto i = 0; i < count; i++) { - switch (req.method) + resultData.totalCount++; + const auto value = pingHelper.ping(host); + const auto _latency = value.first; + const auto errMessage = value.second; + if (!errMessage.isEmpty()) { - case ICMPING: - { - auto ptr = std::make_shared(30); - ptr->start(loop, req, parent); - } - break; - case TCPING: - default: - { - auto ptr = std::make_shared(); - ptr->start(loop, req, parent); - break; - } + resultData.errorMessage.append(NEWLINE + errMessage); + resultData.failedCount++; + } + else + { +#ifdef Q_OS_WIN + // Is it Windows? + #undef min + #undef max +#endif + resultData.avg += _latency; +#define _qvmin_(x, y) ((x) < (y) ? (x) : (y)) +#define _qvmax_(x, y) ((x) > (y) ? (x) : (y)) + resultData.best = _qvmin_(resultData.best, _latency); + resultData.worst = _qvmax_(resultData.worst, _latency); +#undef _qvmax_ +#undef _qvmin_ } } - requests.clear(); + if (resultData.totalCount > 0 && resultData.failedCount != resultData.totalCount) + { + resultData.errorMessage.clear(); + // ms to s + resultData.avg = resultData.avg / (resultData.totalCount - resultData.failedCount) / 1000; + } + else + { + resultData.avg = LATENCY_TEST_VALUE_ERROR; + LOG(MODULE_NETWORK, resultData.errorMessage) + } + // + // + break; + } + case TCPING: + default: + { + this->resultData = tcping::TestTCPLatency(host, port, count); + break; } - }); - stopTimer->start(uvw::TimerHandle::Time{ 500 }, uvw::TimerHandle::Time{ 500 }); - loop->run(); - } - void LatencyTestThread::pushRequest(const QList &ids, int totalTestCount, Qv2rayLatencyTestingMethod method) - { - std::unique_lock lockGuard{ m }; - for (const auto &id : ids) - { - const auto &[protocol, host, port] = GetConnectionInfo(id); - requests.emplace_back(LatencyTestRequest{ id, host, port, totalTestCount, method }); } } } // namespace Qv2ray::components::latency diff --git a/src/components/latency/LatencyTestThread.hpp b/src/components/latency/LatencyTestThread.hpp index e33584f2..45593d53 100644 --- a/src/components/latency/LatencyTestThread.hpp +++ b/src/components/latency/LatencyTestThread.hpp @@ -2,36 +2,27 @@ #include "LatencyTest.hpp" #include -#include -#include -namespace uvw -{ - class Loop; - class TimerHandle; -} namespace Qv2ray::components::latency { class LatencyTestThread : public QThread { Q_OBJECT public: - explicit LatencyTestThread(QObject *parent = nullptr); - void stopLatencyTest() + explicit LatencyTestThread(const QString &host, int port, Qv2rayLatencyTestingMethod, int count, QObject *parent = nullptr); + LatencyTestResult GetResult() const { - isStop = true; + return resultData; } - void pushRequest(const QList &ids, int totalTestCount, Qv2rayLatencyTestingMethod method); - void pushRequest(const ConnectionId &id, int totalTestCount, Qv2rayLatencyTestingMethod method); protected: void run() override; private: - std::shared_ptr loop; - bool isStop = false; - std::shared_ptr stopTimer; - std::vector requests; - std::mutex m; + LatencyTestResult resultData; + QString host; + int port; + int count; + Qv2rayLatencyTestingMethod method; // static LatencyTestResult TestLatency_p(const ConnectionId &id, const int count); }; diff --git a/src/components/latency/TCPing.cpp b/src/components/latency/TCPing.cpp index 34b57d12..789a192d 100644 --- a/src/components/latency/TCPing.cpp +++ b/src/components/latency/TCPing.cpp @@ -1,60 +1,364 @@ #include "TCPing.hpp" -#include "uvw.hpp" +#ifdef _WIN32 + #include + #include +#else + #include + #include + #include + #include + #include + #include +#endif namespace Qv2ray::components::latency::tcping { - void TCPing::start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost) - { - struct sockaddr_storage storage; - data.totalCount = req.totalCount; - data.failedCount = 0; - data.worst = 0; - data.avg = 0; - if (getSockAddress(loop, req.host.toStdString().data(), req.port, &storage, 0) != 0) - { - data.errorMessage = QObject::tr("DNS not resolved"); - data.avg = LATENCY_TEST_VALUE_ERROR; - testHost->OnLatencyTestCompleted(req.id, data); - return; +#ifdef Q_OS_WIN + using qv_socket_t = SOCKET; + struct TranslateWSAError{ + ~TranslateWSAError(){ + errno = translate_sys_error(WSAGetLastError()); } - for (int i = 0; i < req.totalCount; ++i) + int translate_sys_error(int sys_errno) { + LOG(MODULE_NETWORK, "translate_sys_error:WSAGetLastError()==" + QSTRN(sys_errno)) + switch (sys_errno) { + case ERROR_NOACCESS: return EACCES; + case WSAEACCES: return EACCES; + case ERROR_ELEVATION_REQUIRED: return EACCES; + case ERROR_CANT_ACCESS_FILE: return EACCES; + case ERROR_ADDRESS_ALREADY_ASSOCIATED: return EADDRINUSE; + case WSAEADDRINUSE: return EADDRINUSE; + case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL; + case WSAEAFNOSUPPORT: return EAFNOSUPPORT; + case WSAEWOULDBLOCK: return EAGAIN; + case WSAEALREADY: return EALREADY; + case ERROR_INVALID_FLAGS: return EBADF; + case ERROR_INVALID_HANDLE: return EBADF; + case ERROR_LOCK_VIOLATION: return EBUSY; + case ERROR_PIPE_BUSY: return EBUSY; + case ERROR_SHARING_VIOLATION: return EBUSY; + case ERROR_OPERATION_ABORTED: return ECANCELED; + case WSAEINTR: return ECANCELED; + /*case ERROR_NO_UNICODE_TRANSLATION: return ECHARSET;*/ + case ERROR_CONNECTION_ABORTED: return ECONNABORTED; + case WSAECONNABORTED: return ECONNABORTED; + case ERROR_CONNECTION_REFUSED: return ECONNREFUSED; + case WSAECONNREFUSED: return ECONNREFUSED; + case ERROR_NETNAME_DELETED: return ECONNRESET; + case WSAECONNRESET: return ECONNRESET; + case ERROR_ALREADY_EXISTS: return EEXIST; + case ERROR_FILE_EXISTS: return EEXIST; + case ERROR_BUFFER_OVERFLOW: return EFAULT; + case WSAEFAULT: return EFAULT; + case ERROR_HOST_UNREACHABLE: return EHOSTUNREACH; + case WSAEHOSTUNREACH: return EHOSTUNREACH; + case ERROR_INSUFFICIENT_BUFFER: return EINVAL; + case ERROR_INVALID_DATA: return EINVAL; + case ERROR_INVALID_PARAMETER: return EINVAL; + case ERROR_SYMLINK_NOT_SUPPORTED: return EINVAL; + case WSAEINVAL: return EINVAL; + case WSAEPFNOSUPPORT: return EINVAL; + case WSAESOCKTNOSUPPORT: return EINVAL; + case ERROR_BEGINNING_OF_MEDIA: return EIO; + case ERROR_BUS_RESET: return EIO; + case ERROR_CRC: return EIO; + case ERROR_DEVICE_DOOR_OPEN: return EIO; + case ERROR_DEVICE_REQUIRES_CLEANING: return EIO; + case ERROR_DISK_CORRUPT: return EIO; + case ERROR_EOM_OVERFLOW: return EIO; + case ERROR_FILEMARK_DETECTED: return EIO; + case ERROR_GEN_FAILURE: return EIO; + case ERROR_INVALID_BLOCK_LENGTH: return EIO; + case ERROR_IO_DEVICE: return EIO; + case ERROR_NO_DATA_DETECTED: return EIO; + case ERROR_NO_SIGNAL_SENT: return EIO; + case ERROR_OPEN_FAILED: return EIO; + case ERROR_SETMARK_DETECTED: return EIO; + case ERROR_SIGNAL_REFUSED: return EIO; + case WSAEISCONN: return EISCONN; + case ERROR_CANT_RESOLVE_FILENAME: return ELOOP; + case ERROR_TOO_MANY_OPEN_FILES: return EMFILE; + case WSAEMFILE: return EMFILE; + case WSAEMSGSIZE: return EMSGSIZE; + case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG; + case ERROR_NETWORK_UNREACHABLE: return ENETUNREACH; + case WSAENETUNREACH: return ENETUNREACH; + case WSAENOBUFS: return ENOBUFS; + case ERROR_BAD_PATHNAME: return ENOENT; + case ERROR_DIRECTORY: return ENOENT; + case ERROR_ENVVAR_NOT_FOUND: return ENOENT; + case ERROR_FILE_NOT_FOUND: return ENOENT; + case ERROR_INVALID_NAME: return ENOENT; + case ERROR_INVALID_DRIVE: return ENOENT; + case ERROR_INVALID_REPARSE_DATA: return ENOENT; + case ERROR_MOD_NOT_FOUND: return ENOENT; + case ERROR_PATH_NOT_FOUND: return ENOENT; + case WSAHOST_NOT_FOUND: return ENOENT; + case WSANO_DATA: return ENOENT; + case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM; + case ERROR_OUTOFMEMORY: return ENOMEM; + case ERROR_CANNOT_MAKE: return ENOSPC; + case ERROR_DISK_FULL: return ENOSPC; + case ERROR_EA_TABLE_FULL: return ENOSPC; + case ERROR_END_OF_MEDIA: return ENOSPC; + case ERROR_HANDLE_DISK_FULL: return ENOSPC; + case ERROR_NOT_CONNECTED: return ENOTCONN; + case WSAENOTCONN: return ENOTCONN; + case ERROR_DIR_NOT_EMPTY: return ENOTEMPTY; + case WSAENOTSOCK: return ENOTSOCK; + case ERROR_NOT_SUPPORTED: return ENOTSUP; + case ERROR_BROKEN_PIPE: return EOF; + case ERROR_ACCESS_DENIED: return EPERM; + case ERROR_PRIVILEGE_NOT_HELD: return EPERM; + case ERROR_BAD_PIPE: return EPIPE; + case ERROR_NO_DATA: return EPIPE; + case ERROR_PIPE_NOT_CONNECTED: return EPIPE; + case WSAESHUTDOWN: return EPIPE; + case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT; + case ERROR_WRITE_PROTECT: return EROFS; + case ERROR_SEM_TIMEOUT: return ETIMEDOUT; + case WSAETIMEDOUT: return ETIMEDOUT; + case ERROR_NOT_SAME_DEVICE: return EXDEV; + case ERROR_INVALID_FUNCTION: return EISDIR; + case ERROR_META_EXPANSION_TOO_LONG: return E2BIG; + default: return EIO;/*unknown*/ + } + } + }; +#else + using qv_socket_t = int; +#endif + + inline int setnonblocking(qv_socket_t sockno, int &opt) + { +#ifdef _WIN32 + ULONG block = 1; + if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR) { - auto tcpClient = loop->resource(); - system_clock::time_point start = system_clock::now(); - tcpClient->connect(reinterpret_cast(storage)); - tcpClient->once([ptr = shared_from_this(), testHost, this, host = req.host, port = req.port, - id = req.id](const uvw::ErrorEvent &e, uvw::TCPHandle &h) { - LOG(MODULE_NETWORK, "error connecting to host: " + host + ":" + QSTRN(port) + " " + e.what()) - data.failedCount += 1; - data.errorMessage = e.what(); - notifyTestHost(testHost, id); - h.clear(); - }); - tcpClient->once([ptr = shared_from_this(), start, testHost, id = req.id, this](auto &, auto &h) { - ++successCount; - system_clock::time_point end = system_clock::now(); + return -1; + } +#else + if ((opt = fcntl(sockno, F_GETFL, NULL)) < 0) + { + // get socket flags + return -1; + } + if (fcntl(sockno, F_SETFL, opt | O_NONBLOCK) < 0) + { + // set socket non-blocking + return -1; + } +#endif + return 0; + } + + inline int setblocking(qv_socket_t sockno, int &opt) + { +#ifdef _WIN32 + ULONG block = 0; + if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR) + { + return -1; + } +#else + if (fcntl(sockno, F_SETFL, opt) < 0) + { + // reset socket flags + return -1; + } +#endif + return 0; + } + + int connect_wait(qv_socket_t sockno, struct sockaddr *addr, size_t addrlen, int timeout_sec = 5) + { + int res; + int opt; + timeval tv = { 0, 0 }; +#ifdef _WIN32 + TranslateWSAError _translateWSAError_; +#endif + tv.tv_sec = timeout_sec; + tv.tv_usec = 0; + if ((res = setnonblocking(sockno, opt)) != 0) + return -1; + if ((res = ::connect(sockno, addr, addrlen)) < 0) + { +#ifdef _WIN32 + if (WSAGetLastError() == WSAEWOULDBLOCK) + { +#else + if (errno == EINPROGRESS) + { +#endif + // connecting + fd_set wait_set; + FD_ZERO(&wait_set); + FD_SET(sockno, &wait_set); + res = select(sockno + 1, NULL, &wait_set, NULL, &tv); + } + } + else + // connect immediately + res = 1; + + if (setblocking(sockno, opt) != 0) + return -1; + + if (res < 0) + // an error occured + return -1; + else if (res == 0) + { + // timeout + errno = ETIMEDOUT; + return 1; + } + else + { + socklen_t len = sizeof(opt); + if (getsockopt(sockno, SOL_SOCKET, SO_ERROR, (char *) (&opt), &len) < 0) + return -1; + } + return 0; + } + + int resolveHost(const QString &host, int port, addrinfo **res) + { + addrinfo hints; +#ifdef Q_OS_WIN + WSADATA wsadata; + WSAStartup(0x0202, &wsadata); + memset(&hints, 0, sizeof(struct addrinfo)); +#else + hints.ai_flags = AI_NUMERICSERV; +#endif + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + return getaddrinfo(host.toStdString().c_str(), std::to_string(port).c_str(), &hints, res); + } + + int testLatency(struct addrinfo *addr, system_clock::time_point *start, system_clock::time_point *end) + { + qv_socket_t fd; + + const int on = 1; + + /* try to connect for each of the entries: */ + while (addr != nullptr) + { + if (isExiting) + return 0; + + // create socket + if (!(fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol))) + { + goto next_addr0; + } + + // Windows needs special conversion. +#ifdef _WIN32 + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)) < 0) +#else + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) +#endif + goto next_addr1; + *start = system_clock::now(); + if (connect_wait(fd, addr->ai_addr, addr->ai_addrlen) == 0) + { + *end = system_clock::now(); +#ifdef Q_OS_WIN + closesocket(fd); +#else + close(fd); +#endif + return 0; + } + + next_addr1: +#ifdef _WIN32 + closesocket(fd); +#else + close(fd); +#endif + next_addr0: + addr = addr->ai_next; + } + + return -1; + } + + LatencyTestResult TestTCPLatency(const QString &host, int port, int testCount) + { + LatencyTestResult data; + int successCount = 0; + addrinfo *resolved; + int errcode; + + if ((errcode = resolveHost(host, port, &resolved)) != 0) + { +#ifdef Q_OS_WIN + data.errorMessage = QString::fromStdWString(gai_strerror(errcode)); +#else + data.errorMessage = gai_strerror(errcode); +#endif + return data; + } + + int currentCount = 0; + + data.avg = 0; + data.worst = 0; + data.best = 0; + + while (currentCount < testCount) + { + system_clock::time_point start; + system_clock::time_point end; + + if ((errcode = testLatency(resolved, &start, &end)) != 0) + { + LOG(MODULE_NETWORK, "error connecting to host: " + host + ":" + QSTRN(port) + " " + strerror(errno)) + } + else + { + successCount++; auto milliseconds = std::chrono::duration_cast(end - start); long ms = milliseconds.count(); data.avg += ms; - data.worst = std::max(data.worst, ms); - data.best = std::min(data.best, ms); - notifyTestHost(testHost, id); - h.clear(); - h.close(); - }); +#ifdef Q_OS_WIN + // Is it Windows? + #undef min + #undef max +#endif + data.worst = std::min(data.worst, ms); + data.best = std::max(data.best, ms); + + if (ms > 1000) + { + LOG(MODULE_NETWORK, "Stop the test on the first long connect()") + break; /* Stop the test on the first long connect() */ + } + } + + currentCount++; + QThread::msleep(200); } - } - void TCPing::notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id) - { - if (data.failedCount + successCount == data.totalCount) + freeaddrinfo(resolved); + if (successCount > 0) { - if (data.failedCount == data.totalCount) - data.avg = LATENCY_TEST_VALUE_ERROR; - else - data.errorMessage.clear(), data.avg = data.avg / successCount; - testHost->OnLatencyTestCompleted(id, data); + data.avg = data.avg / successCount; } + else + { + data.avg = LATENCY_TEST_VALUE_ERROR; + data.worst = LATENCY_TEST_VALUE_ERROR; + data.best = LATENCY_TEST_VALUE_ERROR; + data.errorMessage = QObject::tr("Timeout"); + } + return data; } } // namespace Qv2ray::components::latency::tcping diff --git a/src/components/latency/TCPing.hpp b/src/components/latency/TCPing.hpp index c9dfa45a..134c8df8 100644 --- a/src/components/latency/TCPing.hpp +++ b/src/components/latency/TCPing.hpp @@ -1,18 +1,7 @@ #pragma once #include "LatencyTest.hpp" #include "base/Qv2rayBase.hpp" -namespace uvw -{ - class Loop; -} namespace Qv2ray::components::latency::tcping { - struct TCPing : public std::enable_shared_from_this - { - TCPing() = default; - void start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost); - void notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id); - int successCount = 0; - LatencyTestResult data; - }; + LatencyTestResult TestTCPLatency(const QString &host, int port, int testCount); } // namespace Qv2ray::components::latency::tcping diff --git a/src/components/latency/unix/ICMPPing.cpp b/src/components/latency/unix/ICMPPing.cpp index 86e7307c..7f899e37 100644 --- a/src/components/latency/unix/ICMPPing.cpp +++ b/src/components/latency/unix/ICMPPing.cpp @@ -7,14 +7,16 @@ #include #ifdef Q_OS_UNIX + #include + // + #include + #include #include - #include //macos need that + #include #include + #include #include #include - #include - #include "uvw.hpp" - #ifdef Q_OS_MAC #define SOL_IP 0 #endif @@ -57,7 +59,8 @@ namespace Qv2ray::components::latency::icmping { auto timeout_s = 5; // create socket - if (((socketId = socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)) < 0)) + if ( //((sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) && + ((socketId = socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)) < 0)) { initErrorMessage = "EPING_SOCK: " + QObject::tr("Socket creation failed"); return; @@ -83,127 +86,71 @@ namespace Qv2ray::components::latency::icmping initialized = true; } - void ICMPPing::start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost) + /// @return value < 0 on error, response time in ms on success + QPair ICMPPing::ping(const QString &address) { if (!initialized) { - data.errorMessage = initErrorMessage; - data.avg = LATENCY_TEST_VALUE_ERROR; - testHost->OnLatencyTestCompleted(req.id, data); - return; + return { 0, initErrorMessage }; } - struct sockaddr_storage storage; - data.totalCount = req.totalCount; - data.failedCount = 0; - data.worst = 0; - data.avg = 0; - if (getSockAddress(loop, req.host.toStdString().data(), req.port, &storage, 0) != 0) + timeval start, end; + socklen_t slen; + // not initialized + if (socketId < 0) + return { 0, "EPING_SOCK:" + QObject::tr("Socket creation failed") }; + + // resolve hostname + hostent *resolvedAddress = gethostbyname(address.toStdString().c_str()); + if (!resolvedAddress) + return { 0, "EPING_HOST: " + QObject::tr("Unresolvable hostname") }; + + // set IP address to ping + sockaddr_in targetAddress; + memset(&targetAddress, 0, sizeof(targetAddress)); + targetAddress.sin_family = resolvedAddress->h_addrtype; + targetAddress.sin_port = 0; + memcpy(&targetAddress.sin_addr, resolvedAddress->h_addr, resolvedAddress->h_length); + + // prepare echo request packet + icmp _icmp_request; + memset(&_icmp_request, 0, sizeof(_icmp_request)); + _icmp_request.icmp_type = ICMP_ECHO; + _icmp_request.icmp_hun.ih_idseq.icd_id = 0; // SOCK_DGRAM & 0 => id will be set by kernel + unsigned short sent_seq; + _icmp_request.icmp_hun.ih_idseq.icd_seq = sent_seq = seq++; + _icmp_request.icmp_cksum = ping_checksum(reinterpret_cast(&_icmp_request), sizeof(_icmp_request)); + + // send echo request + gettimeofday(&start, NULL); + if (sendto(socketId, &_icmp_request, sizeof(icmp), 0, (struct sockaddr *) &targetAddress, sizeof(targetAddress)) <= 0) + return { 0, "EPING_SEND: " + QObject::tr("Sending echo request failed") }; + + // receive response (if any) + sockaddr_in remove_addr; + slen = sizeof(remove_addr); + int rlen; + icmp resp; + while ((rlen = recvfrom(socketId, &resp, sizeof(icmp), 0, (struct sockaddr *) &remove_addr, &slen)) > 0) { - data.errorMessage = QObject::tr("DNS not resolved"); - data.avg = LATENCY_TEST_VALUE_ERROR; - testHost->OnLatencyTestCompleted(req.id, data); - return; - } - uvw::OSSocketHandle osSocketHandle{ socketId }; - auto pollHandle = loop->resource(osSocketHandle); - pollHandle->init(); - auto pollEvent = uvw::Flags::from(); - pollHandle->on([this, testHost, id = req.id, ptr = shared_from_this()](uvw::PollEvent &, uvw::PollHandle &h) { - timeval end; - sockaddr_in remove_addr; - socklen_t slen = sizeof(remove_addr); - int rlen = 0; - icmp resp; - do + gettimeofday(&end, NULL); + + // skip malformed + if (rlen != sizeof(icmp)) + continue; + + // skip the ones we didn't send + if (resp.icmp_hun.ih_idseq.icd_seq != sent_seq) + continue; + + switch (resp.icmp_type) { - do - { - rlen = recvfrom(socketId, &resp, sizeof(icmp), 0, (struct sockaddr *) &remove_addr, &slen); - } while (rlen == -1 && errno == EINTR); - gettimeofday(&end, NULL); - - // skip malformed - if (rlen != sizeof(icmp)) - continue; - - // skip the ones we didn't send - auto cur_seq = resp.icmp_hun.ih_idseq.icd_seq; - if (cur_seq >= seq) - continue; - - switch (resp.icmp_type) - { - case ICMP_ECHOREPLY: - data.avg = - 1000000 * (end.tv_sec - startTimevals[cur_seq - 1].tv_sec) + (end.tv_usec - startTimevals[cur_seq - 1].tv_usec); - successCount++; - notifyTestHost(testHost, id); - continue; - case ICMP_UNREACH: - data.errorMessage = "EPING_DST: " + QObject::tr("Destination unreachable"); - data.failedCount++; - if (notifyTestHost(testHost, id)) - { - h.clear(); - h.close(); - return; - } - continue; - case ICMP_TIMXCEED: - data.errorMessage = "EPING_TIME: " + QObject::tr("Timeout"); - data.failedCount++; - if (notifyTestHost(testHost, id)) - { - h.clear(); - h.close(); - return; - } - continue; - default: - data.errorMessage = "EPING_UNK: " + QObject::tr("Unknown error"); - data.failedCount++; - if (notifyTestHost(testHost, id)) - { - h.clear(); - h.close(); - return; - } - continue; - } - } while (rlen > 0); - }); - pollHandle->start(pollEvent); - for (int i = 0; i < req.totalCount; ++i) - { - // prepare echo request packet - icmp _icmp_request; - memset(&_icmp_request, 0, sizeof(_icmp_request)); - _icmp_request.icmp_type = ICMP_ECHO; - _icmp_request.icmp_hun.ih_idseq.icd_id = 0; // SOCK_DGRAM & 0 => id will be set by kernel - _icmp_request.icmp_hun.ih_idseq.icd_seq = seq++; - _icmp_request.icmp_cksum = ping_checksum(reinterpret_cast(&_icmp_request), sizeof(_icmp_request)); - int n; - timeval start; - gettimeofday(&start, nullptr); - startTimevals.push_back(start); - do - { - n = ::sendto(socketId, &_icmp_request, sizeof(icmp), 0, (struct sockaddr *) &storage, sizeof(struct sockaddr)); - } while (n < 0 && errno == EINTR); + case ICMP_ECHOREPLY: return { 1000000 * (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec), {} }; + case ICMP_UNREACH: return { 0, "EPING_DST: " + QObject::tr("Destination unreachable") }; + case ICMP_TIMXCEED: return { 0, "EPING_TIME: " + QObject::tr("Timeout") }; + default: return { 0, "EPING_UNK: " + QObject::tr("Unknown error") }; + } } - } - bool ICMPPing::notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id) - { - if (data.failedCount + successCount == data.totalCount) - { - if (data.failedCount == data.totalCount) - data.avg = LATENCY_TEST_VALUE_ERROR; - else - data.errorMessage.clear(), data.avg = data.avg / successCount / 1000; - testHost->OnLatencyTestCompleted(id, data); - return true; - } - return false; + return { 0, "EPING_TIME: " + QObject::tr("Timeout") }; } } // namespace Qv2ray::components::latency::icmping #endif diff --git a/src/components/latency/unix/ICMPPing.hpp b/src/components/latency/unix/ICMPPing.hpp index 2aef41a7..987086fc 100644 --- a/src/components/latency/unix/ICMPPing.hpp +++ b/src/components/latency/unix/ICMPPing.hpp @@ -1,17 +1,11 @@ #pragma once #include #ifdef Q_OS_UNIX - #include "components/latency/LatencyTest.hpp" - #include #include -namespace uvw -{ - class Loop; -} namespace Qv2ray::components::latency::icmping { - class ICMPPing : public std::enable_shared_from_this + class ICMPPing { public: explicit ICMPPing(int ttl); @@ -19,8 +13,7 @@ namespace Qv2ray::components::latency::icmping { deinit(); } - void start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost); - bool notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id); + QPair ping(const QString &address); private: void deinit(); @@ -29,9 +22,6 @@ namespace Qv2ray::components::latency::icmping // socket int socketId = -1; bool initialized = false; - int successCount = 0; - LatencyTestResult data; - std::vector startTimevals; QString initErrorMessage; }; } // namespace Qv2ray::components::latency::icmping diff --git a/src/components/latency/win/ICMPPing.cpp b/src/components/latency/win/ICMPPing.cpp index c1620b52..74342742 100644 --- a/src/components/latency/win/ICMPPing.cpp +++ b/src/components/latency/win/ICMPPing.cpp @@ -1,12 +1,16 @@ #include "ICMPPing.hpp" #ifdef Q_OS_WIN +// #include +// #include +// #include +// #include +// #include #include - namespace Qv2ray::components::latency::icmping { ICMPPing::ICMPPing(uint64_t timeout) @@ -79,18 +83,5 @@ namespace Qv2ray::components::latency::icmping const ICMP_ECHO_REPLY *r = (const ICMP_ECHO_REPLY *) reply_buf; return QPair(r->RoundTripTime * 1000, QString{}); } - bool ICMPPing::notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id) - { - if (data.failedCount + successCount == data.totalCount) - { - if (data.failedCount == data.totalCount) - data.avg = LATENCY_TEST_VALUE_ERROR; - else - data.errorMessage.clear(), data.avg = data.avg / successCount / 1000; - testHost->OnLatencyTestCompleted(id, data); - return true; - } - return false; - } } // namespace Qv2ray::components::latency::icmping #endif diff --git a/src/components/latency/win/ICMPPing.hpp b/src/components/latency/win/ICMPPing.hpp index 2466048f..9b9b922b 100644 --- a/src/components/latency/win/ICMPPing.hpp +++ b/src/components/latency/win/ICMPPing.hpp @@ -8,20 +8,15 @@ #include #ifdef Q_OS_WIN - #include "components/latency/LatencyTest.hpp" - #include #include #include #include #include -namespace uvw -{ - class Loop; -} + namespace Qv2ray::components::latency::icmping { - class ICMPPing : public std::enable_shared_from_this + class ICMPPing { public: ICMPPing(uint64_t timeout = DEFAULT_TIMEOUT); @@ -29,16 +24,12 @@ namespace Qv2ray::components::latency::icmping public: static const uint64_t DEFAULT_TIMEOUT = 10000U; - void start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost); - bool notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id); public: QPair ping(const QString &ipAddr); private: uint64_t timeout = DEFAULT_TIMEOUT; - int successCount = 0; - LatencyTestResult data; }; } // namespace Qv2ray::components::latency::icmping #endif diff --git a/src/components/latency/win/ICMPPingWork.cpp b/src/components/latency/win/ICMPPingWork.cpp deleted file mode 100644 index e2007f64..00000000 --- a/src/components/latency/win/ICMPPingWork.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "uvw.hpp" -#include "ICMPPing.hpp" -#ifdef Q_OS_WIN -namespace Qv2ray::components::latency::icmping -{ - void ICMPPing::start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost) - { - data.totalCount = req.totalCount; - data.failedCount = 0; - data.worst = 0; - data.avg = 0; - for (int i = 0; i < req.totalCount; ++i) - { - auto work = loop->resource([ptr = shared_from_this(), this, addr = req.host, id = req.id, testHost]() mutable { - auto pingres = ping(addr); - if (!pingres.second.isEmpty()) - { - data.errorMessage = pingres.second; - data.failedCount++; - } - else - { - data.avg += pingres.first; - data.best = std::min(pingres.first, data.best); - data.worst = std::max(pingres.first, data.worst); - successCount++; - } - notifyTestHost(testHost, id); - ptr.reset(); - }); - work->queue(); - } - } -} -#endif diff --git a/src/core/handler/ConfigHandler.cpp b/src/core/handler/ConfigHandler.cpp index 6ca8be93..ef86feb6 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -124,18 +124,16 @@ namespace Qv2ray::core::handler { for (const auto &connection : connections.keys()) { - emit OnLatencyTestStarted(connection); + StartLatencyTest(connection); } - tcpingHelper->TestLatency(connections.keys(), GlobalConfig.networkConfig.latencyTestingMethod); } void QvConfigHandler::StartLatencyTest(const GroupId &id) { for (const auto &connection : groups[id].connections) { - emit OnLatencyTestStarted(connection); + StartLatencyTest(connection); } - tcpingHelper->TestLatency(groups[id].connections, GlobalConfig.networkConfig.latencyTestingMethod); } void QvConfigHandler::StartLatencyTest(const ConnectionId &id) @@ -358,6 +356,7 @@ namespace Qv2ray::core::handler QvConfigHandler::~QvConfigHandler() { LOG(MODULE_CORE_HANDLER, "Triggering save settings from destructor") + tcpingHelper->StopAllLatencyTest(); delete kernelHandler; SaveConnectionConfig(); } @@ -368,7 +367,7 @@ namespace Qv2ray::core::handler return connectionRootCache.value(id); } - void QvConfigHandler::OnLatencyDataArrived_p(ConnectionId id, LatencyTestResult result) + void QvConfigHandler::OnLatencyDataArrived_p(const ConnectionId &id, const LatencyTestResult &result) { CheckValidId(id, nothing); connections[id].latency = result.avg; diff --git a/src/core/handler/ConfigHandler.hpp b/src/core/handler/ConfigHandler.hpp index 0c1f2777..1b6a43b8 100644 --- a/src/core/handler/ConfigHandler.hpp +++ b/src/core/handler/ConfigHandler.hpp @@ -163,7 +163,7 @@ namespace Qv2ray::core::handler // private slots: void OnKernelCrashed_p(const ConnectionGroupPair &id, const QString &errMessage); - void OnLatencyDataArrived_p(ConnectionId id, LatencyTestResult data); + void OnLatencyDataArrived_p(const ConnectionId &id, const LatencyTestResult &data); void OnStatsDataArrived_p(const ConnectionGroupPair &id, const QMap &data); protected: