diff --git a/.gitignore b/.gitignore index 11af80d0..0845592f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Some files +compile_commands.json .DS_Store *.pro.user *.user diff --git a/.gitmodules b/.gitmodules index 52fdd669..62a6f383 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,13 @@ [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 new file mode 160000 index 00000000..1ab9ea37 --- /dev/null +++ b/3rdparty/libuv @@ -0,0 +1 @@ +Subproject commit 1ab9ea3790378f9f25c4e78e9e2b511c75f9c9ed diff --git a/3rdparty/uvw b/3rdparty/uvw new file mode 160000 index 00000000..52785475 --- /dev/null +++ b/3rdparty/uvw @@ -0,0 +1 @@ +Subproject commit 52785475b9cb727f6912f8cc00ba65ff3bd44e67 diff --git a/CMakeLists.txt b/CMakeLists.txt index bc0a2bac..74602be1 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) + add_definitions(-DUNICODE -D_UNICODE -DNOMINMAX) 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,6 +139,7 @@ endif() # ================================================================================== # 3rdparty Sources # ================================================================================== +include(cmake/libuv.cmake) include(cmake/translations.cmake) include(cmake/qnodeeditor.cmake) if (ANDROID) @@ -234,6 +235,7 @@ target_link_libraries(qv2ray-baselib ${ZXING_LIBRARY} Threads::Threads ${QV2RAY_QT_LIBS} + ${LibUV_LIBRARIES} ) target_link_libraries(qv2ray @@ -257,6 +259,7 @@ 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 5de59a99..c9459623 100644 --- a/cmake/components/qv2ray-lib.cmake +++ b/cmake/components/qv2ray-lib.cmake @@ -37,6 +37,7 @@ 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 new file mode 100644 index 00000000..2f72119d --- /dev/null +++ b/cmake/libuv.cmake @@ -0,0 +1,35 @@ +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/makespec/BUILDVERSION b/makespec/BUILDVERSION index 33595ef7..2e2bad95 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5748 +5748 \ No newline at end of file diff --git a/src/components/latency/LatencyTest.cpp b/src/components/latency/LatencyTest.cpp index 6c60cc8c..f3a30aa0 100644 --- a/src/components/latency/LatencyTest.cpp +++ b/src/components/latency/LatencyTest.cpp @@ -1,50 +1,97 @@ +#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() { - for (const auto &thread : latencyThreads) - { - thread->terminate(); - } - latencyThreads.clear(); + latencyThread->stopLatencyTest(); + latencyThread->wait(); + latencyThread->start(); } + void LatencyTestHost::TestLatency(const ConnectionId &id, Qv2rayLatencyTestingMethod 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(); + latencyThread->pushRequest(id, totalTestCount, method); } - - void LatencyTestHost::OnLatencyThreadProcessCompleted() + void LatencyTestHost::TestLatency(const QList &ids, Qv2rayLatencyTestingMethod 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); + latencyThread->pushRequest(ids, totalTestCount, method); } } // namespace Qv2ray::components::latency diff --git a/src/components/latency/LatencyTest.hpp b/src/components/latency/LatencyTest.hpp index fea763b6..143208ae 100644 --- a/src/components/latency/LatencyTest.hpp +++ b/src/components/latency/LatencyTest.hpp @@ -1,6 +1,10 @@ #pragma once #include "base/Qv2rayBase.hpp" - +namespace uvw +{ + class Loop; +} +struct sockaddr_storage; namespace Qv2ray::components::latency { class LatencyTestThread; @@ -14,6 +18,16 @@ 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 { @@ -21,21 +35,22 @@ 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() - { - StopAllLatencyTest(); - } - signals: - void OnLatencyTestCompleted(const ConnectionId &id, const LatencyTestResult &data); - private slots: - void OnLatencyThreadProcessCompleted(); + ~LatencyTestHost() override; + + signals: + void OnLatencyTestCompleted(ConnectionId id, LatencyTestResult data); private: int totalTestCount; - QList latencyThreads; + // 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; }; } // 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 efc48ea0..d54dea10 100644 --- a/src/components/latency/LatencyTestThread.cpp +++ b/src/components/latency/LatencyTestThread.cpp @@ -1,84 +1,83 @@ #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(const QString &host, int port, Qv2rayLatencyTestingMethod method, int count, QObject *parent) - : QThread(parent) + LatencyTestThread::LatencyTestThread(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() { - resultData.avg = 0; - resultData.best = 0; - resultData.worst = 0; - switch (method) + loop = uvw::Loop::create(); + stopTimer = loop->resource(); + stopTimer->on([this](auto &, auto &handle) { + if (isStop) + { + 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) + { + switch (req.method) + { + 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; + } + } + } + requests.clear(); + } + }); + 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) { - case ICMPING: - { - icmping::ICMPPing pingHelper(30); - for (auto i = 0; i < count; i++) - { - resultData.totalCount++; - const auto value = pingHelper.ping(host); - const auto _latency = value.first; - const auto errMessage = value.second; - if (!errMessage.isEmpty()) - { - 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_ - } - } - 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; - } + 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 45593d53..e33584f2 100644 --- a/src/components/latency/LatencyTestThread.hpp +++ b/src/components/latency/LatencyTestThread.hpp @@ -2,27 +2,36 @@ #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(const QString &host, int port, Qv2rayLatencyTestingMethod, int count, QObject *parent = nullptr); - LatencyTestResult GetResult() const + explicit LatencyTestThread(QObject *parent = nullptr); + void stopLatencyTest() { - return resultData; + isStop = true; } + void pushRequest(const QList &ids, int totalTestCount, Qv2rayLatencyTestingMethod method); + void pushRequest(const ConnectionId &id, int totalTestCount, Qv2rayLatencyTestingMethod method); protected: void run() override; private: - LatencyTestResult resultData; - QString host; - int port; - int count; - Qv2rayLatencyTestingMethod method; + std::shared_ptr loop; + bool isStop = false; + std::shared_ptr stopTimer; + std::vector requests; + std::mutex m; // 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 789a192d..34b57d12 100644 --- a/src/components/latency/TCPing.cpp +++ b/src/components/latency/TCPing.cpp @@ -1,364 +1,60 @@ #include "TCPing.hpp" +#include "uvw.hpp" -#ifdef _WIN32 - #include - #include -#else - #include - #include - #include - #include - #include - #include -#endif namespace Qv2ray::components::latency::tcping { -#ifdef Q_OS_WIN - using qv_socket_t = SOCKET; - struct TranslateWSAError{ - ~TranslateWSAError(){ - errno = translate_sys_error(WSAGetLastError()); - } - 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) + void TCPing::start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost) { -#ifdef _WIN32 - ULONG block = 1; - if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR) - { - 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; + struct sockaddr_storage storage; + data.totalCount = req.totalCount; + data.failedCount = 0; data.worst = 0; - data.best = 0; - - while (currentCount < testCount) + data.avg = 0; + if (getSockAddress(loop, req.host.toStdString().data(), req.port, &storage, 0) != 0) { - 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++; + data.errorMessage = QObject::tr("DNS not resolved"); + data.avg = LATENCY_TEST_VALUE_ERROR; + testHost->OnLatencyTestCompleted(req.id, data); + return; + } + for (int i = 0; i < req.totalCount; ++i) + { + 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(); auto milliseconds = std::chrono::duration_cast(end - start); long ms = milliseconds.count(); data.avg += ms; -#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); + data.worst = std::max(data.worst, ms); + data.best = std::min(data.best, ms); + notifyTestHost(testHost, id); + h.clear(); + h.close(); + }); } - freeaddrinfo(resolved); - if (successCount > 0) + } + void TCPing::notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id) + { + if (data.failedCount + successCount == data.totalCount) { - data.avg = data.avg / successCount; + if (data.failedCount == data.totalCount) + data.avg = LATENCY_TEST_VALUE_ERROR; + else + data.errorMessage.clear(), data.avg = data.avg / successCount; + testHost->OnLatencyTestCompleted(id, data); } - 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 134c8df8..c9dfa45a 100644 --- a/src/components/latency/TCPing.hpp +++ b/src/components/latency/TCPing.hpp @@ -1,7 +1,18 @@ #pragma once #include "LatencyTest.hpp" #include "base/Qv2rayBase.hpp" +namespace uvw +{ + class Loop; +} namespace Qv2ray::components::latency::tcping { - LatencyTestResult TestTCPLatency(const QString &host, int port, int testCount); + 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; + }; } // namespace Qv2ray::components::latency::tcping diff --git a/src/components/latency/unix/ICMPPing.cpp b/src/components/latency/unix/ICMPPing.cpp index 7f899e37..86e7307c 100644 --- a/src/components/latency/unix/ICMPPing.cpp +++ b/src/components/latency/unix/ICMPPing.cpp @@ -7,16 +7,14 @@ #include #ifdef Q_OS_UNIX - #include - // - #include - #include #include - #include + #include //macos need that #include - #include #include #include + #include + #include "uvw.hpp" + #ifdef Q_OS_MAC #define SOL_IP 0 #endif @@ -59,8 +57,7 @@ namespace Qv2ray::components::latency::icmping { auto timeout_s = 5; // create socket - if ( //((sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) && - ((socketId = socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)) < 0)) + if (((socketId = socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP)) < 0)) { initErrorMessage = "EPING_SOCK: " + QObject::tr("Socket creation failed"); return; @@ -86,71 +83,127 @@ namespace Qv2ray::components::latency::icmping initialized = true; } - /// @return value < 0 on error, response time in ms on success - QPair ICMPPing::ping(const QString &address) + void ICMPPing::start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost) { if (!initialized) { - return { 0, initErrorMessage }; + data.errorMessage = initErrorMessage; + data.avg = LATENCY_TEST_VALUE_ERROR; + testHost->OnLatencyTestCompleted(req.id, data); + return; } - 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) + 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) { - 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) - { - 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") }; - } + data.errorMessage = QObject::tr("DNS not resolved"); + data.avg = LATENCY_TEST_VALUE_ERROR; + testHost->OnLatencyTestCompleted(req.id, data); + return; } - return { 0, "EPING_TIME: " + QObject::tr("Timeout") }; + 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 + { + 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); + } + } + 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/unix/ICMPPing.hpp b/src/components/latency/unix/ICMPPing.hpp index 987086fc..2aef41a7 100644 --- a/src/components/latency/unix/ICMPPing.hpp +++ b/src/components/latency/unix/ICMPPing.hpp @@ -1,11 +1,17 @@ #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 + class ICMPPing : public std::enable_shared_from_this { public: explicit ICMPPing(int ttl); @@ -13,7 +19,8 @@ namespace Qv2ray::components::latency::icmping { deinit(); } - QPair ping(const QString &address); + void start(std::shared_ptr loop, LatencyTestRequest &req, LatencyTestHost *testHost); + bool notifyTestHost(LatencyTestHost *testHost, const ConnectionId &id); private: void deinit(); @@ -22,6 +29,9 @@ 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 74342742..c1620b52 100644 --- a/src/components/latency/win/ICMPPing.cpp +++ b/src/components/latency/win/ICMPPing.cpp @@ -1,16 +1,12 @@ #include "ICMPPing.hpp" #ifdef Q_OS_WIN -// #include -// #include -// #include -// #include -// #include #include + namespace Qv2ray::components::latency::icmping { ICMPPing::ICMPPing(uint64_t timeout) @@ -83,5 +79,18 @@ 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 9b9b922b..2466048f 100644 --- a/src/components/latency/win/ICMPPing.hpp +++ b/src/components/latency/win/ICMPPing.hpp @@ -8,15 +8,20 @@ #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 + class ICMPPing : public std::enable_shared_from_this { public: ICMPPing(uint64_t timeout = DEFAULT_TIMEOUT); @@ -24,12 +29,16 @@ 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 new file mode 100644 index 00000000..e2007f64 --- /dev/null +++ b/src/components/latency/win/ICMPPingWork.cpp @@ -0,0 +1,35 @@ +#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 ef86feb6..6ca8be93 100644 --- a/src/core/handler/ConfigHandler.cpp +++ b/src/core/handler/ConfigHandler.cpp @@ -124,16 +124,18 @@ namespace Qv2ray::core::handler { for (const auto &connection : connections.keys()) { - StartLatencyTest(connection); + emit OnLatencyTestStarted(connection); } + tcpingHelper->TestLatency(connections.keys(), GlobalConfig.networkConfig.latencyTestingMethod); } void QvConfigHandler::StartLatencyTest(const GroupId &id) { for (const auto &connection : groups[id].connections) { - StartLatencyTest(connection); + emit OnLatencyTestStarted(connection); } + tcpingHelper->TestLatency(groups[id].connections, GlobalConfig.networkConfig.latencyTestingMethod); } void QvConfigHandler::StartLatencyTest(const ConnectionId &id) @@ -356,7 +358,6 @@ namespace Qv2ray::core::handler QvConfigHandler::~QvConfigHandler() { LOG(MODULE_CORE_HANDLER, "Triggering save settings from destructor") - tcpingHelper->StopAllLatencyTest(); delete kernelHandler; SaveConnectionConfig(); } @@ -367,7 +368,7 @@ namespace Qv2ray::core::handler return connectionRootCache.value(id); } - void QvConfigHandler::OnLatencyDataArrived_p(const ConnectionId &id, const LatencyTestResult &result) + void QvConfigHandler::OnLatencyDataArrived_p(ConnectionId id, 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 1b6a43b8..0c1f2777 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(const ConnectionId &id, const LatencyTestResult &data); + void OnLatencyDataArrived_p(ConnectionId id, LatencyTestResult data); void OnStatsDataArrived_p(const ConnectionGroupPair &id, const QMap &data); protected: