diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f9f4dee..f18213ad 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,41 @@ #################################################################################### cmake_minimum_required(VERSION 3.15) -project(turbo CXX) +# 设置 C++ 编译器目录 +set(CMAKE_CXX_COMPILER "/opt/compiler/gcc-12/bin/g++") +# 设置 C 编译器目录 +set(CMAKE_C_COMPILER "/opt/compiler/gcc-12/bin/gcc") + +project(turbo) + +include(CheckStructHasMember) +include(CheckSymbolExists) + +list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) +check_struct_has_member("struct mmsghdr" msg_hdr sys/socket.h HAVE_MMSG_HDR) +check_symbol_exists(sendmmsg sys/socket.h HAVE_SENDMMSG_API) +check_symbol_exists(recvmmsg sys/socket.h HAVE_RECVMMSG_API) + +if (HAVE_MMSG_HDR) + add_definitions(-DHAVE_MMSG_HDR) +endif () +if (HAVE_SENDMMSG_API) + add_definitions(-DHAVE_SENDMMSG_API) +endif () +if (HAVE_RECVMMSG_API) + add_definitions(-DHAVE_RECVMMSG_API) +endif () + +# check the socket buffer size set by the upper cmake project, if it is set, use the setting of the upper cmake project, otherwise set it to 256K +# if the socket buffer size is set to 0, it means that the socket buffer size is not set, and the kernel default value is used(just for linux) +if(DEFINED SOCKET_DEFAULT_BUF_SIZE) + if (SOCKET_DEFAULT_BUF_SIZE EQUAL 0) + message(STATUS "Socket default buffer size is not set, use the kernel default value") + else() + message(STATUS "Socket default buffer size is set to ${SOCKET_DEFAULT_BUF_SIZE}") + endif () + add_definitions(-DSOCKET_DEFAULT_BUF_SIZE=${SOCKET_DEFAULT_BUF_SIZE}) +endif() set(PROJECT_DESCRIPTION "Hercules is an ahead of time compiler for a subset of the Python language framework.") set(PROJECT_VERSION_MAJOR 0) @@ -29,7 +63,7 @@ set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT set(CARBIN_VERSION 0.2.0) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/carbin_cmake) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -################################################################# +#################################################################= # # modify the CARBIN_DEPS_PREFIX to you prefix if needed. # for example when you install you deps to "/opt/handsome/guy diff --git a/cmake/user_deps.cmake b/cmake/user_deps.cmake index 85fd5837..8e3d481f 100644 --- a/cmake/user_deps.cmake +++ b/cmake/user_deps.cmake @@ -34,15 +34,38 @@ find_package(Threads REQUIRED) # so you can and system pthread and rt, dl already add to # CARBIN_SYSTEM_DYLINK, using it for fun. ########################################################## +if (WIN32) + list(APPEND SUB_DIR_LIST "win32") + #防止Windows.h包含Winsock.h + #Prevent Windows.h from including Winsock.h + add_definitions(-DWIN32_LEAN_AND_MEAN) + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif () + +set(ENABLE_OPENSSL ON CACHE BOOL "enable openssl") +set(ASAN_USE_DELETE OFF CACHE BOOL "use delele[] or free when asan enabled") + +#查找openssl是否安装 +#Find out if openssl is installed +find_package(OpenSSL QUIET) +if (OPENSSL_FOUND AND ENABLE_OPENSSL) + message(STATUS "找到openssl库:\"${OPENSSL_INCLUDE_DIR}\",ENABLE_OPENSSL宏已打开") + include_directories(${OPENSSL_INCLUDE_DIR}) + add_definitions(-DENABLE_OPENSSL) +endif () + +#是否使用delete[]替代free,用于解决开启asan后在MacOS上的卡死问题 +#use delele[] or free when asan enabled +if(ASAN_USE_DELETE) + add_definitions(-DASAN_USE_DELETE) +endif() + set(CARBIN_DEPS_LINK #${TURBO_LIB} ${CARBIN_SYSTEM_DYLINK} + ${OPENSSL_LIBRARIES} + dl pthread ) list(REMOVE_DUPLICATES CARBIN_DEPS_LINK) -carbin_print_list_label("Denpendcies:" CARBIN_DEPS_LINK) - - - - - +carbin_print_list_label("Denpendcies:" CARBIN_DEPS_LINK) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 757e7884..331b95b6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -37,4 +37,5 @@ add_subdirectory(log) add_subdirectory(taskflow) add_subdirectory(system) add_subdirectory(status) -add_subdirectory(var) \ No newline at end of file +add_subdirectory(var) +add_subdirectory(network) \ No newline at end of file diff --git a/tests/network/CMakeLists.txt b/tests/network/CMakeLists.txt new file mode 100644 index 00000000..85fa27de --- /dev/null +++ b/tests/network/CMakeLists.txt @@ -0,0 +1,179 @@ +# +# Copyright 2023 The titan-search Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +carbin_cc_test( + NAME + lib_test + SOURCES + "test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + thread_pool_test + SOURCES + "thread_pool_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + udp_server_test + SOURCES + "udp_server_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + event_poller_test + SOURCES + "event_poller_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + tcp_client_test + SOURCES + "tcp_client_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + tcp_server_test + SOURCES + "tcp_server_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + timer_test + SOURCES + "timer_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + thread_pool_benchmark_test + SOURCES + "thread_pool_benchmark.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + network_resource_pool_test + SOURCES + "resource_pool_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + pipe_test + SOURCES + "pipe_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + dolaytask_test + SOURCES + "dolaytask_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + notice_center_test + SOURCES + "notice_center_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + ini_test + SOURCES + "ini_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + logger_test + SOURCES + "logger_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) + +carbin_cc_test( + NAME + create_test + SOURCES + "create_test.cc" + COPTS + ${CARBIN_CXX_OPTIONS} + DEPS + turbo::turbo +) \ No newline at end of file diff --git a/tests/network/create_test.cc b/tests/network/create_test.cc new file mode 100644 index 00000000..3213589d --- /dev/null +++ b/tests/network/create_test.cc @@ -0,0 +1,174 @@ + +#include +#include "turbo/network/util/logger.h" +using namespace std; +using namespace turbo; + +//测试onCreate和onDestory同时存在 +class TestA { +public: + TestA() { + TraceL; + } + + ~TestA() { + TraceL; + } + + void onCreate() { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__; + } + + void onDestory() { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__; + } +}; + +//测试只存在onCreate +class TestB { +public: + TestB() { + TraceL; + } + + ~TestB() { + TraceL; + } + + void onCreate() { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__; + } +}; + +//测试只存在onDestory +class TestC { +public: + TestC() { + TraceL; + } + + ~TestC() { + TraceL; + } + + void onDestory() { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__; + } +}; + +//测试onCreate和onDestory返回值不为void时 +class TestD { +public: + TestD() { + TraceL; + } + + ~TestD() { + TraceL; + } + + int onCreate() { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__; + return 1; + } + + std::string onDestory() { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__; + return "test"; + } +}; + +//测试onCreate和onDestory都不存在时 +class TestE { +public: + TestE() { + TraceL; + } + + ~TestE() { + TraceL; + } +}; + +//测试自定义构造函数 +class TestF { +public: + TestF(int a, const char *b) { + TraceL << a << " " << b; + } + + ~TestF() { + TraceL; + } +}; + +//测试自定义onCreate函数 +class TestH { +public: + TestH() { + TraceL; + } + + int onCreate(int a = 0, const char *b = nullptr) { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__ << " " << a << " " << b; + return 10; + } + + ~TestH() { + TraceL; + } +}; + +//测试onDestory函数抛异常 +class TestI { +public: + TestI() { + TraceL; + } + + int onDestory() { + TraceL << demangle(typeid(*this).name()) << "::" << __FUNCTION__; + throw std::runtime_error("TestI"); + } + + ~TestI() { + TraceL; + } +}; + +//测试自定义onDestory,onDestory将被忽略调用 +class TestJ { +public: + TestJ() { + TraceL; + } + + int onDestory(int a) { + return a; + } + + ~TestJ() { + TraceL; + } +}; + +int main() { + //初始化日志系统 + Logger::Instance().add(std::make_shared()); + Logger::Instance().setWriter(std::make_shared()); + + Creator::create(); + Creator::create(); + Creator::create(); + Creator::create(); + Creator::create(); + Creator::create(1, "hellow"); + { + auto h = Creator::create2(1, "hellow"); + TraceL << "invoke TestH onCreate ret:" << CLASS_FUNC_INVOKE(TestH, *h, Create, 1, "hellow"); + } + + Creator::create(); + Creator::create(); + return 0; +} diff --git a/tests/network/dolaytask_test.cc b/tests/network/dolaytask_test.cc new file mode 100644 index 00000000..bf6709f4 --- /dev/null +++ b/tests/network/dolaytask_test.cc @@ -0,0 +1,63 @@ + +#include +#include +#include "turbo/network/util/util.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/time_ticker.h" +#include "turbo/network/util/once_token.h" +#include "turbo/network/poller/event_poller.h" + +using namespace std; +using namespace turbo; + +int main() { + //设置日志 + Logger::Instance().add(std::make_shared()); + Logger::Instance().setWriter(std::make_shared()); + + Ticker ticker0; + int nextDelay0 = 50; + std::shared_ptr token0 = std::make_shared(nullptr,[](){ + TraceL << "task 0 被取消,可以立即触发释放lambad表达式捕获的变量!"; + }); + auto tag0 = EventPollerPool::Instance().getPoller()->doDelayTask(nextDelay0, [&,token0]() { + TraceL << "task 0(固定延时重复任务),预期休眠时间 :" << nextDelay0 << " 实际休眠时间" << ticker0.elapsedTime(); + ticker0.resetTime(); + return nextDelay0; + }); + token0 = nullptr; + + Ticker ticker1; + int nextDelay1 = 50; + auto tag1 = EventPollerPool::Instance().getPoller()->doDelayTask(nextDelay1, [&]() { + DebugL << "task 1(可变延时重复任务),预期休眠时间 :" << nextDelay1 << " 实际休眠时间" << ticker1.elapsedTime(); + ticker1.resetTime(); + nextDelay1 += 1; + return nextDelay1; + }); + + Ticker ticker2; + int nextDelay2 = 3000; + auto tag2 = EventPollerPool::Instance().getPoller()->doDelayTask(nextDelay2, [&]() { + InfoL << "task 2(单次延时任务),预期休眠时间 :" << nextDelay2 << " 实际休眠时间" << ticker2.elapsedTime(); + return 0; + }); + + Ticker ticker3; + int nextDelay3 = 50; + auto tag3 = EventPollerPool::Instance().getPoller()->doDelayTask(nextDelay3, [&]() -> uint64_t { + throw std::runtime_error("task 2(测试延时任务中抛异常,将导致不再继续该延时任务)"); + }); + + + sleep(2); + tag0->cancel(); + tag1->cancel(); + WarnL << "取消task 0、1"; + + //退出程序事件处理 + static semaphore sem; + signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 + sem.wait(); + return 0; +} diff --git a/tests/network/event_poller_test.cc b/tests/network/event_poller_test.cc new file mode 100644 index 00000000..c70bbe90 --- /dev/null +++ b/tests/network/event_poller_test.cc @@ -0,0 +1,53 @@ + +#include +#include +#include "turbo/network/util/util.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/time_ticker.h" +#include "turbo/network/poller/event_poller.h" + +using namespace std; +using namespace turbo; + +/** + * cpu负载均衡测试 + * @return + */ +int main() { + static bool exit_flag = false; + signal(SIGINT, [](int) { exit_flag = true; }); + //设置日志 + Logger::Instance().add(std::make_shared()); + + Ticker ticker; + while(!exit_flag){ + + if(ticker.elapsedTime() > 1000){ + auto vec = EventPollerPool::Instance().getExecutorLoad(); + _StrPrinter printer; + for(auto load : vec){ + printer << load << "-"; + } + DebugL << "cpu负载:" << printer; + + EventPollerPool::Instance().getExecutorDelay([](const vector &vec){ + _StrPrinter printer; + for(auto delay : vec){ + printer << delay << "-"; + } + DebugL << "cpu任务执行延时:" << printer; + }); + ticker.resetTime(); + } + + EventPollerPool::Instance().getExecutor()->async([](){ + auto usec = rand() % 4000; + //DebugL << usec; + usleep(usec); + }); + + usleep(2000); + } + + return 0; +} diff --git a/tests/network/ini_test.cc b/tests/network/ini_test.cc new file mode 100644 index 00000000..08c2952f --- /dev/null +++ b/tests/network/ini_test.cc @@ -0,0 +1,56 @@ + +#include +#include "turbo/network/util/logger.h" +#include "turbo/network/util/mini.h" +using namespace std; +using namespace turbo; + +int main() { + //初始化日志系统 + Logger::Instance().add(std::make_shared ()); + Logger::Instance().setWriter(std::make_shared()); + mINI ini; + ini[".dot"] = "dot-value"; + ini["no-dot"] = "no-dot-value"; + ini["no-key-filed."] = "no-key-value"; + ini["field0.multi.dot"] = "filed.multi.dot-value"; + + ini["field0.str"] = "value"; + ini["field0.int"] = 1; + ini["field0.bool"] = true; + + ini["field1.str"] = "value"; + ini["field1.int"] = 1; + ini["field1.bool"] = true; + + auto str = ini.dump(); + InfoL << "\n" << str; + + ini.clear(); + ini.parse(str); + for (auto &pr: ini) { + DebugL << pr.first << " = " << pr.second; + } + + auto ini_str = R"( + no—field=value + + [filed] + a-key + b-key= + c-key=test + ; comment0 + d-key = test + # comment1 + e-key = + =no-key + multi.dot=multi.dot.value + )"; + ini.clear(); + ini.parse(ini_str); + for (auto &pr: ini) { + TraceL << pr.first << " = " << pr.second; + } + + return 0; +} diff --git a/tests/network/logger_test.cc b/tests/network/logger_test.cc new file mode 100644 index 00000000..178505ac --- /dev/null +++ b/tests/network/logger_test.cc @@ -0,0 +1,84 @@ + +#include +#include "turbo/network/util/logger.h" +#include "turbo/network/network/socket.h" +using namespace std; +using namespace turbo; + +class TestLog +{ +public: + template + TestLog(const T &t){ + _ss << t; + }; + ~TestLog(){}; + + //通过此友元方法,可以打印自定义数据类型 + friend ostream& operator<<(ostream& out,const TestLog& obj){ + return out << obj._ss.str(); + } +private: + stringstream _ss; +}; + +int main() { + //初始化日志系统 + Logger::Instance().add(std::make_shared ()); + Logger::Instance().add(std::make_shared()); + Logger::Instance().setWriter(std::make_shared()); + + InfoL << "测试std::cout风格打印:"; + //ostream支持的数据类型都支持,可以通过友元的方式打印自定义类型数据 + TraceL << "object int"<< TestLog((int)1) << endl; + DebugL << "object short:"<; + + BufferRaw(size_t capacity = 0) { + if (capacity) { + setCapacity(capacity); + } + } + + BufferRaw(const char *data, size_t size = 0) { + assign(data, size); + } + +private: + size_t _size = 0; + size_t _capacity = 0; + char *_data = nullptr; + //对象个数统计 + ObjectStatistic _statistic; +}; + +class BufferLikeString : public Buffer { +public: + ~BufferLikeString() override = default; + + BufferLikeString() { + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString(std::string str) { + _str = std::move(str); + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString &operator=(std::string str) { + _str = std::move(str); + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + BufferLikeString(const char *str) { + _str = str; + _erase_head = 0; + _erase_tail = 0; + } + + BufferLikeString &operator=(const char *str) { + _str = str; + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + BufferLikeString(BufferLikeString &&that) { + _str = std::move(that._str); + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + that._erase_head = 0; + that._erase_tail = 0; + } + + BufferLikeString &operator=(BufferLikeString &&that) { + _str = std::move(that._str); + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + that._erase_head = 0; + that._erase_tail = 0; + return *this; + } + + BufferLikeString(const BufferLikeString &that) { + _str = that._str; + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + } + + BufferLikeString &operator=(const BufferLikeString &that) { + _str = that._str; + _erase_head = that._erase_head; + _erase_tail = that._erase_tail; + return *this; + } + + char *data() const override { + return (char *) _str.data() + _erase_head; + } + + size_t size() const override { + return _str.size() - _erase_tail - _erase_head; + } + + BufferLikeString &erase(size_t pos = 0, size_t n = std::string::npos) { + if (pos == 0) { + //移除前面的数据 + if (n != std::string::npos) { + //移除部分 + if (n > size()) { + //移除太多数据了 + throw std::out_of_range("BufferLikeString::erase out_of_range in head"); + } + //设置起始便宜量 + _erase_head += n; + data()[size()] = '\0'; + return *this; + } + //移除全部数据 + _erase_head = 0; + _erase_tail = _str.size(); + data()[0] = '\0'; + return *this; + } + + if (n == std::string::npos || pos + n >= size()) { + //移除末尾所有数据 + if (pos >= size()) { + //移除太多数据 + throw std::out_of_range("BufferLikeString::erase out_of_range in tail"); + } + _erase_tail += size() - pos; + data()[size()] = '\0'; + return *this; + } + + //移除中间的 + if (pos + n > size()) { + //超过长度限制 + throw std::out_of_range("BufferLikeString::erase out_of_range in middle"); + } + _str.erase(_erase_head + pos, n); + return *this; + } + + BufferLikeString &append(const BufferLikeString &str) { + return append(str.data(), str.size()); + } + + BufferLikeString &append(const std::string &str) { + return append(str.data(), str.size()); + } + + BufferLikeString &append(const char *data) { + return append(data, strlen(data)); + } + + BufferLikeString &append(const char *data, size_t len) { + if (len <= 0) { + return *this; + } + if (_erase_head > _str.capacity() / 2) { + moveData(); + } + if (_erase_tail == 0) { + _str.append(data, len); + return *this; + } + _str.insert(_erase_head + size(), data, len); + return *this; + } + + void push_back(char c) { + if (_erase_tail == 0) { + _str.push_back(c); + return; + } + data()[size()] = c; + --_erase_tail; + data()[size()] = '\0'; + } + + BufferLikeString &insert(size_t pos, const char *s, size_t n) { + _str.insert(_erase_head + pos, s, n); + return *this; + } + + BufferLikeString &assign(const char *data) { + return assign(data, strlen(data)); + } + + BufferLikeString &assign(const char *data, size_t len) { + if (len <= 0) { + return *this; + } + if (data >= _str.data() && data < _str.data() + _str.size()) { + _erase_head = data - _str.data(); + if (data + len > _str.data() + _str.size()) { + throw std::out_of_range("BufferLikeString::assign out_of_range"); + } + _erase_tail = _str.data() + _str.size() - (data + len); + return *this; + } + _str.assign(data, len); + _erase_head = 0; + _erase_tail = 0; + return *this; + } + + void clear() { + _erase_head = 0; + _erase_tail = 0; + _str.clear(); + } + + char &operator[](size_t pos) { + if (pos >= size()) { + throw std::out_of_range("BufferLikeString::operator[] out_of_range"); + } + return data()[pos]; + } + + const char &operator[](size_t pos) const { + return (*const_cast(this))[pos]; + } + + size_t capacity() const { + return _str.capacity(); + } + + void reserve(size_t size) { + _str.reserve(size); + } + + void resize(size_t size, char c = '\0') { + _str.resize(size, c); + _erase_head = 0; + _erase_tail = 0; + } + + bool empty() const { + return size() <= 0; + } + + std::string substr(size_t pos, size_t n = std::string::npos) const { + if (n == std::string::npos) { + //获取末尾所有的 + if (pos >= size()) { + throw std::out_of_range("BufferLikeString::substr out_of_range"); + } + return _str.substr(_erase_head + pos, size() - pos); + } + + //获取部分 + if (pos + n > size()) { + throw std::out_of_range("BufferLikeString::substr out_of_range"); + } + return _str.substr(_erase_head + pos, n); + } + +private: + void moveData() { + if (_erase_head) { + _str.erase(0, _erase_head); + _erase_head = 0; + } + } + +private: + size_t _erase_head; + size_t _erase_tail; + std::string _str; + //对象个数统计 + ObjectStatistic _statistic; +}; + +}//namespace turbo +#endif //TURBO_NETWORK_NETWORK_BUFFER_H \ No newline at end of file diff --git a/turbo/network/network/buffer_sock.cc b/turbo/network/network/buffer_sock.cc new file mode 100644 index 00000000..5c79fadf --- /dev/null +++ b/turbo/network/network/buffer_sock.cc @@ -0,0 +1,473 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include "turbo/network/network/buffer_sock.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/uv_errno.h" + +#if defined(__linux__) || defined(__linux) + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifndef MSG_WAITFORONE +#define MSG_WAITFORONE 0x10000 +#endif + +#ifndef HAVE_MMSG_HDR +struct mmsghdr { + struct msghdr msg_hdr; + unsigned msg_len; +}; +#endif + +#ifndef HAVE_SENDMMSG_API +#include +#include +static inline int sendmmsg(int fd, struct mmsghdr *mmsg, + unsigned vlen, unsigned flags) +{ + return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags); +} +#endif + +#ifndef HAVE_RECVMMSG_API +#include +#include +static inline int recvmmsg(int fd, struct mmsghdr *mmsg, + unsigned vlen, unsigned flags, struct timespec *timeout) +{ + return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout); +} +#endif + +#endif// defined(__linux__) || defined(__linux) + +namespace turbo { + +StatisticImp(BufferList) + +/////////////////////////////////////// BufferSock /////////////////////////////////////// + +BufferSock::BufferSock(Buffer::Ptr buffer, struct sockaddr *addr, int addr_len) { + if (addr) { + _addr_len = addr_len ? addr_len : SockUtil::get_sock_len(addr); + memcpy(&_addr, addr, _addr_len); + } + assert(buffer); + _buffer = std::move(buffer); +} + +char *BufferSock::data() const { + return _buffer->data(); +} + +size_t BufferSock::size() const { + return _buffer->size(); +} + +const struct sockaddr *BufferSock::sockaddr() const { + return (struct sockaddr *)&_addr; +} + +socklen_t BufferSock::socklen() const { + return _addr_len; +} + +/////////////////////////////////////// BufferCallBack /////////////////////////////////////// + +class BufferCallBack { +public: + BufferCallBack(List > list, BufferList::SendResult cb) + : _cb(std::move(cb)) + , _pkt_list(std::move(list)) {} + + ~BufferCallBack() { + sendCompleted(false); + } + + void sendCompleted(bool flag) { + if (_cb) { + //全部发送成功或失败回调 + while (!_pkt_list.empty()) { + _cb(_pkt_list.front().first, flag); + _pkt_list.pop_front(); + } + } else { + _pkt_list.clear(); + } + } + + void sendFrontSuccess() { + if (_cb) { + //发送成功回调 + _cb(_pkt_list.front().first, true); + } + _pkt_list.pop_front(); + } + +protected: + BufferList::SendResult _cb; + List > _pkt_list; +}; + +/////////////////////////////////////// BufferSendMsg /////////////////////////////////////// +#if defined(_WIN32) +using SocketBuf = WSABUF; +#else +using SocketBuf = iovec; +#endif + +class BufferSendMsg final : public BufferList, public BufferCallBack { +public: + using SocketBufVec = std::vector; + + BufferSendMsg(List > list, SendResult cb); + ~BufferSendMsg() override = default; + + bool empty() override; + size_t count() override; + ssize_t send(int fd, int flags) override; + +private: + void reOffset(size_t n); + ssize_t send_l(int fd, int flags); + +private: + size_t _iovec_off = 0; + size_t _remain_size = 0; + SocketBufVec _iovec; +}; + +bool BufferSendMsg::empty() { + return _remain_size == 0; +} + +size_t BufferSendMsg::count() { + return _iovec.size() - _iovec_off; +} + +ssize_t BufferSendMsg::send_l(int fd, int flags) { + ssize_t n; +#if !defined(_WIN32) + do { + struct msghdr msg; + msg.msg_name = nullptr; + msg.msg_namelen = 0; + msg.msg_iov = &(_iovec[_iovec_off]); + msg.msg_iovlen = _iovec.size() - _iovec_off; + if (msg.msg_iovlen > IOV_MAX) { + msg.msg_iovlen = IOV_MAX; + } + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = flags; + n = sendmsg(fd, &msg, flags); + } while (-1 == n && UV_EINTR == get_uv_error(true)); +#else + do { + DWORD sent = 0; + n = WSASend(fd, const_cast(&_iovec[0]), static_cast(_iovec.size()), &sent, static_cast(flags), 0, 0); + if (n == SOCKET_ERROR) {return -1;} + n = sent; + } while (n < 0 && UV_ECANCELED == get_uv_error(true)); +#endif + + if (n >= (ssize_t)_remain_size) { + //全部写完了 + _remain_size = 0; + sendCompleted(true); + return n; + } + + if (n > 0) { + //部分发送成功 + reOffset(n); + return n; + } + + //一个字节都未发送 + return n; +} + +ssize_t BufferSendMsg::send(int fd, int flags) { + auto remain_size = _remain_size; + while (_remain_size && send_l(fd, flags) != -1); + + ssize_t sent = remain_size - _remain_size; + if (sent > 0) { + //部分或全部发送成功 + return sent; + } + //一个字节都未发送成功 + return -1; +} + +void BufferSendMsg::reOffset(size_t n) { + _remain_size -= n; + size_t offset = 0; + for (auto i = _iovec_off; i != _iovec.size(); ++i) { + auto &ref = _iovec[i]; +#if !defined(_WIN32) + offset += ref.iov_len; +#else + offset += ref.len; +#endif + if (offset < n) { + //此包发送完毕 + sendFrontSuccess(); + continue; + } + _iovec_off = i; + if (offset == n) { + //这是末尾发送完毕的一个包 + ++_iovec_off; + sendFrontSuccess(); + break; + } + //这是末尾发送部分成功的一个包 + size_t remain = offset - n; +#if !defined(_WIN32) + ref.iov_base = (char *)ref.iov_base + ref.iov_len - remain; + ref.iov_len = remain; +#else + ref.buf = (CHAR *)ref.buf + ref.len - remain; + ref.len = remain; +#endif + break; + } +} + +BufferSendMsg::BufferSendMsg(List> list, SendResult cb) + : BufferCallBack(std::move(list), std::move(cb)) + , _iovec(_pkt_list.size()) { + auto it = _iovec.begin(); + _pkt_list.for_each([&](std::pair &pr) { +#if !defined(_WIN32) + it->iov_base = pr.first->data(); + it->iov_len = pr.first->size(); + _remain_size += it->iov_len; +#else + it->buf = pr.first->data(); + it->len = pr.first->size(); + _remain_size += it->len; +#endif + ++it; + }); +} + +/////////////////////////////////////// BufferSendTo /////////////////////////////////////// +class BufferSendTo final: public BufferList, public BufferCallBack { +public: + BufferSendTo(List > list, SendResult cb, bool is_udp); + ~BufferSendTo() override = default; + + bool empty() override; + size_t count() override; + ssize_t send(int fd, int flags) override; + +private: + bool _is_udp; + size_t _offset = 0; +}; + +BufferSendTo::BufferSendTo(List> list, BufferList::SendResult cb, bool is_udp) + : BufferCallBack(std::move(list), std::move(cb)) + , _is_udp(is_udp) {} + +bool BufferSendTo::empty() { + return _pkt_list.empty(); +} + +size_t BufferSendTo::count() { + return _pkt_list.size(); +} + +static inline BufferSock *getBufferSockPtr(std::pair &pr) { + if (!pr.second) { + return nullptr; + } + return static_cast(pr.first.get()); +} + +ssize_t BufferSendTo::send(int fd, int flags) { + size_t sent = 0; + ssize_t n; + while (!_pkt_list.empty()) { + auto &front = _pkt_list.front(); + auto &buffer = front.first; + if (_is_udp) { + auto ptr = getBufferSockPtr(front); + n = ::sendto(fd, buffer->data() + _offset, buffer->size() - _offset, flags, ptr ? ptr->sockaddr() : nullptr, ptr ? ptr->socklen() : 0); + } else { + n = ::send(fd, buffer->data() + _offset, buffer->size() - _offset, flags); + } + + if (n >= 0) { + assert(n); + _offset += n; + if (_offset == buffer->size()) { + sendFrontSuccess(); + _offset = 0; + } + sent += n; + continue; + } + + //n == -1的情况 + if (get_uv_error(true) == UV_EINTR) { + //被打断,需要继续发送 + continue; + } + //其他原因导致的send返回-1 + break; + } + return sent ? sent : -1; +} + +/////////////////////////////////////// BufferSendMmsg /////////////////////////////////////// + +#if defined(__linux__) || defined(__linux) + +class BufferSendMMsg : public BufferList, public BufferCallBack { +public: + BufferSendMMsg(List > list, SendResult cb); + ~BufferSendMMsg() override = default; + + bool empty() override; + size_t count() override; + ssize_t send(int fd, int flags) override; + +private: + void reOffset(size_t n); + ssize_t send_l(int fd, int flags); + +private: + size_t _remain_size = 0; + std::vector _iovec; + std::vector _hdrvec; +}; + +bool BufferSendMMsg::empty() { + return _remain_size == 0; +} + +size_t BufferSendMMsg::count() { + return _hdrvec.size(); +} + +ssize_t BufferSendMMsg::send_l(int fd, int flags) { + ssize_t n; + do { + n = sendmmsg(fd, &_hdrvec[0], _hdrvec.size(), flags); + } while (-1 == n && UV_EINTR == get_uv_error(true)); + + if (n > 0) { + //部分或全部发送成功 + reOffset(n); + return n; + } + + //一个字节都未发送 + return n; +} + +ssize_t BufferSendMMsg::send(int fd, int flags) { + auto remain_size = _remain_size; + while (_remain_size && send_l(fd, flags) != -1); + ssize_t sent = remain_size - _remain_size; + if (sent > 0) { + //部分或全部发送成功 + return sent; + } + //一个字节都未发送成功 + return -1; +} + +void BufferSendMMsg::reOffset(size_t n) { + for (auto it = _hdrvec.begin(); it != _hdrvec.end();) { + auto &hdr = *it; + auto &io = *(hdr.msg_hdr.msg_iov); + assert(hdr.msg_len <= io.iov_len); + _remain_size -= hdr.msg_len; + if (hdr.msg_len == io.iov_len) { + //这个udp包全部发送成功 + it = _hdrvec.erase(it); + sendFrontSuccess(); + continue; + } + //部分发送成功 + io.iov_base = (char *)io.iov_base + hdr.msg_len; + io.iov_len -= hdr.msg_len; + break; + } +} + +BufferSendMMsg::BufferSendMMsg(List> list, SendResult cb) + : BufferCallBack(std::move(list), std::move(cb)) + , _iovec(_pkt_list.size()) + , _hdrvec(_pkt_list.size()) { + auto i = 0U; + _pkt_list.for_each([&](std::pair &pr) { + auto &io = _iovec[i]; + io.iov_base = pr.first->data(); + io.iov_len = pr.first->size(); + _remain_size += io.iov_len; + + auto ptr = getBufferSockPtr(pr); + auto &mmsg = _hdrvec[i]; + auto &msg = mmsg.msg_hdr; + mmsg.msg_len = 0; + msg.msg_name = ptr ? (void *)ptr->sockaddr() : nullptr; + msg.msg_namelen = ptr ? ptr->socklen() : 0; + msg.msg_iov = &io; + msg.msg_iovlen = 1; + msg.msg_control = nullptr; + msg.msg_controllen = 0; + msg.msg_flags = 0; + ++i; + }); +} + +#endif //defined(__linux__) || defined(__linux) + + +BufferList::Ptr BufferList::create(List > list, SendResult cb, bool is_udp) { +#if defined(_WIN32) + if (is_udp) { + // sendto/send 方案,待优化 + return std::make_shared(std::move(list), std::move(cb), is_udp); + } + // WSASend方案 + return std::make_shared(std::move(list), std::move(cb)); +#elif defined(__linux__) || defined(__linux) + if (is_udp) { + // sendmmsg方案 + return std::make_shared(std::move(list), std::move(cb)); + } + // sendmsg方案 + return std::make_shared(std::move(list), std::move(cb)); +#else + if (is_udp) { + // sendto/send 方案, 可优化? + return std::make_shared(std::move(list), std::move(cb), is_udp); + } + // sendmsg方案 + return std::make_shared(std::move(list), std::move(cb)); +#endif +} + +} //turbo \ No newline at end of file diff --git a/turbo/network/network/buffer_sock.h b/turbo/network/network/buffer_sock.h new file mode 100644 index 00000000..dfa773b0 --- /dev/null +++ b/turbo/network/network/buffer_sock.h @@ -0,0 +1,79 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_BUFFER_SOCK_H_ +#define TURBO_NETWORK_NETWORK_BUFFER_SOCK_H_ + + +#if !defined(_WIN32) +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include "turbo/network/util/util.h" +#include "turbo/network/util/list.h" +#include "turbo/network/util/resource_pool.h" +#include "turbo/network/network/sock_util.h" +#include "turbo/network/network/buffer.h" + +namespace turbo { + +#if !defined(IOV_MAX) +#define IOV_MAX 1024 +#endif + +class BufferSock : public Buffer { +public: + using Ptr = std::shared_ptr; + BufferSock(Buffer::Ptr ptr, struct sockaddr *addr = nullptr, int addr_len = 0); + ~BufferSock() override = default; + + char *data() const override; + size_t size() const override; + const struct sockaddr *sockaddr() const; + socklen_t socklen() const; + +private: + int _addr_len = 0; + struct sockaddr_storage _addr; + Buffer::Ptr _buffer; +}; + +class BufferList : public noncopyable { +public: + using Ptr = std::shared_ptr; + using SendResult = std::function; + + BufferList() = default; + virtual ~BufferList() = default; + + virtual bool empty() = 0; + virtual size_t count() = 0; + virtual ssize_t send(int fd, int flags) = 0; + + static Ptr create(List > list, SendResult cb, bool is_udp); + +private: + //对象个数统计 + ObjectStatistic _statistic; +}; + +} +#endif //TURBO_NETWORK_NETWORK_BUFFER_SOCK_H_ + diff --git a/turbo/network/network/server.cc b/turbo/network/network/server.cc new file mode 100644 index 00000000..d107e639 --- /dev/null +++ b/turbo/network/network/server.cc @@ -0,0 +1,89 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/network/server.h" + + +using namespace std; + +namespace turbo { + +Server::Server(EventPoller::Ptr poller) { + _poller = poller ? std::move(poller) : EventPollerPool::Instance().getPoller(); +} + +//////////////////////////////////////////////////////////////////////////////////// + +SessionHelper::SessionHelper(const std::weak_ptr &server, Session::Ptr session, std::string cls) { + _server = server; + _session = std::move(session); + _cls = std::move(cls); + //记录session至全局的map,方便后面管理 + _session_map = SessionMap::Instance().shared_from_this(); + _identifier = _session->getIdentifier(); + _session_map->add(_identifier, _session); +} + +SessionHelper::~SessionHelper() { + if (!_server.lock()) { + //务必通知Session已从TcpServer脱离 + _session->onError(SockException(ErrCode::Err_other, "Server shutdown")); + } + //从全局map移除相关记录 + _session_map->del(_identifier); +} + +const Session::Ptr &SessionHelper::session() const { + return _session; +} + +const std::string &SessionHelper::className() const { + return _cls; +} + +//////////////////////////////////////////////////////////////////////////////////// + +bool SessionMap::add(const string &tag, const Session::Ptr &session) { + lock_guard lck(_mtx_session); + return _map_session.emplace(tag, session).second; +} + +bool SessionMap::del(const string &tag) { + lock_guard lck(_mtx_session); + return _map_session.erase(tag); +} + +Session::Ptr SessionMap::get(const string &tag) { + lock_guard lck(_mtx_session); + auto it = _map_session.find(tag); + if (it == _map_session.end()) { + return nullptr; + } + return it->second.lock(); +} + +void SessionMap::for_each_session(const function &cb) { + lock_guard lck(_mtx_session); + for (auto it = _map_session.begin(); it != _map_session.end();) { + auto session = it->second.lock(); + if (!session) { + it = _map_session.erase(it); + continue; + } + cb(it->first, session); + ++it; + } +} + +} // namespace turbo \ No newline at end of file diff --git a/turbo/network/network/server.h b/turbo/network/network/server.h new file mode 100644 index 00000000..74ae0f03 --- /dev/null +++ b/turbo/network/network/server.h @@ -0,0 +1,90 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_SERVER_H_ +#define TURBO_NETWORK_NETWORK_SERVER_H_ + +#include +#include "turbo/network/util/mini.h" +#include "turbo/network/network/session.h" + + +namespace turbo { + +// 全局的 Session 记录对象, 方便后面管理 +// 线程安全的 +class SessionMap : public std::enable_shared_from_this { +public: + friend class SessionHelper; + using Ptr = std::shared_ptr; + + //单例 + static SessionMap &Instance(); + ~SessionMap() = default; + + //获取Session + Session::Ptr get(const std::string &tag); + void for_each_session(const std::function &cb); + +private: + SessionMap() = default; + + //移除Session + bool del(const std::string &tag); + //添加Session + bool add(const std::string &tag, const Session::Ptr &session); + +private: + std::mutex _mtx_session; + std::unordered_map > _map_session; +}; + +class Server; + +class SessionHelper { +public: + bool enable = true; + + using Ptr = std::shared_ptr; + + SessionHelper(const std::weak_ptr &server, Session::Ptr session, std::string cls); + ~SessionHelper(); + + const Session::Ptr &session() const; + const std::string &className() const; + +private: + std::string _cls; + std::string _identifier; + Session::Ptr _session; + SessionMap::Ptr _session_map; + std::weak_ptr _server; +}; + +// server 基类, 暂时仅用于剥离 SessionHelper 对 TcpServer 的依赖 +// 后续将 TCP 与 UDP 服务通用部分加到这里. +class Server : public std::enable_shared_from_this, public mINI { +public: + using Ptr = std::shared_ptr; + + explicit Server(EventPoller::Ptr poller = nullptr); + virtual ~Server() = default; + +protected: + EventPoller::Ptr _poller; +}; + +} // namespace turbo + +#endif // TURBO_NETWORK_NETWORK_SERVER_H_ diff --git a/turbo/network/network/session.cc b/turbo/network/network/session.cc new file mode 100644 index 00000000..19329f66 --- /dev/null +++ b/turbo/network/network/session.cc @@ -0,0 +1,45 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include "turbo/network/network/session.h" + + +using namespace std; + +namespace turbo { + +class TcpSession : public Session {}; +class UdpSession : public Session {}; + +StatisticImp(UdpSession) +StatisticImp(TcpSession) + +Session::Session(const Socket::Ptr &sock) : SocketHelper(sock) { + if (sock->sockType() == SockNum::Sock_TCP) { + _statistic_tcp.reset(new ObjectStatistic); + } else { + _statistic_udp.reset(new ObjectStatistic); + } +} + +string Session::getIdentifier() const { + if (_id.empty()) { + static atomic s_session_index{0}; + _id = to_string(++s_session_index) + '-' + to_string(getSock()->rawFD()); + } + return _id; +} + +}// namespace turbo \ No newline at end of file diff --git a/turbo/network/network/session.h b/turbo/network/network/session.h new file mode 100644 index 00000000..0646237c --- /dev/null +++ b/turbo/network/network/session.h @@ -0,0 +1,91 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_SESSION_H_ +#define TURBO_NETWORK_NETWORK_SESSION_H_ + +#include +#include "turbo/network/network/socket.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/ssl_box.h" + +namespace turbo { + +// 会话, 用于存储一对客户端与服务端间的关系 +class Server; +class TcpSession; +class UdpSession; + +class Session : public SocketHelper { +public: + using Ptr = std::shared_ptr; + + Session(const Socket::Ptr &sock); + ~Session() override = default; + + /** + * 在创建 Session 后, Server 会把自身的配置参数通过该函数传递给 Session + * @param server, 服务器对象 + */ + virtual void attachServer(const Server &server) {} + + /** + * 作为该 Session 的唯一标识符 + * @return 唯一标识符 + */ + std::string getIdentifier() const override; + +private: + mutable std::string _id; + std::unique_ptr > _statistic_tcp; + std::unique_ptr > _statistic_udp; +}; + +// 通过该模板可以让TCP服务器快速支持TLS +template +class SessionWithSSL : public SessionType { +public: + template + SessionWithSSL(ArgsType &&...args) + : SessionType(std::forward(args)...) { + _ssl_box.setOnEncData([&](const Buffer::Ptr &buf) { public_send(buf); }); + _ssl_box.setOnDecData([&](const Buffer::Ptr &buf) { public_onRecv(buf); }); + } + + ~SessionWithSSL() override { _ssl_box.flush(); } + + void onRecv(const Buffer::Ptr &buf) override { _ssl_box.onRecv(buf); } + + // 添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug + inline void public_onRecv(const Buffer::Ptr &buf) { SessionType::onRecv(buf); } + inline void public_send(const Buffer::Ptr &buf) { SessionType::send(buf); } + + bool overSsl() const override { return true; } + +protected: + ssize_t send(Buffer::Ptr buf) override { + auto size = buf->size(); + _ssl_box.onSend(std::move(buf)); + return size; + } + +private: + SSL_Box _ssl_box; +}; + +}// namespace turbo + +#endif // TURBO_NETWORK_NETWORK_SESSION_H_ + + diff --git a/turbo/network/network/sock_util.cc b/turbo/network/network/sock_util.cc new file mode 100644 index 00000000..ffea1749 --- /dev/null +++ b/turbo/network/network/sock_util.cc @@ -0,0 +1,1179 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include +#include +#include +#include +#include "turbo/network/network/sock_util.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/uv_errno.h" +#include "turbo/network/util/once_token.h" +#if defined (__APPLE__) +#include +#include +#endif +using namespace std; + +namespace turbo { + +#if defined(_WIN32) +static onceToken g_token([]() { + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(wVersionRequested, &wsaData); +}, []() { + //不关闭socket,防止eventpoller线程无法退出 + //WSACleanup(); +}); +int ioctl(int fd, long cmd, u_long *ptr) { + return ioctlsocket(fd, cmd, ptr); +} +int close(int fd) { + return closesocket(fd); +} +#if (_WIN32_WINNT < _WIN32_WINNT_VISTA) +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { + struct sockaddr_storage ss; + unsigned long s = size; + + ZeroMemory(&ss, sizeof(ss)); + ss.ss_family = af; + + switch (af) { + case AF_INET: + ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; + break; + default: + return NULL; + } + /* cannot direclty use &size because of strict aliasing rules */ + return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0) ? dst : NULL; +} +int inet_pton(int af, const char *src, void *dst) { + struct sockaddr_storage ss; + int size = sizeof(ss); + char src_copy[INET6_ADDRSTRLEN + 1]; + + ZeroMemory(&ss, sizeof(ss)); + /* stupid non-const API */ + strncpy(src_copy, src, INET6_ADDRSTRLEN + 1); + src_copy[INET6_ADDRSTRLEN] = 0; + + if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) { + switch (af) { + case AF_INET: + *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; + return 1; + case AF_INET6: + *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; + return 1; + } + } + return 0; +} +#endif +#endif // defined(_WIN32) + +static inline string my_inet_ntop(int af, const void *addr) { + string ret; + ret.resize(128); + if (!inet_ntop(af, const_cast(addr), (char *) ret.data(), ret.size())) { + ret.clear(); + } else { + ret.resize(strlen(ret.data())); + } + return ret; +} + +static inline bool support_ipv6_l() { + auto fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + if (fd == -1) { + return false; + } + close(fd); + return true; +} + +bool SockUtil::support_ipv6() { + static auto flag = support_ipv6_l(); + return flag; +} + +string SockUtil::inet_ntoa(const struct in_addr &addr) { + return my_inet_ntop(AF_INET, &addr); +} + +std::string SockUtil::inet_ntoa(const struct in6_addr &addr) { + return my_inet_ntop(AF_INET6, &addr); +} + +std::string SockUtil::inet_ntoa(const struct sockaddr *addr) { + switch (addr->sa_family) { + case AF_INET: return SockUtil::inet_ntoa(((struct sockaddr_in *)addr)->sin_addr); + case AF_INET6: { + if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr)) { + struct in_addr addr4; + memcpy(&addr4, 12 + (char *)&(((struct sockaddr_in6 *)addr)->sin6_addr), 4); + return SockUtil::inet_ntoa(addr4); + } + return SockUtil::inet_ntoa(((struct sockaddr_in6 *)addr)->sin6_addr); + } + default: return ""; + } +} + +uint16_t SockUtil::inet_port(const struct sockaddr *addr) { + switch (addr->sa_family) { + case AF_INET: return ntohs(((struct sockaddr_in *)addr)->sin_port); + case AF_INET6: return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); + default: return 0; + } +} + +int SockUtil::setCloseWait(int fd, int second) { + linger m_sLinger; + //在调用closesocket()时还有数据未发送完,允许等待 + // 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭 + m_sLinger.l_onoff = (second > 0); + m_sLinger.l_linger = second; //设置等待时间为x秒 + int ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &m_sLinger, sizeof(linger)); + if (ret == -1) { +#ifndef _WIN32 + TraceL << "setsockopt SO_LINGER failed"; +#endif + } + return ret; +} + +int SockUtil::setNoDelay(int fd, bool on) { + int opt = on ? 1 : 0; + int ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt TCP_NODELAY failed"; + } + return ret; +} + +int SockUtil::setReuseable(int fd, bool on, bool reuse_port) { + int opt = on ? 1 : 0; + int ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_REUSEADDR failed"; + return ret; + } +#if defined(SO_REUSEPORT) + if (reuse_port) { + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_REUSEPORT failed"; + } + } +#endif + return ret; +} + +int SockUtil::setBroadcast(int fd, bool on) { + int opt = on ? 1 : 0; + int ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_BROADCAST failed"; + } + return ret; +} + +int SockUtil::setKeepAlive(int fd, bool on, int interval, int idle, int times) { + // Enable/disable the keep-alive option + int opt = on ? 1 : 0; + int ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, static_cast(sizeof(opt))); + if (ret == -1) { + TraceL << "setsockopt SO_KEEPALIVE failed"; + } +#if !defined(_WIN32) +#if !defined(SOL_TCP) && defined(IPPROTO_TCP) + #define SOL_TCP IPPROTO_TCP +#endif +#if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) + #define TCP_KEEPIDLE TCP_KEEPALIVE +#endif + // Set the keep-alive parameters + if (on && interval > 0 && ret != -1) { + ret = setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (char *) &idle, static_cast(sizeof(idle))); + if (ret == -1) { + TraceL << "setsockopt TCP_KEEPIDLE failed"; + } + ret = setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (char *) &interval, static_cast(sizeof(interval))); + if (ret == -1) { + TraceL << "setsockopt TCP_KEEPINTVL failed"; + } + ret = setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (char *) ×, static_cast(sizeof(times))); + if (ret == -1) { + TraceL << "setsockopt TCP_KEEPCNT failed"; + } + } +#endif + return ret; +} + +int SockUtil::setCloExec(int fd, bool on) { +#if !defined(_WIN32) + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + TraceL << "fcntl F_GETFD failed"; + return -1; + } + if (on) { + flags |= FD_CLOEXEC; + } else { + int cloexec = FD_CLOEXEC; + flags &= ~cloexec; + } + int ret = fcntl(fd, F_SETFD, flags); + if (ret == -1) { + TraceL << "fcntl F_SETFD failed"; + return -1; + } + return ret; +#else + return -1; +#endif +} + +int SockUtil::setNoSigpipe(int fd) { +#if defined(SO_NOSIGPIPE) + int set = 1; + auto ret = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (char *) &set, sizeof(int)); + if (ret == -1) { + TraceL << "setsockopt SO_NOSIGPIPE failed"; + } + return ret; +#else + return -1; +#endif +} + +int SockUtil::setNoBlocked(int fd, bool noblock) { +#if defined(_WIN32) + unsigned long ul = noblock; +#else + int ul = noblock; +#endif //defined(_WIN32) + int ret = ioctl(fd, FIONBIO, &ul); //设置为非阻塞模式 + if (ret == -1) { + TraceL << "ioctl FIONBIO failed"; + } + + return ret; +} + +int SockUtil::setRecvBuf(int fd, int size) { + if (size <= 0) { + // use the system default value + return 0; + } + int ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size)); + if (ret == -1) { + TraceL << "setsockopt SO_RCVBUF failed"; + } + return ret; +} + +int SockUtil::setSendBuf(int fd, int size) { + if (size <= 0) { + return 0; + } + int ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size)); + if (ret == -1) { + TraceL << "setsockopt SO_SNDBUF failed"; + } + return ret; +} + +class DnsCache { +public: + static DnsCache &Instance() { + static DnsCache instance; + return instance; + } + + bool getDomainIP(const char *host, sockaddr_storage &storage, int ai_family = AF_INET, + int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60) { + try { + storage = SockUtil::make_sockaddr(host, 0); + return true; + } catch (...) { + auto item = getCacheDomainIP(host, expire_sec); + if (!item) { + item = getSystemDomainIP(host); + if (item) { + setCacheDomainIP(host, item); + } + } + if (item) { + auto addr = getPerferredAddress(item.get(), ai_family, ai_socktype, ai_protocol); + memcpy(&storage, addr->ai_addr, addr->ai_addrlen); + } + return (bool)item; + } + } + +private: + class DnsItem { + public: + std::shared_ptr addr_info; + time_t create_time; + }; + + std::shared_ptr getCacheDomainIP(const char *host, int expireSec) { + lock_guard lck(_mtx); + auto it = _dns_cache.find(host); + if (it == _dns_cache.end()) { + //没有记录 + return nullptr; + } + if (it->second.create_time + expireSec < time(nullptr)) { + //已过期 + _dns_cache.erase(it); + return nullptr; + } + return it->second.addr_info; + } + + void setCacheDomainIP(const char *host, std::shared_ptr addr) { + lock_guard lck(_mtx); + DnsItem item; + item.addr_info = std::move(addr); + item.create_time = time(nullptr); + _dns_cache[host] = std::move(item); + } + + std::shared_ptr getSystemDomainIP(const char *host) { + struct addrinfo *answer = nullptr; + //阻塞式dns解析,可能被打断 + int ret = -1; + do { + ret = getaddrinfo(host, nullptr, nullptr, &answer); + } while (ret == -1 && get_uv_error(true) == UV_EINTR); + + if (!answer) { + WarnL << "getaddrinfo failed: " << host; + return nullptr; + } + return std::shared_ptr(answer, freeaddrinfo); + } + + struct addrinfo *getPerferredAddress(struct addrinfo *answer, int ai_family, int ai_socktype, int ai_protocol) { + auto ptr = answer; + while (ptr) { + if (ptr->ai_family == ai_family && ptr->ai_socktype == ai_socktype && ptr->ai_protocol == ai_protocol) { + return ptr; + } + ptr = ptr->ai_next; + } + return answer; + } + +private: + mutex _mtx; + unordered_map _dns_cache; +}; + +bool SockUtil::getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr, + int ai_family, int ai_socktype, int ai_protocol, int expire_sec) { + bool flag = DnsCache::Instance().getDomainIP(host, addr, ai_family, ai_socktype, ai_protocol, expire_sec); + if (flag) { + switch (addr.ss_family ) { + case AF_INET : ((sockaddr_in *) &addr)->sin_port = htons(port); break; + case AF_INET6 : ((sockaddr_in6 *) &addr)->sin6_port = htons(port); break; + default: flag = false; break; + } + } + return flag; +} + +static int set_ipv6_only(int fd, bool flag) { + int opt = flag; + int ret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof opt); + if (ret == -1) { + TraceL << "setsockopt IPV6_V6ONLY failed"; + } + return ret; +} + +static int bind_sock6(int fd, const char *ifr_ip, uint16_t port) { + set_ipv6_only(fd, false); + struct sockaddr_in6 addr; + bzero(&addr, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_port = htons(port); + if (1 != inet_pton(AF_INET6, ifr_ip, &(addr.sin6_addr))) { + if (!strcmp(ifr_ip, "0.0.0.0")) { + addr.sin6_addr = IN6ADDR_ANY_INIT; + } else if (!strcmp(ifr_ip, "127.0.0.1")) { + addr.sin6_addr = IN6ADDR_LOOPBACK_INIT; + } else { + WarnL << "inet_pton to ipv6 address failed: " << ifr_ip; + addr.sin6_addr = IN6ADDR_ANY_INIT; + } + } + if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + WarnL << "Bind socket failed: " << get_uv_errmsg(true); + return -1; + } + return 0; +} + +static int bind_sock4(int fd, const char *ifr_ip, uint16_t port) { + struct sockaddr_in addr; + bzero(&addr, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (1 != inet_pton(AF_INET, ifr_ip, &(addr.sin_addr))) { + if (!strcmp(ifr_ip, "::")) { + addr.sin_addr.s_addr = INADDR_ANY; + } else if (!strcmp(ifr_ip, "::1")) { + addr.sin_addr.s_addr = INADDR_LOOPBACK; + } else { + WarnL << "inet_pton to ipv4 address failed: " << ifr_ip; + addr.sin_addr.s_addr = INADDR_ANY; + } + } + if (::bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + WarnL << "Bind socket failed: " << get_uv_errmsg(true); + return -1; + } + return 0; +} + +static int bind_sock(int fd, const char *ifr_ip, uint16_t port, int family) { + switch (family) { + case AF_INET: return bind_sock4(fd, ifr_ip, port); + case AF_INET6: return bind_sock6(fd, ifr_ip, port); + default: return -1; + } +} + +static int bindTcpSock_l(int fd, int family, const char *local_ip, uint16_t local_port, bool re_use_port = true) { + if (fd == -1) { + family = SockUtil::support_ipv6() ? (SockUtil::is_ipv4(local_ip) ? AF_INET : AF_INET6) : AF_INET; + if ((fd = (int) socket(family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + WarnL << "创建套接字失败:" << get_uv_errmsg(true); + return -1; + } + } + + SockUtil::setReuseable(fd, re_use_port); + SockUtil::setNoSigpipe(fd); + SockUtil::setNoDelay(fd); + SockUtil::setSendBuf(fd); + SockUtil::setRecvBuf(fd); + SockUtil::setCloseWait(fd); + SockUtil::setCloExec(fd); + + if (bind_sock(fd, local_ip, local_port, family) == -1) { + close(fd); + return -1; + } + return fd; +} + +int SockUtil::bindTcpSock(uint16_t local_port, const char *local_ip, bool re_use_port) { + return bindTcpSock_l(-1, AF_INET, local_ip, local_port, re_use_port); +} + +static int connectFD_l(int sockfd, const char *host, uint16_t port, bool async, const char *local_ip ,uint16_t local_port) { + sockaddr_storage addr; + //优先使用ipv4地址 + if (!SockUtil::getDomainIP(host, port, addr, AF_INET, SOCK_STREAM, IPPROTO_TCP)) { + //dns解析失败 + if (sockfd != -1) { + close(sockfd); + } + return -1; + } + if (sockfd == -1) { + sockfd = (int) socket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) { + WarnL << "Create socket failed: " << host; + return -1; + } + bindTcpSock_l(sockfd, addr.ss_family, local_ip, local_port); + } + SockUtil::setNoBlocked(sockfd, async); + if (::connect(sockfd, (sockaddr *) &addr, SockUtil::get_sock_len((sockaddr *)&addr)) == 0) { + //同步连接成功 + return sockfd; + } + if (async && get_uv_error(true) == UV_EAGAIN) { + //异步连接成功 + return sockfd; + } + WarnL << "Connect socket to " << host << " " << port << " failed: " << get_uv_errmsg(true); + close(sockfd); + return -1; +} + +int SockUtil::connectFD(int fd, const char *host, uint16_t port, bool async) { + return connectFD_l(fd, host, port, async, nullptr, 0); +} + +int SockUtil::connect(const char *host, uint16_t port, bool async, const char *local_ip, uint16_t local_port) { + return connectFD_l(-1, host, port, async, local_ip, local_port); +} + +int SockUtil::listen(const uint16_t port, const char *local_ip, int back_log) { + int fd = -1; + int family = support_ipv6() ? (is_ipv4(local_ip) ? AF_INET : AF_INET6) : AF_INET; + if ((fd = (int)socket(family, SOCK_STREAM, IPPROTO_TCP)) == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return -1; + } + + setReuseable(fd, true, false); + setNoBlocked(fd); + setCloExec(fd); + + if (bind_sock(fd, local_ip, port, family) == -1) { + close(fd); + return -1; + } + + //开始监听 + if (::listen(fd, back_log) == -1) { + WarnL << "Listen socket failed: " << get_uv_errmsg(true); + close(fd); + return -1; + } + + return fd; +} + +int SockUtil::getSockError(int fd) { + int opt; + socklen_t optLen = static_cast(sizeof(opt)); + + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &opt, &optLen) < 0) { + return get_uv_error(true); + } else { + return uv_translate_posix_error(opt); + } +} + +using getsockname_type = decltype(getsockname); + +static bool get_socket_addr(int fd, struct sockaddr_storage &addr, getsockname_type func) { + socklen_t addr_len = sizeof(addr); + if (-1 == func(fd, (struct sockaddr *)&addr, &addr_len)) { + return false; + } + return true; +} + +bool SockUtil::get_sock_local_addr(int fd, struct sockaddr_storage &addr) { + return get_socket_addr(fd, addr, getsockname); +} + +bool SockUtil::get_sock_peer_addr(int fd, struct sockaddr_storage &addr) { + return get_socket_addr(fd, addr, getpeername); +} + +static string get_socket_ip(int fd, getsockname_type func) { + struct sockaddr_storage addr; + if (!get_socket_addr(fd, addr, func)) { + return ""; + } + return SockUtil::inet_ntoa((struct sockaddr *)&addr); +} + +static uint16_t get_socket_port(int fd, getsockname_type func) { + struct sockaddr_storage addr; + if (!get_socket_addr(fd, addr, func)) { + return 0; + } + return SockUtil::inet_port((struct sockaddr *)&addr); +} + +string SockUtil::get_local_ip(int fd) { + return get_socket_ip(fd, getsockname); +} + +string SockUtil::get_peer_ip(int fd) { + return get_socket_ip(fd, getpeername); +} + +uint16_t SockUtil::get_local_port(int fd) { + return get_socket_port(fd, getsockname); +} + +uint16_t SockUtil::get_peer_port(int fd) { + return get_socket_port(fd, getpeername); +} + +#if defined(__APPLE__) +template +void for_each_netAdapter_apple(FUN &&fun) { //type: struct ifaddrs * + struct ifaddrs *interfaces = nullptr; + struct ifaddrs *adapter = nullptr; + if (getifaddrs(&interfaces) == 0) { + adapter = interfaces; + while (adapter) { + if (adapter->ifa_addr->sa_family == AF_INET) { + if (fun(adapter)) { + break; + } + } + adapter = adapter->ifa_next; + } + freeifaddrs(interfaces); + } +} +#endif //defined(__APPLE__) + +#if defined(_WIN32) +template +void for_each_netAdapter_win32(FUN && fun) { //type: PIP_ADAPTER_INFO + unsigned long nSize = sizeof(IP_ADAPTER_INFO); + PIP_ADAPTER_INFO adapterList = (PIP_ADAPTER_INFO)new char[nSize]; + int nRet = GetAdaptersInfo(adapterList, &nSize); + if (ERROR_BUFFER_OVERFLOW == nRet) { + delete[] adapterList; + adapterList = (PIP_ADAPTER_INFO)new char[nSize]; + nRet = GetAdaptersInfo(adapterList, &nSize); + } + auto adapterPtr = adapterList; + while (adapterPtr && ERROR_SUCCESS == nRet) { + if (fun(adapterPtr)) { + break; + } + adapterPtr = adapterPtr->Next; + } + //释放内存空间 + delete[] adapterList; +} +#endif //defined(_WIN32) + +#if !defined(_WIN32) && !defined(__APPLE__) +template +void for_each_netAdapter_posix(FUN &&fun){ //type: struct ifreq * + struct ifconf ifconf; + char buf[1024 * 10]; + //初始化ifconf + ifconf.ifc_len = sizeof(buf); + ifconf.ifc_buf = buf; + int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return; + } + if (-1 == ioctl(sockfd, SIOCGIFCONF, &ifconf)) { //获取所有接口信息 + WarnL << "ioctl SIOCGIFCONF failed: " << get_uv_errmsg(true); + close(sockfd); + return; + } + close(sockfd); + //接下来一个一个的获取IP地址 + struct ifreq * adapter = (struct ifreq*) buf; + for (int i = (ifconf.ifc_len / sizeof(struct ifreq)); i > 0; --i,++adapter) { + if(fun(adapter)){ + break; + } + } +} +#endif //!defined(_WIN32) && !defined(__APPLE__) + +bool check_ip(string &address, const string &ip) { + if (ip != "127.0.0.1" && ip != "0.0.0.0") { + /*获取一个有效IP*/ + address = ip; + uint32_t addressInNetworkOrder = htonl(inet_addr(ip.data())); + if (/*(addressInNetworkOrder >= 0x0A000000 && addressInNetworkOrder < 0x0E000000) ||*/ + (addressInNetworkOrder >= 0xAC100000 && addressInNetworkOrder < 0xAC200000) || + (addressInNetworkOrder >= 0xC0A80000 && addressInNetworkOrder < 0xC0A90000)) { + //A类私有IP地址: + //10.0.0.0~10.255.255.255 + //B类私有IP地址: + //172.16.0.0~172.31.255.255 + //C类私有IP地址: + //192.168.0.0~192.168.255.255 + //如果是私有地址 说明在nat内部 + + /* 优先采用局域网地址,该地址很可能是wifi地址 + * 一般来说,无线路由器分配的地址段是BC类私有ip地址 + * 而A类地址多用于蜂窝移动网络 + */ + return true; + } + } + return false; +} + +string SockUtil::get_local_ip() { +#if defined(__APPLE__) + string address = "127.0.0.1"; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + string ip = SockUtil::inet_ntoa(adapter->ifa_addr); + if (strstr(adapter->ifa_name, "docker")) { + return false; + } + return check_ip(address, ip); + }); + return address; +#elif defined(_WIN32) + string address = "127.0.0.1"; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr) { + string ip = ipAddr->IpAddress.String; + if (strstr(adapter->AdapterName, "docker")) { + return false; + } + if(check_ip(address,ip)){ + return true; + } + ipAddr = ipAddr->Next; + } + return false; + }); + return address; +#else + string address = "127.0.0.1"; + for_each_netAdapter_posix([&](struct ifreq *adapter){ + string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + if (strstr(adapter->ifr_name, "docker")) { + return false; + } + return check_ip(address,ip); + }); + return address; +#endif +} + +vector > SockUtil::getInterfaceList() { + vector > ret; +#if defined(__APPLE__) + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + map obj; + obj["ip"] = SockUtil::inet_ntoa(adapter->ifa_addr); + obj["name"] = adapter->ifa_name; + ret.emplace_back(std::move(obj)); + return false; + }); +#elif defined(_WIN32) + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr) { + map obj; + obj["ip"] = ipAddr->IpAddress.String; + obj["name"] = adapter->AdapterName; + ret.emplace_back(std::move(obj)); + ipAddr = ipAddr->Next; + } + return false; + }); +#else + for_each_netAdapter_posix([&](struct ifreq *adapter){ + map obj; + obj["ip"] = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + obj["name"] = adapter->ifr_name; + ret.emplace_back(std::move(obj)); + return false; + }); +#endif + return ret; +} + +int SockUtil::bindUdpSock(const uint16_t port, const char *local_ip, bool enable_reuse) { + int fd = -1; + int family = support_ipv6() ? (is_ipv4(local_ip) ? AF_INET : AF_INET6) : AF_INET; + if ((fd = (int)socket(family, SOCK_DGRAM, IPPROTO_UDP)) == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return -1; + } + if (enable_reuse) { + setReuseable(fd); + } + setNoSigpipe(fd); + setNoBlocked(fd); + setSendBuf(fd); + setRecvBuf(fd); + setCloseWait(fd); + setCloExec(fd); + + if (bind_sock(fd, local_ip, port, family) == -1) { + close(fd); + return -1; + } + return fd; +} + +int SockUtil::dissolveUdpSock(int fd) { + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (-1 == getsockname(fd, (struct sockaddr *)&addr, &addr_len)) { + return -1; + } + addr.ss_family = AF_UNSPEC; + if (-1 == ::connect(fd, (struct sockaddr *)&addr, addr_len) && get_uv_error() != UV_EAFNOSUPPORT) { + // mac/ios时返回EAFNOSUPPORT错误 + WarnL << "Connect socket AF_UNSPEC failed: " << get_uv_errmsg(true); + return -1; + } + return 0; +} + +string SockUtil::get_ifr_ip(const char *if_name) { +#if defined(__APPLE__) + string ret; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + if (strcmp(adapter->ifa_name, if_name) == 0) { + ret = SockUtil::inet_ntoa(adapter->ifa_addr); + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr){ + if (strcmp(if_name,adapter->AdapterName) == 0){ + //ip匹配到了 + ret.assign(ipAddr->IpAddress.String); + return true; + } + ipAddr = ipAddr->Next; + } + return false; + }); + return ret; +#else + string ret; + for_each_netAdapter_posix([&](struct ifreq *adapter){ + if(strcmp(adapter->ifr_name,if_name) == 0) { + ret = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + return true; + } + return false; + }); + return ret; +#endif +} + +string SockUtil::get_ifr_name(const char *local_ip) { +#if defined(__APPLE__) + string ret = "en0"; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + string ip = SockUtil::inet_ntoa(adapter->ifa_addr); + if (ip == local_ip) { + ret = adapter->ifa_name; + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret = "en0"; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + while (ipAddr){ + if (strcmp(local_ip,ipAddr->IpAddress.String) == 0){ + //ip匹配到了 + ret.assign(adapter->AdapterName); + return true; + } + ipAddr = ipAddr->Next; + } + return false; + }); + return ret; +#else + string ret = "en0"; + for_each_netAdapter_posix([&](struct ifreq *adapter){ + string ip = SockUtil::inet_ntoa(&(adapter->ifr_addr)); + if(ip == local_ip) { + ret = adapter->ifr_name; + return true; + } + return false; + }); + return ret; +#endif +} + +string SockUtil::get_ifr_mask(const char *if_name) { +#if defined(__APPLE__) + string ret; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + if (strcmp(if_name, adapter->ifa_name) == 0) { + ret = SockUtil::inet_ntoa(adapter->ifa_netmask); + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + if (strcmp(if_name,adapter->AdapterName) == 0){ + //找到了该网卡 + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + //获取第一个ip的子网掩码 + ret.assign(ipAddr->IpMask.String); + return true; + } + return false; + }); + return ret; +#else + int fd; + struct ifreq ifr_mask; + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return ""; + } + memset(&ifr_mask, 0, sizeof(ifr_mask)); + strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1); + if ((ioctl(fd, SIOCGIFNETMASK, &ifr_mask)) < 0) { + WarnL << "ioctl SIOCGIFNETMASK on " << if_name << " failed: " << get_uv_errmsg(true); + close(fd); + return ""; + } + close(fd); + return SockUtil::inet_ntoa(&(ifr_mask.ifr_netmask)); +#endif // defined(_WIN32) +} + +string SockUtil::get_ifr_brdaddr(const char *if_name) { +#if defined(__APPLE__) + string ret; + for_each_netAdapter_apple([&](struct ifaddrs *adapter) { + if (strcmp(if_name, adapter->ifa_name) == 0) { + ret = SockUtil::inet_ntoa(adapter->ifa_broadaddr); + return true; + } + return false; + }); + return ret; +#elif defined(_WIN32) + string ret; + for_each_netAdapter_win32([&](PIP_ADAPTER_INFO adapter) { + if (strcmp(if_name, adapter->AdapterName) == 0) { + //找到该网卡 + IP_ADDR_STRING *ipAddr = &(adapter->IpAddressList); + in_addr broadcast; + broadcast.S_un.S_addr = (inet_addr(ipAddr->IpAddress.String) & inet_addr(ipAddr->IpMask.String)) | (~inet_addr(ipAddr->IpMask.String)); + ret = SockUtil::inet_ntoa(broadcast); + return true; + } + return false; + }); + return ret; +#else + int fd; + struct ifreq ifr_mask; + fd = socket( AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + WarnL << "Create socket failed: " << get_uv_errmsg(true); + return ""; + } + memset(&ifr_mask, 0, sizeof(ifr_mask)); + strncpy(ifr_mask.ifr_name, if_name, sizeof(ifr_mask.ifr_name) - 1); + if ((ioctl(fd, SIOCGIFBRDADDR, &ifr_mask)) < 0) { + WarnL << "ioctl SIOCGIFBRDADDR failed: " << get_uv_errmsg(true); + close(fd); + return ""; + } + close(fd); + return SockUtil::inet_ntoa(&(ifr_mask.ifr_broadaddr)); +#endif +} + +#define ip_addr_netcmp(addr1, addr2, mask) (((addr1) & (mask)) == ((addr2) & (mask))) + +bool SockUtil::in_same_lan(const char *myIp, const char *dstIp) { + string mask = get_ifr_mask(get_ifr_name(myIp).data()); + return ip_addr_netcmp(inet_addr(myIp), inet_addr(dstIp), inet_addr(mask.data())); +} + +static void clearMulticastAllSocketOption(int socket) { +#if defined(IP_MULTICAST_ALL) + // This option is defined in modern versions of Linux to overcome a bug in the Linux kernel's default behavior. + // When set to 0, it ensures that we receive only packets that were sent to the specified IP multicast address, + // even if some other process on the same system has joined a different multicast group with the same port number. + int multicastAll = 0; + (void)setsockopt(socket, IPPROTO_IP, IP_MULTICAST_ALL, (void*)&multicastAll, sizeof multicastAll); + // Ignore the call's result. Should it fail, we'll still receive packets (just perhaps more than intended) +#endif +} + +int SockUtil::setMultiTTL(int fd, uint8_t ttl) { + int ret = -1; +#if defined(IP_MULTICAST_TTL) + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttl, sizeof(ttl)); + if (ret == -1) { + TraceL << "setsockopt IP_MULTICAST_TTL failed"; + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::setMultiIF(int fd, const char *local_ip) { + int ret = -1; +#if defined(IP_MULTICAST_IF) + struct in_addr addr; + addr.s_addr = inet_addr(local_ip); + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addr, sizeof(addr)); + if (ret == -1) { + TraceL << "setsockopt IP_MULTICAST_IF failed"; + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::setMultiLOOP(int fd, bool accept) { + int ret = -1; +#if defined(IP_MULTICAST_LOOP) + uint8_t loop = accept; + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop, sizeof(loop)); + if (ret == -1) { + TraceL << "setsockopt IP_MULTICAST_LOOP failed"; + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::joinMultiAddr(int fd, const char *addr, const char *local_ip) { + int ret = -1; +#if defined(IP_ADD_MEMBERSHIP) + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = inet_addr(addr); + imr.imr_interface.s_addr = inet_addr(local_ip); + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq)); + if (ret == -1) { + TraceL << "setsockopt IP_ADD_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::leaveMultiAddr(int fd, const char *addr, const char *local_ip) { + int ret = -1; +#if defined(IP_DROP_MEMBERSHIP) + struct ip_mreq imr; + imr.imr_multiaddr.s_addr = inet_addr(addr); + imr.imr_interface.s_addr = inet_addr(local_ip); + ret = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq)); + if (ret == -1) { + TraceL << "setsockopt IP_DROP_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +template +static inline void write4Byte(A &&a, B &&b) { + memcpy(&a, &b, sizeof(a)); +} + +int SockUtil::joinMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) { + int ret = -1; +#if defined(IP_ADD_SOURCE_MEMBERSHIP) + struct ip_mreq_source imr; + + write4Byte(imr.imr_multiaddr, inet_addr(addr)); + write4Byte(imr.imr_sourceaddr, inet_addr(src_ip)); + write4Byte(imr.imr_interface, inet_addr(local_ip)); + + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source)); + if (ret == -1) { + TraceL << "setsockopt IP_ADD_SOURCE_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +int SockUtil::leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip) { + int ret = -1; +#if defined(IP_DROP_SOURCE_MEMBERSHIP) + struct ip_mreq_source imr; + + write4Byte(imr.imr_multiaddr, inet_addr(addr)); + write4Byte(imr.imr_sourceaddr, inet_addr(src_ip)); + write4Byte(imr.imr_interface, inet_addr(local_ip)); + + ret = setsockopt(fd, IPPROTO_IP, IP_DROP_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq_source)); + if (ret == -1) { + TraceL << "setsockopt IP_DROP_SOURCE_MEMBERSHIP failed: " << get_uv_errmsg(true); + } +#endif + clearMulticastAllSocketOption(fd); + return ret; +} + +bool SockUtil::is_ipv4(const char *host) { + struct in_addr addr; + return 1 == inet_pton(AF_INET, host, &addr); +} + +bool SockUtil::is_ipv6(const char *host) { + struct in6_addr addr; + return 1 == inet_pton(AF_INET6, host, &addr); +} + +socklen_t SockUtil::get_sock_len(const struct sockaddr *addr) { + switch (addr->sa_family) { + case AF_INET : return sizeof(sockaddr_in); + case AF_INET6 : return sizeof(sockaddr_in6); + default: return 0; + } +} + +struct sockaddr_storage SockUtil::make_sockaddr(const char *host, uint16_t port) { + struct sockaddr_storage storage; + bzero(&storage, sizeof(storage)); + + struct in_addr addr; + struct in6_addr addr6; + if (1 == inet_pton(AF_INET, host, &addr)) { + // host是ipv4 + reinterpret_cast(storage).sin_addr = addr; + reinterpret_cast(storage).sin_family = AF_INET; + reinterpret_cast(storage).sin_port = htons(port); + return storage; + } + if (1 == inet_pton(AF_INET6, host, &addr6)) { + // host是ipv6 + reinterpret_cast(storage).sin6_addr = addr6; + reinterpret_cast(storage).sin6_family = AF_INET6; + reinterpret_cast(storage).sin6_port = htons(port); + return storage; + } + throw std::invalid_argument(string("Not ip address: ") + host); +} + +} // namespace turbo diff --git a/turbo/network/network/sock_util.h b/turbo/network/network/sock_util.h new file mode 100644 index 00000000..e358eaab --- /dev/null +++ b/turbo/network/network/sock_util.h @@ -0,0 +1,367 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_SOCK_UTIL_H_ +#define TURBO_NETWORK_NETWORK_SOCK_UTIL_H_ + +#if defined(_WIN32) +#include +#include +#include +#pragma comment (lib, "Ws2_32.lib") +#pragma comment(lib,"Iphlpapi.lib") +#else +#include +#include +#include +#include +#include +#include +#include +#endif // defined(_WIN32) + +#include +#include +#include +#include +#include + +namespace turbo { + +#if defined(_WIN32) +#ifndef socklen_t +#define socklen_t int +#endif //!socklen_t +int ioctl(int fd, long cmd, u_long *ptr); +int close(int fd); +#endif // defined(_WIN32) + +#if !defined(SOCKET_DEFAULT_BUF_SIZE) +#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024) +#else +#if SOCKET_DEFAULT_BUF_SIZE == 0 && !defined(__linux__) +// just for linux, because in some high-throughput environments, +// kernel control is more efficient and reasonable than program +// settings. For example, refer to cloudflare's blog +#undef SOCKET_DEFAULT_BUF_SIZE +#define SOCKET_DEFAULT_BUF_SIZE (256 * 1024) +#endif +#endif +#define TCP_KEEPALIVE_INTERVAL 30 +#define TCP_KEEPALIVE_PROBE_TIMES 9 +#define TCP_KEEPALIVE_TIME 120 + +//套接字工具类,封装了socket、网络的一些基本操作 +class SockUtil { +public: + /** + * 创建tcp客户端套接字并连接服务器 + * @param host 服务器ip或域名 + * @param port 服务器端口号 + * @param async 是否异步连接 + * @param local_ip 绑定的本地网卡ip + * @param local_port 绑定的本地端口号 + * @return -1代表失败,其他为socket fd号 + */ + static int connect(const char *host, uint16_t port, bool async = true, const char *local_ip = "::", uint16_t local_port = 0); + static int connectFD(int fd, const char *host, uint16_t port, bool bAsync = true); + + /** + * 创建tcp监听套接字 + * @param port 监听的本地端口 + * @param local_ip 绑定的本地网卡ip + * @param back_log accept列队长度 + * @return -1代表失败,其他为socket fd号 + */ + static int listen(const uint16_t port, const char *local_ip = "::", int back_log = 1024); + + /** + * 创建并绑定tcp端口 + * @param localIp 绑定的本地网卡ip + * @param localPort 绑定的本地端口号 + * @return -1代表失败,其他为socket fd号 + */ + static int bindTcpSock(uint16_t localPort = 0, const char *localIp = "::", bool re_use_port = true); + + /** + * 创建udp套接字 + * @param port 监听的本地端口 + * @param local_ip 绑定的本地网卡ip + * @param enable_reuse 是否允许重复bind端口 + * @return -1代表失败,其他为socket fd号 + */ + static int bindUdpSock(const uint16_t port, const char *local_ip = "::", bool enable_reuse = true); + + /** + * @brief 解除与 sock 相关的绑定关系 + * @param sock, socket fd 号 + * @return 0 成功, -1 失败 + */ + static int dissolveUdpSock(int sock); + + /** + * 开启TCP_NODELAY,降低TCP交互延时 + * @param fd socket fd号 + * @param on 是否开启 + * @return 0代表成功,-1为失败 + */ + static int setNoDelay(int fd, bool on = true); + + /** + * 写socket不触发SIG_PIPE信号(貌似只有mac有效) + * @param fd socket fd号 + * @return 0代表成功,-1为失败 + */ + static int setNoSigpipe(int fd); + + /** + * 设置读写socket是否阻塞 + * @param fd socket fd号 + * @param noblock 是否阻塞 + * @return 0代表成功,-1为失败 + */ + static int setNoBlocked(int fd, bool noblock = true); + + /** + * 设置socket接收缓存,默认貌似8K左右,一般有设置上限 + * 可以通过配置内核配置文件调整 + * @param fd socket fd号 + * @param size 接收缓存大小 + * @return 0代表成功,-1为失败 + */ + static int setRecvBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE); + + /** + * 设置socket发送缓存,默认貌似8K左右,一般有设置上限 + * 可以通过配置内核配置文件调整 + * @param fd socket fd号 + * @param size 接收缓存大小 + * @return 0代表成功,-1为失败 + */ + static int setSendBuf(int fd, int size = SOCKET_DEFAULT_BUF_SIZE); + + /** + * 设置后续可绑定复用端口(处于TIME_WAITE状态) + * @param fd socket fd号 + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + */ + static int setReuseable(int fd, bool on = true, bool reuse_port = true); + + /** + * 运行发送或接收udp广播信息 + * @param fd socket fd号 + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + */ + static int setBroadcast(int fd, bool on = true); + + /** + * 是否开启TCP KeepAlive特性 + * @param fd socket fd号 + * @param on 是否开启该特性 + * @param idle keepalive空闲时间 + * @param interval keepalive探测时间间隔 + * @param times keepalive探测次数 + * @return 0代表成功,-1为失败 + */ + static int setKeepAlive(int fd, bool on = true, int interval = TCP_KEEPALIVE_INTERVAL, int idle = TCP_KEEPALIVE_TIME, int times = TCP_KEEPALIVE_PROBE_TIMES); + + /** + * 是否开启FD_CLOEXEC特性(多进程相关) + * @param fd fd号,不一定是socket + * @param on 是否开启该特性 + * @return 0代表成功,-1为失败 + */ + static int setCloExec(int fd, bool on = true); + + /** + * 开启SO_LINGER特性 + * @param sock socket fd号 + * @param second 内核等待关闭socket超时时间,单位秒 + * @return 0代表成功,-1为失败 + */ + static int setCloseWait(int sock, int second = 0); + + /** + * dns解析 + * @param host 域名或ip + * @param port 端口号 + * @param addr sockaddr结构体 + * @return 是否成功 + */ + static bool getDomainIP(const char *host, uint16_t port, struct sockaddr_storage &addr, int ai_family = AF_INET, + int ai_socktype = SOCK_STREAM, int ai_protocol = IPPROTO_TCP, int expire_sec = 60); + + /** + * 设置组播ttl + * @param sock socket fd号 + * @param ttl ttl值 + * @return 0代表成功,-1为失败 + */ + static int setMultiTTL(int sock, uint8_t ttl = 64); + + /** + * 设置组播发送网卡 + * @param sock socket fd号 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int setMultiIF(int sock, const char *local_ip); + + /** + * 设置是否接收本机发出的组播包 + * @param fd socket fd号 + * @param acc 是否接收 + * @return 0代表成功,-1为失败 + */ + static int setMultiLOOP(int fd, bool acc = false); + + /** + * 加入组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int joinMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0"); + + /** + * 退出组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int leaveMultiAddr(int fd, const char *addr, const char *local_ip = "0.0.0.0"); + + /** + * 加入组播并只接受该源端的组播数据 + * @param sock socket fd号 + * @param addr 组播地址 + * @param src_ip 数据源端地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int joinMultiAddrFilter(int sock, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0"); + + /** + * 退出组播 + * @param fd socket fd号 + * @param addr 组播地址 + * @param src_ip 数据源端地址 + * @param local_ip 本机网卡ip + * @return 0代表成功,-1为失败 + */ + static int leaveMultiAddrFilter(int fd, const char *addr, const char *src_ip, const char *local_ip = "0.0.0.0"); + + /** + * 获取该socket当前发生的错误 + * @param fd socket fd号 + * @return 错误代码 + */ + static int getSockError(int fd); + + /** + * 获取网卡列表 + * @return vector > + */ + static std::vector> getInterfaceList(); + + /** + * 获取本机默认网卡ip + */ + static std::string get_local_ip(); + + /** + * 获取该socket绑定的本地ip + * @param sock socket fd号 + */ + static std::string get_local_ip(int sock); + + /** + * 获取该socket绑定的本地端口 + * @param sock socket fd号 + */ + static uint16_t get_local_port(int sock); + + /** + * 获取该socket绑定的远端ip + * @param sock socket fd号 + */ + static std::string get_peer_ip(int sock); + + /** + * 获取该socket绑定的远端端口 + * @param sock socket fd号 + */ + static uint16_t get_peer_port(int sock); + + static bool support_ipv6(); + /** + * 线程安全的in_addr转ip字符串 + */ + static std::string inet_ntoa(const struct in_addr &addr); + static std::string inet_ntoa(const struct in6_addr &addr); + static std::string inet_ntoa(const struct sockaddr *addr); + static uint16_t inet_port(const struct sockaddr *addr); + static struct sockaddr_storage make_sockaddr(const char *ip, uint16_t port); + static socklen_t get_sock_len(const struct sockaddr *addr); + static bool get_sock_local_addr(int fd, struct sockaddr_storage &addr); + static bool get_sock_peer_addr(int fd, struct sockaddr_storage &addr); + + /** + * 获取网卡ip + * @param if_name 网卡名 + */ + static std::string get_ifr_ip(const char *if_name); + + /** + * 获取网卡名 + * @param local_op 网卡ip + */ + static std::string get_ifr_name(const char *local_op); + + /** + * 根据网卡名获取子网掩码 + * @param if_name 网卡名 + */ + static std::string get_ifr_mask(const char *if_name); + + /** + * 根据网卡名获取广播地址 + * @param if_name 网卡名 + */ + static std::string get_ifr_brdaddr(const char *if_name); + + /** + * 判断两个ip是否为同一网段 + * @param src_ip 我的ip + * @param dts_ip 对方ip + */ + static bool in_same_lan(const char *src_ip, const char *dts_ip); + + /** + * 判断是否为ipv4地址 + */ + static bool is_ipv4(const char *str); + + /** + * 判断是否为ipv6地址 + */ + static bool is_ipv6(const char *str); +}; + +} // namespace turbo +#endif // TURBO_NETWORK_NETWORK_SOCK_UTIL_H \ No newline at end of file diff --git a/turbo/network/network/socket.cc b/turbo/network/network/socket.cc new file mode 100644 index 00000000..15185c73 --- /dev/null +++ b/turbo/network/network/socket.cc @@ -0,0 +1,1124 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include "turbo/network/network/sock_util.h" +#include "turbo/network/network/socket.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/uv_errno.h" +#include "turbo/network/thread/semaphore.h" +#include "turbo/network/poller/event_poller.h" +#include "turbo/network/thread/work_pool.h" +using namespace std; + + +#define LOCK_GUARD(mtx) lock_guard lck(mtx) + +namespace turbo { + +StatisticImp(Socket) +INSTANCE_IMP(NetStatus) + +static SockException toSockException(int error) { + switch (error) { + case 0: + case UV_EAGAIN: return SockException(ErrCode::Err_success, "success"); + case UV_ECONNREFUSED: return SockException(ErrCode::Err_refused, uv_strerror(error), error); + case UV_ETIMEDOUT: return SockException(ErrCode::Err_timeout, uv_strerror(error), error); + case UV_ECONNRESET: return SockException(ErrCode::Err_reset, uv_strerror(error), error); + default: return SockException(ErrCode::Err_other, uv_strerror(error), error); + } +} + +static SockException getSockErr(int sock, bool try_errno = true) { + int error = 0, len = sizeof(int); + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, (socklen_t *)&len); + if (error == 0) { + if (try_errno) { + error = get_uv_error(true); + } + } else { + error = uv_translate_posix_error(error); + } + return toSockException(error); +} + +void NetStatus::sendSuccess(size_t size) { + _send_success += size; +} + +void NetStatus::sendFailed(size_t size) { + _send_failed += size; +} + +void NetStatus::received(size_t size) { + _received += size; +} + +uint64_t NetStatus::getSendSuccessBytes() const { + return _send_success; +} + +uint64_t NetStatus::getSendFailedBytes() const { + return _send_failed; +} + +uint64_t NetStatus::getReceivedBytes() const { + return _received; +} + +Socket::Ptr Socket::createSocket(const EventPoller::Ptr &poller_in, bool enable_mutex) { + auto poller = poller_in ? poller_in : EventPollerPool::Instance().getPoller(); + return Socket::Ptr(new Socket(poller, enable_mutex), [poller](Socket *ptr) { poller->async([ptr]() { delete ptr; }); }); +} + +Socket::Socket(EventPoller::Ptr poller, bool enable_mutex) + : _poller(std::move(poller)) + , _mtx_sock_fd(enable_mutex) + , _mtx_event(enable_mutex) + , _mtx_send_buf_waiting(enable_mutex) + , _mtx_send_buf_sending(enable_mutex) { + _status = NetStatus::Instance().shared_from_this(); + setOnRead(nullptr); + setOnErr(nullptr); + setOnAccept(nullptr); + setOnFlush(nullptr); + setOnBeforeAccept(nullptr); + setOnSendResult(nullptr); +} + +Socket::~Socket() { + closeSock(); +} + +void Socket::setOnRead(onReadCB cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_read = std::move(cb); + } else { + _on_read = [](const Buffer::Ptr &buf, struct sockaddr *, int) { WarnL << "Socket not set read callback, data ignored: " << buf->size(); }; + } +} + +void Socket::setOnErr(onErrCB cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_err = std::move(cb); + } else { + _on_err = [](const SockException &err) { WarnL << "Socket not set err callback, err: " << err; }; + } +} + +void Socket::setOnAccept(onAcceptCB cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_accept = std::move(cb); + } else { + _on_accept = [](Socket::Ptr &sock, shared_ptr &complete) { WarnL << "Socket not set accept callback, peer fd: " << sock->rawFD(); }; + } +} + +void Socket::setOnFlush(onFlush cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_flush = std::move(cb); + } else { + _on_flush = []() { return true; }; + } +} + +void Socket::setOnBeforeAccept(onCreateSocket cb) { + LOCK_GUARD(_mtx_event); + if (cb) { + _on_before_accept = std::move(cb); + } else { + _on_before_accept = [](const EventPoller::Ptr &poller) { return nullptr; }; + } +} + +void Socket::setOnSendResult(onSendResult cb) { + LOCK_GUARD(_mtx_event); + _send_result = std::move(cb); +} + +void Socket::connect(int fd, const string &url, uint16_t port, onErrCB con_cb, float timeout_sec) { + auto lam = [url, port, fd]() { + return SockUtil::connectFD(fd, url.data(), port, true); + }; + weak_ptr weak_self = shared_from_this(); + // 因为涉及到异步回调,所以在poller线程中执行确保线程安全 + _poller->async([=] { + if (auto strong_self = weak_self.lock()) { + connect_l(!isIP(url.data()), lam, con_cb, timeout_sec); + } + }); +} + +void Socket::connect(const string &url, uint16_t port, onErrCB con_cb, float timeout_sec, const string &local_ip, + uint16_t local_port) { + auto lam = [url, port, local_ip, local_port]() { + return SockUtil::connect(url.data(), port, true, local_ip.data(), local_port); + }; + weak_ptr weak_self = shared_from_this(); + // 因为涉及到异步回调,所以在poller线程中执行确保线程安全 + _poller->async([=] { + if (auto strong_self = weak_self.lock()) { + connect_l(!isIP(url.data()), lam, con_cb, timeout_sec); + } + }); +} + +void Socket::connect_l(bool async, const function &fd_cb, onErrCB con_cb_in, float timeout_sec){ + // 重置当前socket + closeSock(); + + auto ticker = std::make_shared(); + auto delays = std::make_shared > (); + weak_ptr weak_self = shared_from_this(); + auto con_cb = [con_cb_in, weak_self](const SockException &err) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + strong_self->_async_con_cb = nullptr; + strong_self->_con_timer = nullptr; + if (err) { + strong_self->setSock(nullptr); + } + con_cb_in(err); + }; + + auto async_con_cb = std::make_shared>([weak_self, con_cb, ticker, delays](const SockNum::Ptr &sock) { + auto strong_self = weak_self.lock(); + if (!sock || !strong_self) { + con_cb(SockException(ErrCode::Err_dns, get_uv_errmsg(true))); + return; + } + + // 监听该socket是否可写,可写表明已经连接服务器成功 + int result = strong_self->_poller->addEvent(sock->rawFd(), EventPoller::Event_Write | EventPoller::Event_Error, + [weak_self, sock, con_cb, ticker, delays](int event) { + + delays->emplace_back(ticker->elapsedTime()); + ticker->resetTime(); + + if (ticker->createdTime() > 100) { + TraceL << "work poller async delay:" << (*delays)[0] << "ms, " + << "dns delay:" << (*delays)[1] << "ms, " + << "event poller async delay:" << (*delays)[2] << "ms, " + << "tcp handshake delay:" << (*delays)[3] << "ms, " + << "total delay:" << ticker->createdTime() << "ms"; + } + if (auto strong_self = weak_self.lock()) { + strong_self->onConnected(sock, con_cb); + } + }); + + if (result == -1) { + con_cb(SockException(ErrCode::Err_other, std::string("add event to poller failed when start connect:") + get_uv_errmsg())); + } else { + // 先创建SockFD对象,防止SockNum由于未执行delEvent无法析构 + strong_self->setSock(sock); + } + }); + + // 连接超时定时器 + _con_timer = std::make_shared(timeout_sec,[weak_self, con_cb]() { + con_cb(SockException(ErrCode::Err_timeout, uv_strerror(UV_ETIMEDOUT))); + return false; + }, _poller); + + if (!async) { + //work poller async delay + delays->emplace_back(0); + //dns delay + delays->emplace_back(0); + //event poller async delay + delays->emplace_back(0); + auto fd = fd_cb(); + (*async_con_cb)(fd == -1 ? nullptr : std::make_shared(fd, SockNum::Sock_TCP)); + } else { + auto poller = _poller; + weak_ptr> weak_task = async_con_cb; + WorkThreadPool::Instance().getExecutor()->async([weak_task, poller, fd_cb, ticker, delays]() mutable { + delays->emplace_back(ticker->elapsedTime()); + ticker->resetTime(); + // 阻塞式dns解析放在后台线程执行 + int fd = fd_cb(); + auto sock = fd == -1 ? nullptr : std::make_shared(fd, SockNum::Sock_TCP); + + delays->emplace_back(ticker->elapsedTime()); + ticker->resetTime(); + + poller->async([sock, weak_task, ticker, delays]() { + delays->emplace_back(ticker->elapsedTime()); + ticker->resetTime(); + if (auto strong_task = weak_task.lock()) { + (*strong_task)(sock); + } + }); + }); + _async_con_cb = async_con_cb; + } +} + +void Socket::onConnected(const SockNum::Ptr &sock, const onErrCB &cb) { + auto err = getSockErr(sock->rawFd(), false); + if (err) { + // 连接失败 + cb(err); + return; + } + + // 更新地址信息 + setSock(sock); + // 先删除之前的可写事件监听 + _poller->delEvent(sock->rawFd(), [sock](bool) {}); + if (!attachEvent(sock)) { + // 连接失败 + cb(SockException(ErrCode::Err_other, "add event to poller failed when connected")); + return; + } + + { + LOCK_GUARD(_mtx_sock_fd); + if (_sock_fd) { + _sock_fd->setConnected(); + } + } + // 连接成功 + cb(err); +} + +bool Socket::attachEvent(const SockNum::Ptr &sock) { + weak_ptr weak_self = shared_from_this(); + if (sock->type() == SockNum::Sock_TCP_Server) { + // tcp服务器 + auto result = _poller->addEvent(sock->rawFd(), EventPoller::Event_Read | EventPoller::Event_Error, [weak_self, sock](int event) { + if (auto strong_self = weak_self.lock()) { + strong_self->onAccept(sock, event); + } + }); + return -1 != result; + } + + // tcp客户端或udp + auto read_buffer = _poller->getSharedBuffer(); + auto result = _poller->addEvent(sock->rawFd(), EventPoller::Event_Read | EventPoller::Event_Error | EventPoller::Event_Write, [weak_self, sock, read_buffer](int event) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + + if (event & EventPoller::Event_Read) { + strong_self->onRead(sock, read_buffer); + } + if (event & EventPoller::Event_Write) { + strong_self->onWriteAble(sock); + } + if (event & EventPoller::Event_Error) { + strong_self->emitErr(getSockErr(sock->rawFd())); + } + }); + + return -1 != result; +} + +ssize_t Socket::onRead(const SockNum::Ptr &sock, const BufferRaw::Ptr &buffer) noexcept { + ssize_t ret = 0, nread = 0; + auto data = buffer->data(); + // 最后一个字节设置为'\0' + auto capacity = buffer->getCapacity() - 1; + + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + // 边缘触发方式,读事件来,就一直读完所有数据 + while (_enable_recv) { + // 接收中断的话,会继续读取 + do { + nread = recvfrom(sock->rawFd(), data, capacity, 0, (struct sockaddr *)&addr, &len); + } while (-1 == nread && UV_EINTR == get_uv_error(true)); + + if (nread == 0) { + if (sock->type() == SockNum::Sock_TCP) { + emitErr(SockException(ErrCode::Err_eof, "end of file")); + } else { + WarnL << "Recv eof on udp socket[" << sock->rawFd() << "]"; + } + return ret; + } + + if (nread == -1) { + auto err = get_uv_error(true); + if (err != UV_EAGAIN) { + if (sock->type() == SockNum::Sock_TCP) { + // 客户端断开连接时,socket会变为可读,但是会读到eof + // 或者fd发生其他错误,会上报错误,并close关闭fd + emitErr(toSockException(err)); + } else { + WarnL << "Recv err on udp socket[" << sock->rawFd() << "]: " << uv_strerror(err); + } + } + return ret; + } + + if (_enable_speed) { + // 更新接收速率 + _recv_speed += nread; + } + _status->received(nread); + ret += nread; + data[nread] = '\0'; + // 设置buffer有效数据大小 + buffer->setSize(nread); + + // 触发回调 + LOCK_GUARD(_mtx_event); + try { + // 此处捕获异常,目的是防止数据未读尽,epoll边沿触发失效的问题 + _on_read(buffer, (struct sockaddr *)&addr, len); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_read: " << ex.what(); + } + } + return 0; +} + +bool Socket::emitErr(const SockException &err) noexcept { + if (_err_emit) { + return true; + } + _err_emit = true; + weak_ptr weak_self = shared_from_this(); + _poller->async([weak_self, err]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + LOCK_GUARD(strong_self->_mtx_event); + try { + strong_self->_on_err(err); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_err: " << ex.what(); + } + // 延后关闭socket,只移除其io事件,防止Session对象析构时获取fd相关信息失败 + strong_self->closeSock(false); + }); + return true; +} + +ssize_t Socket::send(const char *buf, size_t size, struct sockaddr *addr, socklen_t addr_len, bool try_flush) { + if (size <= 0) { + size = strlen(buf); + if (!size) { + return 0; + } + } + auto ptr = BufferRaw::create(); + ptr->assign(buf, size); + return send(std::move(ptr), addr, addr_len, try_flush); +} + +ssize_t Socket::send(string buf, struct sockaddr *addr, socklen_t addr_len, bool try_flush) { + return send(std::make_shared(std::move(buf)), addr, addr_len, try_flush); +} + +ssize_t Socket::send(Buffer::Ptr buf, struct sockaddr *addr, socklen_t addr_len, bool try_flush) { + if (!addr) { + if (!_udp_send_dst) { + return send_l(std::move(buf), false, try_flush); + } + // 本次发送未指定目标地址,但是目标定制已通过bindPeerAddr指定 + addr = (struct sockaddr *)_udp_send_dst.get(); + addr_len = SockUtil::get_sock_len(addr); + } + return send_l(std::make_shared(std::move(buf), addr, addr_len), true, try_flush); +} + +ssize_t Socket::send_l(Buffer::Ptr buf, bool is_buf_sock, bool try_flush) { + auto size = buf ? buf->size() : 0; + if (!size) { + return 0; + } + + { + LOCK_GUARD(_mtx_send_buf_waiting); + _send_buf_waiting.emplace_back(std::move(buf), is_buf_sock); + } + + if (try_flush) { + if (flushAll()) { + return -1; + } + } + + return size; +} + +int Socket::flushAll() { + LOCK_GUARD(_mtx_sock_fd); + + if (!_sock_fd) { + // 如果已断开连接或者发送超时 + return -1; + } + if (_sendable) { + // 该socket可写 + return flushData(_sock_fd->sockNum(), false) ? 0 : -1; + } + + // 该socket不可写,判断发送超时 + if (_send_flush_ticker.elapsedTime() > _max_send_buffer_ms) { + // 如果发送列队中最老的数据距今超过超时时间限制,那么就断开socket连接 + emitErr(SockException(ErrCode::Err_other, "socket send timeout, remain data count:" + to_string(getSendBufferCount()))); + return -1; + } + return 0; +} + +void Socket::onFlushed() { + bool flag; + { + LOCK_GUARD(_mtx_event); + flag = _on_flush(); + } + if (!flag) { + setOnFlush(nullptr); + } +} + +void Socket::closeSock(bool close_fd) { + _sendable = true; + _enable_recv = true; + _enable_speed = false; + _con_timer = nullptr; + _async_con_cb = nullptr; + _send_flush_ticker.resetTime(); + + { + LOCK_GUARD(_mtx_send_buf_waiting); + _send_buf_waiting.clear(); + } + + { + LOCK_GUARD(_mtx_send_buf_sending); + _send_buf_sending.clear(); + } + + { + LOCK_GUARD(_mtx_sock_fd); + if (close_fd) { + _err_emit = false; + _sock_fd = nullptr; + } else if (_sock_fd) { + _sock_fd->delEvent(); + } + } +} + +size_t Socket::getSendBufferCount() { + size_t ret = 0; + { + LOCK_GUARD(_mtx_send_buf_waiting); + ret += _send_buf_waiting.size(); + } + + { + LOCK_GUARD(_mtx_send_buf_sending); + _send_buf_sending.for_each([&](BufferList::Ptr &buf) { ret += buf->count(); }); + } + return ret; +} + +uint64_t Socket::elapsedTimeAfterFlushed() { + return _send_flush_ticker.elapsedTime(); +} + +int Socket::getRecvSpeed() { + _enable_speed = true; + return _recv_speed.getSpeed(); +} + +int Socket::getSendSpeed() { + _enable_speed = true; + return _send_speed.getSpeed(); +} + +bool Socket::listen(uint16_t port, const string &local_ip, int backlog) { + closeSock(); + int fd = SockUtil::listen(port, local_ip.data(), backlog); + if (fd == -1) { + return false; + } + return fromSock_l(std::make_shared(fd, SockNum::Sock_TCP_Server)); +} + +bool Socket::bindUdpSock(uint16_t port, const string &local_ip, bool enable_reuse) { + closeSock(); + int fd = SockUtil::bindUdpSock(port, local_ip.data(), enable_reuse); + if (fd == -1) { + return false; + } + return fromSock_l(std::make_shared(fd, SockNum::Sock_UDP)); +} + +bool Socket::fromSock(int fd, SockNum::SockType type) { + closeSock(); + SockUtil::setNoSigpipe(fd); + SockUtil::setNoBlocked(fd); + SockUtil::setCloExec(fd); + return fromSock_l(std::make_shared(fd, type)); +} + +bool Socket::fromSock_l(SockNum::Ptr sock) { + if (!attachEvent(sock)) { + return false; + } + setSock(std::move(sock)); + return true; +} + +int Socket::onAccept(const SockNum::Ptr &sock, int event) noexcept { + int fd; + struct sockaddr_storage peer_addr; + socklen_t addr_len = sizeof(peer_addr); + while (true) { + if (event & EventPoller::Event_Read) { + // 非阻塞套接字,accept时防止被中断信号打断,打断时需要重新accept + do { + fd = (int)accept(sock->rawFd(), (struct sockaddr *)&peer_addr, &addr_len); + } while (-1 == fd && UV_EINTR == get_uv_error(true)); + + if (fd == -1) { + // accept失败 + int err = get_uv_error(true); + if (err == UV_EAGAIN) { + // 没有新连接 + return 0; + } + auto ex = toSockException(err); + // emitErr(ex); https://github.com/ZLMediaKit/ZLMediaKit/issues/2946 + ErrorL << "Accept socket failed: " << ex.what(); + // 可能打开的文件描述符太多了:UV_EMFILE/UV_ENFILE +#if defined(HAS_EPOLL) && !defined(_WIN32) + // 边缘触发,还需要手动再触发accept事件, + //wepoll, Edge-triggered (`EPOLLET`) mode isn't supported. + std::weak_ptr weak_self = shared_from_this(); + _poller->doDelayTask(100, [weak_self, sock]() { + if (auto strong_self = weak_self.lock()) { + // 100ms后再处理accept事件,说不定已经有空闲的fd + strong_self->onAccept(sock, EventPoller::Event_Read); + } + return 0; + }); + // 暂时不处理accept事件,等待100ms后手动触发onAccept(只有EAGAIN读空后才能通过epoll再次触发事件) + return -1; +#else + // 水平触发;休眠10ms,防止无谓的accept失败 + this_thread::sleep_for(std::chrono::milliseconds(10)); + // 暂时不处理accept事件,由于是水平触发,下次还会再次自动进入onAccept函数 + return -1; +#endif + } + + SockUtil::setNoSigpipe(fd); + SockUtil::setNoBlocked(fd); + SockUtil::setNoDelay(fd); + SockUtil::setSendBuf(fd); + SockUtil::setRecvBuf(fd); + SockUtil::setCloseWait(fd); + SockUtil::setCloExec(fd); + + Socket::Ptr peer_sock; + try { + // 此处捕获异常,目的是防止socket未accept尽,epoll边沿触发失效的问题 + LOCK_GUARD(_mtx_event); + // 拦截Socket对象的构造 + peer_sock = _on_before_accept(_poller); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_before_accept: " << ex.what(); + close(fd); + continue; + } + + if (!peer_sock) { + // 此处是默认构造行为,也就是子Socket共用父Socket的poll线程并且关闭互斥锁 + // 如果共用poller线程了,就不会存在多线程安全问题,所以不需要锁 + // 所有操作都是在一个poller线程里执行的 + peer_sock = Socket::createSocket(_poller, false); + } + + auto sock = std::make_shared(fd, SockNum::Sock_TCP); + // 设置好fd,以备在onAccept事件中可以正常访问该fd + peer_sock->setSock(sock); + // 赋值peer ip,防止在执行setSock时,fd已经被reset断开 + memcpy(&peer_sock->_peer_addr, &peer_addr, addr_len); + + shared_ptr completed(nullptr, [peer_sock, sock](void *) { + try { + // 然后把该fd加入poll监听(确保先触发onAccept事件然后再触发onRead等事件) + if (!peer_sock->attachEvent(sock)) { + // 加入poll监听失败,触发onErr事件,通知该Socket无效 + peer_sock->emitErr(SockException(ErrCode::Err_eof, "add event to poller failed when accept a socket")); + } + } catch (std::exception &ex) { + ErrorL << "Exception occurred: " << ex.what(); + } + }); + + try { + // 此处捕获异常,目的是防止socket未accept尽,epoll边沿触发失效的问题 + LOCK_GUARD(_mtx_event); + // 先触发onAccept事件,此时应该监听该Socket的onRead等事件 + _on_accept(peer_sock, completed); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when emit on_accept: " << ex.what(); + continue; + } + } + + if (event & EventPoller::Event_Error) { + auto ex = getSockErr(sock->rawFd()); + emitErr(ex); + ErrorL << "TCP listener occurred a err: " << ex.what(); + return -1; + } + } +} + +void Socket::setSock(SockNum::Ptr sock) { + LOCK_GUARD(_mtx_sock_fd); + if (sock) { + _sock_fd = std::make_shared(std::move(sock), _poller); + SockUtil::get_sock_local_addr(_sock_fd->rawFd(), _local_addr); + SockUtil::get_sock_peer_addr(_sock_fd->rawFd(), _peer_addr); + } else { + _sock_fd = nullptr; + } +} + +string Socket::get_local_ip() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return ""; + } + return SockUtil::inet_ntoa((struct sockaddr *)&_local_addr); +} + +uint16_t Socket::get_local_port() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return 0; + } + return SockUtil::inet_port((struct sockaddr *)&_local_addr); +} + +string Socket::get_peer_ip() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return ""; + } + if (_udp_send_dst) { + return SockUtil::inet_ntoa((struct sockaddr *)_udp_send_dst.get()); + } + return SockUtil::inet_ntoa((struct sockaddr *)&_peer_addr); +} + +uint16_t Socket::get_peer_port() { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return 0; + } + if (_udp_send_dst) { + return SockUtil::inet_port((struct sockaddr *)_udp_send_dst.get()); + } + return SockUtil::inet_port((struct sockaddr *)&_peer_addr); +} + +string Socket::getIdentifier() const { + static string class_name = "Socket: "; + return class_name + to_string(reinterpret_cast(this)); +} + +bool Socket::flushData(const SockNum::Ptr &sock, bool poller_thread) { + decltype(_send_buf_sending) send_buf_sending_tmp; + { + // 转移出二级缓存 + LOCK_GUARD(_mtx_send_buf_sending); + if (!_send_buf_sending.empty()) { + send_buf_sending_tmp.swap(_send_buf_sending); + } + } + + if (send_buf_sending_tmp.empty()) { + _send_flush_ticker.resetTime(); + do { + { + // 二级发送缓存为空,那么我们接着消费一级缓存中的数据 + LOCK_GUARD(_mtx_send_buf_waiting); + if (!_send_buf_waiting.empty()) { + // 把一级缓中数数据放置到二级缓存中并清空 + auto send_result = [this](const Buffer::Ptr &buffer, bool send_success) { + if (send_success) { + // 更新发送速率 + if (_enable_speed) { + _send_speed += buffer->size(); + } + _status->sendSuccess(buffer->size()); + } else { + _status->sendFailed(buffer->size()); + } + LOCK_GUARD(_mtx_event); + if (_send_result) { + _send_result(buffer, send_success); + } + }; + send_buf_sending_tmp.emplace_back(BufferList::create(std::move(_send_buf_waiting), std::move(send_result), sock->type() == SockNum::Sock_UDP)); + break; + } + } + // 如果一级缓存也为空,那么说明所有数据均写入socket了 + if (poller_thread) { + // poller线程触发该函数,那么该socket应该已经加入了可写事件的监听; + // 那么在数据列队清空的情况下,我们需要关闭监听以免触发无意义的事件回调 + stopWriteAbleEvent(sock); + onFlushed(); + } + return true; + } while (false); + } + + while (!send_buf_sending_tmp.empty()) { + auto &packet = send_buf_sending_tmp.front(); + auto n = packet->send(sock->rawFd(), _sock_flags); + if (n > 0) { + // 全部或部分发送成功 + if (packet->empty()) { + // 全部发送成功 + send_buf_sending_tmp.pop_front(); + continue; + } + // 部分发送成功 + if (!poller_thread) { + // 如果该函数是poller线程触发的,那么该socket应该已经加入了可写事件的监听,所以我们不需要再次加入监听 + startWriteAbleEvent(sock); + } + break; + } + + // 一个都没发送成功 + int err = get_uv_error(true); + if (err == UV_EAGAIN) { + // 等待下一次发送 + if (!poller_thread) { + // 如果该函数是poller线程触发的,那么该socket应该已经加入了可写事件的监听,所以我们不需要再次加入监听 + startWriteAbleEvent(sock); + } + break; + } + + // 其他错误代码,发生异常 + if (sock->type() == SockNum::Sock_UDP) { + // udp发送异常,把数据丢弃 + send_buf_sending_tmp.pop_front(); + WarnL << "Send udp socket[" << sock << "] failed, data ignored: " << uv_strerror(err); + continue; + } + // tcp发送失败时,触发异常 + emitErr(toSockException(err)); + return false; + } + + // 回滚未发送完毕的数据 + if (!send_buf_sending_tmp.empty()) { + // 有剩余数据 + LOCK_GUARD(_mtx_send_buf_sending); + send_buf_sending_tmp.swap(_send_buf_sending); + _send_buf_sending.append(send_buf_sending_tmp); + // 二级缓存未全部发送完毕,说明该socket不可写,直接返回 + return true; + } + + // 二级缓存已经全部发送完毕,说明该socket还可写,我们尝试继续写 + // 如果是poller线程,我们尝试再次写一次(因为可能其他线程调用了send函数又有新数据了) + return poller_thread ? flushData(sock, poller_thread) : true; +} + +void Socket::onWriteAble(const SockNum::Ptr &sock) { + bool empty_waiting; + bool empty_sending; + { + LOCK_GUARD(_mtx_send_buf_waiting); + empty_waiting = _send_buf_waiting.empty(); + } + + { + LOCK_GUARD(_mtx_send_buf_sending); + empty_sending = _send_buf_sending.empty(); + } + + if (empty_waiting && empty_sending) { + // 数据已经清空了,我们停止监听可写事件 + stopWriteAbleEvent(sock); + } else { + // socket可写,我们尝试发送剩余的数据 + flushData(sock, true); + } +} + +void Socket::startWriteAbleEvent(const SockNum::Ptr &sock) { + // 开始监听socket可写事件 + _sendable = false; + int flag = _enable_recv ? EventPoller::Event_Read : 0; + _poller->modifyEvent(sock->rawFd(), flag | EventPoller::Event_Error | EventPoller::Event_Write, [sock](bool) {}); +} + +void Socket::stopWriteAbleEvent(const SockNum::Ptr &sock) { + // 停止监听socket可写事件 + _sendable = true; + int flag = _enable_recv ? EventPoller::Event_Read : 0; + _poller->modifyEvent(sock->rawFd(), flag | EventPoller::Event_Error, [sock](bool) {}); +} + +void Socket::enableRecv(bool enabled) { + if (_enable_recv == enabled) { + return; + } + _enable_recv = enabled; + int read_flag = _enable_recv ? EventPoller::Event_Read : 0; + // 可写时,不监听可写事件 + int send_flag = _sendable ? 0 : EventPoller::Event_Write; + _poller->modifyEvent(rawFD(), read_flag | send_flag | EventPoller::Event_Error); +} + +int Socket::rawFD() const { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return -1; + } + return _sock_fd->rawFd(); +} + +bool Socket::alive() const { + LOCK_GUARD(_mtx_sock_fd); + return _sock_fd && !_err_emit; +} + +SockNum::SockType Socket::sockType() const { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return SockNum::Sock_Invalid; + } + return _sock_fd->type(); +} + +void Socket::setSendTimeOutSecond(uint32_t second) { + _max_send_buffer_ms = second * 1000; +} + +bool Socket::isSocketBusy() const { + return !_sendable.load(); +} + +const EventPoller::Ptr &Socket::getPoller() const { + return _poller; +} + +bool Socket::cloneSocket(const Socket &other) { + closeSock(); + SockNum::Ptr sock; + { + LOCK_GUARD(other._mtx_sock_fd); + if (!other._sock_fd) { + WarnL << "sockfd of src socket is null"; + return false; + } + sock = other._sock_fd->sockNum(); + } + return fromSock_l(sock); +} + +bool Socket::bindPeerAddr(const struct sockaddr *dst_addr, socklen_t addr_len, bool soft_bind) { + LOCK_GUARD(_mtx_sock_fd); + if (!_sock_fd) { + return false; + } + if (_sock_fd->type() != SockNum::Sock_UDP) { + return false; + } + addr_len = addr_len ? addr_len : SockUtil::get_sock_len(dst_addr); + if (soft_bind) { + // 软绑定,只保存地址 + _udp_send_dst = std::make_shared(); + memcpy(_udp_send_dst.get(), dst_addr, addr_len); + } else { + // 硬绑定后,取消软绑定,防止memcpy目标地址的性能损失 + _udp_send_dst = nullptr; + if (-1 == ::connect(_sock_fd->rawFd(), dst_addr, addr_len)) { + WarnL << "Connect socket to peer address failed: " << SockUtil::inet_ntoa(dst_addr); + return false; + } + memcpy(&_peer_addr, dst_addr, addr_len); + } + return true; +} + +void Socket::setSendFlags(int flags) { + _sock_flags = flags; +} + +///////////////SockSender/////////////////// + +SockSender &SockSender::operator<<(const char *buf) { + send(buf); + return *this; +} + +SockSender &SockSender::operator<<(string buf) { + send(std::move(buf)); + return *this; +} + +SockSender &SockSender::operator<<(Buffer::Ptr buf) { + send(std::move(buf)); + return *this; +} + +ssize_t SockSender::send(string buf) { + return send(std::make_shared(std::move(buf))); +} + +ssize_t SockSender::send(const char *buf, size_t size) { + auto buffer = BufferRaw::create(); + buffer->assign(buf, size); + return send(std::move(buffer)); +} + +///////////////SocketHelper/////////////////// + +SocketHelper::SocketHelper(const Socket::Ptr &sock) { + setSock(sock); + setOnCreateSocket(nullptr); +} + +void SocketHelper::setPoller(const EventPoller::Ptr &poller) { + _poller = poller; +} + +void SocketHelper::setSock(const Socket::Ptr &sock) { + _sock = sock; + if (_sock) { + _poller = _sock->getPoller(); + } +} + +const EventPoller::Ptr &SocketHelper::getPoller() const { + assert(_poller); + return _poller; +} + +const Socket::Ptr &SocketHelper::getSock() const { + return _sock; +} + +int SocketHelper::flushAll() { + if (!_sock) { + return -1; + } + return _sock->flushAll(); +} + +ssize_t SocketHelper::send(Buffer::Ptr buf) { + if (!_sock) { + return -1; + } + return _sock->send(std::move(buf), nullptr, 0, _try_flush); +} + +void SocketHelper::shutdown(const SockException &ex) { + if (_sock) { + _sock->emitErr(ex); + } +} + +void SocketHelper::safeShutdown(const SockException &ex) { + std::weak_ptr weak_self = shared_from_this(); + async_first([weak_self, ex]() { + if (auto strong_self = weak_self.lock()) { + strong_self->shutdown(ex); + } + }); +} + +string SocketHelper::get_local_ip() { + return _sock ? _sock->get_local_ip() : ""; +} + +uint16_t SocketHelper::get_local_port() { + return _sock ? _sock->get_local_port() : 0; +} + +string SocketHelper::get_peer_ip() { + return _sock ? _sock->get_peer_ip() : ""; +} + +uint16_t SocketHelper::get_peer_port() { + return _sock ? _sock->get_peer_port() : 0; +} + +bool SocketHelper::isSocketBusy() const { + if (!_sock) { + return true; + } + return _sock->isSocketBusy(); +} + +Task::Ptr SocketHelper::async(TaskIn task, bool may_sync) { + return _poller->async(std::move(task), may_sync); +} + +Task::Ptr SocketHelper::async_first(TaskIn task, bool may_sync) { + return _poller->async_first(std::move(task), may_sync); +} + +void SocketHelper::setSendFlushFlag(bool try_flush) { + _try_flush = try_flush; +} + +void SocketHelper::setSendFlags(int flags) { + if (!_sock) { + return; + } + _sock->setSendFlags(flags); +} + +void SocketHelper::setOnCreateSocket(Socket::onCreateSocket cb) { + if (cb) { + _on_create_socket = std::move(cb); + } else { + _on_create_socket = [](const EventPoller::Ptr &poller) { return Socket::createSocket(poller, false); }; + } +} + +Socket::Ptr SocketHelper::createSocket() { + return _on_create_socket(_poller); +} + +std::ostream &operator<<(std::ostream &ost, const SockException &err) { + ost << static_cast(err.getErrCode()) << "(" << err.what() << ")"; + return ost; +} + +} // namespace turbo diff --git a/turbo/network/network/socket.h b/turbo/network/network/socket.h new file mode 100644 index 00000000..60024e47 --- /dev/null +++ b/turbo/network/network/socket.h @@ -0,0 +1,791 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_SOCKET_H_ +#define TURBO_NETWORK_NETWORK_SOCKET_H_ + + +#include +#include +#include +#include +#include +#include +#include "turbo/network/util/speed_statistic.h" +#include "turbo/network/network/sock_util.h" +#include "turbo/network/poller/timer.h" +#include "turbo/network/poller/event_poller.h" +#include "turbo/network/network/buffer_sock.h" + +namespace turbo { + +#if defined(MSG_NOSIGNAL) +#define FLAG_NOSIGNAL MSG_NOSIGNAL +#else +#define FLAG_NOSIGNAL 0 +#endif //MSG_NOSIGNAL + +#if defined(MSG_MORE) +#define FLAG_MORE MSG_MORE +#else +#define FLAG_MORE 0 +#endif //MSG_MORE + +#if defined(MSG_DONTWAIT) +#define FLAG_DONTWAIT MSG_DONTWAIT +#else +#define FLAG_DONTWAIT 0 +#endif //MSG_DONTWAIT + +//默认的socket flags:不触发SIGPIPE,非阻塞发送 +#define SOCKET_DEFAULE_FLAGS (FLAG_NOSIGNAL | FLAG_DONTWAIT ) + +//发送超时时间,如果在规定时间内一直没有发送数据成功,那么将触发onErr事件 +#define SEND_TIME_OUT_SEC 10 + +//错误类型枚举 +enum class ErrCode { + Err_success = 0, //成功 success + Err_eof, //eof + Err_timeout, //超时 socket timeout + Err_refused,//连接被拒绝 socket refused + Err_reset,//连接被重置 socket reset + Err_dns,//dns解析失败 dns resolve failed + Err_shutdown,//主动关闭 socket shutdown + Err_other = 0xFF,//其他错误 other error +}; + +//错误信息类 +class SockException : public std::exception { +public: + SockException(ErrCode code = ErrCode::Err_success, const std::string &msg = "", int custom_code = 0) { + _msg = msg; + _code = code; + _custom_code = custom_code; + } + + //重置错误 + void reset(ErrCode code, const std::string &msg, int custom_code = 0) { + _msg = msg; + _code = code; + _custom_code = custom_code; + } + + //错误提示 + const char *what() const noexcept override { + return _msg.c_str(); + } + + //错误代码 + ErrCode getErrCode() const { + return _code; + } + + //用户自定义错误代码 + int getCustomCode() const { + return _custom_code; + } + + //判断是否真的有错 + operator bool() const { + return _code != ErrCode::Err_success; + } + +private: + ErrCode _code; + int _custom_code = 0; + std::string _msg; +}; + +//std::cout等输出流可以直接输出SockException对象 +std::ostream &operator<<(std::ostream &ost, const SockException &err); + +class SockNum { +public: + using Ptr = std::shared_ptr; + + typedef enum { + Sock_Invalid = -1, + Sock_TCP = 0, + Sock_UDP = 1, + Sock_TCP_Server = 2 + } SockType; + + SockNum(int fd, SockType type) { + _fd = fd; + _type = type; + } + + ~SockNum() { +#if defined (OS_IPHONE) + unsetSocketOfIOS(_fd); +#endif //OS_IPHONE + // 停止socket收发能力 + #if defined(_WIN32) + ::shutdown(_fd, SD_BOTH); + #else + ::shutdown(_fd, SHUT_RDWR); + #endif + close(_fd); + } + + int rawFd() const { + return _fd; + } + + SockType type() { + return _type; + } + + void setConnected() { +#if defined (OS_IPHONE) + setSocketOfIOS(_fd); +#endif //OS_IPHONE + } + +#if defined (OS_IPHONE) +private: + void *readStream=nullptr; + void *writeStream=nullptr; + bool setSocketOfIOS(int socket); + void unsetSocketOfIOS(int socket); +#endif //OS_IPHONE + +private: + int _fd; + SockType _type; +}; + +//socket 文件描述符的包装 +//在析构时自动溢出监听并close套接字 +//防止描述符溢出 +class SockFD : public noncopyable { +public: + using Ptr = std::shared_ptr; + + /** + * 创建一个fd对象 + * @param num 文件描述符,int数字 + * @param poller 事件监听器 + */ + SockFD(SockNum::Ptr num, const EventPoller::Ptr &poller) { + _num = std::move(num); + _poller = poller; + } + + /** + * 复制一个fd对象 + * @param that 源对象 + * @param poller 事件监听器 + */ + SockFD(const SockFD &that, const EventPoller::Ptr &poller) { + _num = that._num; + _poller = poller; + if (_poller == that._poller) { + throw std::invalid_argument("Copy a SockFD with same poller"); + } + } + + ~SockFD() { delEvent(); } + + void delEvent() { + if (_poller) { + auto num = _num; + // 移除io事件成功后再close fd + _poller->delEvent(num->rawFd(), [num](bool) {}); + _poller = nullptr; + } + } + + void setConnected() { + _num->setConnected(); + } + + int rawFd() const { + return _num->rawFd(); + } + + const SockNum::Ptr& sockNum() const { + return _num; + } + + SockNum::SockType type() { + return _num->type(); + } + + const EventPoller::Ptr& getPoller() const { + return _poller; + } + +private: + SockNum::Ptr _num; + EventPoller::Ptr _poller; +}; + +template +class MutexWrapper { +public: + MutexWrapper(bool enable) { + _enable = enable; + } + + ~MutexWrapper() = default; + + inline void lock() { + if (_enable) { + _mtx.lock(); + } + } + + inline void unlock() { + if (_enable) { + _mtx.unlock(); + } + } + +private: + bool _enable; + Mtx _mtx; +}; + +class SockInfo { +public: + SockInfo() = default; + virtual ~SockInfo() = default; + + //获取本机ip + virtual std::string get_local_ip() = 0; + //获取本机端口号 + virtual uint16_t get_local_port() = 0; + //获取对方ip + virtual std::string get_peer_ip() = 0; + //获取对方端口号 + virtual uint16_t get_peer_port() = 0; + //获取标识符 + virtual std::string getIdentifier() const { return ""; } +}; + +#define TraceP(ptr) TraceL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define DebugP(ptr) DebugL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define InfoP(ptr) InfoL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define WarnP(ptr) WarnL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " +#define ErrorP(ptr) ErrorL << ptr->getIdentifier() << "(" << ptr->get_peer_ip() << ":" << ptr->get_peer_port() << ") " + +class NetStatus : public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + static NetStatus& Instance(); + void sendSuccess(size_t size); + void sendFailed(size_t size); + void received(size_t size); + + uint64_t getSendSuccessBytes() const; + uint64_t getSendFailedBytes() const; + uint64_t getReceivedBytes() const; + +private: + std::atomic _send_success{0}; + std::atomic _send_failed{0}; + std::atomic _received{0}; +}; + +//异步IO Socket对象,包括tcp客户端、服务器和udp套接字 +class Socket : public std::enable_shared_from_this, public noncopyable, public SockInfo { +public: + using Ptr = std::shared_ptr; + //接收数据回调 + using onReadCB = std::function; + //发生错误回调 + using onErrCB = std::function; + //tcp监听接收到连接请求 + using onAcceptCB = std::function &complete)>; + //socket发送缓存清空事件,返回true代表下次继续监听该事件,否则停止 + using onFlush = std::function; + //在接收到连接请求前,拦截Socket默认生成方式 + using onCreateSocket = std::function; + //发送buffer成功与否回调 + using onSendResult = BufferList::SendResult; + + /** + * 构造socket对象,尚未有实质操作 + * @param poller 绑定的poller线程 + * @param enable_mutex 是否启用互斥锁(接口是否线程安全) + */ + static Ptr createSocket(const EventPoller::Ptr &poller = nullptr, bool enable_mutex = true); + ~Socket() override; + + /** + * 创建tcp客户端并异步连接服务器 + * @param url 目标服务器ip或域名 + * @param port 目标服务器端口 + * @param con_cb 结果回调 + * @param timeout_sec 超时时间 + * @param local_ip 绑定本地网卡ip + * @param local_port 绑定本地网卡端口号 + */ + virtual void connect(const std::string &url, uint16_t port, onErrCB con_cb, float timeout_sec = 5, const std::string &local_ip = "::", uint16_t local_port = 0); + + /** + * 使用已有的fd去异步连接服务器 + * @param fd socket fd + * @param url 目标服务器ip或域名 + * @param port 目标服务器端口 + * @param con_cb 结果回调 + * @param timeout_sec 超时时间 + */ + void connect(int fd, const std::string &url, uint16_t port, onErrCB con_cb, float timeout_sec = 5); + /** + * 创建tcp监听服务器 + * @param port 监听端口,0则随机 + * @param local_ip 监听的网卡ip + * @param backlog tcp最大积压数 + * @return 是否成功 + */ + bool listen(uint16_t port, const std::string &local_ip = "::", int backlog = 1024); + + /** + * 创建udp套接字,udp是无连接的,所以可以作为服务器和客户端 + * @param port 绑定的端口为0则随机 + * @param local_ip 绑定的网卡ip + * @return 是否成功 + */ + bool bindUdpSock(uint16_t port, const std::string &local_ip = "::", bool enable_reuse = true); + + /** + * 包装外部fd,本对象负责close fd + * 内部会设置fd为NoBlocked,NoSigpipe,CloExec + * 其他设置需要自行使用SockUtil进行设置 + */ + bool fromSock(int fd, SockNum::SockType type); + + /** + * 从另外一个Socket克隆 + * 目的是一个socket可以被多个poller对象监听,提高性能或实现Socket归属线程的迁移 + * @param other 原始的socket对象 + * @return 是否成功 + */ + bool cloneSocket(const Socket &other); + + ////////////设置事件回调//////////// + + /** + * 设置数据接收回调,tcp或udp客户端有效 + * @param cb 回调对象 + */ + void setOnRead(onReadCB cb); + + /** + * 设置异常事件(包括eof等)回调 + * @param cb 回调对象 + */ + void setOnErr(onErrCB cb); + + /** + * 设置tcp监听接收到连接回调 + * @param cb 回调对象 + */ + void setOnAccept(onAcceptCB cb); + + /** + * 设置socket写缓存清空事件回调 + * 通过该回调可以实现发送流控 + * @param cb 回调对象 + */ + void setOnFlush(onFlush cb); + + /** + * 设置accept时,socket构造事件回调 + * @param cb 回调 + */ + void setOnBeforeAccept(onCreateSocket cb); + + /** + * 设置发送buffer结果回调 + * @param cb 回调 + */ + void setOnSendResult(onSendResult cb); + + ////////////发送数据相关接口//////////// + + /** + * 发送数据指针 + * @param buf 数据指针 + * @param size 数据长度 + * @param addr 目标地址 + * @param addr_len 目标地址长度 + * @param try_flush 是否尝试写socket + * @return -1代表失败(socket无效),0代表数据长度为0,否则返回数据长度 + */ + ssize_t send(const char *buf, size_t size = 0, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 发送string + */ + ssize_t send(std::string buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 发送Buffer对象,Socket对象发送数据的统一出口 + * socket对象发送数据的统一出口 + */ + ssize_t send(Buffer::Ptr buf, struct sockaddr *addr = nullptr, socklen_t addr_len = 0, bool try_flush = true); + + /** + * 尝试将所有数据写socket + * @return -1代表失败(socket无效或者发送超时),0代表成功? + */ + int flushAll(); + + /** + * 关闭socket且触发onErr回调,onErr回调将在poller线程中进行 + * @param err 错误原因 + * @return 是否成功触发onErr回调 + */ + bool emitErr(const SockException &err) noexcept; + + /** + * 关闭或开启数据接收 + * @param enabled 是否开启 + */ + void enableRecv(bool enabled); + + /** + * 获取裸文件描述符,请勿进行close操作(因为Socket对象会管理其生命周期) + * @return 文件描述符 + */ + int rawFD() const; + + /** + * tcp客户端是否处于连接状态 + * 支持Sock_TCP类型socket + */ + bool alive() const; + + /** + * 返回socket类型 + */ + SockNum::SockType sockType() const; + + /** + * 设置发送超时主动断开时间;默认10秒 + * @param second 发送超时数据,单位秒 + */ + void setSendTimeOutSecond(uint32_t second); + + /** + * 套接字是否忙,如果套接字写缓存已满则返回true + * @return 套接字是否忙 + */ + bool isSocketBusy() const; + + /** + * 获取poller线程对象 + * @return poller线程对象 + */ + const EventPoller::Ptr &getPoller() const; + + /** + * 绑定udp 目标地址,后续发送时就不用再单独指定了 + * @param dst_addr 目标地址 + * @param addr_len 目标地址长度 + * @param soft_bind 是否软绑定,软绑定时不调用udp connect接口,只保存目标地址信息,发送时再传递到sendto函数 + * @return 是否成功 + */ + bool bindPeerAddr(const struct sockaddr *dst_addr, socklen_t addr_len = 0, bool soft_bind = false); + + /** + * 设置发送flags + * @param flags 发送的flag + */ + void setSendFlags(int flags = SOCKET_DEFAULE_FLAGS); + + /** + * 关闭套接字 + * @param close_fd 是否关闭fd还是只移除io事件监听 + */ + void closeSock(bool close_fd = true); + + /** + * 获取发送缓存包个数(不是字节数) + */ + size_t getSendBufferCount(); + + /** + * 获取上次socket发送缓存清空至今的毫秒数,单位毫秒 + */ + uint64_t elapsedTimeAfterFlushed(); + + /** + * 获取接收速率,单位bytes/s + */ + int getRecvSpeed(); + + /** + * 获取发送速率,单位bytes/s + */ + int getSendSpeed(); + + ////////////SockInfo override//////////// + std::string get_local_ip() override; + uint16_t get_local_port() override; + std::string get_peer_ip() override; + uint16_t get_peer_port() override; + std::string getIdentifier() const override; + +protected: + Socket(EventPoller::Ptr poller, bool enable_mutex = true); + +private: + void setSock(SockNum::Ptr sock); + int onAccept(const SockNum::Ptr &sock, int event) noexcept; + ssize_t onRead(const SockNum::Ptr &sock, const BufferRaw::Ptr &buffer) noexcept; + void onWriteAble(const SockNum::Ptr &sock); + void onConnected(const SockNum::Ptr &sock, const onErrCB &cb); + void onFlushed(); + void startWriteAbleEvent(const SockNum::Ptr &sock); + void stopWriteAbleEvent(const SockNum::Ptr &sock); + bool flushData(const SockNum::Ptr &sock, bool poller_thread); + bool attachEvent(const SockNum::Ptr &sock); + ssize_t send_l(Buffer::Ptr buf, bool is_buf_sock, bool try_flush = true); + void connect_l(bool async, const std::function &cb, onErrCB con_cb, float timeout_sec = 5); + bool fromSock_l(SockNum::Ptr sock); + +private: + //send socket时的flag + int _sock_flags = SOCKET_DEFAULE_FLAGS; + //最大发送缓存,单位毫秒,距上次发送缓存清空时间不能超过该参数 + uint32_t _max_send_buffer_ms = SEND_TIME_OUT_SEC * 1000; + //控制是否接收监听socket可读事件,关闭后可用于流量控制 + std::atomic _enable_recv {true}; + //标记该socket是否可写,socket写缓存满了就不可写 + std::atomic _sendable {true}; + //是否已经触发err回调了 + bool _err_emit = false; + //是否启用网速统计 + bool _enable_speed = false; + // udp发送目标地址 + std::shared_ptr _udp_send_dst; + + //接收速率统计 + BytesSpeed _recv_speed; + //发送速率统计 + BytesSpeed _send_speed; + + //tcp连接超时定时器 + Timer::Ptr _con_timer; + //tcp连接结果回调对象 + std::shared_ptr _async_con_cb; + + //记录上次发送缓存(包括socket写缓存、应用层缓存)清空的计时器 + Ticker _send_flush_ticker; + //socket fd的抽象类 + SockFD::Ptr _sock_fd; + //本socket绑定的poller线程,事件触发于此线程 + EventPoller::Ptr _poller; + //跨线程访问_sock_fd时需要上锁 + mutable MutexWrapper _mtx_sock_fd; + + //socket异常事件(比如说断开) + onErrCB _on_err; + //收到数据事件 + onReadCB _on_read; + //socket缓存清空事件(可用于发送流速控制) + onFlush _on_flush; + //tcp监听收到accept请求事件 + onAcceptCB _on_accept; + //tcp监听收到accept请求,自定义创建peer Socket事件(可以控制子Socket绑定到其他poller线程) + onCreateSocket _on_before_accept; + //设置上述回调函数的锁 + MutexWrapper _mtx_event; + + //一级发送缓存, socket可写时,会把一级缓存批量送入到二级缓存 + List > _send_buf_waiting; + //一级发送缓存锁 + MutexWrapper _mtx_send_buf_waiting; + //二级发送缓存, socket可写时,会把二级缓存批量写入到socket + List _send_buf_sending; + //二级发送缓存锁 + MutexWrapper _mtx_send_buf_sending; + //发送buffer结果回调 + BufferList::SendResult _send_result; + //对象个数统计 + ObjectStatistic _statistic; + + //链接缓存地址,防止tcp reset 导致无法获取对端的地址 + struct sockaddr_storage _local_addr; + struct sockaddr_storage _peer_addr; + //网络状态统计 + NetStatus::Ptr _status; +}; + +class SockSender { +public: + SockSender() = default; + virtual ~SockSender() = default; + virtual ssize_t send(Buffer::Ptr buf) = 0; + virtual void shutdown(const SockException &ex = SockException(ErrCode::Err_shutdown, "self shutdown")) = 0; + + //发送char * + SockSender &operator << (const char *buf); + //发送字符串 + SockSender &operator << (std::string buf); + //发送Buffer对象 + SockSender &operator << (Buffer::Ptr buf); + + //发送其他类型是数据 + template + SockSender &operator << (T &&buf) { + std::ostringstream ss; + ss << std::forward(buf); + send(ss.str()); + return *this; + } + + ssize_t send(std::string buf); + ssize_t send(const char *buf, size_t size = 0); +}; + +//Socket对象的包装类 +class SocketHelper : public SockSender, public SockInfo, public TaskExecutorInterface, public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + SocketHelper(const Socket::Ptr &sock); + ~SocketHelper() override = default; + + ///////////////////// Socket util std::functions ///////////////////// + /** + * 获取poller线程 + */ + const EventPoller::Ptr& getPoller() const; + + /** + * 设置批量发送标记,用于提升性能 + * @param try_flush 批量发送标记 + */ + void setSendFlushFlag(bool try_flush); + + /** + * 设置socket发送flags + * @param flags socket发送flags + */ + void setSendFlags(int flags); + + /** + * 套接字是否忙,如果套接字写缓存已满则返回true + */ + bool isSocketBusy() const; + + /** + * 设置Socket创建器,自定义Socket创建方式 + * @param cb 创建器 + */ + void setOnCreateSocket(Socket::onCreateSocket cb); + + /** + * 创建socket对象 + */ + Socket::Ptr createSocket(); + + /** + * 获取socket对象 + */ + const Socket::Ptr &getSock() const; + + /** + * 尝试将所有数据写socket + * @return -1代表失败(socket无效或者发送超时),0代表成功? + */ + int flushAll(); + + /** + * 是否ssl加密 + */ + virtual bool overSsl() const { return false; } + + ///////////////////// SockInfo override ///////////////////// + std::string get_local_ip() override; + uint16_t get_local_port() override; + std::string get_peer_ip() override; + uint16_t get_peer_port() override; + + ///////////////////// TaskExecutorInterface override ///////////////////// + /** + * 任务切换到所属poller线程执行 + * @param task 任务 + * @param may_sync 是否运行同步执行任务 + */ + Task::Ptr async(TaskIn task, bool may_sync = true) override; + Task::Ptr async_first(TaskIn task, bool may_sync = true) override; + + ///////////////////// SockSender override ///////////////////// + + /** + * 使能 SockSender 其他未被重写的send重载函数 + */ + using SockSender::send; + + /** + * 统一发送数据的出口 + */ + ssize_t send(Buffer::Ptr buf) override; + + /** + * 触发onErr事件 + */ + void shutdown(const SockException &ex = SockException(ErrCode::Err_shutdown, "self shutdown")) override; + + /** + * 线程安全的脱离 Server 并触发 onError 事件 + * @param ex 触发 onError 事件的原因 + */ + void safeShutdown(const SockException &ex = SockException(ErrCode::Err_shutdown, "self shutdown")); + + ///////////////////// event functions ///////////////////// + /** + * 接收数据入口 + * @param buf 数据,可以重复使用内存区,不可被缓存使用 + */ + virtual void onRecv(const Buffer::Ptr &buf) = 0; + + /** + * 收到 eof 或其他导致脱离 Server 事件的回调 + * 收到该事件时, 该对象一般将立即被销毁 + * @param err 原因 + */ + virtual void onError(const SockException &err) = 0; + + /** + * 数据全部发送完毕后回调 + */ + virtual void onFlush() {} + + /** + * 每隔一段时间触发, 用来做超时管理 + */ + virtual void onManager() = 0; + +protected: + void setPoller(const EventPoller::Ptr &poller); + void setSock(const Socket::Ptr &sock); + +private: + bool _try_flush = true; + Socket::Ptr _sock; + EventPoller::Ptr _poller; + Socket::onCreateSocket _on_create_socket; +}; + +} // namespace turbo +#endif /* TURBO_NETWORK_NETWORK_SOCKET_H_ */ + diff --git a/turbo/network/network/tcp_client.cc b/turbo/network/network/tcp_client.cc new file mode 100644 index 00000000..ad903307 --- /dev/null +++ b/turbo/network/network/tcp_client.cc @@ -0,0 +1,143 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/network/tcp_client.h" + +using namespace std; + +namespace turbo { + +StatisticImp(TcpClient) + +TcpClient::TcpClient(const EventPoller::Ptr &poller) : SocketHelper(nullptr) { + setPoller(poller ? poller : EventPollerPool::Instance().getPoller()); + setOnCreateSocket([](const EventPoller::Ptr &poller) { + //TCP客户端默认开启互斥锁 + return Socket::createSocket(poller, true); + }); +} + +TcpClient::~TcpClient() { + TraceL << "~" << TcpClient::getIdentifier(); +} + +void TcpClient::shutdown(const SockException &ex) { + _timer.reset(); + SocketHelper::shutdown(ex); +} + +bool TcpClient::alive() const { + if (_timer) { + //连接中或已连接 + return true; + } + //在websocket client(zlmediakit)相关代码中, + //_timer一直为空,但是socket fd有效,alive状态也应该返回true + auto sock = getSock(); + return sock && sock->alive(); +} + +void TcpClient::setNetAdapter(const string &local_ip) { + _net_adapter = local_ip; +} + +void TcpClient::startConnect(const string &url, uint16_t port, float timeout_sec, uint16_t local_port) { + weak_ptr weak_self = static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->onManager(); + return true; + }, getPoller()); + + setSock(createSocket()); + + auto sock_ptr = getSock().get(); + sock_ptr->setOnErr([weak_self, sock_ptr](const SockException &ex) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上次的socket的事件忽略掉 + return; + } + strong_self->_timer.reset(); + TraceL << strong_self->getIdentifier() << " on err: " << ex; + strong_self->onError(ex); + }); + + TraceL << getIdentifier() << " start connect " << url << ":" << port; + sock_ptr->connect(url, port, [weak_self](const SockException &err) { + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->onSockConnect(err); + } + }, timeout_sec, _net_adapter, local_port); +} + +void TcpClient::onSockConnect(const SockException &ex) { + TraceL << getIdentifier() << " connect result: " << ex; + if (ex) { + //连接失败 + _timer.reset(); + onConnect(ex); + return; + } + + auto sock_ptr = getSock().get(); + weak_ptr weak_self = static_pointer_cast(shared_from_this()); + sock_ptr->setOnFlush([weak_self, sock_ptr]() { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上传socket的事件忽略掉 + return false; + } + strong_self->onFlush(); + return true; + }); + + sock_ptr->setOnRead([weak_self, sock_ptr](const Buffer::Ptr &pBuf, struct sockaddr *, int) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + if (sock_ptr != strong_self->getSock().get()) { + //已经重连socket,上传socket的事件忽略掉 + return; + } + try { + strong_self->onRecv(pBuf); + } catch (std::exception &ex) { + strong_self->shutdown(SockException(ErrCode::Err_other, ex.what())); + } + }); + + onConnect(ex); +} + +std::string TcpClient::getIdentifier() const { + if (_id.empty()) { + static atomic s_index { 0 }; + _id = turbo::demangle(typeid(*this).name()) + "-" + to_string(++s_index); + } + return _id; +} + +} /* namespace turbo */ \ No newline at end of file diff --git a/turbo/network/network/tcp_client.h b/turbo/network/network/tcp_client.h new file mode 100644 index 00000000..9dea15f2 --- /dev/null +++ b/turbo/network/network/tcp_client.h @@ -0,0 +1,180 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_TCP_CLIENT_H_ +#define TURBO_NETWORK_NETWORK_TCP_CLIENT_H_ + +#include +#include "turbo/network/network/socket.h" +#include "turbo/network/util/ssl_box.h" + +namespace turbo { + +//Tcp客户端,Socket对象默认开始互斥锁 +class TcpClient : public SocketHelper { +public: + using Ptr = std::shared_ptr; + TcpClient(const EventPoller::Ptr &poller = nullptr); + ~TcpClient() override; + + /** + * 开始连接tcp服务器 + * @param url 服务器ip或域名 + * @param port 服务器端口 + * @param timeout_sec 超时时间,单位秒 + * @param local_port 本地端口 + */ + virtual void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0); + + /** + * 通过代理开始连接tcp服务器 + * @param url 服务器ip或域名 + * @proxy_host 代理ip + * @proxy_port 代理端口 + * @param timeout_sec 超时时间,单位秒 + * @param local_port 本地端口 + */ + virtual void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0){}; + + /** + * 主动断开连接 + * @param ex 触发onErr事件时的参数 + */ + void shutdown(const SockException &ex = SockException(ErrCode::Err_shutdown, "self shutdown")) override; + + /** + * 连接中或已连接返回true,断开连接时返回false + */ + virtual bool alive() const; + + /** + * 设置网卡适配器,使用该网卡与服务器通信 + * @param local_ip 本地网卡ip + */ + virtual void setNetAdapter(const std::string &local_ip); + + /** + * 唯一标识 + */ + std::string getIdentifier() const override; + +protected: + /** + * 连接服务器结果回调 + * @param ex 成功与否 + */ + virtual void onConnect(const SockException &ex) = 0; + + /** + * tcp连接成功后每2秒触发一次该事件 + */ + void onManager() override {} + +private: + void onSockConnect(const SockException &ex); + +private: + mutable std::string _id; + std::string _net_adapter = "::"; + std::shared_ptr _timer; + //对象个数统计 + ObjectStatistic _statistic; +}; + +//用于实现TLS客户端的模板对象 +template +class TcpClientWithSSL : public TcpClientType { +public: + using Ptr = std::shared_ptr; + + template + TcpClientWithSSL(ArgsType &&...args):TcpClientType(std::forward(args)...) {} + + ~TcpClientWithSSL() override { + if (_ssl_box) { + _ssl_box->flush(); + } + } + + void onRecv(const Buffer::Ptr &buf) override { + if (_ssl_box) { + _ssl_box->onRecv(buf); + } else { + TcpClientType::onRecv(buf); + } + } + + // 使能其他未被重写的send函数 + using TcpClientType::send; + + ssize_t send(Buffer::Ptr buf) override { + if (_ssl_box) { + auto size = buf->size(); + _ssl_box->onSend(std::move(buf)); + return size; + } + return TcpClientType::send(std::move(buf)); + } + + //添加public_onRecv和public_send函数是解决较低版本gcc一个lambad中不能访问protected或private方法的bug + inline void public_onRecv(const Buffer::Ptr &buf) { + TcpClientType::onRecv(buf); + } + + inline void public_send(const Buffer::Ptr &buf) { + TcpClientType::send(buf); + } + + void startConnect(const std::string &url, uint16_t port, float timeout_sec = 5, uint16_t local_port = 0) override { + _host = url; + TcpClientType::startConnect(url, port, timeout_sec, local_port); + } + void startConnectWithProxy(const std::string &url, const std::string &proxy_host, uint16_t proxy_port, float timeout_sec = 5, uint16_t local_port = 0) override { + _host = url; + TcpClientType::startConnect(proxy_host, proxy_port, timeout_sec, local_port); + } + + bool overSsl() const override { return (bool)_ssl_box; } + +protected: + void onConnect(const SockException &ex) override { + if (!ex) { + _ssl_box = std::make_shared(false); + _ssl_box->setOnDecData([this](const Buffer::Ptr &buf) { + public_onRecv(buf); + }); + _ssl_box->setOnEncData([this](const Buffer::Ptr &buf) { + public_send(buf); + }); + + if (!isIP(_host.data())) { + //设置ssl域名 + _ssl_box->setHost(_host.data()); + } + } + TcpClientType::onConnect(ex); + } + /** + * 重置ssl, 主要为了解决一些302跳转时http与https的转换 + */ + void setDoNotUseSSL() { + _ssl_box.reset(); + } +private: + std::string _host; + std::shared_ptr _ssl_box; +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_NETWORK_TCP_CLIENT_H_ */ \ No newline at end of file diff --git a/turbo/network/network/tcp_server.cc b/turbo/network/network/tcp_server.cc new file mode 100644 index 00000000..3bdcfe27 --- /dev/null +++ b/turbo/network/network/tcp_server.cc @@ -0,0 +1,270 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/network/tcp_server.h" +#include "turbo/network/util/uv_errno.h" +#include "turbo/network/util/once_token.h" + + +using namespace std; + +namespace turbo { + +INSTANCE_IMP(SessionMap) +StatisticImp(TcpServer) + +TcpServer::TcpServer(const EventPoller::Ptr &poller) : Server(poller) { + setOnCreateSocket(nullptr); +} + +void TcpServer::setupEvent() { + _socket = createSocket(_poller); + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + // 拦截服务监听到的accpet fd连接构造,主要是指定socket的生成,保障负载均衡 + _socket->setOnBeforeAccept([weak_self](const EventPoller::Ptr &poller) -> Socket::Ptr { + if (auto strong_self = weak_self.lock()) { + return strong_self->onBeforeAcceptConnection(poller); + } + return nullptr; + }); + // 设置当前sever的socket接收accept时的回调 + // 对不同的子服务器,接收的连接socket连接到自己server来处理,具体是处理socket的poller是从poller池中选择的负载最小的poller + _socket->setOnAccept([weak_self](Socket::Ptr &sock, shared_ptr &complete) { + if (auto strong_self = weak_self.lock()) { + auto ptr = sock->getPoller().get(); + auto server = strong_self->getServer(ptr); + ptr->async([server, sock, complete]() { + //该tcp客户端派发给对应线程的TcpServer服务器 + server->onAcceptConnection(sock); + }); + } + }); +} + +TcpServer::~TcpServer() { + if (_main_server && _socket && _socket->rawFD() != -1) { + InfoL << "Close tcp server [" << _socket->get_local_ip() << "]: " << _socket->get_local_port(); + } + _timer.reset(); + //先关闭socket监听,防止收到新的连接 + _socket.reset(); + _session_map.clear(); + _cloned_server.clear(); +} + +uint16_t TcpServer::getPort() { + if (!_socket) { + return 0; + } + return _socket->get_local_port(); +} + +void TcpServer::setOnCreateSocket(Socket::onCreateSocket cb) { + if (cb) { + _on_create_socket = std::move(cb); + } else { + _on_create_socket = [](const EventPoller::Ptr &poller) { + return Socket::createSocket(poller, false); + }; + } + for (auto &pr : _cloned_server) { + pr.second->setOnCreateSocket(cb); + } +} + +TcpServer::Ptr TcpServer::onCreatServer(const EventPoller::Ptr &poller) { + return Ptr(new TcpServer(poller), [poller](TcpServer *ptr) { poller->async([ptr]() { delete ptr; }); }); +} + +Socket::Ptr TcpServer::onBeforeAcceptConnection(const EventPoller::Ptr &poller) { + assert(_poller->isCurrentThread()); + // 此处改成自定义获取poller对象,防止负载不均衡:因为虽然把连接派发到子tcp服务,但是也会存在子tcp服务抢占次数比较多,负载不均衡的情况,这里还是拦截peer socket的构造 + // 从poller池中选取一个负载最小的poller来负载管理监听socket + return createSocket(EventPollerPool::Instance().getPoller(false)); +} + +void TcpServer::cloneFrom(const TcpServer &that) { + if (!that._socket) { + throw std::invalid_argument("TcpServer::cloneFrom other with null socket"); + } + setupEvent(); + _main_server = false; + _on_create_socket = that._on_create_socket; + _session_alloc = that._session_alloc; + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() -> bool { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->onManagerSession(); + return true; + }, _poller); + this->mINI::operator=(that); + _parent = static_pointer_cast(const_cast(that).shared_from_this()); +} + +// 接收到客户端连接请求 +Session::Ptr TcpServer::onAcceptConnection(const Socket::Ptr &sock) { + assert(_poller->isCurrentThread()); + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + //创建一个Session;这里实现创建不同的服务会话实例 + auto helper = _session_alloc(std::static_pointer_cast(shared_from_this()), sock); + auto session = helper->session(); + //把本服务器的配置传递给Session + session->attachServer(*this); + + //_session_map::emplace肯定能成功 + auto success = _session_map.emplace(helper.get(), helper).second; + assert(success == true); + + weak_ptr weak_session = session; + //会话接收数据事件 + sock->setOnRead([weak_session](const Buffer::Ptr &buf, struct sockaddr *, int) { + //获取会话强应用 + auto strong_session = weak_session.lock(); + if (!strong_session) { + return; + } + try { + strong_session->onRecv(buf); + } catch (SockException &ex) { + strong_session->shutdown(ex); + } catch (exception &ex) { + strong_session->shutdown(SockException(ErrCode::Err_shutdown, ex.what())); + } + }); + + SessionHelper *ptr = helper.get(); + auto cls = ptr->className(); + //会话接收到错误事件 + sock->setOnErr([weak_self, weak_session, ptr, cls](const SockException &err) { + //在本函数作用域结束时移除会话对象 + //目的是确保移除会话前执行其onError函数 + //同时避免其onError函数抛异常时没有移除会话对象 + onceToken token(nullptr, [&]() { + //移除掉会话 + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + + assert(strong_self->_poller->isCurrentThread()); + if (!strong_self->_is_on_manager) { + //该事件不是onManager时触发的,直接操作map + strong_self->_session_map.erase(ptr); + } else { + //遍历map时不能直接删除元素 + strong_self->_poller->async([weak_self, ptr]() { + auto strong_self = weak_self.lock(); + if (strong_self) { + strong_self->_session_map.erase(ptr); + } + }, false); + } + }); + + //获取会话强应用 + auto strong_session = weak_session.lock(); + if (strong_session) { + //触发onError事件回调 + TraceP(strong_session) << cls << " on err: " << err; + strong_session->onError(err); + } + }); + return session; +} + +void TcpServer::start_l(uint16_t port, const std::string &host, uint32_t backlog) { + setupEvent(); + + //新建一个定时器定时管理这些tcp会话 + weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() -> bool { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return false; + } + strong_self->onManagerSession(); + return true; + }, _poller); + // 对每个poller创建一个子server,创建对应套接字,相关事件回调 + EventPollerPool::Instance().for_each([&](const TaskExecutor::Ptr &executor) { + EventPoller::Ptr poller = static_pointer_cast(executor); + if (poller == _poller) { + return; + } + // 如果当前事件轮询器没有指定服务器,则创建一个 + auto &serverRef = _cloned_server[poller.get()]; + if (!serverRef) { + serverRef = onCreatServer(poller); + } + if (serverRef) { + serverRef->cloneFrom(*this); + } + }); + // 当前server的事件轮询器开始监听端口 + if (!_socket->listen(port, host.c_str(), backlog)) { + // 创建tcp监听失败,可能是由于端口占用或权限问题 + string err = (StrPrinter << "Listen on " << host << " " << port << " failed: " << get_uv_errmsg(true)); + throw std::runtime_error(err); + } + for (auto &pr: _cloned_server) { + // 启动子Server, 监听相同的listen fd, epoll操作的accept是抢占式的 + pr.second->_socket->cloneSocket(*_socket); + } + + InfoL << "TCP server listening on [" << host << "]: " << port; +} + +void TcpServer::onManagerSession() { + assert(_poller->isCurrentThread()); + + onceToken token([&]() { + _is_on_manager = true; + }, [&]() { + _is_on_manager = false; + }); + + for (auto &pr : _session_map) { + //遍历时,可能触发onErr事件(也会操作_session_map) + try { + pr.second->session()->onManager(); + } catch (exception &ex) { + WarnL << ex.what(); + } + } +} + +Socket::Ptr TcpServer::createSocket(const EventPoller::Ptr &poller) { + return _on_create_socket(poller); +} + +TcpServer::Ptr TcpServer::getServer(const EventPoller *poller) const { + auto parent = _parent.lock(); + auto &ref = parent ? parent->_cloned_server : _cloned_server; + auto it = ref.find(poller); + if (it != ref.end()) { + //派发到cloned server + return it->second; + } + //派发到parent server + return static_pointer_cast(parent ? parent : const_cast(this)->shared_from_this()); +} + +Session::Ptr TcpServer::createSession(const Socket::Ptr &sock) { + return getServer(sock->getPoller().get())->onAcceptConnection(sock); +} + +} /* namespace turbo */ \ No newline at end of file diff --git a/turbo/network/network/tcp_server.h b/turbo/network/network/tcp_server.h new file mode 100644 index 00000000..993d0829 --- /dev/null +++ b/turbo/network/network/tcp_server.h @@ -0,0 +1,112 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_TCP_SERVER_H_ +#define TURBO_NETWORK_NETWORK_TCP_SERVER_H_ + +#include +#include +#include +#include "turbo/network/network/server.h" +#include "turbo/network/network/session.h" +#include "turbo/network/poller/timer.h" +#include "turbo/network/util/util.h" + + +namespace turbo { + +//TCP服务器,可配置的;配置通过Session::attachServer方法传递给会话对象 +class TcpServer : public Server { +public: + using Ptr = std::shared_ptr; + + /** + * 创建tcp服务器,listen fd的accept事件会加入到所有的poller线程中监听 + * 在调用TcpServer::start函数时,内部会创建多个子TcpServer对象, + * 这些子TcpServer对象通过Socket对象克隆的方式在多个poller线程中监听同一个listen fd + * 这样这个TCP服务器将会通过抢占式accept的方式把客户端均匀的分布到不同的poller线程 + * 通过该方式能实现客户端负载均衡以及提高连接接收速度 + */ + explicit TcpServer(const EventPoller::Ptr &poller = nullptr); + ~TcpServer() override; + + /** + * @brief 开始tcp server + * @param port 本机端口,0则随机 + * @param host 监听网卡ip + * @param backlog tcp listen backlog + */ + template + void start(uint16_t port, const std::string &host = "::", uint32_t backlog = 1024) { + static std::string cls_name = turbo::demangle(typeid(SessionType).name()); + //Session创建器,通过它创建不同类型的服务器 + _session_alloc = [](const TcpServer::Ptr &server, const Socket::Ptr &sock) { + auto session = std::shared_ptr(new SessionType(sock), [](SessionType * ptr) { + TraceP(static_cast(ptr)) << "~" << cls_name; + delete ptr; + }); + TraceP(static_cast(session.get())) << cls_name; + session->setOnCreateSocket(server->_on_create_socket); + return std::make_shared(server, std::move(session), cls_name); + }; + start_l(port, host, backlog); + } + + /** + * @brief 获取服务器监听端口号, 服务器可以选择监听随机端口 + */ + uint16_t getPort(); + + /** + * @brief 自定义socket构建行为 + */ + void setOnCreateSocket(Socket::onCreateSocket cb); + + /** + * 根据socket对象创建Session对象 + * 需要确保在socket归属poller线程执行本函数 + */ + Session::Ptr createSession(const Socket::Ptr &socket); + +protected: + virtual void cloneFrom(const TcpServer &that); + virtual TcpServer::Ptr onCreatServer(const EventPoller::Ptr &poller); + + virtual Session::Ptr onAcceptConnection(const Socket::Ptr &sock); + virtual Socket::Ptr onBeforeAcceptConnection(const EventPoller::Ptr &poller); + +private: + void onManagerSession(); + Socket::Ptr createSocket(const EventPoller::Ptr &poller); + void start_l(uint16_t port, const std::string &host, uint32_t backlog); + Ptr getServer(const EventPoller *) const; + void setupEvent(); + +private: + bool _is_on_manager = false; + bool _main_server = true; + // 父tcp server + std::weak_ptr _parent; + Socket::Ptr _socket; + std::shared_ptr _timer; + Socket::onCreateSocket _on_create_socket; + std::unordered_map _session_map; + std::function _session_alloc; + std::unordered_map _cloned_server; + //对象个数统计 + ObjectStatistic _statistic; +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_NETWORK_TCP_SERVER_H_ */ \ No newline at end of file diff --git a/turbo/network/network/udp_server.cc b/turbo/network/network/udp_server.cc new file mode 100644 index 00000000..ea344179 --- /dev/null +++ b/turbo/network/network/udp_server.cc @@ -0,0 +1,369 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/network/udp_server.h" +#include "turbo/network/util/uv_errno.h" +#include "turbo/network/util/once_token.h" + + +using namespace std; + +namespace turbo { + +static const uint8_t s_in6_addr_maped[] + = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; + +static constexpr auto kUdpDelayCloseMS = 3 * 1000; + +// 套接字标识符 +static UdpServer::PeerIdType makeSockId(sockaddr *addr, int) { + UdpServer::PeerIdType ret; + switch (addr->sa_family) { + case AF_INET : { + ret.resize(18); + ret[0] = ((struct sockaddr_in *) addr)->sin_port >> 8; + ret[1] = ((struct sockaddr_in *) addr)->sin_port & 0xFF; + //ipv4地址统一转换为ipv6方式处理 + memcpy(&ret[2], &s_in6_addr_maped, 12); + memcpy(&ret[14], &(((struct sockaddr_in *) addr)->sin_addr), 4); + return ret; + } + case AF_INET6 : { + ret.resize(18); + ret[0] = ((struct sockaddr_in6 *) addr)->sin6_port >> 8; + ret[1] = ((struct sockaddr_in6 *) addr)->sin6_port & 0xFF; + memcpy(&ret[2], &(((struct sockaddr_in6 *)addr)->sin6_addr), 16); + return ret; + } + default: assert(0); return ""; + } +} + +UdpServer::UdpServer(const EventPoller::Ptr &poller) : Server(poller) { + setOnCreateSocket(nullptr); +} + +void UdpServer::setupEvent() { + _socket = createSocket(_poller); + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _socket->setOnRead([weak_self](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + if (auto strong_self = weak_self.lock()) { + strong_self->onRead(buf, addr, addr_len); + } + }); +} + +UdpServer::~UdpServer() { + if (!_cloned && _socket && _socket->rawFD() != -1) { + InfoL << "Close udp server [" << _socket->get_local_ip() << "]: " << _socket->get_local_port(); + } + _timer.reset(); + _socket.reset(); + _cloned_server.clear(); + if (!_cloned && _session_mutex && _session_map) { + lock_guard lck(*_session_mutex); + _session_map->clear(); + } +} + +void UdpServer::start_l(uint16_t port, const std::string &host) { + setupEvent(); + //主server才创建session map,其他cloned server共享之 + _session_mutex = std::make_shared(); + _session_map = std::make_shared >(); + + // 新建一个定时器定时管理这些 udp 会话,这些对象只由主server做超时管理,cloned server不管理 + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + _timer = std::make_shared(2.0f, [weak_self]() -> bool { + if (auto strong_self = weak_self.lock()) { + strong_self->onManagerSession(); + return true; + } + return false; + }, _poller); + + //clone server至不同线程,让udp server支持多线程 + EventPollerPool::Instance().for_each([&](const TaskExecutor::Ptr &executor) { + auto poller = std::static_pointer_cast(executor); + if (poller == _poller) { + return; + } + auto &serverRef = _cloned_server[poller.get()]; + if (!serverRef) { + serverRef = onCreatServer(poller); + } + if (serverRef) { + serverRef->cloneFrom(*this); + } + }); + + if (!_socket->bindUdpSock(port, host.c_str())) { + // udp 绑定端口失败, 可能是由于端口占用或权限问题 + std::string err = (StrPrinter << "Bind udp socket on " << host << " " << port << " failed: " << get_uv_errmsg(true)); + throw std::runtime_error(err); + } + + for (auto &pr: _cloned_server) { + // 启动子Server +#if 0 + pr.second->_socket->cloneSocket(*_socket); +#else + // 实验发现cloneSocket方式虽然可以节省fd资源,但是在某些系统上线程漂移问题更严重 + pr.second->_socket->bindUdpSock(_socket->get_local_port(), _socket->get_local_ip()); +#endif + } + InfoL << "UDP server bind to [" << host << "]: " << port; +} + +UdpServer::Ptr UdpServer::onCreatServer(const EventPoller::Ptr &poller) { + return std::make_shared(poller); +} + +void UdpServer::cloneFrom(const UdpServer &that) { + if (!that._socket) { + throw std::invalid_argument("UdpServer::cloneFrom other with null socket"); + } + setupEvent(); + _cloned = true; + // clone callbacks + _on_create_socket = that._on_create_socket; + _session_alloc = that._session_alloc; + _session_mutex = that._session_mutex; + _session_map = that._session_map; + // clone properties + this->mINI::operator=(that); +} + +void UdpServer::onRead(const Buffer::Ptr &buf, sockaddr *addr, int addr_len) { + const auto id = makeSockId(addr, addr_len); + onRead_l(true, id, buf, addr, addr_len); +} + +static void emitSessionRecv(const SessionHelper::Ptr &helper, const Buffer::Ptr &buf) { + if (!helper->enable) { + // 延时销毁中 + return; + } + try { + helper->session()->onRecv(buf); + } catch (SockException &ex) { + helper->session()->shutdown(ex); + } catch (exception &ex) { + helper->session()->shutdown(SockException(ErrCode::Err_shutdown, ex.what())); + } +} + +void UdpServer::onRead_l(bool is_server_fd, const UdpServer::PeerIdType &id, const Buffer::Ptr &buf, sockaddr *addr, int addr_len) { + // udp server fd收到数据时触发此函数;大部分情况下数据应该在peer fd触发,此函数应该不是热点函数 + bool is_new = false; + if (auto helper = getOrCreateSession(id, buf, addr, addr_len, is_new)) { + if (helper->session()->getPoller()->isCurrentThread()) { + //当前线程收到数据,直接处理数据 + emitSessionRecv(helper, buf); + } else { + //数据漂移到其他线程,需要先切换线程 + WarnL << "UDP packet incoming from other thread"; + std::weak_ptr weak_helper = helper; + //由于socket读buffer是该线程上所有socket共享复用的,所以不能跨线程使用,必须先拷贝一下 + auto cacheable_buf = std::make_shared(buf->toString()); + helper->session()->async([weak_helper, cacheable_buf]() { + if (auto strong_helper = weak_helper.lock()) { + emitSessionRecv(strong_helper, cacheable_buf); + } + }); + } + +#if !defined(NDEBUG) + if (!is_new) { + TraceL << "UDP packet incoming from " << (is_server_fd ? "server fd" : "other peer fd"); + } +#endif + } +} + +void UdpServer::onManagerSession() { + decltype(_session_map) copy_map; + { + std::lock_guard lock(*_session_mutex); + //拷贝map,防止遍历时移除对象 + copy_map = std::make_shared >(*_session_map); + } + EventPollerPool::Instance().for_each([copy_map](const TaskExecutor::Ptr &executor) { + auto poller = std::static_pointer_cast(executor); + poller->async([copy_map]() { + for (auto &pr : *copy_map) { + auto &session = pr.second->session(); + if (!session->getPoller()->isCurrentThread()) { + //该session不归属该poller管理 + continue; + } + try { + // UDP 会话需要处理超时 + session->onManager(); + } catch (exception &ex) { + WarnL << "Exception occurred when emit onManager: " << ex.what(); + } + } + }); + }); +} + +SessionHelper::Ptr UdpServer::getOrCreateSession(const UdpServer::PeerIdType &id, const Buffer::Ptr &buf, sockaddr *addr, int addr_len, bool &is_new) { + { + //减小临界区 + std::lock_guard lock(*_session_mutex); + auto it = _session_map->find(id); + if (it != _session_map->end()) { + return it->second; + } + } + is_new = true; + return createSession(id, buf, addr, addr_len); +} + +SessionHelper::Ptr UdpServer::createSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + // 此处改成自定义获取poller对象,防止负载不均衡 + auto socket = createSocket(EventPollerPool::Instance().getPoller(false), buf, addr, addr_len); + if (!socket) { + //创建socket失败,本次onRead事件收到的数据直接丢弃 + return nullptr; + } + + auto addr_str = string((char *) addr, addr_len); + std::weak_ptr weak_self = std::static_pointer_cast(shared_from_this()); + auto helper_creator = [this, weak_self, socket, addr_str, id]() -> SessionHelper::Ptr { + auto server = weak_self.lock(); + if (!server) { + return nullptr; + } + + //如果已经创建该客户端对应的UdpSession类,那么直接返回 + lock_guard lck(*_session_mutex); + auto it = _session_map->find(id); + if (it != _session_map->end()) { + return it->second; + } + + assert(_socket); + socket->bindUdpSock(_socket->get_local_port(), _socket->get_local_ip()); + socket->bindPeerAddr((struct sockaddr *) addr_str.data(), addr_str.size()); + + auto helper = _session_alloc(server, socket); + // 把本服务器的配置传递给 Session + helper->session()->attachServer(*this); + + std::weak_ptr weak_helper = helper; + socket->setOnRead([weak_self, weak_helper, id](const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + + //快速判断是否为本会话的的数据, 通常应该成立 + if (id == makeSockId(addr, addr_len)) { + if (auto strong_helper = weak_helper.lock()) { + emitSessionRecv(strong_helper, buf); + } + return; + } + + //收到非本peer fd的数据,让server去派发此数据到合适的session对象 + strong_self->onRead_l(false, id, buf, addr, addr_len); + }); + socket->setOnErr([weak_self, weak_helper, id](const SockException &err) { + // 在本函数作用域结束时移除会话对象 + // 目的是确保移除会话前执行其 onError 函数 + // 同时避免其 onError 函数抛异常时没有移除会话对象 + onceToken token(nullptr, [&]() { + // 移除掉会话 + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + // 延时移除udp session, 防止频繁快速重建对象 + strong_self->_poller->doDelayTask(kUdpDelayCloseMS, [weak_self, id]() { + if (auto strong_self = weak_self.lock()) { + // 从共享map中移除本session对象 + lock_guard lck(*strong_self->_session_mutex); + strong_self->_session_map->erase(id); + } + return 0; + }); + }); + + // 获取会话强应用 + if (auto strong_helper = weak_helper.lock()) { + // 触发 onError 事件回调 + TraceP(strong_helper->session()) << strong_helper->className() << " on err: " << err; + strong_helper->enable = false; + strong_helper->session()->onError(err); + } + }); + + auto pr = _session_map->emplace(id, std::move(helper)); + assert(pr.second); + return pr.first->second; + }; + + if (socket->getPoller()->isCurrentThread()) { + // 该socket分配在本线程,直接创建helper对象 + return helper_creator(); + } + + // 该socket分配在其他线程,需要先拷贝buffer,然后在其所在线程创建helper对象并处理数据 + // 这里主要是为了负载均衡,每次来数据了选择一个负载最小的线程来处理数据 + auto cacheable_buf = std::make_shared(buf->toString()); + socket->getPoller()->async([helper_creator, cacheable_buf]() { + // 在该socket所在线程创建helper对象 + auto helper = helper_creator(); + if (helper) { + // 可能未实质创建hlepr对象成功,可能获取到其他线程创建的helper对象 + helper->session()->getPoller()->async([helper, cacheable_buf]() { + // 该数据不能丢弃,给session对象消费 + emitSessionRecv(helper, cacheable_buf); + }); + } + }); + return nullptr; +} + +void UdpServer::setOnCreateSocket(onCreateSocket cb) { + if (cb) { + _on_create_socket = std::move(cb); + } else { + _on_create_socket = [](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + return Socket::createSocket(poller, false); + }; + } + for (auto &pr : _cloned_server) { + pr.second->setOnCreateSocket(cb); + } +} + +uint16_t UdpServer::getPort() { + if (!_socket) { + return 0; + } + return _socket->get_local_port(); +} + +Socket::Ptr UdpServer::createSocket(const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len) { + return _on_create_socket(poller, buf, addr, addr_len); +} + + +StatisticImp(UdpServer) + +} // namespace turbo + diff --git a/turbo/network/network/udp_server.h b/turbo/network/network/udp_server.h new file mode 100644 index 00000000..824bae01 --- /dev/null +++ b/turbo/network/network/udp_server.h @@ -0,0 +1,127 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_NETWORK_UDP_SERVER_H_ +#define TURBO_NETWORK_NETWORK_UDP_SERVER_H_ + +#include "turbo/network/network/server.h" +#include "turbo/network/network/session.h" + +namespace turbo { + +class UdpServer : public Server { +public: + using Ptr = std::shared_ptr; + using PeerIdType = std::string; + using onCreateSocket = std::function; + + explicit UdpServer(const EventPoller::Ptr &poller = nullptr); + ~UdpServer() override; + + /** + * @brief 开始监听服务器 + */ + template + void start(uint16_t port, const std::string &host = "::") { + static std::string cls_name = turbo::demangle(typeid(SessionType).name()); + // Session 创建器, 通过它创建不同类型的服务器 + _session_alloc = [](const UdpServer::Ptr &server, const Socket::Ptr &sock) { + auto session = std::shared_ptr(new SessionType(sock), [](SessionType * ptr) { + TraceP(static_cast(ptr)) << "~" << cls_name; + delete ptr; + }); + TraceP(static_cast(session.get())) << cls_name; + auto sock_creator = server->_on_create_socket; + session->setOnCreateSocket([sock_creator](const EventPoller::Ptr &poller) { + return sock_creator(poller, nullptr, nullptr, 0); + }); + return std::make_shared(server, std::move(session), cls_name); + }; + start_l(port, host); + } + + /** + * @brief 获取服务器监听端口号, 服务器可以选择监听随机端口 + */ + uint16_t getPort(); + + /** + * @brief 自定义socket构建行为 + */ + void setOnCreateSocket(onCreateSocket cb); + +protected: + virtual Ptr onCreatServer(const EventPoller::Ptr &poller); + virtual void cloneFrom(const UdpServer &that); + +private: + /** + * @brief 开始udp server + * @param port 本机端口,0则随机 + * @param host 监听网卡ip + */ + void start_l(uint16_t port, const std::string &host = "::"); + + /** + * @brief 定时管理 Session, UDP 会话需要根据需要处理超时 + */ + void onManagerSession(); + + void onRead(const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 接收到数据,可能来自server fd,也可能来自peer fd + * @param is_server_fd 时候为server fd + * @param id 客户端id + * @param buf 数据 + * @param addr 客户端地址 + * @param addr_len 客户端地址长度 + */ + void onRead_l(bool is_server_fd, const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 根据对端信息获取或创建一个会话 + */ + SessionHelper::Ptr getOrCreateSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len, bool &is_new); + + /** + * @brief 创建一个会话, 同时进行必要的设置 + */ + SessionHelper::Ptr createSession(const PeerIdType &id, const Buffer::Ptr &buf, struct sockaddr *addr, int addr_len); + + /** + * @brief 创建socket + */ + Socket::Ptr createSocket(const EventPoller::Ptr &poller, const Buffer::Ptr &buf = nullptr, struct sockaddr *addr = nullptr, int addr_len = 0); + + void setupEvent(); + +private: + bool _cloned = false; + Socket::Ptr _socket; + std::shared_ptr _timer; + onCreateSocket _on_create_socket; + //cloned server共享主server的session map,防止数据在不同server间漂移 + std::shared_ptr _session_mutex; + std::shared_ptr > _session_map; + //主server持有cloned server的引用 + std::unordered_map _cloned_server; + std::function _session_alloc; + // 对象个数统计 + ObjectStatistic _statistic; +}; + +} // namespace turbo + +#endif // TURBO_NETWORK_NETWORK_UDP_SERVER_H_ diff --git a/turbo/network/poller/event_poller.cc b/turbo/network/poller/event_poller.cc new file mode 100644 index 00000000..3ca3fa23 --- /dev/null +++ b/turbo/network/poller/event_poller.cc @@ -0,0 +1,526 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/poller/select_wrap.h" +#include "turbo/network/poller/event_poller.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/uv_errno.h" +#include "turbo/network/util/time_ticker.h" +#include "turbo/network/util/notice_center.h" +#include "turbo/network/network/sock_util.h" + + +#if defined(HAS_EPOLL) +#include + +#if !defined(EPOLLEXCLUSIVE) +#define EPOLLEXCLUSIVE 0 +#endif + +#define EPOLL_SIZE 1024 + +//防止epoll惊群 +#ifndef EPOLLEXCLUSIVE +#define EPOLLEXCLUSIVE 0 +#endif + +#define toEpoll(event) (((event) & Event_Read) ? EPOLLIN : 0) \ + | (((event) & Event_Write) ? EPOLLOUT : 0) \ + | (((event) & Event_Error) ? (EPOLLHUP | EPOLLERR) : 0) \ + | (((event) & Event_LT) ? 0 : EPOLLET) + +#define toPoller(epoll_event) (((epoll_event) & EPOLLIN) ? Event_Read : 0) \ + | (((epoll_event) & EPOLLOUT) ? Event_Write : 0) \ + | (((epoll_event) & EPOLLHUP) ? Event_Error : 0) \ + | (((epoll_event) & EPOLLERR) ? Event_Error : 0) +#endif //HAS_EPOLL + +using namespace std; + +namespace turbo { + +EventPoller &EventPoller::Instance() { + return *(EventPollerPool::Instance().getFirstPoller()); +} + +void EventPoller::addEventPipe() { + SockUtil::setNoBlocked(_pipe.readFD()); + SockUtil::setNoBlocked(_pipe.writeFD()); + + // 添加内部管道事件 + if (addEvent(_pipe.readFD(), EventPoller::Event_Read, [this](int event) { onPipeEvent(); }) == -1) { + throw std::runtime_error("Add pipe fd to poller failed"); + } +} + +EventPoller::EventPoller(std::string name) { + _name = std::move(name); +#if defined(HAS_EPOLL) + _epoll_fd = epoll_create(EPOLL_SIZE); + if (_epoll_fd == -1) { + throw runtime_error(StrPrinter << "Create epoll fd failed: " << get_uv_errmsg()); + } + SockUtil::setCloExec(_epoll_fd); +#endif //HAS_EPOLL + _logger = Logger::Instance().shared_from_this(); + addEventPipe(); +} + +void EventPoller::shutdown() { + async_l([]() { + throw ExitException(); + }, false, true); + + if (_loop_thread) { + //防止作为子进程时崩溃 + try { _loop_thread->join(); } catch (...) {} + delete _loop_thread; + _loop_thread = nullptr; + } +} + +EventPoller::~EventPoller() { + shutdown(); +#if defined(HAS_EPOLL) + if (_epoll_fd != -1) { + close(_epoll_fd); + _epoll_fd = -1; + } +#endif //defined(HAS_EPOLL) + //退出前清理管道中的数据 + onPipeEvent(); + InfoL << this; +} + +int EventPoller::addEvent(int fd, int event, PollEventCB cb) { + TimeTicker(); + if (!cb) { + WarnL << "PollEventCB is empty"; + return -1; + } + + if (isCurrentThread()) { +#if defined(HAS_EPOLL) + struct epoll_event ev = {0}; + ev.events = (toEpoll(event)) | EPOLLEXCLUSIVE; + ev.data.fd = fd; + int ret = epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, fd, &ev); + if (ret == 0) { + _event_map.emplace(fd, std::make_shared(std::move(cb))); + } + return ret; +#else +#ifndef _WIN32 + //win32平台,socket套接字不等于文件描述符,所以可能不适用这个限制 + if (fd >= FD_SETSIZE || _event_map.size() >= FD_SETSIZE) { + WarnL << "select() can not watch fd bigger than " << FD_SETSIZE; + return -1; + } +#endif + auto record = std::make_shared(); + record->fd = fd; + record->event = event; + record->call_back = std::move(cb); + _event_map.emplace(fd, record); + return 0; +#endif //HAS_EPOLL + } + + async([this, fd, event, cb]() mutable { + addEvent(fd, event, std::move(cb)); + }); + return 0; +} + +int EventPoller::delEvent(int fd, PollCompleteCB cb) { + TimeTicker(); + if (!cb) { + cb = [](bool success) {}; + } + + if (isCurrentThread()) { +#if defined(HAS_EPOLL) + bool success = epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr) == 0 && _event_map.erase(fd) > 0; + if (success) { + _event_cache_expired_map[fd] = true; + } + cb(success); + return success ? 0 : -1; +#else + bool success = _event_map.erase(fd); + if (success) { + _event_cache_expired_map[fd] = true; + } + cb(success); + return 0; +#endif //HAS_EPOLL + } + + //跨线程操作 + async([this, fd, cb]() mutable { + delEvent(fd, std::move(cb)); + }); + return 0; +} + +int EventPoller::modifyEvent(int fd, int event, PollCompleteCB cb) { + TimeTicker(); + if (!cb) { + cb = [](bool success) {}; + } + if (isCurrentThread()) { +#if defined(HAS_EPOLL) + struct epoll_event ev = { 0 }; + ev.events = toEpoll(event); + ev.data.fd = fd; + auto ret = epoll_ctl(_epoll_fd, EPOLL_CTL_MOD, fd, &ev); + cb(ret == 0); + return ret; +#else + auto it = _event_map.find(fd); + if (it != _event_map.end()) { + it->second->event = event; + } + cb(it != _event_map.end()); + return 0; +#endif // HAS_EPOLL + } + async([this, fd, event, cb]() mutable { + modifyEvent(fd, event, std::move(cb)); + }); + return 0; +} + +Task::Ptr EventPoller::async(TaskIn task, bool may_sync) { + return async_l(std::move(task), may_sync, false); +} + +Task::Ptr EventPoller::async_first(TaskIn task, bool may_sync) { + return async_l(std::move(task), may_sync, true); +} + +Task::Ptr EventPoller::async_l(TaskIn task, bool may_sync, bool first) { + TimeTicker(); + if (may_sync && isCurrentThread()) { + task(); + return nullptr; + } + + auto ret = std::make_shared(std::move(task)); + { + lock_guard lck(_mtx_task); + if (first) { + _list_task.emplace_front(ret); + } else { + _list_task.emplace_back(ret); + } + } + //写数据到管道,唤醒主线程 + _pipe.write("", 1); + return ret; +} + +bool EventPoller::isCurrentThread() { + return !_loop_thread || _loop_thread->get_id() == this_thread::get_id(); +} +// 执行处理异步队列中的任务,队列中的任务可以通过外部指针cancel +inline void EventPoller::onPipeEvent() { + char buf[1024]; + int err = 0; + while (true) { + if ((err = _pipe.read(buf, sizeof(buf))) > 0) { + // 读到管道数据,继续读,直到读空为止 + continue; + } + if (err == 0 || get_uv_error(true) != UV_EAGAIN) { + // 收到eof或非EAGAIN(无更多数据)错误,说明管道无效了,重新打开管道 + ErrorL << "Invalid pipe fd of event poller, reopen it"; + delEvent(_pipe.readFD()); + _pipe.reOpen(); + addEventPipe(); + } + break; + } + + decltype(_list_task) _list_swap; + { + lock_guard lck(_mtx_task); + _list_swap.swap(_list_task); + } + + _list_swap.for_each([&](const Task::Ptr &task) { + try { + (*task)(); + } catch (ExitException &) { + _exit_flag = true; + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do async task: " << ex.what(); + } + }); +} + +static mutex s_all_poller_mtx; +static map > s_all_poller; + +BufferRaw::Ptr EventPoller::getSharedBuffer() { + auto ret = _shared_buffer.lock(); + if (!ret) { + //预留一个字节存放\0结尾符 + ret = BufferRaw::create(); + ret->setCapacity(1 + SOCKET_DEFAULT_BUF_SIZE); + _shared_buffer = ret; + } + return ret; +} + +thread::id EventPoller::getThreadId() const { + return _loop_thread ? _loop_thread->get_id() : thread::id(); +} + +const std::string& EventPoller::getThreadName() const { + return _name; +} + +//static +// 获取当前线程的poller +EventPoller::Ptr EventPoller::getCurrentPoller(){ + lock_guard lck(s_all_poller_mtx); + auto it = s_all_poller.find(this_thread::get_id()); + if (it == s_all_poller.end()) { + return nullptr; + } + return it->second.lock(); +} + +// ref_self注册为全局可见的poller线程 +void EventPoller::runLoop(bool blocked,bool ref_self) { + if (blocked) { + if (ref_self) { + lock_guard lck(s_all_poller_mtx); + s_all_poller[this_thread::get_id()] = shared_from_this(); + } + _sem_run_started.post(); + _exit_flag = false; + uint64_t minDelay; +#if defined(HAS_EPOLL) + struct epoll_event events[EPOLL_SIZE]; + while (!_exit_flag) { + minDelay = getMinDelay(); + startSleep();//用于统计当前线程负载情况 + int ret = epoll_wait(_epoll_fd, events, EPOLL_SIZE, minDelay ? minDelay : -1); + sleepWakeUp();//用于统计当前线程负载情况 + if (ret <= 0) { + //超时或被打断 + continue; + } + // delevent + _event_cache_expired_map.clear(); + + for (int i = 0; i < ret; ++i) { + struct epoll_event &ev = events[i]; + int fd = ev.data.fd; + if (_event_cache_expired_map.find(fd) != _event_cache_expired_map.end()) { + //event cache refresh + continue; + } + + auto it = _event_map.find(fd); + if (it == _event_map.end()) { + epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, fd, nullptr); + continue; + } + auto cb = it->second; + try { + (*cb)(toPoller(ev.events)); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do event task: " << ex.what(); + } + } + } +#else + int ret, max_fd; + FdSet set_read, set_write, set_err; + List callback_list; + struct timeval tv; + while (!_exit_flag) { + //定时器事件中可能操作_event_map + minDelay = getMinDelay(); + tv.tv_sec = (decltype(tv.tv_sec)) (minDelay / 1000); + tv.tv_usec = 1000 * (minDelay % 1000); + + set_read.fdZero(); + set_write.fdZero(); + set_err.fdZero(); + max_fd = 0; + for (auto &pr : _event_map) { + if (pr.first > max_fd) { + max_fd = pr.first; + } + if (pr.second->event & Event_Read) { + set_read.fdSet(pr.first);//监听管道可读事件 + } + if (pr.second->event & Event_Write) { + set_write.fdSet(pr.first);//监听管道可写事件 + } + if (pr.second->event & Event_Error) { + set_err.fdSet(pr.first);//监听管道错误事件 + } + } + + startSleep();//用于统计当前线程负载情况 + ret = turbo_select(max_fd + 1, &set_read, &set_write, &set_err, minDelay ? &tv : nullptr); + sleepWakeUp();//用于统计当前线程负载情况 + + if (ret <= 0) { + //超时或被打断 + continue; + } + + _event_cache_expired_map.clear(); + + //收集select事件类型 + for (auto &pr : _event_map) { + int event = 0; + if (set_read.isSet(pr.first)) { + event |= Event_Read; + } + if (set_write.isSet(pr.first)) { + event |= Event_Write; + } + if (set_err.isSet(pr.first)) { + event |= Event_Error; + } + if (event != 0) { + pr.second->attach = event; + callback_list.emplace_back(pr.second); + } + } + + callback_list.for_each([this](Poll_Record::Ptr &record) { + if (this->_event_cache_expired_map.find(record->fd) != this->_event_cache_expired_map.end()) { + //event cache refresh + return; + } + + try { + record->call_back(record->attach); + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do event task: " << ex.what(); + } + }); + callback_list.clear(); + } +#endif //HAS_EPOLL + } else { + _loop_thread = new thread(&EventPoller::runLoop, this, true, ref_self); + _sem_run_started.wait(); + } +} + +uint64_t EventPoller::flushDelayTask(uint64_t now_time) { + decltype(_delay_task_map) task_copy; + task_copy.swap(_delay_task_map); + + for (auto it = task_copy.begin(); it != task_copy.end() && it->first <= now_time; it = task_copy.erase(it)) { + //已到期的任务 + try { + auto next_delay = (*(it->second))(); + if (next_delay) { + //可重复任务,更新时间截止线 + _delay_task_map.emplace(next_delay + now_time, std::move(it->second)); + } + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do delay task: " << ex.what(); + } + } + + task_copy.insert(_delay_task_map.begin(), _delay_task_map.end()); + task_copy.swap(_delay_task_map); + + auto it = _delay_task_map.begin(); + if (it == _delay_task_map.end()) { + //没有剩余的定时器了 + return 0; + } + //最近一个定时器的执行延时 + return it->first - now_time; +} + +uint64_t EventPoller::getMinDelay() { + auto it = _delay_task_map.begin(); + if (it == _delay_task_map.end()) { + //没有剩余的定时器了 + return 0; + } + auto now = getCurrentMillisecond(); + if (it->first > now) { + //所有任务尚未到期 + return it->first - now; + } + //执行已到期的任务并刷新休眠延时 + return flushDelayTask(now); +} + +EventPoller::DelayTask::Ptr EventPoller::doDelayTask(uint64_t delay_ms, function task) { + DelayTask::Ptr ret = std::make_shared(std::move(task)); + auto time_line = getCurrentMillisecond() + delay_ms; + async_first([time_line, ret, this]() { + //异步执行的目的是刷新select或epoll的休眠时间 + _delay_task_map.emplace(time_line, ret); + }); + return ret; +} + + +/////////////////////////////////////////////// + +static size_t s_pool_size = 0; +static bool s_enable_cpu_affinity = true; + +INSTANCE_IMP(EventPollerPool) + +EventPoller::Ptr EventPollerPool::getFirstPoller() { + return static_pointer_cast(_threads.front()); +} + +EventPoller::Ptr EventPollerPool::getPoller(bool prefer_current_thread) { + auto poller = EventPoller::getCurrentPoller(); + if (prefer_current_thread && _prefer_current_thread && poller) { + return poller; + } + return static_pointer_cast(getExecutor()); +} + +void EventPollerPool::preferCurrentThread(bool flag) { + _prefer_current_thread = flag; +} + +const std::string EventPollerPool::kOnStarted = "kBroadcastEventPollerPoolStarted"; + +EventPollerPool::EventPollerPool() { + auto size = addPoller("event poller", s_pool_size, static_cast(ThreadPool::Priority::PRIORITY_HIGHEST), true, s_enable_cpu_affinity); + NOTICE_EMIT(EventPollerPoolOnStartedArgs, kOnStarted, *this, size); + InfoL << "EventPoller created size: " << size; +} + +void EventPollerPool::setPoolSize(size_t size) { + s_pool_size = size; +} + +void EventPollerPool::enableCpuAffinity(bool enable) { + s_enable_cpu_affinity = enable; +} + +} // namespace turbo + diff --git a/turbo/network/poller/event_poller.h b/turbo/network/poller/event_poller.h new file mode 100644 index 00000000..adeda64b --- /dev/null +++ b/turbo/network/poller/event_poller.h @@ -0,0 +1,287 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_POLLER_EVENT_POLLER_H_ +#define TURBO_NETWORK_POLLER_EVENT_POLLER_H_ + +#include +#include +#include +#include +#include +#include +#include "turbo/network/poller/pipe_wrap.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/list.h" +#include "turbo/network/thread/task_executor.h" +#include "turbo/network/thread/thread_pool.h" +#include "turbo/network/network/buffer.h" + +#if defined(__linux__) || defined(__linux) +#define HAS_EPOLL +#endif //__linux__ + +namespace turbo { + +class EventPoller : public TaskExecutor, public AnyStorage, public std::enable_shared_from_this { +public: + friend class TaskExecutorGetterImp; + + using Ptr = std::shared_ptr; + using PollEventCB = std::function; + using PollCompleteCB = std::function; + using DelayTask = TaskCancelableImp; + + typedef enum { + Event_Read = 1 << 0, //读事件 + Event_Write = 1 << 1, //写事件 + Event_Error = 1 << 2, //错误事件 + Event_LT = 1 << 3,//水平触发 + } Poll_Event; + + ~EventPoller(); + + /** + * 获取EventPollerPool单例中的第一个EventPoller实例, + * 保留该接口是为了兼容老代码 + * @return 单例 + */ + static EventPoller &Instance(); + + /** + * 添加事件监听 + * @param fd 监听的文件描述符 + * @param event 事件类型,例如 Event_Read | Event_Write + * @param cb 事件回调functional + * @return -1:失败,0:成功 + */ + int addEvent(int fd, int event, PollEventCB cb); + + /** + * 删除事件监听 + * @param fd 监听的文件描述符 + * @param cb 删除成功回调functional + * @return -1:失败,0:成功 + */ + int delEvent(int fd, PollCompleteCB cb = nullptr); + + /** + * 修改监听事件类型 + * @param fd 监听的文件描述符 + * @param event 事件类型,例如 Event_Read | Event_Write + * @return -1:失败,0:成功 + */ + int modifyEvent(int fd, int event, PollCompleteCB cb = nullptr); + + /** + * 异步执行任务 + * @param task 任务 + * @param may_sync 如果调用该函数的线程就是本对象的轮询线程,那么may_sync为true时就是同步执行任务 + * @return 是否成功,一定会返回true + */ + Task::Ptr async(TaskIn task, bool may_sync = true) override; + + /** + * 同async方法,不过是把任务打入任务列队头,这样任务优先级最高 + * @param task 任务 + * @param may_sync 如果调用该函数的线程就是本对象的轮询线程,那么may_sync为true时就是同步执行任务 + * @return 是否成功,一定会返回true + */ + Task::Ptr async_first(TaskIn task, bool may_sync = true) override; + + /** + * 判断执行该接口的线程是否为本对象的轮询线程 + * @return 是否为本对象的轮询线程 + */ + bool isCurrentThread(); + + /** + * 延时执行某个任务 + * @param delay_ms 延时毫秒数 + * @param task 任务,返回值为0时代表不再重复任务,否则为下次执行延时,如果任务中抛异常,那么默认不重复任务 + * @return 可取消的任务标签 + */ + DelayTask::Ptr doDelayTask(uint64_t delay_ms, std::function task); + + /** + * 获取当前线程关联的Poller实例 + */ + static EventPoller::Ptr getCurrentPoller(); + + /** + * 获取当前线程下所有socket共享的读缓存 + */ + BufferRaw::Ptr getSharedBuffer(); + + /** + * 获取poller线程id + */ + std::thread::id getThreadId() const; + + /** + * 获取线程名 + */ + const std::string& getThreadName() const; + +private: + /** + * 本对象只允许在EventPollerPool中构造 + */ + // 构造的时候添加管道读的回调事件,会去触发调用异步加入的任务 + EventPoller(std::string name); + + /** + * 执行事件轮询 + * @param blocked 是否用执行该接口的线程执行轮询 + * @param ref_self 是记录本对象到thread local变量 + */ + void runLoop(bool blocked, bool ref_self); + + /** + * 内部管道事件,用于唤醒轮询线程用 + */ + void onPipeEvent(); + + /** + * 切换线程并执行任务 + * @param task + * @param may_sync + * @param first + * @return 可取消的任务本体,如果已经同步执行,则返回nullptr + */ + Task::Ptr async_l(TaskIn task, bool may_sync = true, bool first = false); + + /** + * 结束事件轮询 + * 需要指出的是,一旦结束就不能再次恢复轮询线程 + */ + void shutdown(); + + /** + * 刷新延时任务 + */ + uint64_t flushDelayTask(uint64_t now); + + /** + * 获取select或epoll休眠时间 + */ + uint64_t getMinDelay(); + + /** + * 添加管道监听事件 + */ + void addEventPipe(); + +private: + class ExitException : public std::exception {}; + +private: + //标记loop线程是否退出 + bool _exit_flag; + //线程名 + std::string _name; + //当前线程下,所有socket共享的读缓存 + std::weak_ptr _shared_buffer; + //执行事件循环的线程 + std::thread *_loop_thread = nullptr; + //通知事件循环的线程已启动 + semaphore _sem_run_started; + + //内部事件管道 + PipeWrap _pipe; + //从其他线程切换过来的任务 + std::mutex _mtx_task; + List _list_task; + + //保持日志可用 + Logger::Ptr _logger; + +#if defined(HAS_EPOLL) + //epoll相关 + int _epoll_fd = -1; + unordered_map > _event_map; +#else + //select相关 + struct Poll_Record { + using Ptr = std::shared_ptr; + int fd; + int event; + int attach; + PollEventCB call_back; + }; + unordered_map _event_map; +#endif //HAS_EPOLL + unordered_map _event_cache_expired_map; + + //定时器相关 + std::multimap _delay_task_map; +}; + +class EventPollerPool : public std::enable_shared_from_this, public TaskExecutorGetterImp { +public: + using Ptr = std::shared_ptr; + static const std::string kOnStarted; + #define EventPollerPoolOnStartedArgs EventPollerPool &pool, size_t &size + + ~EventPollerPool() = default; + + /** + * 获取单例 + * @return + */ + static EventPollerPool &Instance(); + + /** + * 设置EventPoller个数,在EventPollerPool单例创建前有效 + * 在不调用此方法的情况下,默认创建thread::hardware_concurrency()个EventPoller实例 + * @param size EventPoller个数,如果为0则为thread::hardware_concurrency() + */ + static void setPoolSize(size_t size = 0); + + /** + * 内部创建线程是否设置cpu亲和性,默认设置cpu亲和性 + */ + static void enableCpuAffinity(bool enable); + + /** + * 获取第一个实例 + * @return + */ + EventPoller::Ptr getFirstPoller(); + + /** + * 根据负载情况获取轻负载的实例 + * 如果优先返回当前线程,那么会返回当前线程 + * 返回当前线程的目的是为了提高线程安全性 + * @param prefer_current_thread 是否优先获取当前线程 + */ + EventPoller::Ptr getPoller(bool prefer_current_thread = true); + + /** + * 设置 getPoller() 是否优先返回当前线程 + * 在批量创建Socket对象时,如果优先返回当前线程, + * 那么将导致负载不够均衡,所以可以暂时关闭然后再开启 + * @param flag 是否优先返回当前线程 + */ + void preferCurrentThread(bool flag = true); + +private: + EventPollerPool(); + +private: + bool _prefer_current_thread = true; +}; + +} // namespace turbo +#endif /* TURBO_NETWORK_POLLER_EVENT_POLLER_H_ */ \ No newline at end of file diff --git a/turbo/network/poller/pipe.cc b/turbo/network/poller/pipe.cc new file mode 100644 index 00000000..15c6b24c --- /dev/null +++ b/turbo/network/poller/pipe.cc @@ -0,0 +1,66 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include "turbo/network/poller/pipe.h" +#include "turbo/network/network/sock_util.h" + +using namespace std; + +namespace turbo { + +Pipe::Pipe(const onRead &cb, const EventPoller::Ptr &poller) { + _poller = poller; + if (!_poller) { + _poller = EventPollerPool::Instance().getPoller(); + } + _pipe = std::make_shared(); + auto pipe = _pipe; + _poller->addEvent(_pipe->readFD(), EventPoller::Event_Read, [cb, pipe](int event) { +#if defined(_WIN32) + unsigned long nread = 1024; +#else + int nread = 1024; +#endif //defined(_WIN32) + ioctl(pipe->readFD(), FIONREAD, &nread); +#if defined(_WIN32) + std::shared_ptr buf(new char[nread + 1], [](char *ptr) {delete[] ptr; }); + buf.get()[nread] = '\0'; + nread = pipe->read(buf.get(), nread + 1); + if (cb) { + cb(nread, buf.get()); + } +#else + char buf[nread + 1]; + buf[nread] = '\0'; + nread = pipe->read(buf, sizeof(buf)); + if (cb) { + cb(nread, buf); + } +#endif // defined(_WIN32) + }); +} + +Pipe::~Pipe() { + if (_pipe) { + auto pipe = _pipe; + _poller->delEvent(pipe->readFD(), [pipe](bool success) {}); + } +} + +void Pipe::send(const char *buf, int size) { + _pipe->write(buf, size); +} + +} // namespace turbo \ No newline at end of file diff --git a/turbo/network/poller/pipe.h b/turbo/network/poller/pipe.h new file mode 100644 index 00000000..4a650e90 --- /dev/null +++ b/turbo/network/poller/pipe.h @@ -0,0 +1,40 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_POLLER_PIPE_H_ +#define TURBO_NETWORK_POLLER_PIPE_H_ + +#include +#include "turbo/network/poller/pipe_wrap.h" +#include "turbo/network/poller/event_poller.h" + + +namespace turbo { + +class Pipe { +public: + using onRead = std::function; + + Pipe(const onRead &cb = nullptr, const EventPoller::Ptr &poller = nullptr); + ~Pipe(); + + void send(const char *send, int size = 0); + +private: + std::shared_ptr _pipe; + EventPoller::Ptr _poller; +}; + +} // namespace turbo +#endif /* TURBO_NETWORK_POLLER_PIPE_H_ */ \ No newline at end of file diff --git a/turbo/network/poller/pipe_wrap.cc b/turbo/network/poller/pipe_wrap.cc new file mode 100644 index 00000000..6d11a0b8 --- /dev/null +++ b/turbo/network/poller/pipe_wrap.cc @@ -0,0 +1,101 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include "turbo/network/poller/pipe_wrap.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/uv_errno.h" +#include "turbo/network/network/sock_util.h" + + +using namespace std; + +#define checkFD(fd) \ + if (fd == -1) { \ + clearFD(); \ + throw runtime_error(StrPrinter << "Create windows pipe failed: " << get_uv_errmsg());\ + } + +#define closeFD(fd) \ + if (fd != -1) { \ + close(fd);\ + fd = -1;\ + } + +namespace turbo { + +PipeWrap::PipeWrap() { + reOpen(); +} + +void PipeWrap::reOpen() { + clearFD(); +#if defined(_WIN32) + const char *localip = SockUtil::support_ipv6() ? "::1" : "127.0.0.1"; + auto listener_fd = SockUtil::listen(0, localip); + checkFD(listener_fd) + SockUtil::setNoBlocked(listener_fd,false); + auto localPort = SockUtil::get_local_port(listener_fd); + _pipe_fd[1] = SockUtil::connect(localip, localPort,false); + checkFD(_pipe_fd[1]) + _pipe_fd[0] = (int)accept(listener_fd, nullptr, nullptr); + checkFD(_pipe_fd[0]) + SockUtil::setNoDelay(_pipe_fd[0]); + SockUtil::setNoDelay(_pipe_fd[1]); + close(listener_fd); +#else + if (pipe(_pipe_fd) == -1) { + throw runtime_error(StrPrinter << "Create posix pipe failed: " << get_uv_errmsg()); + } +#endif // defined(_WIN32) + SockUtil::setNoBlocked(_pipe_fd[0], true); + SockUtil::setNoBlocked(_pipe_fd[1], false); + SockUtil::setCloExec(_pipe_fd[0]); + SockUtil::setCloExec(_pipe_fd[1]); +} + +void PipeWrap::clearFD() { + closeFD(_pipe_fd[0]); + closeFD(_pipe_fd[1]); +} + +PipeWrap::~PipeWrap() { + clearFD(); +} + +int PipeWrap::write(const void *buf, int n) { + int ret; + do { +#if defined(_WIN32) + ret = send(_pipe_fd[1], (char *)buf, n, 0); +#else + ret = ::write(_pipe_fd[1], buf, n); +#endif // defined(_WIN32) + } while (-1 == ret && UV_EINTR == get_uv_error(true)); + return ret; +} + +int PipeWrap::read(void *buf, int n) { + int ret; + do { +#if defined(_WIN32) + ret = recv(_pipe_fd[0], (char *)buf, n, 0); +#else + ret = ::read(_pipe_fd[0], buf, n); +#endif // defined(_WIN32) + } while (-1 == ret && UV_EINTR == get_uv_error(true)); + return ret; +} + +} /* namespace turbo*/ \ No newline at end of file diff --git a/turbo/network/poller/pipe_wrap.h b/turbo/network/poller/pipe_wrap.h new file mode 100644 index 00000000..301ccff3 --- /dev/null +++ b/turbo/network/poller/pipe_wrap.h @@ -0,0 +1,39 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_POLLER_PIPE_WRAP_H_ +#define TURBO_NETWORK_POLLER_PIPE_WRAP_H_ + + +namespace turbo { + +class PipeWrap { +public: + PipeWrap(); + ~PipeWrap(); + int write(const void *buf, int n); + int read(void *buf, int n); + int readFD() const { return _pipe_fd[0]; } + int writeFD() const { return _pipe_fd[1]; } + void reOpen(); + +private: + void clearFD(); + +private: + int _pipe_fd[2] = { -1, -1 }; +}; + +} /* namespace turbo */ +#endif // TURBO_NETWORK_POLLER_PIPE_WRAP_H_ \ No newline at end of file diff --git a/turbo/network/poller/select_wrap.cc b/turbo/network/poller/select_wrap.cc new file mode 100644 index 00000000..978ee748 --- /dev/null +++ b/turbo/network/poller/select_wrap.cc @@ -0,0 +1,53 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/poller/select_wrap.h" + +using namespace std; + +namespace turbo { + +FdSet::FdSet() { + _ptr = new fd_set; +} + +FdSet::~FdSet() { + delete (fd_set *)_ptr; +} + +void FdSet::fdZero() { + FD_ZERO((fd_set *)_ptr); +} + +void FdSet::fdClr(int fd) { + FD_CLR(fd, (fd_set *)_ptr); +} + +void FdSet::fdSet(int fd) { + FD_SET(fd, (fd_set *)_ptr); +} + +bool FdSet::isSet(int fd) { + return FD_ISSET(fd, (fd_set *)_ptr); +} + +int turbo_select(int cnt, FdSet *read, FdSet *write, FdSet *err, struct timeval *tv) { + void *rd, *wt, *er; + rd = read ? read->_ptr : nullptr; + wt = write ? write->_ptr : nullptr; + er = err ? err->_ptr : nullptr; + return ::select(cnt, (fd_set *) rd, (fd_set *) wt, (fd_set *) er, tv); +} + +} /* namespace turbo */ \ No newline at end of file diff --git a/turbo/network/poller/select_wrap.h b/turbo/network/poller/select_wrap.h new file mode 100644 index 00000000..5e22837b --- /dev/null +++ b/turbo/network/poller/select_wrap.h @@ -0,0 +1,36 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_POLLER_SELECT_WRAP_H_ +#define TURBO_NETWORK_POLLER_SELECT_WRAP_H_ + +#include "turbo/network/util/util.h" + +namespace turbo { + +class FdSet { +public: + FdSet(); + ~FdSet(); + void fdZero(); + void fdSet(int fd); + void fdClr(int fd); + bool isSet(int fd); + void *_ptr; +}; + +int turbo_select(int cnt, FdSet *read, FdSet *write, FdSet *err, struct timeval *tv); + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_POLLER_SELECT_WRAP_H_ */ \ No newline at end of file diff --git a/turbo/network/poller/timer.cc b/turbo/network/poller/timer.cc new file mode 100644 index 00000000..9cadb3d6 --- /dev/null +++ b/turbo/network/poller/timer.cc @@ -0,0 +1,46 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/poller/timer.h" + +namespace turbo { + +Timer::Timer(float second, const std::function &cb, const EventPoller::Ptr &poller) { + _poller = poller; + if (!_poller) { + _poller = EventPollerPool::Instance().getPoller(); + } + _tag = _poller->doDelayTask((uint64_t) (second * 1000), [cb, second]() { + try { + if (cb()) { + //重复的任务,基于epoll系统调用的定时器 + return (uint64_t) (1000 * second); + } + //该任务不再重复 + return (uint64_t) 0; + } catch (std::exception &ex) { + ErrorL << "Exception occurred when do timer task: " << ex.what(); + return (uint64_t) (1000 * second); + } + }); +} + +Timer::~Timer() { + auto tag = _tag.lock(); + if (tag) { + tag->cancel(); + } +} + +} // namespace turbo \ No newline at end of file diff --git a/turbo/network/poller/timer.h b/turbo/network/poller/timer.h new file mode 100644 index 00000000..a0dc64eb --- /dev/null +++ b/turbo/network/poller/timer.h @@ -0,0 +1,43 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_POLLER_TIMER_H_ +#define TURBO_NETWORK_POLLER_TIMER_H_ + +#include +#include "turbo/network/poller/event_poller.h" + +namespace turbo { + +class Timer { +public: + using Ptr = std::shared_ptr; + + /** + * 构造定时器 + * @param second 定时器重复秒数 + * @param cb 定时器任务,返回true表示重复下次任务,否则不重复,如果任务中抛异常,则默认重复下次任务 + * @param poller EventPoller对象,可以为nullptr + */ + Timer(float second, const std::function &cb, const EventPoller::Ptr &poller); + ~Timer(); + +private: + std::weak_ptr _tag; + //定时器保持EventPoller的强引用 + EventPoller::Ptr _poller; +}; + +} // namespace turbo +#endif /* TURBO_NETWORK_POLLER_TIMER_H_ */ \ No newline at end of file diff --git a/turbo/network/thread/semaphore.h b/turbo/network/thread/semaphore.h new file mode 100644 index 00000000..34b465be --- /dev/null +++ b/turbo/network/thread/semaphore.h @@ -0,0 +1,58 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_THREAD_SEMAPHORE_H_ +#define TURBO_NETWORK_THREAD_SEMAPHORE_H_ + +#include +#include + +namespace turbo { + +class semaphore { + public: + explicit semaphore(size_t initial = 0) { + _count = 0; + } + + ~semaphore() {} + + void post(size_t n = 1) { + std::unique_lock lock(_mutex); + _count += n; + if(n == 1) { + _condition.notify_one(); + } + else { + _condition.notify_all(); + } + } + + void wait() { + std::unique_lock lock(_mutex); + while(_count == 0) { + _condition.wait(lock); + } + --_count; + } + + private: + size_t _count; + std::recursive_mutex _mutex; + std::condition_variable_any _condition; +}; + +} // namespace turbo + +#endif \ No newline at end of file diff --git a/turbo/network/thread/task_executor.cc b/turbo/network/thread/task_executor.cc new file mode 100644 index 00000000..a21f9e40 --- /dev/null +++ b/turbo/network/thread/task_executor.cc @@ -0,0 +1,220 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include "turbo/network/thread/task_executor.h" +#include "turbo/network/poller/event_poller.h" +#include "turbo/network/util/notice_center.h" +#include "turbo/network/util/time_ticker.h" +#include "turbo/network/util/once_token.h" + +using namespace std; + +namespace turbo { + +ThreadLoadCounter::ThreadLoadCounter(uint64_t max_size, uint64_t max_usec) { + _last_sleep_time = _last_wake_time = getCurrentMicrosecond(); + _max_size = max_size; + _max_usec = max_usec; +} + +void ThreadLoadCounter::startSleep() { + lock_guard lck(_mtx); + _sleeping = true; + auto current_time = getCurrentMicrosecond(); + auto run_time = current_time - _last_wake_time; + _last_sleep_time = current_time; + _time_list.emplace_back(run_time, false); + if (_time_list.size() > _max_size) { + _time_list.pop_front(); + } +} + +void ThreadLoadCounter::sleepWakeUp() { + lock_guard lck(_mtx); + _sleeping = false; + auto current_time = getCurrentMicrosecond(); + auto sleep_time = current_time - _last_sleep_time; + _last_wake_time = current_time; + _time_list.emplace_back(sleep_time, true); + if (_time_list.size() > _max_size) { + _time_list.pop_front(); + } +} + +int ThreadLoadCounter::load() { + lock_guard lck(_mtx); + uint64_t totalSleepTime = 0; + uint64_t totalRunTime = 0; + _time_list.for_each([&](const TimeRecord &rcd) { + if (rcd._sleep) { + totalSleepTime += rcd._time; + } else { + totalRunTime += rcd._time; + } + }); + + if (_sleeping) { + totalSleepTime += (getCurrentMicrosecond() - _last_sleep_time); + } else { + totalRunTime += (getCurrentMicrosecond() - _last_wake_time); + } + + uint64_t totalTime = totalRunTime + totalSleepTime; + while ((_time_list.size() != 0) && (totalTime > _max_usec || _time_list.size() > _max_size)) { + TimeRecord &rcd = _time_list.front(); + if (rcd._sleep) { + totalSleepTime -= rcd._time; + } else { + totalRunTime -= rcd._time; + } + totalTime -= rcd._time; + _time_list.pop_front(); + } + if (totalTime == 0) { + return 0; + } + return (int) (totalRunTime * 100 / totalTime); +} + +//////////////////////////////////////////////////////////////////////////// + +Task::Ptr TaskExecutorInterface::async_first(TaskIn task, bool may_sync) { + return async(std::move(task), may_sync); +} + +void TaskExecutorInterface::sync(const TaskIn &task) { + semaphore sem; + auto ret = async([&]() { + onceToken token(nullptr, [&]() { + //通过RAII原理防止抛异常导致不执行这句代码 + sem.post(); + }); + task(); + }); + if (ret && *ret) { + sem.wait(); + } +} + +void TaskExecutorInterface::sync_first(const TaskIn &task) { + semaphore sem; + auto ret = async_first([&]() { + onceToken token(nullptr, [&]() { + //通过RAII原理防止抛异常导致不执行这句代码 + sem.post(); + }); + task(); + }); + if (ret && *ret) { + sem.wait(); + } +} + +////////////////////////////////////////////////////////////////// + +TaskExecutor::TaskExecutor(uint64_t max_size, uint64_t max_usec) : ThreadLoadCounter(max_size, max_usec) {} + +////////////////////////////////////////////////////////////////// + +TaskExecutor::Ptr TaskExecutorGetterImp::getExecutor() { + auto thread_pos = _thread_pos; + if (thread_pos >= _threads.size()) { + thread_pos = 0; + } + + TaskExecutor::Ptr executor_min_load = _threads[thread_pos]; + auto min_load = executor_min_load->load(); + + for (size_t i = 0; i < _threads.size(); ++i) { + ++thread_pos; + if (thread_pos >= _threads.size()) { + thread_pos = 0; + } + + auto th = _threads[thread_pos]; + auto load = th->load(); + + if (load < min_load) { + min_load = load; + executor_min_load = th; + } + if (min_load == 0) { + break; + } + } + _thread_pos = thread_pos; + return executor_min_load; +} + +vector TaskExecutorGetterImp::getExecutorLoad() { + vector vec(_threads.size()); + int i = 0; + for (auto &executor : _threads) { + vec[i++] = executor->load(); + } + return vec; +} + +void TaskExecutorGetterImp::getExecutorDelay(const function &)> &callback) { + std::shared_ptr > delay_vec = std::make_shared>(_threads.size()); + shared_ptr finished(nullptr, [callback, delay_vec](void *) { + //此析构回调触发时,说明已执行完毕所有async任务 + callback((*delay_vec)); + }); + int index = 0; + for (auto &th : _threads) { + std::shared_ptr delay_ticker = std::make_shared(); + th->async([finished, delay_vec, index, delay_ticker]() { + (*delay_vec)[index] = (int) delay_ticker->elapsedTime(); + }, false); + ++index; + } +} + +void TaskExecutorGetterImp::for_each(const function &cb) { + for (auto &th : _threads) { + cb(th); + } +} + +size_t TaskExecutorGetterImp::getExecutorSize() const { + return _threads.size(); +} + +size_t TaskExecutorGetterImp::addPoller(const string &name, size_t size, int priority, bool register_thread, bool enable_cpu_affinity) { + auto cpus = thread::hardware_concurrency(); + size = size > 0 ? size : cpus; + for (size_t i = 0; i < size; ++i) { + auto full_name = name + " " + to_string(i); + auto cpu_index = i % cpus; + EventPoller::Ptr poller(new EventPoller(full_name)); + poller->runLoop(false, register_thread); + poller->async([cpu_index, full_name, priority, enable_cpu_affinity]() { + // 设置线程优先级 + ThreadPool::setPriority((ThreadPool::Priority)priority); + // 设置线程名 + setThreadName(full_name.data()); + // 设置cpu亲和性 + if (enable_cpu_affinity) { + //todo 由于FFmpeg软件转码,所以不能设置cpu亲和性,否则所有转码线程都运行在一个cpu核心上,导致转性能低下 + //setThreadAffinity(cpu_index); + } + }); + _threads.emplace_back(std::move(poller)); + } + return size; +} + +} //namespace turbo diff --git a/turbo/network/thread/task_executor.h b/turbo/network/thread/task_executor.h new file mode 100644 index 00000000..ccb2727e --- /dev/null +++ b/turbo/network/thread/task_executor.h @@ -0,0 +1,257 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_THREAD_TASK_EXECUTOR_H_ +#define TURBO_NETWORK_THREAD_TASK_EXECUTOR_H_ + +#include +#include +#include +#include "turbo/network/util/list.h" +#include "turbo/network/util/util.h" + +namespace turbo { + +/** +* cpu负载计算器 +*/ +class ThreadLoadCounter { +public: + /** + * 构造函数 + * @param max_size 统计样本数量 + * @param max_usec 统计时间窗口,亦即最近{max_usec}的cpu负载率 + */ + ThreadLoadCounter(uint64_t max_size, uint64_t max_usec); + ~ThreadLoadCounter() = default; + + /** + * 线程进入休眠 + */ + void startSleep(); + + /** + * 休眠唤醒,结束休眠 + */ + void sleepWakeUp(); + + /** + * 返回当前线程cpu使用率,范围为 0 ~ 100 + * @return 当前线程cpu使用率 + */ + int load(); + +private: + struct TimeRecord { + TimeRecord(uint64_t tm, bool slp) { + _time = tm; + _sleep = slp; + } + + bool _sleep; + uint64_t _time; + }; + +private: + bool _sleeping = true; + uint64_t _last_sleep_time; + uint64_t _last_wake_time; + uint64_t _max_size; + uint64_t _max_usec; + std::mutex _mtx; + List _time_list; +}; + +class TaskCancelable : public noncopyable { +public: + TaskCancelable() = default; + virtual ~TaskCancelable() = default; + virtual void cancel() = 0; +}; + +template +class TaskCancelableImp; + +template +class TaskCancelableImp : public TaskCancelable { +public: + using Ptr = std::shared_ptr; + using func_type = std::function; + + ~TaskCancelableImp() = default; + + template + TaskCancelableImp(FUNC &&task) { + _strongTask = std::make_shared(std::forward(task)); + _weakTask = _strongTask; + } + + void cancel() override { + _strongTask = nullptr; + } + + operator bool() { + return _strongTask && *_strongTask; + } + + void operator=(std::nullptr_t) { + _strongTask = nullptr; + } + + R operator()(ArgTypes ...args) const { + auto strongTask = _weakTask.lock(); + if (strongTask && *strongTask) { + return (*strongTask)(std::forward(args)...); + } + return defaultValue(); + } + + template + static typename std::enable_if::value, void>::type + defaultValue() {} + + template + static typename std::enable_if::value, T>::type + defaultValue() { + return nullptr; + } + + template + static typename std::enable_if::value, T>::type + defaultValue() { + return 0; + } + +protected: + std::weak_ptr _weakTask; + std::shared_ptr _strongTask; +}; + +using TaskIn = std::function; +using Task = TaskCancelableImp; + +class TaskExecutorInterface { +public: + TaskExecutorInterface() = default; + virtual ~TaskExecutorInterface() = default; + + /** + * 异步执行任务 + * @param task 任务 + * @param may_sync 是否允许同步执行该任务 + * @return 任务是否添加成功 + */ + virtual Task::Ptr async(TaskIn task, bool may_sync = true) = 0; + + /** + * 最高优先级方式异步执行任务 + * @param task 任务 + * @param may_sync 是否允许同步执行该任务 + * @return 任务是否添加成功 + */ + virtual Task::Ptr async_first(TaskIn task, bool may_sync = true); + + /** + * 同步执行任务 + * @param task + * @return + */ + void sync(const TaskIn &task); + + /** + * 最高优先级方式同步执行任务 + * @param task + * @return + */ + void sync_first(const TaskIn &task); +}; + +/** +* 任务执行器 +*/ +class TaskExecutor : public ThreadLoadCounter, public TaskExecutorInterface { +public: + using Ptr = std::shared_ptr; + + /** + * 构造函数 + * @param max_size cpu负载统计样本数 + * @param max_usec cpu负载统计时间窗口大小 + */ + TaskExecutor(uint64_t max_size = 32, uint64_t max_usec = 2 * 1000 * 1000); + ~TaskExecutor() = default; +}; + +class TaskExecutorGetter { +public: + using Ptr = std::shared_ptr; + + virtual ~TaskExecutorGetter() = default; + + /** + * 获取任务执行器 + * @return 任务执行器 + */ + virtual TaskExecutor::Ptr getExecutor() = 0; + + /** + * 获取执行器个数 + */ + virtual size_t getExecutorSize() const = 0; +}; + +class TaskExecutorGetterImp : public TaskExecutorGetter { +public: + TaskExecutorGetterImp() = default; + ~TaskExecutorGetterImp() = default; + + /** + * 根据线程负载情况,获取最空闲的任务执行器 + * @return 任务执行器 + */ + TaskExecutor::Ptr getExecutor() override; + + /** + * 获取所有线程的负载率 + * @return 所有线程的负载率 + */ + std::vector getExecutorLoad(); + + /** + * 获取所有线程任务执行延时,单位毫秒 + * 通过此函数也可以大概知道线程负载情况 + * @return + */ + void getExecutorDelay(const std::function &)> &callback); + + /** + * 遍历所有线程 + */ + void for_each(const std::function &cb); + + /** + * 获取线程数 + */ + size_t getExecutorSize() const override; + +protected: + size_t addPoller(const std::string &name, size_t size, int priority, bool register_thread, bool enable_cpu_affinity = true); + +protected: + size_t _thread_pos = 0; + std::vector _threads; +}; + +}//turbo +#endif //TURBO_NETWORK_THREAD_TASK_EXECUTOR_H \ No newline at end of file diff --git a/turbo/network/thread/task_queue.h b/turbo/network/thread/task_queue.h new file mode 100644 index 00000000..5c608833 --- /dev/null +++ b/turbo/network/thread/task_queue.h @@ -0,0 +1,78 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_THREAD_TASK_QUEUE_H_ +#define TURBO_NETWORK_THREAD_TASK_QUEUE_H_ + +#include +#include "turbo/network/util/list.h" +#include "semaphore.h" + +namespace turbo { + +//实现了一个基于函数对象的任务列队,该列队是线程安全的,任务列队任务数由信号量控制 +template +class TaskQueue { +public: + //打入任务至列队 + template + void push_task(C &&task_func) { + { + std::lock_guard lock(_mutex); + _queue.emplace_back(std::forward(task_func)); + } + _sem.post(); + } + + template + void push_task_first(C &&task_func) { + { + std::lock_guard lock(_mutex); + _queue.emplace_front(std::forward(task_func)); + } + _sem.post(); + } + + //清空任务列队 + void push_exit(size_t n) { + _sem.post(n); + } + + //从列队获取一个任务,由执行线程执行 + bool get_task(T &tsk) { + _sem.wait(); + std::lock_guard lock(_mutex); + if (_queue.empty()) { + return false; + } + tsk = std::move(_queue.front()); + _queue.pop_front(); + return true; + } + + size_t size() const { + std::lock_guard lock(_mutex); + return _queue.size(); + } + +private: + List _queue; + // mutable是因为要在const成员函数中修改成员变量,对mutex加锁和解锁 + mutable std::mutex _mutex; + semaphore _sem; +}; + +} // namespace turbo + +#endif //TURBO_NETWORK_THREAD_TASK_QUEUE_H_ \ No newline at end of file diff --git a/turbo/network/thread/thread_group.h b/turbo/network/thread/thread_group.h new file mode 100644 index 00000000..c3cd6c92 --- /dev/null +++ b/turbo/network/thread/thread_group.h @@ -0,0 +1,90 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_THREAD_THREAD_GROUP_H_ +#define TURBO_NETWORK_THREAD_THREAD_GROUP_H_ + +#include +#include +#include +#include + +namespace turbo { + +class thread_group { + private: + thread_group(thread_group const &); + thread_group & operator=(thread_group const &); + + public: + thread_group() {} + ~thread_group() { + _threads.clear(); + } + + bool is_this_thread_in() const { + auto thread_id = std::this_thread::get_id(); + if (_thread_id == thread_id) { + return true; + } + return _threads.find(thread_id) != _threads.end(); + } + + bool is_thread_in(std::thread *thrd) { + if (!thrd) { + return false; + } + auto it = _threads.find(thrd->get_id()); + return it != _threads.end(); + } + + template + std::thread *create_thread(F &&threadfunc) { + auto thread_new = std::make_shared(std::forward(threadfunc)); + _thread_id = thread_new->get_id(); + _threads[_thread_id] = thread_new; + return thread_new.get(); + } + + void remove_thread(std::thread *thrd) { + auto it = _threads.find(thrd->get_id()); + if (it != _threads.end()) { + _threads.erase(it); + } + } + + void join_all() { + if (is_this_thread_in()) { + throw std::runtime_error("Trying joining itself in thread_group"); + } + for (auto &it : _threads) { + if (it.second->joinable()) { + it.second->join(); //等待线程主动退出 + } + } + _threads.clear(); + } + + size_t size() { + return _threads.size(); + } + + private: + std::thread::id _thread_id; + std::unordered_map> _threads; +}; + +} // namespace turbo + +#endif //TURBO_NETWORK_THREAD_THREAD_GROUP_H_ \ No newline at end of file diff --git a/turbo/network/thread/thread_pool.h b/turbo/network/thread/thread_pool.h new file mode 100644 index 00000000..bdeda38c --- /dev/null +++ b/turbo/network/thread/thread_pool.h @@ -0,0 +1,161 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_THREAD_THREAD_POOL_H_ +#define TURBO_NETWORK_THREAD_THREAD_POOL_H_ + + +#include "turbo/network/thread/thread_group.h" +#include "turbo/network/thread/task_executor.h" +#include "turbo/network/thread/task_queue.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/logger.h" + + +namespace turbo { + +class ThreadPool : public TaskExecutor { +public: + enum class Priority { + PRIORITY_LOWEST = 0, + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH, + PRIORITY_HIGHEST + }; + + ThreadPool(int num = 1, Priority priority = Priority::PRIORITY_HIGHEST, bool auto_run = true, bool set_affinity = true, + const std::string &pool_name = "thread pool") { + _thread_num = num; + _on_setup = [pool_name, priority, set_affinity](int index) { + std::string name = pool_name + ' ' + std::to_string(index); + setPriority(priority); + setThreadName(name.data()); + if (set_affinity) { + setThreadAffinity(index % std::thread::hardware_concurrency()); + } + }; + _logger = Logger::Instance().shared_from_this(); + if (auto_run) { + start(); + } + } + + ~ThreadPool() { + shutdown(); + wait(); + } + + //把任务打入线程池并异步执行 + Task::Ptr async(TaskIn task, bool may_sync = true) override { + if (may_sync && _thread_group.is_this_thread_in()) { + task(); + return nullptr; + } + auto ret = std::make_shared(std::move(task)); + _queue.push_task(ret); + return ret; + } + + Task::Ptr async_first(TaskIn task, bool may_sync = true) override { + if (may_sync && _thread_group.is_this_thread_in()) { + task(); + return nullptr; + } + + auto ret = std::make_shared(std::move(task)); + _queue.push_task_first(ret); + return ret; + } + + size_t size() { + return _queue.size(); + } + + static bool setPriority(Priority priority = Priority::PRIORITY_NORMAL, std::thread::native_handle_type threadId = 0) { + // set priority +#if defined(_WIN32) + static int Priorities[] = { THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST }; + if (priority != PRIORITY_NORMAL && SetThreadPriority(GetCurrentThread(), Priorities[priority]) == 0) { + return false; + } + return true; +#else + static int Min = sched_get_priority_min(SCHED_FIFO); + if (Min == -1) { + return false; + } + static int Max = sched_get_priority_max(SCHED_FIFO); + if (Max == -1) { + return false; + } + static int Priorities[] = {Min, Min + (Max - Min) / 4, Min + (Max - Min) / 2, Min + (Max - Min) * 3 / 4, Max}; + + if (threadId == 0) { + threadId = pthread_self(); + } + struct sched_param params; + params.sched_priority = Priorities[static_cast(priority)]; + return pthread_setschedparam(threadId, SCHED_FIFO, ¶ms) == 0; +#endif + } + + void start() { + if (_thread_num <= 0) { + return; + } + size_t total = _thread_num - _thread_group.size(); + for (size_t i = 0; i < total; ++i) { + _thread_group.create_thread([this, i]() {run(i);}); + } + } + +private: + void run(size_t index) { + _on_setup(index); + Task::Ptr task; + while (true) { + //startSleep(); + if (!_queue.get_task(task)) { + //空任务,退出线程 + break; + } + //sleepWakeUp(); + try { + (*task)(); + task = nullptr; + } catch (std::exception &ex) { + ErrorL << "ThreadPool catch a exception: " << ex.what(); + } + } + } + + void wait() { + _thread_group.join_all(); + } + + void shutdown() { + _queue.push_exit(_thread_num); + } + +private: + size_t _thread_num; + Logger::Ptr _logger; + thread_group _thread_group; + TaskQueue _queue; + std::function _on_setup; +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_THREAD_THREAD_POOL_H_ */ \ No newline at end of file diff --git a/turbo/network/thread/work_pool.cc b/turbo/network/thread/work_pool.cc new file mode 100644 index 00000000..97261a98 --- /dev/null +++ b/turbo/network/thread/work_pool.cc @@ -0,0 +1,45 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/thread/work_pool.h" + +namespace turbo { + +static size_t s_pool_size = 0; +static bool s_enable_cpu_affinity = true; + +INSTANCE_IMP(WorkThreadPool) + +EventPoller::Ptr WorkThreadPool::getFirstPoller() { + return std::static_pointer_cast(_threads.front()); +} + +EventPoller::Ptr WorkThreadPool::getPoller() { + return std::static_pointer_cast(getExecutor()); +} + +WorkThreadPool::WorkThreadPool() { + //最低优先级 + addPoller("work poller", s_pool_size, static_cast(ThreadPool::Priority::PRIORITY_LOWEST), false, s_enable_cpu_affinity); +} + +void WorkThreadPool::setPoolSize(size_t size) { + s_pool_size = size; +} + +void WorkThreadPool::enableCpuAffinity(bool enable) { + s_enable_cpu_affinity = enable; +} + +} /* namespace turbo */ diff --git a/turbo/network/thread/work_pool.h b/turbo/network/thread/work_pool.h new file mode 100644 index 00000000..09108679 --- /dev/null +++ b/turbo/network/thread/work_pool.h @@ -0,0 +1,69 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_THREAD_WORK_POOL_H_ +#define TURBO_NETWORK_THREAD_WORK_POOL_H_ + +// 基于epoll io复用及管道通信的多线程工作池,可以负载均衡的分布任务到各个线程中 + + +#include +#include "turbo/network/thread/task_executor.h" +#include "turbo/network/poller/event_poller.h" + +namespace turbo { + +class WorkThreadPool : public std::enable_shared_from_this, public TaskExecutorGetterImp { +public: + using Ptr = std::shared_ptr; + + ~WorkThreadPool() override = default; + + /** + * 获取单例 + */ + static WorkThreadPool &Instance(); + + /** + * 设置EventPoller个数,在WorkThreadPool单例创建前有效 + * 在不调用此方法的情况下,默认创建thread::hardware_concurrency()个EventPoller实例 + * @param size EventPoller个数,如果为0则为thread::hardware_concurrency() + */ + static void setPoolSize(size_t size = 0); + + /** + * 内部创建线程是否设置cpu亲和性,默认设置cpu亲和性 + */ + static void enableCpuAffinity(bool enable); + + /** + * 获取第一个实例 + * @return + */ + EventPoller::Ptr getFirstPoller(); + + /** + * 根据负载情况获取轻负载的实例 + * 如果优先返回当前线程,那么会返回当前线程 + * 返回当前线程的目的是为了提高线程安全性 + * @return + */ + EventPoller::Ptr getPoller(); + +protected: + WorkThreadPool(); +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_WORK_POOL_H_ */ \ No newline at end of file diff --git a/turbo/network/util/file.cc b/turbo/network/util/file.cc new file mode 100644 index 00000000..44aa7bbf --- /dev/null +++ b/turbo/network/util/file.cc @@ -0,0 +1,371 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + +#if defined(_WIN32) +#include +#include +#else +#include +#include +#endif // WIN32 + +#include +#include "turbo/network/util/file.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/uv_errno.h" + +using namespace std; +using namespace turbo; + +#if !defined(_WIN32) +#define _unlink unlink +#define _rmdir rmdir +#define _access access +#endif + +#if defined(_WIN32) + +int mkdir(const char *path, int mode) { + return _mkdir(path); +} + +DIR *opendir(const char *name) { + char namebuf[512]; + snprintf(namebuf, sizeof(namebuf), "%s\\*.*", name); + + WIN32_FIND_DATAA FindData; + auto hFind = FindFirstFileA(namebuf, &FindData); + if (hFind == INVALID_HANDLE_VALUE) { + return nullptr; + } + DIR *dir = (DIR *)malloc(sizeof(DIR)); + memset(dir, 0, sizeof(DIR)); + dir->dd_fd = 0; // simulate return + dir->handle = hFind; + return dir; +} + +struct dirent *readdir(DIR *d) { + HANDLE hFind = d->handle; + WIN32_FIND_DATAA FileData; + //fail or end + if (!FindNextFileA(hFind, &FileData)) { + return nullptr; + } + struct dirent *dir = (struct dirent *)malloc(sizeof(struct dirent) + sizeof(FileData.cFileName)); + strcpy(dir->d_name, (char *)FileData.cFileName); + dir->d_reclen = (uint16_t)strlen(dir->d_name); + //check there is file or directory + if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + dir->d_type = 2; + } + else { + dir->d_type = 1; + } + if (d->index) { + //覆盖前释放内存 + free(d->index); + d->index = nullptr; + } + d->index = dir; + return dir; +} + +int closedir(DIR *d) { + if (!d) { + return -1; + } + //关闭句柄 + if (d->handle != INVALID_HANDLE_VALUE) { + FindClose(d->handle); + d->handle = INVALID_HANDLE_VALUE; + } + //释放内存 + if (d->index) { + free(d->index); + d->index = nullptr; + } + free(d); + return 0; +} +#endif // defined(_WIN32) + +namespace turbo { + +FILE *File::create_file(const std::string &file, const std::string &mode) { + std::string path = file; + std::string dir; + size_t index = 1; + FILE *ret = nullptr; + while (true) { + index = path.find('/', index) + 1; + dir = path.substr(0, index); + if (dir.length() == 0) { + break; + } + if (_access(dir.data(), 0) == -1) { //access函数是查看是不是存在 + if (mkdir(dir.data(), 0777) == -1) { //如果不存在就用mkdir函数来创建 + WarnL << "mkdir " << dir << " failed: " << get_uv_errmsg(); + return nullptr; + } + } + } + if (path[path.size() - 1] != '/') { + ret = fopen(file.data(), mode.data()); + } + return ret; +} + +bool File::create_path(const std::string &file, unsigned int mod) { + std::string path = file; + std::string dir; + size_t index = 1; + while (true) { + index = path.find('/', index) + 1; + dir = path.substr(0, index); + if (dir.length() == 0) { + break; + } + if (_access(dir.data(), 0) == -1) { //access函数是查看是不是存在 + if (mkdir(dir.data(), mod) == -1) { //如果不存在就用mkdir函数来创建 + WarnL << "mkdir " << dir << " failed: " << get_uv_errmsg(); + return false; + } + } + } + return true; +} + +//判断是否为目录 +bool File::is_dir(const std::string &path) { + auto dir = opendir(path.data()); + if (!dir) { + return false; + } + closedir(dir); + return true; +} + +//判断是否为常规文件 +bool File::fileExist(const std::string &path) { + auto fp = fopen(path.data(), "rb"); + if (!fp) { + return false; + } + fclose(fp); + return true; +} + +//判断是否是特殊目录 +bool File::is_special_dir(const std::string &path) { + return path == "." || path == ".."; +} + +static int delete_file_l(const std::string &path_in) { + DIR *dir; + dirent *dir_info; + auto path = path_in; + if (path.back() == '/') { + path.pop_back(); + } + if (File::is_dir(path)) { + if ((dir = opendir(path.data())) == nullptr) { + return _rmdir(path.data()); + } + while ((dir_info = readdir(dir)) != nullptr) { + if (File::is_special_dir(dir_info->d_name)) { + continue; + } + File::delete_file(path + "/" + dir_info->d_name); + } + auto ret = _rmdir(path.data()); + closedir(dir); + return ret; + } + return remove(path.data()) ? _unlink(path.data()) : 0; +} + +int File::delete_file(const std::string &path, bool del_empty_dir, bool backtrace) { + auto ret = delete_file_l(path); + if (!ret && del_empty_dir) { + // delete success + File::deleteEmptyDir(parentDir(path), backtrace); + } + return ret; +} + +string File::loadFile(const std::string &path) { + FILE *fp = fopen(path.data(), "rb"); + if (!fp) { + return ""; + } + fseek(fp, 0, SEEK_END); + auto len = ftell(fp); + fseek(fp, 0, SEEK_SET); + string str(len, '\0'); + if (len != (decltype(len))fread((char *)str.data(), 1, str.size(), fp)) { + WarnL << "fread " << path << " failed: " << get_uv_errmsg(); + } + fclose(fp); + return str; +} + +bool File::saveFile(const string &data, const std::string &path) { + FILE *fp = fopen(path.data(), "wb"); + if (!fp) { + return false; + } + fwrite(data.data(), data.size(), 1, fp); + fclose(fp); + return true; +} + +string File::parentDir(const std::string &path) { + auto parent_dir = path; + if (parent_dir.back() == '/') { + parent_dir.pop_back(); + } + auto pos = parent_dir.rfind('/'); + if (pos != string::npos) { + parent_dir = parent_dir.substr(0, pos + 1); + } + return parent_dir; +} + +string File::absolutePath(const std::string &path, const std::string ¤t_path, bool can_access_parent) { + string currentPath = current_path; + if (!currentPath.empty()) { + //当前目录不为空 + if (currentPath.front() == '.') { + //如果当前目录是相对路径,那么先转换成绝对路径 + currentPath = absolutePath(current_path, exeDir(), true); + } + } else { + currentPath = exeDir(); + } + + if (path.empty()) { + //相对路径为空,那么返回当前目录 + return currentPath; + } + + if (currentPath.back() != '/') { + //确保当前目录最后字节为'/' + currentPath.push_back('/'); + } + auto rootPath = currentPath; + auto dir_vec = split(path, "/"); + for (auto &dir : dir_vec) { + if (dir.empty() || dir == ".") { + //忽略空或本文件夹 + continue; + } + if (dir == "..") { + //访问上级目录 + if (!can_access_parent && currentPath.size() <= rootPath.size()) { + //不能访问根目录之外的目录, 返回根目录 + return rootPath; + } + currentPath = parentDir(currentPath); + continue; + } + currentPath.append(dir); + currentPath.append("/"); + } + + if (path.back() != '/' && currentPath.back() == '/') { + //在路径是文件的情况下,防止转换成目录 + currentPath.pop_back(); + } + return currentPath; +} + +void File::scanDir(const std::string &path_in, const function &cb, + bool enter_subdirectory, bool show_hidden_file) { + string path = path_in; + if (path.back() == '/') { + path.pop_back(); + } + + DIR *pDir; + dirent *pDirent; + if ((pDir = opendir(path.data())) == nullptr) { + //文件夹无效 + return; + } + while ((pDirent = readdir(pDir)) != nullptr) { + if (is_special_dir(pDirent->d_name)) { + continue; + } + if (!show_hidden_file && pDirent->d_name[0] == '.') { + //隐藏的文件 + continue; + } + string strAbsolutePath = path + "/" + pDirent->d_name; + bool isDir = is_dir(strAbsolutePath); + if (!cb(strAbsolutePath, isDir)) { + //不再继续扫描 + break; + } + + if (isDir && enter_subdirectory) { + //如果是文件夹并且扫描子文件夹,那么递归扫描 + scanDir(strAbsolutePath, cb, enter_subdirectory); + } + } + closedir(pDir); +} + +uint64_t File::fileSize(FILE *fp, bool remain_size) { + if (!fp) { + return 0; + } + auto current = ftell64(fp); + fseek64(fp, 0L, SEEK_END); /* 定位到文件末尾 */ + auto end = ftell64(fp); /* 得到文件大小 */ + fseek64(fp, current, SEEK_SET); + return end - (remain_size ? current : 0); +} + +uint64_t File::fileSize(const std::string &path) { + if (path.empty()) { + return 0; + } + auto fp = std::unique_ptr(fopen(path.data(), "rb"), fclose); + return fileSize(fp.get()); +} + +static bool isEmptyDir(const std::string &path) { + bool empty = true; + File::scanDir(path,[&](const std::string &path, bool isDir) { + empty = false; + return false; + }, true, true); + return empty; +} + +void File::deleteEmptyDir(const std::string &dir, bool backtrace) { + if (!File::is_dir(dir) || !isEmptyDir(dir)) { + // 不是文件夹或者非空 + return; + } + File::delete_file(dir); + if (backtrace) { + deleteEmptyDir(File::parentDir(dir), true); + } +} + +} /* namespace turbo */ diff --git a/turbo/network/util/file.h b/turbo/network/util/file.h new file mode 100644 index 00000000..595ad6d6 --- /dev/null +++ b/turbo/network/util/file.h @@ -0,0 +1,158 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_FILE_H_ +#define TURBO_NETWORK_UTIL_FILE_H_ + +#include +#include +#include +#include "turbo/network/util/util.h" +#include + +#if defined(__linux__) +#include +#endif + +#if defined(_WIN32) +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif // !PATH_MAX + +struct dirent{ + long d_ino; /* inode number*/ + off_t d_off; /* offset to this dirent*/ + unsigned short d_reclen; /* length of this d_name*/ + unsigned char d_type; /* the type of d_name*/ + char d_name[1]; /* file name (null-terminated)*/ +}; +typedef struct _dirdesc { + int dd_fd; /** file descriptor associated with directory */ + long dd_loc; /** offset in current buffer */ + long dd_size; /** amount of data returned by getdirentries */ + char *dd_buf; /** data buffer */ + int dd_len; /** size of data buffer */ + long dd_seek; /** magic cookie returned by getdirentries */ + HANDLE handle; + struct dirent *index; +} DIR; +# define __dirfd(dp) ((dp)->dd_fd) + +int mkdir(const char *path, int mode); +DIR *opendir(const char *); +int closedir(DIR *); +struct dirent *readdir(DIR *); + +#endif // defined(_WIN32) + +#if defined(_WIN32) || defined(_WIN64) +#define fseek64 _fseeki64 +#define ftell64 _ftelli64 +#else +#define fseek64 fseek +#define ftell64 ftell +#endif + +namespace turbo { + +class File { +public: + //创建路径 + static bool create_path(const std::string &file, unsigned int mod); + + //新建文件,目录文件夹自动生成 + static FILE *create_file(const std::string &file, const std::string &mode); + + //判断是否为目录 + static bool is_dir(const std::string &path); + + //判断是否是特殊目录(. or ..) + static bool is_special_dir(const std::string &path); + + //删除目录或文件 + static int delete_file(const std::string &path, bool del_empty_dir = false, bool backtrace = true); + + //判断文件是否存在 + static bool fileExist(const std::string &path); + + /** + * 加载文件内容至string + * @param path 加载的文件路径 + * @return 文件内容 + */ + static std::string loadFile(const std::string &path); + + /** + * 保存内容至文件 + * @param data 文件内容 + * @param path 保存的文件路径 + * @return 是否保存成功 + */ + static bool saveFile(const std::string &data, const std::string &path); + + /** + * 获取父文件夹 + * @param path 路径 + * @return 文件夹 + */ + static std::string parentDir(const std::string &path); + + /** + * 替换"../",获取绝对路径 + * @param path 相对路径,里面可能包含 "../" + * @param current_path 当前目录 + * @param can_access_parent 能否访问父目录之外的目录 + * @return 替换"../"之后的路径 + */ + static std::string absolutePath(const std::string &path, const std::string ¤t_path, bool can_access_parent = false); + + /** + * 遍历文件夹下的所有文件 + * @param path 文件夹路径 + * @param cb 回调对象 ,path为绝对路径,isDir为该路径是否为文件夹,返回true代表继续扫描,否则中断 + * @param enter_subdirectory 是否进入子目录扫描 + * @param show_hidden_file 是否显示隐藏的文件 + */ + static void scanDir(const std::string &path, const std::function &cb, + bool enter_subdirectory = false, bool show_hidden_file = false); + + /** + * 获取文件大小 + * @param fp 文件句柄 + * @param remain_size true:获取文件剩余未读数据大小,false:获取文件总大小 + */ + static uint64_t fileSize(FILE *fp, bool remain_size = false); + + /** + * 获取文件大小 + * @param path 文件路径 + * @return 文件大小 + * @warning 调用者应确保文件存在 + */ + static uint64_t fileSize(const std::string &path); + + /** + * 尝试删除空文件夹 + * @param dir 文件夹路径 + * @param backtrace 是否回溯上层文件夹,上层文件夹为空也一并删除,以此类推 + */ + static void deleteEmptyDir(const std::string &dir, bool backtrace = true); + +private: + File(); + ~File(); +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_FILE_H_ */ \ No newline at end of file diff --git a/turbo/network/util/function_traits.h b/turbo/network/util/function_traits.h new file mode 100644 index 00000000..7b5f97ca --- /dev/null +++ b/turbo/network/util/function_traits.h @@ -0,0 +1,69 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_FUNCTION_TRAITS_H_ +#define TURBO_NETWORK_UTIL_FUNCTION_TRAITS_H_ + +#include +#include + +namespace turbo { + +template +struct function_traits; + +//普通函数 +template +struct function_traits +{ +public: + enum { arity = sizeof...(Args) }; + typedef Ret function_type(Args...); + typedef Ret return_type; + using stl_function_type = std::function; + typedef Ret(*pointer)(Args...); + + template + struct args + { + static_assert(I < arity, "index is out of range, index must less than sizeof Args"); + using type = typename std::tuple_element >::type; + }; +}; + +//函数指针 +template +struct function_traits : function_traits{}; + +//std::function +template +struct function_traits> : function_traits{}; + +//member function +#define FUNCTION_TRAITS(...) \ + template \ + struct function_traits : function_traits{}; \ + +FUNCTION_TRAITS() +FUNCTION_TRAITS(const) +FUNCTION_TRAITS(volatile) +FUNCTION_TRAITS(const volatile) + +//函数对象 +template +struct function_traits : function_traits{}; + +} /* namespace turbo */ + +#endif /* TURBO_NETWORK_UTIL_FUNCTION_TRAITS_H_ */ diff --git a/turbo/network/util/list.h b/turbo/network/util/list.h new file mode 100644 index 00000000..cb75ffa0 --- /dev/null +++ b/turbo/network/util/list.h @@ -0,0 +1,56 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_LIST_H_ +#define TURBO_NETWORK_UTIL_LIST_H_ + +#include +#include + +namespace turbo { + +template +class List : public std::list { +public: + template + List(ARGS &&...args) : std::list(std::forward(args)...) {}; + + ~List() = default; + + void append(List &other) { + if (other.empty()) { + return; + } + this->insert(this->end(), other.begin(), other.end()); + other.clear(); + } + + template + void for_each(FUNC &&func) { + for (auto &t : *this) { + func(t); + } + } + + template + void for_each(FUNC &&func) const { + for (auto &t : *this) { + func(t); + } + } +}; + +} // namespace turbo + +#endif // TURBO_NETWORK_THREAD_LIST_H_ \ No newline at end of file diff --git a/turbo/network/util/local_time.cc b/turbo/network/util/local_time.cc new file mode 100644 index 00000000..3beb79cd --- /dev/null +++ b/turbo/network/util/local_time.cc @@ -0,0 +1,156 @@ +// Copyright 2024 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/util/local_time.h" +#include +#ifndef _WIN32 +#include +#endif + +/* This is a safe version of localtime() which contains no locks and is + * fork() friendly. Even the _r version of localtime() cannot be used safely + * in Redis. Another thread may be calling localtime() while the main thread + * forks(). Later when the child process calls localtime() again, for instance + * in order to log something to the Redis log, it may deadlock: in the copy + * of the address space of the forked process the lock will never be released. + * + * This function takes the timezone 'tz' as argument, and the 'dst' flag is + * used to check if daylight saving time is currently in effect. The caller + * of this function should obtain such information calling tzset() ASAP in the + * main() function to obtain the timezone offset from the 'timezone' global + * variable. To obtain the daylight information, if it is currently active or not, + * one trick is to call localtime() in main() ASAP as well, and get the + * information from the tm_isdst field of the tm structure. However the daylight + * time may switch in the future for long running processes, so this information + * should be refreshed at safe times. + * + * Note that this function does not work for dates < 1/1/1970, it is solely + * designed to work with what time(NULL) may return, and to support Redis + * logging of the dates, it's not really a complete implementation. */ +namespace turbo { +static int _daylight_active; +static long _current_timezone; + +int get_daylight_active() { + return _daylight_active; +} + +static int is_leap_year(time_t year) { + if (year % 4) + return 0; /* A year not divisible by 4 is not leap. */ + else if (year % 100) + return 1; /* If div by 4 and not 100 is surely leap. */ + else if (year % 400) + return 0; /* If div by 100 *and* not by 400 is not leap. */ + else + return 1; /* If div by 100 and 400 is leap. */ +} + +void no_locks_localtime(struct tm *tmp, time_t t) { + const time_t secs_min = 60; + const time_t secs_hour = 3600; + const time_t secs_day = 3600 * 24; + + t -= _current_timezone; /* Adjust for timezone. */ + t += 3600 * get_daylight_active(); /* Adjust for daylight time. */ + time_t days = t / secs_day; /* Days passed since epoch. */ + time_t seconds = t % secs_day; /* Remaining seconds. */ + + tmp->tm_isdst = get_daylight_active(); + tmp->tm_hour = seconds / secs_hour; + tmp->tm_min = (seconds % secs_hour) / secs_min; + tmp->tm_sec = (seconds % secs_hour) % secs_min; +#ifndef _WIN32 + tmp->tm_gmtoff = -_current_timezone; +#endif + /* 1/1/1970 was a Thursday, that is, day 4 from the POV of the tm structure + * where sunday = 0, so to calculate the day of the week we have to add 4 + * and take the modulo by 7. */ + tmp->tm_wday = (days + 4) % 7; + + /* Calculate the current year. */ + tmp->tm_year = 1970; + while (1) { + /* Leap years have one day more. */ + time_t days_this_year = 365 + is_leap_year(tmp->tm_year); + if (days_this_year > days) + break; + days -= days_this_year; + tmp->tm_year++; + } + tmp->tm_yday = days; /* Number of day of the current year. */ + + /* We need to calculate in which month and day of the month we are. To do + * so we need to skip days according to how many days there are in each + * month, and adjust for the leap year that has one more day in February. */ + int mdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + mdays[1] += is_leap_year(tmp->tm_year); + + tmp->tm_mon = 0; + while (days >= mdays[tmp->tm_mon]) { + days -= mdays[tmp->tm_mon]; + tmp->tm_mon++; + } + + tmp->tm_mday = days + 1; /* Add 1 since our 'days' is zero-based. */ + tmp->tm_year -= 1900; /* Surprisingly tm_year is year-1900. */ +} + +void local_time_init() { + /* Obtain timezone and daylight info. */ + tzset(); /* Now 'timezome' global is populated. */ +#if defined(__linux__) || defined(__sun) + _current_timezone = timezone; +#elif defined(_WIN32) + time_t time_utc; + struct tm tm_local; + + // Get the UTC time + time(&time_utc); + + // Get the local time + // Use localtime_r for threads safe for linux + //localtime_r(&time_utc, &tm_local); + localtime_s(&tm_local, &time_utc); + + time_t time_local; + struct tm tm_gmt; + + // Change tm to time_t + time_local = mktime(&tm_local); + + // Change it to GMT tm + //gmtime_r(&time_utc, &tm_gmt);//linux + gmtime_s(&tm_gmt, &time_utc); + + int time_zone = tm_local.tm_hour - tm_gmt.tm_hour; + if (time_zone < -12) { + time_zone += 24; + } + else if (time_zone > 12) { + time_zone -= 24; + } + + _current_timezone = time_zone; +#else + struct timeval tv; + struct timezone tz; + gettimeofday(&tv, &tz); + _current_timezone = tz.tz_minuteswest * 60L; +#endif + time_t t = time(NULL); + struct tm *aux = localtime(&t); + _daylight_active = aux->tm_isdst; +} +} // namespace turbo \ No newline at end of file diff --git a/turbo/network/util/local_time.h b/turbo/network/util/local_time.h new file mode 100644 index 00000000..62d5f535 --- /dev/null +++ b/turbo/network/util/local_time.h @@ -0,0 +1,26 @@ +// Copyright 2024 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_LOCAL_TIME_H_ +#define TURBO_NETWORK_UTIL_LOCAL_TIME_H_ + +#include + +namespace turbo { +void no_locks_localtime(struct tm *tmp, time_t t); +void local_time_init(); +int get_daylight_active(); + +} // namespace turbo +#endif // TURBO_NETWORK_UTIL_LOCAL_TIME_H_ \ No newline at end of file diff --git a/turbo/network/util/logger.cc b/turbo/network/util/logger.cc new file mode 100644 index 00000000..6f4db451 --- /dev/null +++ b/turbo/network/util/logger.cc @@ -0,0 +1,716 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include +#include +#include +#include "turbo/network/util/logger.h" +#include "turbo/network/util/once_token.h" +#include "turbo/network/util/file.h" +#include "turbo/network/util/notice_center.h" + +#if defined(_WIN32) +#include "strptime_win.h" +#endif +#ifdef ANDROID +#include +#endif //ANDROID + +#if defined(__MACH__) || ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) +#include +#endif + +using namespace std; + +namespace turbo { +#ifdef _WIN32 +#define CLEAR_COLOR 7 +static const WORD LOG_CONST_TABLE[][3] = { + {0x97, 0x09 , 'T'},//蓝底灰字,黑底蓝字,window console默认黑底 + {0xA7, 0x0A , 'D'},//绿底灰字,黑底绿字 + {0xB7, 0x0B , 'I'},//天蓝底灰字,黑底天蓝字 + {0xE7, 0x0E , 'W'},//黄底灰字,黑底黄字 + {0xC7, 0x0C , 'E'} };//红底灰字,黑底红字 + +bool SetConsoleColor(WORD Color) +{ + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + if (handle == 0) + return false; + + BOOL ret = SetConsoleTextAttribute(handle, Color); + return(ret == TRUE); +} +#else +#define CLEAR_COLOR "\033[0m" +static const char *LOG_CONST_TABLE[][3] = { + {"\033[44;37m", "\033[34m", "T"}, + {"\033[42;37m", "\033[32m", "D"}, + {"\033[46;37m", "\033[36m", "I"}, + {"\033[43;37m", "\033[33m", "W"}, + {"\033[41;37m", "\033[31m", "E"}}; +#endif + +Logger *g_defaultLogger = nullptr; + +Logger &getLogger() { + if (!g_defaultLogger) { + g_defaultLogger = &Logger::Instance(); + } + return *g_defaultLogger; +} + +void setLogger(Logger *logger) { + g_defaultLogger = logger; +} + +///////////////////Logger/////////////////// + +INSTANCE_IMP(Logger, exeName()) + +Logger::Logger(const string &loggerName) { + _logger_name = loggerName; + _last_log = std::make_shared(); + _default_channel = std::make_shared("default", LTrace); +} + +Logger::~Logger() { + _writer.reset(); + { + LogContextCapture(*this, LInfo, __FILE__, __FUNCTION__, __LINE__); + } + _channels.clear(); +} + +void Logger::add(const std::shared_ptr &channel) { + _channels[channel->name()] = channel; +} + +void Logger::del(const string &name) { + _channels.erase(name); +} + +std::shared_ptr Logger::get(const string &name) { + auto it = _channels.find(name); + if (it == _channels.end()) { + return nullptr; + } + return it->second; +} + +void Logger::setWriter(const std::shared_ptr &writer) { + _writer = writer; +} + +void Logger::write(const LogContextPtr &ctx) { + if (_writer) { + _writer->write(ctx, *this); + } else { + writeChannels(ctx); + } +} + +void Logger::setLevel(LogLevel level) { + for (auto &chn : _channels) { + chn.second->setLevel(level); + } +} + +void Logger::writeChannels_l(const LogContextPtr &ctx) { + if (_channels.empty()) { + _default_channel->write(*this, ctx); + } else { + for (auto &chn : _channels) { + chn.second->write(*this, ctx); + } + } + _last_log = ctx; + _last_log->_repeat = 0; +} + +//返回毫秒 +static int64_t timevalDiff(struct timeval &a, struct timeval &b) { + return (1000 * (b.tv_sec - a.tv_sec)) + ((b.tv_usec - a.tv_usec) / 1000); +} + +void Logger::writeChannels(const LogContextPtr &ctx) { + if (ctx->_line == _last_log->_line && ctx->_file == _last_log->_file && ctx->str() == _last_log->str()) { + //重复的日志每隔500ms打印一次,过滤频繁的重复日志 + ++_last_log->_repeat; + if (timevalDiff(_last_log->_tv, ctx->_tv) > 500) { + ctx->_repeat = _last_log->_repeat; + writeChannels_l(ctx); + } + return; + } + if (_last_log->_repeat) { + writeChannels_l(_last_log); + } + writeChannels_l(ctx); +} + +const string &Logger::getName() const { + return _logger_name; +} + +///////////////////LogContext/////////////////// +static inline const char *getFileName(const char *file) { + auto pos = strrchr(file, '/'); +#ifdef _WIN32 + if(!pos){ + pos = strrchr(file, '\\'); + } +#endif + return pos ? pos + 1 : file; +} + +static inline const char *getFunctionName(const char *func) { +#ifndef _WIN32 + return func; +#else + auto pos = strrchr(func, ':'); + return pos ? pos + 1 : func; +#endif +} + +LogContext::LogContext(LogLevel level, const char *file, const char *function, int line, const char *module_name, const char *flag) + : _level(level), _line(line), _file(getFileName(file)), _function(getFunctionName(function)), + _module_name(module_name), _flag(flag) { + gettimeofday(&_tv, nullptr); + _thread_name = getThreadName(); +} + +const string &LogContext::str() { + if (_got_content) { + return _content; + } + _content = ostringstream::str(); + _got_content = true; + return _content; +} + +///////////////////AsyncLogWriter/////////////////// + +LogContextCapture::LogContextCapture(Logger &logger, LogLevel level, const char *file, const char *function, int line, + const char *flag) : _logger(logger) { + static string s_module_name = exeName(false); + _ctx = std::make_shared(level, file, function, line, s_module_name.c_str(), flag); +} + +LogContextCapture::LogContextCapture(const LogContextCapture &that) : _ctx(that._ctx), _logger(that._logger) { + const_cast(that._ctx).reset(); +} + +LogContextCapture::~LogContextCapture() { + *this << endl; +} + +LogContextCapture &LogContextCapture::operator<<(ostream &(*f)(ostream &)) { + if (!_ctx) { + return *this; + } + _logger.write(_ctx); + _ctx.reset(); + return *this; +} + +void LogContextCapture::clear() { + _ctx.reset(); +} + +///////////////////AsyncLogWriter/////////////////// + +AsyncLogWriter::AsyncLogWriter() : _exit_flag(false) { + _thread = std::make_shared([this]() { this->run(); }); +} + +AsyncLogWriter::~AsyncLogWriter() { + _exit_flag = true; + _sem.post(); + _thread->join(); + flushAll(); +} + +void AsyncLogWriter::write(const LogContextPtr &ctx, Logger &logger) { + { + lock_guard lock(_mutex); + _pending.emplace_back(std::make_pair(ctx, &logger)); + } + _sem.post(); +} + +void AsyncLogWriter::run() { + setThreadName("async log"); + while (!_exit_flag) { + _sem.wait(); + flushAll(); + } +} + +void AsyncLogWriter::flushAll() { + decltype(_pending) tmp; + { + lock_guard lock(_mutex); + tmp.swap(_pending); + } + + tmp.for_each([&](std::pair &pr) { + pr.second->writeChannels(pr.first); + }); +} + +///////////////////EventChannel//////////////////// + +const string EventChannel::kBroadcastLogEvent = "kBroadcastLogEvent"; + +EventChannel::EventChannel(const string &name, LogLevel level) : LogChannel(name, level) { + _notice = NoticeCenter::Instance().shared_from_this(); +} + +void EventChannel::write(const Logger &logger, const LogContextPtr &ctx) { + if (_level > ctx->_level) { + return; + } + NoticeHelper::emit(*_notice, kBroadcastLogEvent, logger, ctx); +} + +///////////////////ConsoleChannel/////////////////// + +ConsoleChannel::ConsoleChannel(const string &name, LogLevel level) : LogChannel(name, level) {} + +void ConsoleChannel::write(const Logger &logger, const LogContextPtr &ctx) { + if (_level > ctx->_level) { + return; + } + +#if defined(OS_IPHONE) + //ios禁用日志颜色 + format(logger, std::cout, ctx, false); +#elif defined(ANDROID) + static android_LogPriority LogPriorityArr[10]; + static onceToken s_token([](){ + LogPriorityArr[LTrace] = ANDROID_LOG_VERBOSE; + LogPriorityArr[LDebug] = ANDROID_LOG_DEBUG; + LogPriorityArr[LInfo] = ANDROID_LOG_INFO; + LogPriorityArr[LWarn] = ANDROID_LOG_WARN; + LogPriorityArr[LError] = ANDROID_LOG_ERROR; + }); + __android_log_print(LogPriorityArr[ctx->_level],"JNI","%s %s",ctx->_function.data(),ctx->str().data()); +#else + //linux/windows日志启用颜色并显示日志详情 + format(logger, std::cout, ctx); +#endif +} + +///////////////////SysLogChannel/////////////////// + +#if defined(__MACH__) || ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) + +SysLogChannel::SysLogChannel(const string &name, LogLevel level) : LogChannel(name, level) {} + +void SysLogChannel::write(const Logger &logger, const LogContextPtr &ctx) { + if (_level > ctx->_level) { + return; + } + static int s_syslog_lev[10]; + static onceToken s_token([]() { + s_syslog_lev[LTrace] = LOG_DEBUG; + s_syslog_lev[LDebug] = LOG_INFO; + s_syslog_lev[LInfo] = LOG_NOTICE; + s_syslog_lev[LWarn] = LOG_WARNING; + s_syslog_lev[LError] = LOG_ERR; + }, nullptr); + + syslog(s_syslog_lev[ctx->_level], "-> %s %d\r\n", ctx->_file.data(), ctx->_line); + syslog(s_syslog_lev[ctx->_level], "## %s %s | %s %s\r\n", printTime(ctx->_tv).data(), + LOG_CONST_TABLE[ctx->_level][2], ctx->_function.data(), ctx->str().data()); +} + +#endif//#if defined(__MACH__) || ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) + +///////////////////LogChannel/////////////////// +LogChannel::LogChannel(const string &name, LogLevel level) : _name(name), _level(level) {} + +LogChannel::~LogChannel() {} + +const string &LogChannel::name() const { return _name; } + +void LogChannel::setLevel(LogLevel level) { _level = level; } + +std::string LogChannel::printTime(const timeval &tv) { + auto tm = getLocalTime(tv.tv_sec); + char buf[128]; + snprintf(buf, sizeof(buf), "%d-%02d-%02d %02d:%02d:%02d.%03d", + 1900 + tm.tm_year, + 1 + tm.tm_mon, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (int) (tv.tv_usec / 1000)); + return buf; +} + +#ifdef _WIN32 +#define printf_pid() GetCurrentProcessId() +#else +#define printf_pid() getpid() +#endif + +void LogChannel::format(const Logger &logger, ostream &ost, const LogContextPtr &ctx, bool enable_color, bool enable_detail) { + if (!enable_detail && ctx->str().empty()) { + // 没有任何信息打印 + return; + } + + if (enable_color) { + // color console start +#ifdef _WIN32 + SetConsoleColor(LOG_CONST_TABLE[ctx->_level][1]); +#else + ost << LOG_CONST_TABLE[ctx->_level][1]; +#endif + } + + // print log time and level +#ifdef _WIN32 + ost << printTime(ctx->_tv) << " " << (char)LOG_CONST_TABLE[ctx->_level][2] << " "; +#else + ost << printTime(ctx->_tv) << " " << LOG_CONST_TABLE[ctx->_level][2] << " "; +#endif + + if (enable_detail) { + // tag or process name + ost << (!ctx->_flag.empty() ? ctx->_flag : logger.getName()); + // pid and thread_name + ost << "[" << printf_pid() << "-" << ctx->_thread_name << "] "; + // source file location + ost << ctx->_file << ":" << ctx->_line << " " << ctx->_function << " | "; + } + + // log content + ost << ctx->str(); + + if (enable_color) { + // color console end +#ifdef _WIN32 + SetConsoleColor(CLEAR_COLOR); +#else + ost << CLEAR_COLOR; +#endif + } + + if (ctx->_repeat > 1) { + // log repeated + ost << "\r\n Last message repeated " << ctx->_repeat << " times"; + } + + // flush log and new line + ost << endl; +} + +///////////////////FileChannelBase/////////////////// + +FileChannelBase::FileChannelBase(const string &name, const string &path, LogLevel level) : LogChannel(name, level), _path(path) {} + +FileChannelBase::~FileChannelBase() { + close(); +} + +void FileChannelBase::write(const Logger &logger, const std::shared_ptr &ctx) { + if (_level > ctx->_level) { + return; + } + if (!_fstream.is_open()) { + open(); + } + //打印至文件,不启用颜色 + format(logger, _fstream, ctx, false); +} + +bool FileChannelBase::setPath(const string &path) { + _path = path; + return open(); +} + +const string &FileChannelBase::path() const { + return _path; +} + +bool FileChannelBase::open() { + // Ensure a path was set + if (_path.empty()) { + throw runtime_error("Log file path must be set"); + } + // Open the file stream + _fstream.close(); +#if !defined(_WIN32) + //创建文件夹 + File::create_path(_path, S_IRWXO | S_IRWXG | S_IRWXU); +#else + File::create_path(_path,0); +#endif + _fstream.open(_path.data(), ios::out | ios::app); + if (!_fstream.is_open()) { + return false; + } + //打开文件成功 + return true; +} + +void FileChannelBase::close() { + _fstream.close(); +} + +size_t FileChannelBase::size() { + return (_fstream << std::flush).tellp(); +} + +///////////////////FileChannel/////////////////// + +static const auto s_second_per_day = 24 * 60 * 60; + +//根据GMT UNIX时间戳生产日志文件名 +static string getLogFilePath(const string &dir, time_t second, int32_t index) { + auto tm = getLocalTime(second); + char buf[64]; + snprintf(buf, sizeof(buf), "%d-%02d-%02d_%02d.log", 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday, index); + return dir + buf; +} + +//根据日志文件名返回GMT UNIX时间戳 +static time_t getLogFileTime(const string &full_path) { + auto name = getFileName(full_path.data()); + struct tm tm{0}; + if (!strptime(name, "%Y-%m-%d", &tm)) { + return 0; + } + //此函数会把本地时间转换成GMT时间戳 + return mktime(&tm); +} + +//获取1970年以来的第几天 +static uint64_t getDay(time_t second) { + return (second + getGMTOff()) / s_second_per_day; +} + +static constexpr char kLastLog[] = "last.log"; + +FileChannel::FileChannel(const string &name, const string &dir, LogLevel level) : FileChannelBase(name, "", level) { + _dir = dir; + if (_dir.back() != '/') { + _dir.append("/"); + } + + //收集所有日志文件 + File::scanDir(_dir, [this](const string &path, bool isDir) -> bool { + if (!isDir && end_with(path, ".log") && !end_with(path, kLastLog)) { + _log_file_map.emplace(path); + } + return true; + }, false); + + //获取今天日志文件的最大index号 + auto log_name_prefix = getTimeStr("%Y-%m-%d_"); + for (auto it = _log_file_map.begin(); it != _log_file_map.end(); ++it) { + auto name = getFileName(it->data()); + //筛选出今天所有的日志文件 + if (start_with(name, log_name_prefix)) { + int tm_mday; // day of the month - [1, 31] + int tm_mon; // months since January - [0, 11] + int tm_year; // years since 1900 + uint32_t index; + //今天第几个文件 + int count = sscanf(name, "%d-%02d-%02d_%d.log", &tm_year, &tm_mon, &tm_mday, &index); + if (count == 4) { + _index = index >= _index ? index : _index; + } + } + } +} + +void FileChannel::write(const Logger &logger, const LogContextPtr &ctx) { + //GMT UNIX时间戳 + time_t second = ctx->_tv.tv_sec; + //这条日志所在第几天 + auto day = getDay(second); + if ((int64_t) day != _last_day) { + if (_last_day != -1) { + //重置日志index + _index = 0; + } + //这条日志是新的一天,记录这一天 + _last_day = day; + //获取日志当天对应的文件,每天可能有多个日志切片文件 + changeFile(second); + } else { + //检查该天日志是否需要重新分片 + checkSize(second); + } + + //写日志 + if (_can_write) { + FileChannelBase::write(logger, ctx); + } +} + +void FileChannel::clean() { + //获取今天是第几天 + auto today = getDay(time(nullptr)); + //遍历所有日志文件,删除超过若干天前的过期日志文件 + for (auto it = _log_file_map.begin(); it != _log_file_map.end();) { + auto day = getDay(getLogFileTime(it->data())); + if (today < day + _log_max_day) { + //这个日志文件距今尚未超过一定天数,后面的文件更新,所以停止遍历 + break; + } + //这个文件距今超过了一定天数,则删除文件 + File::delete_file(*it); + //删除这条记录 + it = _log_file_map.erase(it); + } + + //按文件个数清理,限制最大文件切片个数 + while (_log_file_map.size() > _log_max_count) { + auto it = _log_file_map.begin(); + if (*it == path()) { + //当前文件,停止删除 + break; + } + //删除文件 + File::delete_file(*it); + //删除这条记录 + _log_file_map.erase(it); + } +} + +void FileChannel::checkSize(time_t second) { + //每60秒检查一下文件大小,防止频繁flush日志文件 + if (second - _last_check_time > 60) { + if (FileChannelBase::size() > _log_max_size * 1024 * 1024) { + changeFile(second); + } + _last_check_time = second; + } +} + +void FileChannel::changeFile(time_t second) { + auto log_file = getLogFilePath(_dir, second, _index++); + auto link_log_file = _dir + kLastLog; + //记录所有的日志文件,以便后续删除老的日志 + _log_file_map.emplace(log_file); + //打开新的日志文件 + _can_write = setPath(log_file); + if (!_can_write) { + ErrorL << "Failed to open log file: " << _path; + } else { +#ifndef _WIN32 + unlink(link_log_file.data()); + symlink(log_file.data(), link_log_file.data()); +#endif + } + //尝试删除过期的日志文件 + clean(); +} + +void FileChannel::setMaxDay(size_t max_day) { + _log_max_day = max_day > 1 ? max_day : 1; +} + +void FileChannel::setFileMaxSize(size_t max_size) { + _log_max_size = max_size > 1 ? max_size : 1; +} + +void FileChannel::setFileMaxCount(size_t max_count) { + _log_max_count = max_count > 1 ? max_count : 1; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +FileChannelHours::FileChannelHours(const std::string &name, const std::string &path, LogLevel level) : + FileChannelBase(name, "", level) { + _path_prefix = path; + scanLogFiles(); +} + +void FileChannelHours::setMaxDay(int max_days) { + _max_days = max_days; + clearOldLogs(); +} + +void FileChannelHours::write(const Logger &logger, const LogContextPtr &ctx) { + //1970年以来第几个小时 + int64_t hours = (getGMTOff() + ctx->_tv.tv_sec) / 3600; + if (hours != _hours_index) { + _hours_index = hours; + //24小时求余代表当天第几个小时 + auto path = getLogFilePath(_path_prefix, ctx->_tv.tv_sec, _hours_index % 24); + _log_file_map.emplace(path); + clearOldLogs(); + setPath(path); +#ifndef _WIN32 + auto link_log_file = _path_prefix + kLastLog; + unlink(link_log_file.data()); + symlink(path.data(), link_log_file.data()); +#endif + } + FileChannelBase::write(logger, ctx); +} + +void FileChannelHours::clearOldLogs() { + //获取今天是第几天 + auto today = getDay(time(nullptr)); + //遍历所有日志文件,删除超过若干天前的过期日志文件 + for (auto it = _log_file_map.begin(); it != _log_file_map.end();) { + auto day = getDay(getLogFileTime(it->data())); + if (today < day + _max_days) { + //这个日志文件距今尚未超过一定天数,后面的文件更新,所以停止遍历 + break; + } + //这个文件距今超过了一定天数,则删除文件 + File::delete_file(it->data()); + //删除这条记录 + it = _log_file_map.erase(it); + } +} + +void FileChannelHours::scanLogFiles() { + //收集所有日志文件 + File::scanDir(_path_prefix, [&](const std::string &path, bool isDir) -> bool { + if (!isDir && end_with(path, ".log") && !end_with(path, kLastLog)) { + _log_file_map.emplace(path); + } + return true; + }, false); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +void LoggerWrapper::printLogV(Logger &logger, int level, const char *file, const char *function, int line, const char *fmt, va_list ap) { + LogContextCapture info(logger, (LogLevel) level, file, function, line); + char *str = nullptr; + if (vasprintf(&str, fmt, ap) > 0 && str) { + info << str; + delete [] str; // 开启asan后,用free会卡死 + } +} + +void LoggerWrapper::printLog(Logger &logger, int level, const char *file, const char *function, int line, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + printLogV(logger, level, file, function, line, fmt, ap); + va_end(ap); +} + +} // namespace turbo diff --git a/turbo/network/util/logger.h b/turbo/network/util/logger.h new file mode 100644 index 00000000..b6b1c6e9 --- /dev/null +++ b/turbo/network/util/logger.h @@ -0,0 +1,475 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_LOGGER_H_ +#define TURBO_NETWORK_UTIL_LOGGER_H_ + + +#include +#include +#include +#include +#include +#include +#include +#include "turbo/network/util/util.h" +#include "turbo/network/util/list.h" +#include "turbo/network/util/notice_center.h" +#include "turbo/network/thread/semaphore.h" + +namespace turbo { + +class LogContext; +class LogChannel; +class LogWriter; +class Logger; + +using LogContextPtr = std::shared_ptr; + +typedef enum { + LTrace = 0, LDebug, LInfo, LWarn, LError +} LogLevel; + +Logger &getLogger(); +void setLogger(Logger *logger); + +/** +* 日志类 +*/ +class Logger : public std::enable_shared_from_this, public noncopyable { +public: + friend class AsyncLogWriter; + using Ptr = std::shared_ptr; + + /** + * 获取日志单例 + * @return + */ + static Logger &Instance(); + + explicit Logger(const std::string &loggerName); + ~Logger(); + + /** + * 添加日志通道,非线程安全的 + * @param channel log通道 + */ + void add(const std::shared_ptr &channel); + + /** + * 删除日志通道,非线程安全的 + * @param name log通道名 + */ + void del(const std::string &name); + + /** + * 获取日志通道,非线程安全的 + * @param name log通道名 + * @return 线程通道 + */ + std::shared_ptr get(const std::string &name); + + /** + * 设置写log器,非线程安全的 + * @param writer 写log器 + */ + void setWriter(const std::shared_ptr &writer); + + /** + * 设置所有日志通道的log等级 + * @param level log等级 + */ + void setLevel(LogLevel level); + + /** + * 获取logger名 + * @return logger名 + */ + const std::string &getName() const; + + /** + * 写日志 + * @param ctx 日志信息 + */ + void write(const LogContextPtr &ctx); + +private: + /** + * 写日志到各channel,仅供AsyncLogWriter调用 + * @param ctx 日志信息 + */ + void writeChannels(const LogContextPtr &ctx); + void writeChannels_l(const LogContextPtr &ctx); + +private: + LogContextPtr _last_log; + std::string _logger_name; + std::shared_ptr _writer; + std::shared_ptr _default_channel; + std::map > _channels; +}; + +///////////////////LogContext/////////////////// +/** +* 日志上下文 +*/ +class LogContext : public std::ostringstream { +public: + //_file,_function改成string保存,目的是有些情况下,指针可能会失效 + //比如说动态库中打印了一条日志,然后动态库卸载了,那么指向静态数据区的指针就会失效 + LogContext() = default; + LogContext(LogLevel level, const char *file, const char *function, int line, const char *module_name, const char *flag); + ~LogContext() = default; + + LogLevel _level; + int _line; + int _repeat = 0; + std::string _file; + std::string _function; + std::string _thread_name; + std::string _module_name; + std::string _flag; + struct timeval _tv; + + const std::string &str(); + +private: + bool _got_content = false; + std::string _content; +}; + +/** + * 日志上下文捕获器 + */ +class LogContextCapture { +public: + using Ptr = std::shared_ptr; + + LogContextCapture(Logger &logger, LogLevel level, const char *file, const char *function, int line, const char *flag = ""); + LogContextCapture(const LogContextCapture &that); + ~LogContextCapture(); + + /** + * 输入std::endl(回车符)立即输出日志 + * @param f std::endl(回车符) + * @return 自身引用 + */ + LogContextCapture &operator<<(std::ostream &(*f)(std::ostream &)); + + template + LogContextCapture &operator<<(T &&data) { + if (!_ctx) { + return *this; + } + (*_ctx) << std::forward(data); + return *this; + } + + void clear(); + +private: + LogContextPtr _ctx; + Logger &_logger; +}; + + +///////////////////LogWriter/////////////////// +/** + * 写日志器 + */ +class LogWriter : public noncopyable { +public: + LogWriter() = default; + virtual ~LogWriter() = default; + + virtual void write(const LogContextPtr &ctx, Logger &logger) = 0; +}; + +class AsyncLogWriter : public LogWriter { +public: + AsyncLogWriter(); + ~AsyncLogWriter(); + +private: + void run(); + void flushAll(); + void write(const LogContextPtr &ctx, Logger &logger) override; + +private: + bool _exit_flag; + semaphore _sem; + std::mutex _mutex; + std::shared_ptr _thread; + List > _pending; +}; + +///////////////////LogChannel/////////////////// +/** + * 日志通道 + */ +class LogChannel : public noncopyable { +public: + LogChannel(const std::string &name, LogLevel level = LTrace); + virtual ~LogChannel(); + + virtual void write(const Logger &logger, const LogContextPtr &ctx) = 0; + const std::string &name() const; + void setLevel(LogLevel level); + static std::string printTime(const timeval &tv); + +protected: + /** + * 打印日志至输出流 + * @param ost 输出流 + * @param enable_color 是否启用颜色 + * @param enable_detail 是否打印细节(函数名、源码文件名、源码行) + */ + virtual void format(const Logger &logger, std::ostream &ost, const LogContextPtr &ctx, bool enable_color = true, bool enable_detail = true); + +protected: + std::string _name; + LogLevel _level; +}; + +/** + * 输出日至到广播 + */ +class EventChannel : public LogChannel { +public: + //输出日志时的广播名 + static const std::string kBroadcastLogEvent; + //日志广播参数类型和列表 + #define BroadcastLogEventArgs const Logger &logger, const LogContextPtr &ctx + + EventChannel(const std::string &name = "EventChannel", LogLevel level = LTrace); + ~EventChannel() override = default; + + void write(const Logger &logger, const LogContextPtr &ctx) override; + +private: + NoticeCenter::Ptr _notice; +}; + +/** + * 输出日志至终端,支持输出日志至android logcat + */ +class ConsoleChannel : public LogChannel { +public: + ConsoleChannel(const std::string &name = "ConsoleChannel", LogLevel level = LTrace); + ~ConsoleChannel() override = default; + + void write(const Logger &logger, const LogContextPtr &logContext) override; +}; + +/** + * 输出日志至文件 + */ +class FileChannelBase : public LogChannel { +public: + FileChannelBase(const std::string &name = "FileChannelBase", const std::string &path = exePath() + ".log", LogLevel level = LTrace); + ~FileChannelBase() override; + + void write(const Logger &logger, const LogContextPtr &ctx) override; + bool setPath(const std::string &path); + const std::string &path() const; + +protected: + virtual bool open(); + virtual void close(); + virtual size_t size(); + +protected: + std::string _path; + std::ofstream _fstream; +}; + +class Ticker; + +/** + * 自动清理的日志文件通道 + * 默认最多保存30天的日志 + */ +class FileChannel : public FileChannelBase { +public: + FileChannel(const std::string &name = "FileChannel", const std::string &dir = exeDir() + "log/", LogLevel level = LTrace); + ~FileChannel() override = default; + + /** + * 写日志时才会触发新建日志文件或者删除老的日志文件 + * @param logger + * @param stream + */ + void write(const Logger &logger, const LogContextPtr &ctx) override; + + /** + * 设置日志最大保存天数 + * @param max_day 天数 + */ + void setMaxDay(size_t max_day); + + /** + * 设置日志切片文件最大大小 + * @param max_size 单位MB + */ + void setFileMaxSize(size_t max_size); + + /** + * 设置日志切片文件最大个数 + * @param max_count 个数 + */ + void setFileMaxCount(size_t max_count); + +private: + /** + * 删除日志切片文件,条件为超过最大保存天数与最大切片个数 + */ + void clean(); + + /** + * 检查当前日志切片文件大小,如果超过限制,则创建新的日志切片文件 + */ + void checkSize(time_t second); + + /** + * 创建并切换到下一个日志切片文件 + */ + void changeFile(time_t second); + +private: + bool _can_write = false; + //默认最多保存30天的日志文件 + size_t _log_max_day = 30; + //每个日志切片文件最大默认128MB + size_t _log_max_size = 128; + //最多默认保持30个日志切片文件 + size_t _log_max_count = 30; + //当前日志切片文件索引 + size_t _index = 0; + int64_t _last_day = -1; + time_t _last_check_time = 0; + std::string _dir; + std::set _log_file_map; +}; + +class FileChannelHours : public FileChannelBase { +public: + FileChannelHours(const std::string &name = "FileChannelBase", const std::string &path = exeDir() + "log/", LogLevel level = LTrace); + ~FileChannelHours() override = default; + + void setMaxDay(int max_days); + void setFileMaxSize(size_t max_size) {} + void setFileMaxCount(size_t max_count) {} + +protected: + void clearOldLogs(); + void scanLogFiles(); + void write(const Logger &logger, const LogContextPtr &ctx) override; + +private: + int _max_days = 7; + int64_t _hours_index = -1; + std::string _path_prefix; + std::set _log_file_map; +}; + +#if defined(__MACH__) || ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) +class SysLogChannel : public LogChannel { +public: + SysLogChannel(const std::string &name = "SysLogChannel", LogLevel level = LTrace); + ~SysLogChannel() override = default; + + void write(const Logger &logger, const LogContextPtr &logContext) override; +}; + +#endif//#if defined(__MACH__) || ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) + +class BaseLogFlagInterface { +protected: + virtual ~BaseLogFlagInterface() {} + // 获得日志标记Flag + const char* getLogFlag(){ + return _log_flag; + } + void setLogFlag(const char *flag) { _log_flag = flag; } +private: + const char *_log_flag = ""; +}; + +class LoggerWrapper { +public: + template + static inline void printLogArray(Logger &logger, LogLevel level, const char *file, const char *function, int line, First &&first, ARGS &&...args) { + LogContextCapture log(logger, level, file, function, line); + log << std::forward(first); + appendLog(log, std::forward(args)...); + } + + static inline void printLogArray(Logger &logger, LogLevel level, const char *file, const char *function, int line) { + LogContextCapture log(logger, level, file, function, line); + } + + template + static inline void appendLog(Log &out, First &&first, ARGS &&...args) { + out << std::forward(first); + appendLog(out, std::forward(args)...); + } + + template + static inline void appendLog(Log &out) {} + + //printf样式的日志打印 + static void printLog(Logger &logger, int level, const char *file, const char *function, int line, const char *fmt, ...); + static void printLogV(Logger &logger, int level, const char *file, const char *function, int line, const char *fmt, va_list ap); +}; + +//可重置默认值 +extern Logger *g_defaultLogger; + +//用法: DebugL << 1 << "+" << 2 << '=' << 3; +#define WriteL(level) ::turbo::LogContextCapture(::turbo::getLogger(), level, __FILE__, __FUNCTION__, __LINE__) +#define TraceL WriteL(::turbo::LTrace) +#define DebugL WriteL(::turbo::LDebug) +#define InfoL WriteL(::turbo::LInfo) +#define WarnL WriteL(::turbo::LWarn) +#define ErrorL WriteL(::turbo::LError) + +//只能在虚继承BaseLogFlagInterface的类中使用 +#define WriteF(level) ::turbo::LogContextCapture(::turbo::getLogger(), level, __FILE__, __FUNCTION__, __LINE__, getLogFlag()) +#define TraceF WriteF(::turbo::LTrace) +#define DebugF WriteF(::turbo::LDebug) +#define InfoF WriteF(::turbo::LInfo) +#define WarnF WriteF(::turbo::LWarn) +#define ErrorF WriteF(::turbo::LError) + +//用法: PrintD("%d + %s = %c", 1 "2", 'c'); +#define PrintLog(level, ...) ::turbo::LoggerWrapper::printLog(::turbo::getLogger(), level, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define PrintT(...) PrintLog(::turbo::LTrace, ##__VA_ARGS__) +#define PrintD(...) PrintLog(::turbo::LDebug, ##__VA_ARGS__) +#define PrintI(...) PrintLog(::turbo::LInfo, ##__VA_ARGS__) +#define PrintW(...) PrintLog(::turbo::LWarn, ##__VA_ARGS__) +#define PrintE(...) PrintLog(::turbo::LError, ##__VA_ARGS__) + +//用法: LogD(1, "+", "2", '=', 3); +//用于模板实例化的原因,如果每次打印参数个数和类型不一致,可能会导致二进制代码膨胀 +#define LogL(level, ...) ::turbo::LoggerWrapper::printLogArray(::turbo::getLogger(), (LogLevel)level, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__) +#define LogT(...) LogL(::turbo::LTrace, ##__VA_ARGS__) +#define LogD(...) LogL(::turbo::LDebug, ##__VA_ARGS__) +#define LogI(...) LogL(::turbo::LInfo, ##__VA_ARGS__) +#define LogW(...) LogL(::turbo::LWarn, ##__VA_ARGS__) +#define LogE(...) LogL(::turbo::LError, ##__VA_ARGS__) + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_LOGGER_H_ */ + + diff --git a/turbo/network/util/mini.cc b/turbo/network/util/mini.cc new file mode 100644 index 00000000..8889a2bc --- /dev/null +++ b/turbo/network/util/mini.cc @@ -0,0 +1,49 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/util/mini.h" + + +using namespace std; + +namespace turbo { + +template<> +mINI_basic &mINI_basic::Instance(){ + static mINI_basic instance; + return instance; +} + +template <> +bool variant::as() const { + if (empty() || isdigit(front())) { + //数字开头 + return as_default(); + } + if (strToLower(std::string(*this)) == "true") { + return true; + } + if (strToLower(std::string(*this)) == "false") { + return false; + } + //未识别字符串 + return as_default(); +} + +template<> +uint8_t variant::as() const { + return 0xFF & as_default(); +} + +} // namespace turbo diff --git a/turbo/network/util/mini.h b/turbo/network/util/mini.h new file mode 100644 index 00000000..03d67003 --- /dev/null +++ b/turbo/network/util/mini.h @@ -0,0 +1,181 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_MINI_H_ +#define TURBO_NETWORK_UTIL_MINI_H_ + +#include +#include +#include +#include +#include +#include "turbo/network/util/util.h" + + +namespace turbo { + +template +class mINI_basic : public std::map { + // Public API : existing map<> interface plus following methods +public: + void parse(const std::string &text) { + // reset, split lines and parse + std::vector lines = tokenize(text, "\n"); + std::string symbol, tag; + for (auto &line : lines) { + // trim blanks + line = trim(line); + // split line into tokens and parse tokens + if (line.empty() || line.front() == ';' || line.front() == '#') { + continue; + } + if (line.size() >= 3 && line.front() == '[' && line.back() == ']') { + tag = trim(line.substr(1, line.size() - 2)); + } else { + auto at = line.find('='); + symbol = trim(tag + "." + line.substr(0, at)); + (*this)[symbol] = (at == std::string::npos ? std::string() : trim(line.substr(at + 1))); + } + } + } + + void parseFile(const std::string &fileName = exePath() + ".ini") { + std::ifstream in(fileName, std::ios::in | std::ios::binary | std::ios::ate); + if (!in.good()) { + throw std::invalid_argument("Invalid ini file: " + fileName); + } + auto size = in.tellg(); + in.seekg(0, std::ios::beg); + std::string buf; + buf.resize(size); + in.read((char *) buf.data(), size); + parse(buf); + } + + std::string dump(const std::string &header = "; auto-generated by mINI class {", + const std::string &footer = "; } ---") const { + std::string front(header + (header.empty() ? "" : "\r\n")), output, tag; + std::vector kv; + for (auto &pr : *this) { + auto pos = pr.first.find('.'); + if (pos == std::string::npos) { + kv = {"", pr.first}; + } else { + kv = {pr.first.substr(0, pos), pr.first.substr(pos + 1)}; + } + if (kv[0].empty()) { + front += kv[1] + "=" + pr.second + "\r\n"; + continue; + } + if (tag != kv[0]) { + output += "\r\n[" + (tag = kv[0]) + "]\r\n"; + } + output += kv[1] + "=" + pr.second + "\r\n"; + } + return front + output + "\r\n" + footer + (footer.empty() ? "" : "\r\n"); + } + + void dumpFile(const std::string &fileName = exePath() + ".ini") { + std::ofstream out(fileName, std::ios::out | std::ios::binary | std::ios::trunc); + auto dmp = dump(); + out.write(dmp.data(), dmp.size()); + } + + static mINI_basic &Instance(); + +private: + std::vector tokenize(const std::string &self, const std::string &chars) const { + std::vector tokens(1); + std::string map(256, '\0'); + for (char ch : chars) { + map[(uint8_t) ch] = '\1'; + } + for (char ch : self) { + if (!map.at((uint8_t) ch)) { + tokens.back().push_back(ch); + } else if (tokens.back().size()) { + tokens.push_back(std::string()); + } + } + while (tokens.size() && tokens.back().empty()) { + tokens.pop_back(); + } + return tokens; + } +}; + +// handy variant class as key/values +struct variant : public std::string { + template + variant(const T &t) : + std::string(std::to_string(t)) { + } + + template + variant(const char (&s)[N]) : + std::string(s, N) { + } + + variant(const char *cstr) : + std::string(cstr) { + } + + variant(const std::string &other = std::string()) : + std::string(other) { + } + + template + operator T() const { + return as(); + } + + template + bool operator==(const T &t) const { + return 0 == this->compare(variant(t)); + } + + bool operator==(const char *t) const { + return this->compare(t) == 0; + } + + template + typename std::enable_if::value, T>::type as() const { + return as_default(); + } + + template + typename std::enable_if::value, T>::type as() const { + return T((const std::string &)*this); + } + +private: + template + T as_default() const { + T t; + std::stringstream ss; + return ss << *this && ss >> t ? t : T(); + } +}; + +template <> +bool variant::as() const; + +template <> +uint8_t variant::as() const; + +using mINI = mINI_basic; + +} // namespace turbo +#endif //TURBO_NETWORK_UTIL_MINI_H_ + diff --git a/turbo/network/util/notice_center.cc b/turbo/network/util/notice_center.cc new file mode 100644 index 00000000..8ac9e2d2 --- /dev/null +++ b/turbo/network/util/notice_center.cc @@ -0,0 +1,22 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/util/util.h" +#include "turbo/network/util/notice_center.h" + +namespace turbo { + +INSTANCE_IMP(NoticeCenter) + +} /* namespace turbo */ \ No newline at end of file diff --git a/turbo/network/util/notice_center.h b/turbo/network/util/notice_center.h new file mode 100644 index 00000000..92996139 --- /dev/null +++ b/turbo/network/util/notice_center.h @@ -0,0 +1,204 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_NOTICE_CENTER_H_ +#define TURBO_NETWORK_UTIL_NOTICE_CENTER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "turbo/network/util/util.h" +#include "turbo/network/util/function_traits.h" + +namespace turbo { + +class EventDispatcher { +public: + friend class NoticeCenter; + using Ptr = std::shared_ptr; + + ~EventDispatcher() = default; + +private: + using MapType = std::unordered_multimap; + + EventDispatcher() = default; + + class InterruptException : public std::runtime_error { + public: + InterruptException() : std::runtime_error("InterruptException") {} + ~InterruptException() {} + }; + + template + int emitEvent(bool safe, ArgsType &&...args) { + using stl_func = std::function(args))...)>; + decltype(_mapListener) copy; + { + // 先拷贝(开销比较小),目的是防止在触发回调时还是上锁状态从而导致交叉互锁 + std::lock_guard lck(_mtxListener); + copy = _mapListener; + } + + int ret = 0; + for (auto &pr : copy) { + try { + pr.second.get(safe)(std::forward(args)...); + ++ret; + } catch (InterruptException &) { + ++ret; + break; + } + } + return ret; + } + + template + void addListener(void *tag, FUNC &&func) { + using stl_func = typename function_traits::type>::stl_function_type; + Any listener; + listener.set(std::forward(func)); + std::lock_guard lck(_mtxListener); + _mapListener.emplace(tag, listener); + } + + void delListener(void *tag, bool &empty) { + std::lock_guard lck(_mtxListener); + _mapListener.erase(tag); + empty = _mapListener.empty(); + } + +private: + std::recursive_mutex _mtxListener; + MapType _mapListener; +}; + +class NoticeCenter : public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + static NoticeCenter &Instance(); + + template + int emitEvent(const std::string &event, ArgsType &&...args) { + return emitEvent_l(false, event, std::forward(args)...); + } + + template + int emitEventSafe(const std::string &event, ArgsType &&...args) { + return emitEvent_l(true, event, std::forward(args)...); + } + + template + void addListener(void *tag, const std::string &event, FUNC &&func) { + getDispatcher(event, true)->addListener(tag, std::forward(func)); + } + + void delListener(void *tag, const std::string &event) { + auto dispatcher = getDispatcher(event); + if (!dispatcher) { + // 不存在该事件 + return; + } + bool empty; + dispatcher->delListener(tag, empty); + if (empty) { + delDispatcher(event, dispatcher); + } + } + + // 这个方法性能比较差 + void delListener(void *tag) { + std::lock_guard lck(_mtxListener); + bool empty; + for (auto it = _mapListener.begin(); it != _mapListener.end();) { + it->second->delListener(tag, empty); + if (empty) { + it = _mapListener.erase(it); + continue; + } + ++it; + } + } + + void clearAll() { + std::lock_guard lck(_mtxListener); + _mapListener.clear(); + } + +private: + template + int emitEvent_l(bool safe, const std::string &event, ArgsType &&...args) { + auto dispatcher = getDispatcher(event); + if (!dispatcher) { + // 该事件无人监听 + return 0; + } + return dispatcher->emitEvent(safe, std::forward(args)...); + } + + EventDispatcher::Ptr getDispatcher(const std::string &event, bool create = false) { + std::lock_guard lck(_mtxListener); + auto it = _mapListener.find(event); + if (it != _mapListener.end()) { + return it->second; + } + if (create) { + // 如果为空则创建一个 + EventDispatcher::Ptr dispatcher(new EventDispatcher()); + _mapListener.emplace(event, dispatcher); + return dispatcher; + } + return nullptr; + } + + void delDispatcher(const std::string &event, const EventDispatcher::Ptr &dispatcher) { + std::lock_guard lck(_mtxListener); + auto it = _mapListener.find(event); + if (it != _mapListener.end() && dispatcher == it->second) { + // 两者相同则删除 + _mapListener.erase(it); + } + } + +private: + std::recursive_mutex _mtxListener; + std::unordered_map _mapListener; +}; + +template +struct NoticeHelper; + +template +struct NoticeHelper { +public: + template + static int emit(const std::string &event, ArgsType &&...args) { + return emit(NoticeCenter::Instance(), event, std::forward(args)...); + } + + template + static int emit(NoticeCenter ¢er, const std::string &event, ArgsType &&...args) { + return center.emitEventSafe(event, std::forward(args)...); + } +}; + +#define NOTICE_EMIT(types, ...) NoticeHelper::emit(__VA_ARGS__) + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_NOTICE_CENTER_H_ */ \ No newline at end of file diff --git a/turbo/network/util/once_token.h b/turbo/network/util/once_token.h new file mode 100644 index 00000000..36642add --- /dev/null +++ b/turbo/network/util/once_token.h @@ -0,0 +1,55 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_ONCE_TOKEN_H_ +#define TURBO_NETWORK_UTIL_ONCE_TOKEN_H_ + +#include +#include + +namespace turbo { +// 设计思想,用栈变量管理堆变量,同理也可以使用智能指针+删除器实现 +class onceToken { +public: + using task = std::function; + + template + onceToken(const FUNC &onConstructed, task onDestructed = nullptr) { + onConstructed(); + _onDestructed = std::move(onDestructed); + } + + onceToken(std::nullptr_t, task onDestructed = nullptr) { + _onDestructed = std::move(onDestructed); + } + + ~onceToken() { + if (_onDestructed) { + _onDestructed(); + } + } + +private: + onceToken() = delete; + onceToken(const onceToken &) = delete; + onceToken(onceToken &&) = delete; + onceToken &operator=(const onceToken &) = delete; + onceToken &operator=(onceToken &&) = delete; + +private: + task _onDestructed; +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK__ONCE_TOKEN_H_ */ \ No newline at end of file diff --git a/turbo/network/util/resource_pool.h b/turbo/network/util/resource_pool.h new file mode 100644 index 00000000..2a4b2302 --- /dev/null +++ b/turbo/network/util/resource_pool.h @@ -0,0 +1,209 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_RESOURCE_POOL_H_ +#define TURBO_NETWORK_UTIL_RESOURCE_POOL_H_ + +#include "turbo/network/util/list.h" +#include +#include +#include +#include +#include +#include + +namespace turbo { + +#if (defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 9))) || defined(__clang__) \ + || !defined(__GNUC__) +#define SUPPORT_DYNAMIC_TEMPLATE +#endif + +template +class ResourcePool_l; +template +class ResourcePool; + +template +class shared_ptr_imp : public std::shared_ptr { +public: + shared_ptr_imp() {} + + /** + * 构造智能指针 + * @param ptr 裸指针 + * @param weakPool 管理本指针的循环池 + * @param quit 对接是否放弃循环使用 + */ + shared_ptr_imp( + C *ptr, const std::weak_ptr> &weakPool, std::shared_ptr quit, + const std::function &on_recycle); + + /** + * 放弃或恢复回到循环池继续使用 + * @param flag + */ + void quit(bool flag = true) { + if (_quit) { + *_quit = flag; + } + } + +private: + std::shared_ptr _quit; +}; + +template +class ResourcePool_l : public std::enable_shared_from_this> { +public: + using ValuePtr = shared_ptr_imp; + friend class shared_ptr_imp; + friend class ResourcePool; + + ResourcePool_l() { + _alloc = []() -> C * { return new C(); }; + } + +#if defined(SUPPORT_DYNAMIC_TEMPLATE) + template + ResourcePool_l(ArgTypes &&...args) { + _alloc = [args...]() -> C * { return new C(args...); }; + } +#endif // defined(SUPPORT_DYNAMIC_TEMPLATE) + + ~ResourcePool_l() { + for (auto ptr : _objs) { + delete ptr; + } + } + + void setSize(size_t size) { + _pool_size = size; + _objs.reserve(size); + } + + ValuePtr obtain(const std::function &on_recycle = nullptr) { + return ValuePtr(getPtr(), _weak_self, std::make_shared(false), on_recycle); + } + + std::shared_ptr obtain2() { + auto weak_self = _weak_self; + return std::shared_ptr(getPtr(), [weak_self](C *ptr) { + auto strongPool = weak_self.lock(); + if (strongPool) { + //放入循环池 + strongPool->recycle(ptr); + } else { + delete ptr; + } + }); + } + +private: + void recycle(C *obj) { + auto is_busy = _busy.test_and_set(); + if (!is_busy) { + //获取到锁 + if (_objs.size() >= _pool_size) { + delete obj; + } else { + _objs.emplace_back(obj); + } + _busy.clear(); + } else { + //未获取到锁 + delete obj; + } + } + + C *getPtr() { + C *ptr; + auto is_busy = _busy.test_and_set(); + if (!is_busy) { + //获取到锁 + if (_objs.size() == 0) { + ptr = _alloc(); + } else { + ptr = _objs.back(); + _objs.pop_back(); + } + _busy.clear(); + } else { + //未获取到锁 + ptr = _alloc(); + } + return ptr; + } + + void setup() { _weak_self = this->shared_from_this(); } + +private: + size_t _pool_size = 8; + std::vector _objs; + std::function _alloc; + std::atomic_flag _busy { false }; + std::weak_ptr _weak_self; +}; + +/** + * 循环池,注意,循环池里面的对象不能继承enable_shared_from_this! + * @tparam C + */ +template +class ResourcePool { +public: + using ValuePtr = shared_ptr_imp; + ResourcePool() { + pool.reset(new ResourcePool_l()); + pool->setup(); + } +#if defined(SUPPORT_DYNAMIC_TEMPLATE) + template + ResourcePool(ArgTypes &&...args) { + pool = std::make_shared>(std::forward(args)...); + pool->setup(); + } +#endif // defined(SUPPORT_DYNAMIC_TEMPLATE) + void setSize(size_t size) { pool->setSize(size); } + + //获取一个对象,性能差些,但是功能丰富些 + ValuePtr obtain(const std::function &on_recycle = nullptr) { return pool->obtain(on_recycle); } + + //获取一个对象,性能好些 + std::shared_ptr obtain2() { return pool->obtain2(); } + +private: + std::shared_ptr> pool; +}; + +template +shared_ptr_imp::shared_ptr_imp(C *ptr, + const std::weak_ptr > &weakPool, + std::shared_ptr quit, + const std::function &on_recycle) : + std::shared_ptr(ptr, [weakPool, quit, on_recycle](C *ptr) { + if (on_recycle) { + on_recycle(ptr); + } + auto strongPool = weakPool.lock(); + if (strongPool && !(*quit)) { + //循环池还在并且不放弃放入循环池 + strongPool->recycle(ptr); + } else { + delete ptr; + } + }), _quit(std::move(quit)) {} + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_RESOURCE_POOL_H_ */ diff --git a/turbo/network/util/speed_statistic.h b/turbo/network/util/speed_statistic.h new file mode 100644 index 00000000..a6e3254e --- /dev/null +++ b/turbo/network/util/speed_statistic.h @@ -0,0 +1,70 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_SPEED_STATISTIC_H_ +#define TURBO_NETWORK_UTIL_SPEED_STATISTIC_H_ + +#include "turbo/network/util/time_ticker.h" + + +namespace turbo { + +class BytesSpeed { +public: + BytesSpeed() = default; + ~BytesSpeed() = default; + + /** + * 添加统计字节 + */ + BytesSpeed &operator+=(size_t bytes) { + _bytes += bytes; + if (_bytes > 1024 * 1024) { + //数据大于1MB就计算一次网速 + computeSpeed(); + } + return *this; + } + + /** + * 获取速度,单位bytes/s + */ + int getSpeed() { + if (_ticker.elapsedTime() < 1000) { + //获取频率小于1秒,那么返回上次计算结果 + return _speed; + } + return computeSpeed(); + } + +private: + int computeSpeed() { + auto elapsed = _ticker.elapsedTime(); + if (!elapsed) { + return _speed; + } + _speed = (int)(_bytes * 1000 / elapsed); + _ticker.resetTime(); + _bytes = 0; + return _speed; + } + +private: + int _speed = 0; + size_t _bytes = 0; + Ticker _ticker; +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_SPEED_STATISTIC_H_ */ \ No newline at end of file diff --git a/turbo/network/util/ssl_box.cc b/turbo/network/util/ssl_box.cc new file mode 100644 index 00000000..8005be7f --- /dev/null +++ b/turbo/network/util/ssl_box.cc @@ -0,0 +1,508 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/util/ssl_box.h" +#include "turbo/network/util/once_token.h" +#include "turbo/network/util/ssl_util.h" + +#if defined(ENABLE_OPENSSL) +#include +#include +#include +#include +#include +#include +#include +#endif //defined(ENABLE_OPENSSL) + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME +//openssl版本是否支持sni +#define SSL_ENABLE_SNI +#endif + +using namespace std; + +namespace turbo { + +static bool s_ignore_invalid_cer = true; + +SSL_Initor &SSL_Initor::Instance() { + static SSL_Initor obj; + return obj; +} + +void SSL_Initor::ignoreInvalidCertificate(bool ignore) { + s_ignore_invalid_cer = ignore; +} + +SSL_Initor::SSL_Initor() { +#if defined(ENABLE_OPENSSL) + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_digests(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_algorithms(); + CRYPTO_set_locking_callback([](int mode, int n, const char *file, int line) { + static mutex *s_mutexes = new mutex[CRYPTO_num_locks()]; + static onceToken token(nullptr, []() { + delete[] s_mutexes; + }); + if (mode & CRYPTO_LOCK) { + s_mutexes[n].lock(); + } else { + s_mutexes[n].unlock(); + } + }); + + CRYPTO_set_id_callback([]() -> unsigned long { +#if !defined(_WIN32) + return (unsigned long) pthread_self(); +#else + return (unsigned long) GetCurrentThreadId(); +#endif + }); + + setContext("", SSLUtil::makeSSLContext(vector >(), nullptr, false), false); + setContext("", SSLUtil::makeSSLContext(vector >(), nullptr, true), true); +#endif //defined(ENABLE_OPENSSL) +} + +SSL_Initor::~SSL_Initor() { +#if defined(ENABLE_OPENSSL) + EVP_cleanup(); + ERR_free_strings(); + ERR_clear_error(); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_remove_thread_state(nullptr); +#elif OPENSSL_VERSION_NUMBER < 0x10000000L + ERR_remove_state(0); +#endif + CRYPTO_set_locking_callback(nullptr); + //sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); + CRYPTO_cleanup_all_ex_data(); + CONF_modules_unload(1); + CONF_modules_free(); +#endif //defined(ENABLE_OPENSSL) +} + +bool SSL_Initor::loadCertificate(const string &pem_or_p12, bool server_mode, const string &password, bool is_file, + bool is_default) { + auto cers = SSLUtil::loadPublicKey(pem_or_p12, password, is_file); + auto key = SSLUtil::loadPrivateKey(pem_or_p12, password, is_file); + auto ssl_ctx = SSLUtil::makeSSLContext(cers, key, server_mode, true); + if (!ssl_ctx) { + return false; + } + for (auto &cer : cers) { + auto server_name = SSLUtil::getServerName(cer.get()); + setContext(server_name, ssl_ctx, server_mode, is_default); + break; + } + return true; +} + +int SSL_Initor::findCertificate(SSL *ssl, int *, void *arg) { +#if !defined(ENABLE_OPENSSL) || !defined(SSL_ENABLE_SNI) + return 0; +#else + if (!ssl) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + SSL_CTX *ctx = nullptr; + static auto &ref = SSL_Initor::Instance(); + const char *vhost = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + + if (vhost && vhost[0] != '\0') { + //根据域名找到证书 + ctx = ref.getSSLCtx(vhost, (bool) (arg)).get(); + if (!ctx) { + //未找到对应的证书 + WarnL << "Can not find any certificate of host: " << vhost + << ", select default certificate of: " << ref._default_vhost[(bool) (arg)]; + } + } + + if (!ctx) { + //客户端未指定域名或者指定的证书不存在,那么选择一个默认的证书 + ctx = ref.getSSLCtx("", (bool) (arg)).get(); + } + + if (!ctx) { + //未有任何有效的证书 + WarnL << "Can not find any available certificate of host: " << (vhost ? vhost : "default host") + << ", tls handshake failed"; + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + SSL_set_SSL_CTX(ssl, ctx); + return SSL_TLSEXT_ERR_OK; +#endif +} + +bool SSL_Initor::setContext(const string &vhost, const shared_ptr &ctx, bool server_mode, bool is_default) { + if (!ctx) { + return false; + } + setupCtx(ctx.get()); +#if defined(ENABLE_OPENSSL) + if (vhost.empty()) { + _ctx_empty[server_mode] = ctx; +#ifdef SSL_ENABLE_SNI + if (server_mode) { + SSL_CTX_set_tlsext_servername_callback(ctx.get(), findCertificate); + SSL_CTX_set_tlsext_servername_arg(ctx.get(), (void *) server_mode); + } +#endif // SSL_ENABLE_SNI + + } else { + _ctxs[server_mode][vhost] = ctx; + if (is_default) { + _default_vhost[server_mode] = vhost; + } + if (vhost.find("*.") == 0) { + //通配符证书 + _ctxs_wildcards[server_mode][vhost.substr(1)] = ctx; + } + DebugL << "Add certificate of: " << vhost; + } + return true; +#else + WarnL << "ENABLE_OPENSSL disabled, you can not use any features based on openssl"; + return false; +#endif //defined(ENABLE_OPENSSL) +} + +void SSL_Initor::setupCtx(SSL_CTX *ctx) { +#if defined(ENABLE_OPENSSL) + //加载默认信任证书 + SSLUtil::loadDefaultCAs(ctx); + SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:!3DES:@STRENGTH"); + SSL_CTX_set_verify_depth(ctx, 9); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, [](int ok, X509_STORE_CTX *pStore) { + if (!ok) { + int depth = X509_STORE_CTX_get_error_depth(pStore); + int err = X509_STORE_CTX_get_error(pStore); + WarnL << "SSL_CTX_set_verify callback, depth: " << depth << " ,err: " << X509_verify_cert_error_string(err); + } + return s_ignore_invalid_cer ? 1 : ok; + }); + +#ifndef SSL_OP_NO_COMPRESSION +#define SSL_OP_NO_COMPRESSION 0 +#endif +#ifndef SSL_MODE_RELEASE_BUFFERS /* OpenSSL >= 1.0.0 */ +#define SSL_MODE_RELEASE_BUFFERS 0 +#endif + unsigned long ssloptions = SSL_OP_ALL + | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + | SSL_OP_NO_COMPRESSION; + +#ifdef SSL_OP_NO_RENEGOTIATION /* openssl 1.1.0 */ + ssloptions |= SSL_OP_NO_RENEGOTIATION; +#endif + SSL_CTX_set_options(ctx, ssloptions); + +#endif //defined(ENABLE_OPENSSL) +} + +shared_ptr SSL_Initor::makeSSL(bool server_mode) { +#if defined(ENABLE_OPENSSL) +#ifdef SSL_ENABLE_SNI + //openssl 版本支持SNI + return SSLUtil::makeSSL(_ctx_empty[server_mode].get()); +#else + //openssl 版本不支持SNI,选择默认证书 + return SSLUtil::makeSSL(getSSLCtx("",server_mode).get()); +#endif//SSL_CTRL_SET_TLSEXT_HOSTNAME +#else + return nullptr; +#endif //defined(ENABLE_OPENSSL) +} + +bool SSL_Initor::trustCertificate(X509 *cer, bool server_mode) { + return SSLUtil::trustCertificate(_ctx_empty[server_mode].get(), cer); +} + +bool SSL_Initor::trustCertificate(const string &pem_p12_cer, bool server_mode, const string &password, bool is_file) { + auto cers = SSLUtil::loadPublicKey(pem_p12_cer, password, is_file); + for (auto &cer : cers) { + trustCertificate(cer.get(), server_mode); + } + return true; +} + +std::shared_ptr SSL_Initor::getSSLCtx(const string &vhost, bool server_mode) { + auto ret = getSSLCtx_l(vhost, server_mode); + if (ret) { + return ret; + } + return getSSLCtxWildcards(vhost, server_mode); +} + +std::shared_ptr SSL_Initor::getSSLCtxWildcards(const string &vhost, bool server_mode) { + for (auto &pr : _ctxs_wildcards[server_mode]) { + auto pos = strcasestr(vhost.data(), pr.first.data()); + if (pos && pos + pr.first.size() == &vhost.back() + 1) { + return pr.second; + } + } + return nullptr; +} + +std::shared_ptr SSL_Initor::getSSLCtx_l(const string &vhost_in, bool server_mode) { + auto vhost = vhost_in; + if (vhost.empty()) { + if (!_default_vhost[server_mode].empty()) { + vhost = _default_vhost[server_mode]; + } else { + //没默认主机,选择空主机 + if (server_mode) { + WarnL << "Server with ssl must have certification and key"; + } + return _ctx_empty[server_mode]; + } + } + //根据主机名查找证书 + auto it = _ctxs[server_mode].find(vhost); + if (it == _ctxs[server_mode].end()) { + return nullptr; + } + return it->second; +} + +string SSL_Initor::defaultVhost(bool server_mode) { + return _default_vhost[server_mode]; +} + +////////////////////////////////////////////////////SSL_Box//////////////////////////////////////////////////////////// + +SSL_Box::~SSL_Box() {} + +SSL_Box::SSL_Box(bool server_mode, bool enable, int buff_size) { +#if defined(ENABLE_OPENSSL) + _read_bio = BIO_new(BIO_s_mem()); + _server_mode = server_mode; + if (enable) { + _ssl = SSL_Initor::Instance().makeSSL(server_mode); + } + if (_ssl) { + _write_bio = BIO_new(BIO_s_mem()); + SSL_set_bio(_ssl.get(), _read_bio, _write_bio); + _server_mode ? SSL_set_accept_state(_ssl.get()) : SSL_set_connect_state(_ssl.get()); + } else { + WarnL << "makeSSL failed"; + } + _send_handshake = false; + _buff_size = buff_size; +#endif //defined(ENABLE_OPENSSL) +} + +void SSL_Box::shutdown() { +#if defined(ENABLE_OPENSSL) + _buffer_send.clear(); + int ret = SSL_shutdown(_ssl.get()); + if (ret != 1) { + ErrorL << "SSL_shutdown failed: " << SSLUtil::getLastError(); + } else { + flush(); + } +#endif //defined(ENABLE_OPENSSL) +} + +void SSL_Box::onRecv(const Buffer::Ptr &buffer) { + if (!buffer->size()) { + return; + } + if (!_ssl) { + if (_on_dec) { + _on_dec(buffer); + } + return; + } +#if defined(ENABLE_OPENSSL) + uint32_t offset = 0; + while (offset < buffer->size()) { + auto nwrite = BIO_write(_read_bio, buffer->data() + offset, buffer->size() - offset); + if (nwrite > 0) { + //部分或全部写入bio完毕 + offset += nwrite; + flush(); + continue; + } + //nwrite <= 0,出现异常 + ErrorL << "Ssl error on BIO_write: " << SSLUtil::getLastError(); + shutdown(); + break; + } +#endif //defined(ENABLE_OPENSSL) +} + +void SSL_Box::onSend(Buffer::Ptr buffer) { + if (!buffer->size()) { + return; + } + if (!_ssl) { + if (_on_enc) { + _on_enc(buffer); + } + return; + } +#if defined(ENABLE_OPENSSL) + if (!_server_mode && !_send_handshake) { + _send_handshake = true; + SSL_do_handshake(_ssl.get()); + } + _buffer_send.emplace_back(std::move(buffer)); + flush(); +#endif //defined(ENABLE_OPENSSL) +} + +void SSL_Box::setOnDecData(const function &cb) { + _on_dec = cb; +} + +void SSL_Box::setOnEncData(const function &cb) { + _on_enc = cb; +} + +void SSL_Box::flushWriteBio() { +#if defined(ENABLE_OPENSSL) + int total = 0; + int nread = 0; + auto buffer_bio = _buffer_pool.obtain2(); + buffer_bio->setCapacity(_buff_size); + auto buf_size = buffer_bio->getCapacity() - 1; + do { + nread = BIO_read(_write_bio, buffer_bio->data() + total, buf_size - total); + if (nread > 0) { + total += nread; + } + } while (nread > 0 && buf_size - total > 0); + + if (!total) { + //未有数据 + return; + } + + //触发此次回调 + buffer_bio->data()[total] = '\0'; + buffer_bio->setSize(total); + if (_on_enc) { + _on_enc(buffer_bio); + } + + if (nread > 0) { + //还有剩余数据,读取剩余数据 + flushWriteBio(); + } +#endif //defined(ENABLE_OPENSSL) +} + +void SSL_Box::flushReadBio() { +#if defined(ENABLE_OPENSSL) + int total = 0; + int nread = 0; + auto buffer_bio = _buffer_pool.obtain2(); + buffer_bio->setCapacity(_buff_size); + auto buf_size = buffer_bio->getCapacity() - 1; + do { + nread = SSL_read(_ssl.get(), buffer_bio->data() + total, buf_size - total); + if (nread > 0) { + total += nread; + } + } while (nread > 0 && buf_size - total > 0); + + if (!total) { + //未有数据 + return; + } + + //触发此次回调 + buffer_bio->data()[total] = '\0'; + buffer_bio->setSize(total); + if (_on_dec) { + _on_dec(buffer_bio); + } + + if (nread > 0) { + //还有剩余数据,读取剩余数据 + flushReadBio(); + } +#endif //defined(ENABLE_OPENSSL) +} + +void SSL_Box::flush() { +#if defined(ENABLE_OPENSSL) + if (_is_flush) { + return; + } + onceToken token([&] { + _is_flush = true; + }, [&]() { + _is_flush = false; + }); + + flushReadBio(); + if (!SSL_is_init_finished(_ssl.get()) || _buffer_send.empty()) { + //ssl未握手结束或没有需要发送的数据 + flushWriteBio(); + return; + } + + //加密数据并发送 + while (!_buffer_send.empty()) { + auto &front = _buffer_send.front(); + uint32_t offset = 0; + while (offset < front->size()) { + auto nwrite = SSL_write(_ssl.get(), front->data() + offset, front->size() - offset); + if (nwrite > 0) { + //部分或全部写入完毕 + offset += nwrite; + flushWriteBio(); + continue; + } + //nwrite <= 0,出现异常 + break; + } + + if (offset != front->size()) { + //这个包未消费完毕,出现了异常,清空数据并断开ssl + ErrorL << "Ssl error on SSL_write: " << SSLUtil::getLastError(); + shutdown(); + break; + } + + //这个包消费完毕,开始消费下一个包 + _buffer_send.pop_front(); + } +#endif //defined(ENABLE_OPENSSL) +} + +bool SSL_Box::setHost(const char *host) { + if (!_ssl) { + return false; + } +#ifdef SSL_ENABLE_SNI + return 0 != SSL_set_tlsext_host_name(_ssl.get(), host); +#else + return false; +#endif//SSL_ENABLE_SNI +} + +} /* namespace turbo */ + + diff --git a/turbo/network/util/ssl_box.h b/turbo/network/util/ssl_box.h new file mode 100644 index 00000000..0fa2109a --- /dev/null +++ b/turbo/network/util/ssl_box.h @@ -0,0 +1,211 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_SSL_BOX_H_ +#define TURBO_NETWORK_UTIL_SSL_BOX_H_ + + +#include +#include +#include +#include "turbo/network/util/logger.h" +#include "turbo/network/util/list.h" +#include "turbo/network/util/util.h" +#include "turbo/network/network/buffer.h" +#include "turbo/network/util/resource_pool.h" + +typedef struct x509_st X509; +typedef struct evp_pkey_st EVP_PKEY; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct ssl_st SSL; +typedef struct bio_st BIO; + +namespace turbo { + +class SSL_Initor { +public: + friend class SSL_Box; + + static SSL_Initor &Instance(); + + /** + * 从文件或字符串中加载公钥和私钥 + * 该证书文件必须同时包含公钥和私钥(cer格式的证书只包括公钥,请使用后面的方法加载) + * 客户端默认可以不加载证书(除非服务器要求客户端提供证书) + * @param pem_or_p12 pem或p12文件路径或者文件内容字符串 + * @param server_mode 是否为服务器模式 + * @param password 私钥加密密码 + * @param is_file 参数pem_or_p12是否为文件路径 + * @param is_default 是否为默认证书 + */ + bool loadCertificate(const std::string &pem_or_p12, bool server_mode = true, const std::string &password = "", + bool is_file = true, bool is_default = true); + + /** + * 是否忽略无效的证书 + * 默认忽略,强烈建议不要忽略! + * @param ignore 标记 + */ + void ignoreInvalidCertificate(bool ignore = true); + + /** + * 信任某证书,一般用于客户端信任自签名的证书或自签名CA签署的证书使用 + * 比如说我的客户端要信任我自己签发的证书,那么我们可以只信任这个证书 + * @param pem_p12_cer pem文件或p12文件或cer文件路径或内容 + * @param server_mode 是否为服务器模式 + * @param password pem或p12证书的密码 + * @param is_file 是否为文件路径 + * @return 是否加载成功 + */ + bool trustCertificate(const std::string &pem_p12_cer, bool server_mode = false, const std::string &password = "", + bool is_file = true); + + /** + * 信任某证书 + * @param cer 证书公钥 + * @param server_mode 是否为服务模式 + * @return 是否加载成功 + */ + bool trustCertificate(X509 *cer, bool server_mode = false); + + /** + * 根据虚拟主机获取SSL_CTX对象 + * @param vhost 虚拟主机名 + * @param server_mode 是否为服务器模式 + * @return SSL_CTX对象 + */ + std::shared_ptr getSSLCtx(const std::string &vhost, bool server_mode); + +private: + SSL_Initor(); + ~SSL_Initor(); + + /** + * 创建SSL对象 + */ + std::shared_ptr makeSSL(bool server_mode); + + /** + * 设置ssl context + * @param vhost 虚拟主机名 + * @param ctx ssl context + * @param server_mode ssl context + * @param is_default 是否为默认证书 + */ + bool setContext(const std::string &vhost, const std::shared_ptr &ctx, bool server_mode, bool is_default = true); + + /** + * 设置SSL_CTX的默认配置 + * @param ctx 对象指针 + */ + void setupCtx(SSL_CTX *ctx); + + std::shared_ptr getSSLCtx_l(const std::string &vhost, bool server_mode); + + std::shared_ptr getSSLCtxWildcards(const std::string &vhost, bool server_mode); + + /** + * 获取默认的虚拟主机 + */ + std::string defaultVhost(bool server_mode); + + /** + * 完成vhost name 匹配的回调函数 + */ + static int findCertificate(SSL *ssl, int *ad, void *arg); + +private: + struct less_nocase { + bool operator()(const std::string &x, const std::string &y) const { + return strcasecmp(x.data(), y.data()) < 0; + } + }; + +private: + std::string _default_vhost[2]; + std::shared_ptr _ctx_empty[2]; + std::map, less_nocase> _ctxs[2]; + std::map, less_nocase> _ctxs_wildcards[2]; +}; + +//////////////////////////////////////////////////////////////////////////////////// + +class SSL_Box { +public: + SSL_Box(bool server_mode = true, bool enable = true, int buff_size = 32 * 1024); + + ~SSL_Box(); + + /** + * 收到密文后,调用此函数解密 + * @param buffer 收到的密文数据 + */ + void onRecv(const Buffer::Ptr &buffer); + + /** + * 需要加密明文调用此函数 + * @param buffer 需要加密的明文数据 + */ + void onSend(Buffer::Ptr buffer); + + /** + * 设置解密后获取明文的回调 + * @param cb 回调对象 + */ + void setOnDecData(const std::function &cb); + + /** + * 设置加密后获取密文的回调 + * @param cb 回调对象 + */ + void setOnEncData(const std::function &cb); + + /** + * 终结ssl + */ + void shutdown(); + + /** + * 清空数据 + */ + void flush(); + + /** + * 设置虚拟主机名 + * @param host 虚拟主机名 + * @return 是否成功 + */ + bool setHost(const char *host); + +private: + void flushWriteBio(); + + void flushReadBio(); + +private: + bool _server_mode; + bool _send_handshake; + bool _is_flush = false; + int _buff_size; + BIO *_read_bio; + BIO *_write_bio; + std::shared_ptr _ssl; + List _buffer_send; + ResourcePool _buffer_pool; + std::function _on_dec; + std::function _on_enc; +}; + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_SSL_BOX_H_ */ \ No newline at end of file diff --git a/turbo/network/util/ssl_util.cc b/turbo/network/util/ssl_util.cc new file mode 100644 index 00000000..bf42c02f --- /dev/null +++ b/turbo/network/util/ssl_util.cc @@ -0,0 +1,390 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/util/ssl_util.h" +#include "turbo/network/util/once_token.h" +#include "turbo/network/util/logger.h" + +#if defined(ENABLE_OPENSSL) +#include +#include +#include +#include +#include +#include +#include +#include +#endif //defined(ENABLE_OPENSSL) + +using namespace std; + +namespace turbo { + +std::string SSLUtil::getLastError() { +#if defined(ENABLE_OPENSSL) + unsigned long errCode = ERR_get_error(); + if (errCode != 0) { + char buffer[256]; + ERR_error_string_n(errCode, buffer, sizeof(buffer)); + return buffer; + } else +#endif //defined(ENABLE_OPENSSL) + { + return "No error"; + } +} + +#if defined(ENABLE_OPENSSL) + +static int getCerType(BIO *bio, const char *passwd, X509 **x509, int type) { + //尝试pem格式 + if (type == 1 || type == 0) { + if (type == 0) { + BIO_reset(bio); + } + // 尝试PEM格式 + *x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr); + if (*x509) { + return 1; + } + } + + if (type == 2 || type == 0) { + if (type == 0) { + BIO_reset(bio); + } + //尝试DER格式 + *x509 = d2i_X509_bio(bio, nullptr); + if (*x509) { + return 2; + } + } + + if (type == 3 || type == 0) { + if (type == 0) { + BIO_reset(bio); + } + //尝试p12格式 + PKCS12 *p12 = d2i_PKCS12_bio(bio, nullptr); + if (p12) { + EVP_PKEY *pkey = nullptr; + PKCS12_parse(p12, passwd, &pkey, x509, nullptr); + PKCS12_free(p12); + if (pkey) { + EVP_PKEY_free(pkey); + } + if (*x509) { + return 3; + } + } + } + + return 0; +} + +#endif //defined(ENABLE_OPENSSL) + +vector > SSLUtil::loadPublicKey(const string &file_path_or_data, const string &passwd, bool isFile) { + vector > ret; +#if defined(ENABLE_OPENSSL) + BIO *bio = isFile ? BIO_new_file((char *) file_path_or_data.data(), "r") : + BIO_new_mem_buf((char *) file_path_or_data.data(), file_path_or_data.size()); + if (!bio) { + WarnL << (isFile ? "BIO_new_file" : "BIO_new_mem_buf") << " failed: " << getLastError(); + return ret; + } + + onceToken token0(nullptr, [&]() { + BIO_free(bio); + }); + + int cer_type = 0; + X509 *x509 = nullptr; + do { + cer_type = getCerType(bio, passwd.data(), &x509, cer_type); + if (cer_type) { + ret.push_back(shared_ptr(x509, [](X509 *ptr) { X509_free(ptr); })); + } + } while (cer_type != 0); + return ret; +#else + return ret; +#endif //defined(ENABLE_OPENSSL) +} + +shared_ptr SSLUtil::loadPrivateKey(const string &file_path_or_data, const string &passwd, bool isFile) { +#if defined(ENABLE_OPENSSL) + BIO *bio = isFile ? + BIO_new_file((char *) file_path_or_data.data(), "r") : + BIO_new_mem_buf((char *) file_path_or_data.data(), file_path_or_data.size()); + if (!bio) { + WarnL << (isFile ? "BIO_new_file" : "BIO_new_mem_buf") << " failed: " << getLastError(); + return nullptr; + } + + pem_password_cb *cb = [](char *buf, int size, int rwflag, void *userdata) -> int { + const string *passwd = (const string *) userdata; + size = size < (int) passwd->size() ? size : (int) passwd->size(); + memcpy(buf, passwd->data(), size); + return size; + }; + + onceToken token0(nullptr, [&]() { + BIO_free(bio); + }); + + //尝试pem格式 + EVP_PKEY *evp_key = PEM_read_bio_PrivateKey(bio, nullptr, cb, (void *) &passwd); + if (!evp_key) { + //尝试p12格式 + BIO_reset(bio); + PKCS12 *p12 = d2i_PKCS12_bio(bio, nullptr); + if (!p12) { + return nullptr; + } + X509 *x509 = nullptr; + PKCS12_parse(p12, passwd.data(), &evp_key, &x509, nullptr); + PKCS12_free(p12); + if (x509) { + X509_free(x509); + } + if (!evp_key) { + return nullptr; + } + } + + return shared_ptr(evp_key, [](EVP_PKEY *ptr) { + EVP_PKEY_free(ptr); + }); +#else + return nullptr; +#endif //defined(ENABLE_OPENSSL) +} + +shared_ptr SSLUtil::makeSSLContext(const vector > &cers, const shared_ptr &key, bool serverMode, bool checkKey) { +#if defined(ENABLE_OPENSSL) + SSL_CTX *ctx = SSL_CTX_new(serverMode ? SSLv23_server_method() : SSLv23_client_method()); + if (!ctx) { + WarnL << "SSL_CTX_new " << (serverMode ? "SSLv23_server_method" : "SSLv23_client_method") << " failed: " << getLastError(); + return nullptr; + } + int i = 0; + for (auto &cer : cers) { + //加载公钥 + if (i++ == 0) { + //SSL_CTX_use_certificate内部会调用X509_up_ref,所以这里不用X509_dup + SSL_CTX_use_certificate(ctx, cer.get()); + } else { + //需要先拷贝X509对象,否则指针会失效 + SSL_CTX_add_extra_chain_cert(ctx, X509_dup(cer.get())); + } + } + + if (key) { + //提供了私钥 + if (SSL_CTX_use_PrivateKey(ctx, key.get()) != 1) { + WarnL << "SSL_CTX_use_PrivateKey failed: " << getLastError(); + SSL_CTX_free(ctx); + return nullptr; + } + } + + if (key || checkKey) { + //加载私钥成功 + if (SSL_CTX_check_private_key(ctx) != 1) { + WarnL << "SSL_CTX_check_private_key failed: " << getLastError(); + SSL_CTX_free(ctx); + return nullptr; + } + } + + //公钥私钥匹配或者没有公私钥 + return shared_ptr(ctx, [](SSL_CTX *ptr) { SSL_CTX_free(ptr); }); +#else + return nullptr; +#endif //defined(ENABLE_OPENSSL) +} + +shared_ptr SSLUtil::makeSSL(SSL_CTX *ctx) { +#if defined(ENABLE_OPENSSL) + auto *ssl = SSL_new(ctx); + if (!ssl) { + return nullptr; + } + return shared_ptr(ssl, [](SSL *ptr) { + SSL_free(ptr); + }); +#else + return nullptr; +#endif //defined(ENABLE_OPENSSL) +} + +bool SSLUtil::loadDefaultCAs(SSL_CTX *ctx) { +#if defined(ENABLE_OPENSSL) + if (!ctx) { + return false; + } + + if (SSL_CTX_set_default_verify_paths(ctx) != 1) { + WarnL << "SSL_CTX_set_default_verify_paths failed: " << getLastError(); + return false; + } + return true; +#else + return false; +#endif //defined(ENABLE_OPENSSL) +} + +bool SSLUtil::trustCertificate(SSL_CTX *ctx, X509 *cer) { +#if defined(ENABLE_OPENSSL) + X509_STORE *store = SSL_CTX_get_cert_store(ctx); + if (store && cer) { + if (X509_STORE_add_cert(store, cer) != 1) { + WarnL << "X509_STORE_add_cert failed: " << getLastError(); + return false; + } + return true; + } +#endif //defined(ENABLE_OPENSSL) + return false; +} + +bool SSLUtil::verifyX509(X509 *cer, ...) { +#if defined(ENABLE_OPENSSL) + va_list args; + va_start(args, cer); + X509_STORE *store = X509_STORE_new(); + do { + X509 *ca; + if ((ca = va_arg(args, X509*)) == nullptr) { + break; + } + X509_STORE_add_cert(store, ca); + } while (true); + va_end(args); + + X509_STORE_CTX *store_ctx = X509_STORE_CTX_new(); + X509_STORE_CTX_init(store_ctx, store, cer, nullptr); + auto ret = X509_verify_cert(store_ctx); + if (ret != 1) { + int depth = X509_STORE_CTX_get_error_depth(store_ctx); + int err = X509_STORE_CTX_get_error(store_ctx); + WarnL << "X509_verify_cert failed, depth: " << depth << ", err: " << X509_verify_cert_error_string(err); + } + + X509_STORE_CTX_free(store_ctx); + X509_STORE_free(store); + return ret == 1; +#else + WarnL << "ENABLE_OPENSSL disabled, you can not use any features based on openssl"; + return false; +#endif //defined(ENABLE_OPENSSL) +} + +#ifdef ENABLE_OPENSSL +#ifndef X509_F_X509_PUBKEY_GET0 +EVP_PKEY *X509_get0_pubkey(X509 *x){ + EVP_PKEY *ret = X509_get_pubkey(x); + if(ret){ + EVP_PKEY_free(ret); + } + return ret; +} +#endif //X509_F_X509_PUBKEY_GET0 + +#ifndef EVP_F_EVP_PKEY_GET0_RSA +RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey){ + RSA *ret = EVP_PKEY_get1_RSA(pkey); + if(ret){ + RSA_free(ret); + } + return ret; +} +#endif //EVP_F_EVP_PKEY_GET0_RSA +#endif //ENABLE_OPENSSL + +string SSLUtil::cryptWithRsaPublicKey(X509 *cer, const string &in_str, bool enc_or_dec) { +#if defined(ENABLE_OPENSSL) + EVP_PKEY *public_key = X509_get0_pubkey(cer); + if (!public_key) { + return ""; + } + auto rsa = EVP_PKEY_get1_RSA(public_key); + if (!rsa) { + return ""; + } + string out_str(RSA_size(rsa), '\0'); + int ret = 0; + if (enc_or_dec) { + ret = RSA_public_encrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, + RSA_PKCS1_PADDING); + } else { + ret = RSA_public_decrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, + RSA_PKCS1_PADDING); + } + if (ret > 0) { + out_str.resize(ret); + return out_str; + } + WarnL << (enc_or_dec ? "RSA_public_encrypt" : "RSA_public_decrypt") << " failed: " << getLastError(); + return ""; +#else + WarnL << "ENABLE_OPENSSL disabled, you can not use any features based on openssl"; + return ""; +#endif //defined(ENABLE_OPENSSL) +} + +string SSLUtil::cryptWithRsaPrivateKey(EVP_PKEY *private_key, const string &in_str, bool enc_or_dec) { +#if defined(ENABLE_OPENSSL) + auto rsa = EVP_PKEY_get1_RSA(private_key); + if (!rsa) { + return ""; + } + string out_str(RSA_size(rsa), '\0'); + int ret = 0; + if (enc_or_dec) { + ret = RSA_private_encrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, + RSA_PKCS1_PADDING); + } else { + ret = RSA_private_decrypt(in_str.size(), (uint8_t *) in_str.data(), (uint8_t *) out_str.data(), rsa, + RSA_PKCS1_PADDING); + } + if (ret > 0) { + out_str.resize(ret); + return out_str; + } + WarnL << getLastError(); + return ""; +#else + WarnL << "ENABLE_OPENSSL disabled, you can not use any features based on openssl"; + return ""; +#endif //defined(ENABLE_OPENSSL) +} + +string SSLUtil::getServerName(X509 *cer) { +#if defined(ENABLE_OPENSSL) && defined(SSL_CTRL_SET_TLSEXT_HOSTNAME) + if (!cer) { + return ""; + } + //获取证书里的域名 + X509_NAME *name = X509_get_subject_name(cer); + char ret[256] = {0}; + X509_NAME_get_text_by_NID(name, NID_commonName, ret, sizeof(ret)); + return ret; +#else + return ""; +#endif +} + +}//namespace turbo \ No newline at end of file diff --git a/turbo/network/util/ssl_util.h b/turbo/network/util/ssl_util.h new file mode 100644 index 00000000..ccc28b01 --- /dev/null +++ b/turbo/network/util/ssl_util.h @@ -0,0 +1,126 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_SSL_UTIL_H_ +#define TURBO_NETWORK_UTIL_SSL_UTIL_H_ + + +#include +#include +#include + +typedef struct x509_st X509; +typedef struct evp_pkey_st EVP_PKEY; +typedef struct ssl_ctx_st SSL_CTX; +typedef struct ssl_st SSL; +typedef struct bio_st BIO; + +namespace turbo { +/** + * ssl证书后缀一般分为以下几种 + * pem:这个是base64的字符编码串,可能存在公钥、私钥或者两者都存在 + * cer:只且只能是公钥,可以与pem的私钥配合使用 + * p12:必须包括私钥和公钥 + */ +class SSLUtil { +public: + static std::string getLastError(); + + /** + * 加载公钥证书,支持pem,p12,cer后缀 + * 由于openssl加载p12证书时会校验公钥和私钥是否匹对,所以加载p12的公钥时可能需要传入证书密码 + * @param file_path_or_data 文件路径或文件内容 + * @param isFile 是否为文件 + * @return 公钥证书列表 + */ + static std::vector > loadPublicKey(const std::string &file_path_or_data, const std::string &passwd = "", bool isFile = true); + + /** + * 加载私钥证书,支持pem,p12后缀 + * @param file_path_or_data 文件路径或文件内容 + * @param passwd 密码 + * @param isFile 是否为文件 + * @return 私钥证书 + */ + static std::shared_ptr loadPrivateKey(const std::string &file_path_or_data, const std::string &passwd = "", bool isFile = true); + + /** + * 创建SSL_CTX对象 + * @param cer 公钥数组 + * @param key 私钥 + * @param serverMode 是否为服务器模式或客户端模式 + * @return SSL_CTX对象 + */ + static std::shared_ptr makeSSLContext(const std::vector > &cers, const std::shared_ptr &key, bool serverMode = true, bool checkKey = false); + + /** + * 创建ssl对象 + * @param ctx SSL_CTX对象 + */ + static std::shared_ptr makeSSL(SSL_CTX *ctx); + + /** + * specifies that the default locations from which CA certificates are loaded should be used. + * There is one default directory and one default file. + * The default CA certificates directory is called "certs" in the default OpenSSL directory. + * Alternatively the SSL_CERT_DIR environment variable can be defined to override this location. + * The default CA certificates file is called "cert.pem" in the default OpenSSL directory. + * Alternatively the SSL_CERT_FILE environment variable can be defined to override this location. + * 信任/usr/local/ssl/certs/目录下的所有证书/usr/local/ssl/cert.pem的证书 + * 环境变量SSL_CERT_FILE将替换/usr/local/ssl/cert.pem的路径 + */ + static bool loadDefaultCAs(SSL_CTX *ctx); + + /** + * 信任某公钥 + */ + static bool trustCertificate(SSL_CTX *ctx, X509 *cer); + + + /** + * 验证证书合法性 + * @param cer 待验证的证书 + * @param ... 信任的CA根证书,X509类型,以nullptr结尾 + * @return 是否合法 + */ + static bool verifyX509(X509 *cer, ...); + + /** + * 使用公钥加解密数据 + * @param cer 公钥,必须为ras的公钥 + * @param in_str 加密或解密的原始数据,实测加密最大支持245个字节,加密后数据长度固定为256个字节 + * @param enc_or_dec true:加密,false:解密 + * @return 加密或解密后的数据 + */ + static std::string cryptWithRsaPublicKey(X509 *cer, const std::string &in_str, bool enc_or_dec); + + /** + * 使用私钥加解密数据 + * @param private_key 私钥,必须为ras的私钥 + * @param in_str 加密或解密的原始数据,实测加密最大支持245个字节,加密后数据长度固定为256个字节 + * @param enc_or_dec true:加密,false:解密 + * @return 加密或解密后的数据 + */ + static std::string cryptWithRsaPrivateKey(EVP_PKEY *private_key, const std::string &in_str, bool enc_or_dec); + + /** + * 获取证书域名 + * @param cer 证书公钥 + * @return 证书域名 + */ + static std::string getServerName(X509 *cer); +}; + +}//namespace turbo +#endif //TURBO_NETWORK_UTIL_SSL_UTIL_H_ \ No newline at end of file diff --git a/turbo/network/util/time_ticker.h b/turbo/network/util/time_ticker.h new file mode 100644 index 00000000..d1a02bff --- /dev/null +++ b/turbo/network/util/time_ticker.h @@ -0,0 +1,151 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_TIME_TICKER_H_ +#define TURBO_NETWORK_UTIL_TIME_TICKER_H_ + +#include +#include "turbo/network/util/logger.h" + +namespace turbo { + +class Ticker { +public: + /** + * 此对象可以用于代码执行时间统计,以可以用于一般计时 + * @param min_ms 开启码执行时间统计时,如果代码执行耗时超过该参数,则打印警告日志 + * @param ctx 日志上下文捕获,用于捕获当前日志代码所在位置 + * @param print_log 是否打印代码执行时间 + */ + Ticker(uint64_t min_ms = 0, + LogContextCapture ctx = LogContextCapture(Logger::Instance(), LWarn, __FILE__, "", __LINE__), + bool print_log = false) : _ctx(std::move(ctx)) { + if (!print_log) { + _ctx.clear(); + } + _created = _begin = getCurrentMillisecond(); + _min_ms = min_ms; + } + + ~Ticker() { + uint64_t tm = createdTime(); + if (tm > _min_ms) { + _ctx << "take time: " << tm << "ms" << ", thread may be overloaded"; + } else { + _ctx.clear(); + } + } + + /** + * 获取上次resetTime后至今的时间,单位毫秒 + */ + uint64_t elapsedTime() const { + return getCurrentMillisecond() - _begin; + } + + /** + * 获取从创建至今的时间,单位毫秒 + */ + uint64_t createdTime() const { + return getCurrentMillisecond() - _created; + } + + /** + * 重置计时器 + */ + void resetTime() { + _begin = getCurrentMillisecond(); + } + +private: + uint64_t _min_ms; + uint64_t _begin; + uint64_t _created; + LogContextCapture _ctx; +}; + +class SmoothTicker { +public: + /** + * 此对象用于生成平滑的时间戳 + * @param reset_ms 时间戳重置间隔,没间隔reset_ms毫秒, 生成的时间戳会同步一次系统时间戳 + */ + SmoothTicker(uint64_t reset_ms = 10000) { + _reset_ms = reset_ms; + _ticker.resetTime(); + } + + ~SmoothTicker() {} + + /** + * 返回平滑的时间戳,防止由于网络抖动导致时间戳不平滑 + */ + uint64_t elapsedTime() { + auto now_time = _ticker.elapsedTime(); + if (_first_time == 0) { + if (now_time < _last_time) { + auto last_time = _last_time - _time_inc; + double elapse_time = (now_time - last_time); + _time_inc += (elapse_time / ++_pkt_count) / 3; + auto ret_time = last_time + _time_inc; + _last_time = (uint64_t) ret_time; + return (uint64_t) ret_time; + } + _first_time = now_time; + _last_time = now_time; + _pkt_count = 0; + _time_inc = 0; + return now_time; + } + + auto elapse_time = (now_time - _first_time); + _time_inc += elapse_time / ++_pkt_count; + auto ret_time = _first_time + _time_inc; + if (elapse_time > _reset_ms) { + _first_time = 0; + } + _last_time = (uint64_t) ret_time; + return (uint64_t) ret_time; + } + + /** + * 时间戳重置为0开始 + */ + void resetTime() { + _first_time = 0; + _pkt_count = 0; + _ticker.resetTime(); + } + +private: + double _time_inc = 0; + uint64_t _first_time = 0; + uint64_t _last_time = 0; + uint64_t _pkt_count = 0; + uint64_t _reset_ms; + Ticker _ticker; +}; + +#if !defined(NDEBUG) +#define TimeTicker() Ticker __ticker(5,WarnL,true) +#define TimeTicker1(tm) Ticker __ticker1(tm,WarnL,true) +#define TimeTicker2(tm, log) Ticker __ticker2(tm,log,true) +#else +#define TimeTicker() +#define TimeTicker1(tm) +#define TimeTicker2(tm,log) +#endif + +} /* namespace turbo */ +#endif /* TURBO_NETWORK_UTIL_TIME_TICKER_H_ */ \ No newline at end of file diff --git a/turbo/network/util/util.cc b/turbo/network/util/util.cc new file mode 100644 index 00000000..f19c55bc --- /dev/null +++ b/turbo/network/util/util.cc @@ -0,0 +1,653 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + + +#include +#include +#include +#include +#include +#include +#include + +#include "turbo/network/util/util.h" +#include "turbo/network/util/local_time.h" +#include "turbo/network/util/file.h" +#include "turbo/network/util/once_token.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/uv_errno.h" +#include "turbo/network/network/sock_util.h" + +#if defined(_WIN32) +#include +#include +#include +#include +#pragma comment(lib, "shlwapi.lib") +extern "C" const IMAGE_DOS_HEADER __ImageBase; +#endif // defined(_WIN32) + +#if defined(__MACH__) || defined(__APPLE__) +#include +#include /* _NSGetExecutablePath */ + +int uv_exepath(char *buffer, int *size) { + /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */ + char abspath[PATH_MAX * 2 + 1]; + char exepath[PATH_MAX + 1]; + uint32_t exepath_size; + size_t abspath_size; + + if (buffer == nullptr || size == nullptr || *size == 0) + return -EINVAL; + + exepath_size = sizeof(exepath); + if (_NSGetExecutablePath(exepath, &exepath_size)) + return -EIO; + + if (realpath(exepath, abspath) != abspath) + return -errno; + + abspath_size = strlen(abspath); + if (abspath_size == 0) + return -EIO; + + *size -= 1; + if ((size_t) *size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + return 0; +} + +#endif //defined(__MACH__) || defined(__APPLE__) + +using namespace std; + +namespace turbo { + +static constexpr char CCH[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +string makeRandStr(int sz, bool printable) { + string ret; + ret.resize(sz); + std::mt19937 rng(std::random_device{}()); + for (int i = 0; i < sz; ++i) { + if (printable) { + uint32_t x = rng() % (sizeof(CCH) - 1); + ret[i] = CCH[x]; + } else { + ret[i] = rng() % 0xFF; + } + } + return ret; +} + +bool is_safe(uint8_t b) { + return b >= ' ' && b < 128; +} + +string hexdump(const void *buf, size_t len) { + string ret("\r\n"); + char tmp[8]; + const uint8_t *data = (const uint8_t *) buf; + for (size_t i = 0; i < len; i += 16) { + for (int j = 0; j < 16; ++j) { + if (i + j < len) { + int sz = snprintf(tmp, sizeof(tmp), "%.2x ", data[i + j]); + ret.append(tmp, sz); + } else { + int sz = snprintf(tmp, sizeof(tmp), " "); + ret.append(tmp, sz); + } + } + for (int j = 0; j < 16; ++j) { + if (i + j < len) { + ret += (is_safe(data[i + j]) ? data[i + j] : '.'); + } else { + ret += (' '); + } + } + ret += ('\n'); + } + return ret; +} + +string hexmem(const void *buf, size_t len) { + string ret; + char tmp[8]; + const uint8_t *data = (const uint8_t *) buf; + for (size_t i = 0; i < len; ++i) { + int sz = sprintf(tmp, "%.2x ", data[i]); + ret.append(tmp, sz); + } + return ret; +} + +string exePath(bool isExe /*= true*/) { + char buffer[PATH_MAX * 2 + 1] = {0}; + int n = -1; +#if defined(_WIN32) + n = GetModuleFileNameA(isExe?nullptr:(HINSTANCE)&__ImageBase, buffer, sizeof(buffer)); +#elif defined(__MACH__) || defined(__APPLE__) + n = sizeof(buffer); + if (uv_exepath(buffer, &n) != 0) { + n = -1; + } +#elif defined(__linux__) + n = readlink("/proc/self/exe", buffer, sizeof(buffer)); +#endif + + string filePath; + if (n <= 0) { + filePath = "./"; + } else { + filePath = buffer; + } + +#if defined(_WIN32) + //windows下把路径统一转换层unix风格,因为后续都是按照unix风格处理的 + for (auto &ch : filePath) { + if (ch == '\\') { + ch = '/'; + } + } +#endif //defined(_WIN32) + + return filePath; +} + +string exeDir(bool isExe /*= true*/) { + auto path = exePath(isExe); + return path.substr(0, path.rfind('/') + 1); +} + +string exeName(bool isExe /*= true*/) { + auto path = exePath(isExe); + return path.substr(path.rfind('/') + 1); +} + +// string转小写 +std::string &strToLower(std::string &str) { + transform(str.begin(), str.end(), str.begin(), towlower); + return str; +} + +// string转大写 +std::string &strToUpper(std::string &str) { + transform(str.begin(), str.end(), str.begin(), towupper); + return str; +} + +// string转小写 +std::string strToLower(std::string &&str) { + transform(str.begin(), str.end(), str.begin(), towlower); + return std::move(str); +} + +// string转大写 +std::string strToUpper(std::string &&str) { + transform(str.begin(), str.end(), str.begin(), towupper); + return std::move(str); +} + +vector split(const string &s, const char *delim) { + vector ret; + size_t last = 0; + auto index = s.find(delim, last); + while (index != string::npos) { + if (index - last > 0) { + ret.push_back(s.substr(last, index - last)); + } + last = index + strlen(delim); + index = s.find(delim, last); + } + if (!s.size() || s.size() - last > 0) { + ret.push_back(s.substr(last)); + } + return ret; +} + +#define TRIM(s, chars) \ +do{ \ + string map(0xFF, '\0'); \ + for (auto &ch : chars) { \ + map[(unsigned char &)ch] = '\1'; \ + } \ + while( s.size() && map.at((unsigned char &)s.back())) s.pop_back(); \ + while( s.size() && map.at((unsigned char &)s.front())) s.erase(0,1); \ +}while(0); + +//去除前后的空格、回车符、制表符 +std::string &trim(std::string &s, const string &chars) { + TRIM(s, chars); + return s; +} + +std::string trim(std::string &&s, const string &chars) { + TRIM(s, chars); + return std::move(s); +} + +void replace(string &str, const string &old_str, const string &new_str,std::string::size_type b_pos) { + if (old_str.empty() || old_str == new_str) { + return; + } + auto pos = str.find(old_str,b_pos); + if (pos == string::npos) { + return; + } + str.replace(pos, old_str.size(), new_str); + replace(str, old_str, new_str,pos + new_str.length()); +} + +bool start_with(const string &str, const string &substr) { + return str.find(substr) == 0; +} + +bool end_with(const string &str, const string &substr) { + auto pos = str.rfind(substr); + return pos != string::npos && pos == str.size() - substr.size(); +} + +bool isIP(const char *str) { + return SockUtil::is_ipv4(str) || SockUtil::is_ipv6(str); +} + +#if defined(_WIN32) +void sleep(int second) { + Sleep(1000 * second); +} +void usleep(int micro_seconds) { + this_thread::sleep_for(std::chrono::microseconds(micro_seconds)); +} + +int gettimeofday(struct timeval *tp, void *tzp) { + auto now_stamp = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + tp->tv_sec = (decltype(tp->tv_sec))(now_stamp / 1000000LL); + tp->tv_usec = now_stamp % 1000000LL; + return 0; +} + +const char *strcasestr(const char *big, const char *little){ + string big_str = big; + string little_str = little; + strToLower(big_str); + strToLower(little_str); + auto pos = strstr(big_str.data(), little_str.data()); + if (!pos){ + return nullptr; + } + return big + (pos - big_str.data()); +} + +int vasprintf(char **strp, const char *fmt, va_list ap) { + // _vscprintf tells you how big the buffer needs to be + int len = _vscprintf(fmt, ap); + if (len == -1) { + return -1; + } + size_t size = (size_t)len + 1; + char *str = (char*)malloc(size); + if (!str) { + return -1; + } + // _vsprintf_s is the "secure" version of vsprintf + int r = vsprintf_s(str, len + 1, fmt, ap); + if (r == -1) { + free(str); + return -1; + } + *strp = str; + return r; +} + + int asprintf(char **strp, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int r = vasprintf(strp, fmt, ap); + va_end(ap); + return r; +} + +#endif //WIN32 + +static long s_gmtoff = 0; //时间差 +static onceToken s_token([]() { +#ifdef _WIN32 + TIME_ZONE_INFORMATION tzinfo; + DWORD dwStandardDaylight; + long bias; + dwStandardDaylight = GetTimeZoneInformation(&tzinfo); + bias = tzinfo.Bias; + if (dwStandardDaylight == TIME_ZONE_ID_STANDARD) { + bias += tzinfo.StandardBias; + } + if (dwStandardDaylight == TIME_ZONE_ID_DAYLIGHT) { + bias += tzinfo.DaylightBias; + } + s_gmtoff = -bias * 60; //时间差(分钟) +#else + local_time_init(); + s_gmtoff = getLocalTime(time(nullptr)).tm_gmtoff; +#endif // _WIN32 +}); + +long getGMTOff() { + return s_gmtoff; +} + +static inline uint64_t getCurrentMicrosecondOrigin() { +#if !defined(_WIN32) + struct timeval tv; + gettimeofday(&tv, nullptr); + return tv.tv_sec * 1000000LL + tv.tv_usec; +#else + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +#endif +} + +static atomic s_currentMicrosecond(0); +static atomic s_currentMillisecond(0); +static atomic s_currentMicrosecond_system(getCurrentMicrosecondOrigin()); +static atomic s_currentMillisecond_system(getCurrentMicrosecondOrigin() / 1000); + +static inline bool initMillisecondThread() { + static std::thread s_thread([]() { + setThreadName("stamp thread"); + DebugL << "Stamp thread started"; + uint64_t last = getCurrentMicrosecondOrigin(); + uint64_t now; + uint64_t microsecond = 0; + while (true) { + now = getCurrentMicrosecondOrigin(); + //记录系统时间戳,可回退 + s_currentMicrosecond_system.store(now, memory_order_release); + s_currentMillisecond_system.store(now / 1000, memory_order_release); + + //记录流逝时间戳,不可回退 + int64_t expired = now - last; + last = now; + if (expired > 0 && expired < 1000 * 1000) { + //流逝时间处于0~1000ms之间,那么是合理的,说明没有调整系统时间 + microsecond += expired; + s_currentMicrosecond.store(microsecond, memory_order_release); + s_currentMillisecond.store(microsecond / 1000, memory_order_release); + } else if (expired != 0) { + WarnL << "Stamp expired is abnormal: " << expired; + } + //休眠0.5 ms + usleep(500); + } + }); + static onceToken s_token([]() { + s_thread.detach(); + }); + return true; +} + +uint64_t getCurrentMillisecond(bool system_time) { + static bool flag = initMillisecondThread(); + if (system_time) { + return s_currentMillisecond_system.load(memory_order_acquire); + } + return s_currentMillisecond.load(memory_order_acquire); +} + +uint64_t getCurrentMicrosecond(bool system_time) { + static bool flag = initMillisecondThread(); + if (system_time) { + return s_currentMicrosecond_system.load(memory_order_acquire); + } + return s_currentMicrosecond.load(memory_order_acquire); +} + +string getTimeStr(const char *fmt, time_t time) { + if (!time) { + time = ::time(nullptr); + } + auto tm = getLocalTime(time); + size_t size = strlen(fmt) + 64; + string ret; + ret.resize(size); + size = std::strftime(&ret[0], size, fmt, &tm); + if (size > 0) { + ret.resize(size); + } + else{ + ret = fmt; + } + return ret; +} + + +struct tm getLocalTime(time_t sec) { + struct tm tm; +#ifdef _WIN32 + localtime_s(&tm, &sec); +#else + no_locks_localtime(&tm, sec); +#endif //_WIN32 + return tm; +} + +//todo mingw 环境下测试thread_local实现又bug,在线程退出时,对象析构触发崩溃 +static thread_local string thread_name; + +static string limitString(const char *name, size_t max_size) { + string str = name; + if (str.size() + 1 > max_size) { + auto erased = str.size() + 1 - max_size + 3; + str.replace(5, erased, "..."); + } + return str; +} + +void setThreadName(const char *name) { + assert(name); +#if defined(__linux) || defined(__linux__) + pthread_setname_np(pthread_self(), limitString(name, 16).data()); +#elif defined(__MACH__) || defined(__APPLE__) + pthread_setname_np(limitString(name, 32).data()); +#elif defined(_MSC_VER) + // SetThreadDescription was added in 1607 (aka RS1). Since we can't guarantee the user is running 1607 or later, we need to ask for the function from the kernel. + using SetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PCWSTR lpThreadDescription); + static auto setThreadDescription = reinterpret_cast(::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "SetThreadDescription")); + if (setThreadDescription) { + // Convert the thread name to Unicode + wchar_t threadNameW[MAX_PATH]; + size_t numCharsConverted; + errno_t wcharResult = mbstowcs_s(&numCharsConverted, threadNameW, name, MAX_PATH - 1); + if (wcharResult == 0) { + HRESULT hr = setThreadDescription(::GetCurrentThread(), threadNameW); + if (!SUCCEEDED(hr)) { + int i = 0; + i++; + } + } + } else { + // For understanding the types and values used here, please see: + // https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code + + const DWORD MS_VC_EXCEPTION = 0x406D1388; +#pragma pack(push, 8) + struct THREADNAME_INFO { + DWORD dwType = 0x1000; // Must be 0x1000 + LPCSTR szName; // Pointer to name (in user address space) + DWORD dwThreadID; // Thread ID (-1 for caller thread) + DWORD dwFlags = 0; // Reserved for future use; must be zero + }; +#pragma pack(pop) + + THREADNAME_INFO info; + info.szName = name; + info.dwThreadID = (DWORD) - 1; + + __try{ + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast(&info)); + } __except(GetExceptionCode() == MS_VC_EXCEPTION ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER) { + } + } +#else + thread_name = name ? name : ""; +#endif +} + +string getThreadName() { +#if ((defined(__linux) || defined(__linux__)) && !defined(ANDROID)) || (defined(__MACH__) || defined(__APPLE__)) || (defined(ANDROID) && __ANDROID_API__ >= 26) + string ret; + ret.resize(32); + auto tid = pthread_self(); + pthread_getname_np(tid, (char *) ret.data(), ret.size()); + if (ret[0]) { + ret.resize(strlen(ret.data())); + return ret; + } + return to_string((uint64_t) tid); +#elif defined(_MSC_VER) + using GetThreadDescriptionFunc = HRESULT(WINAPI * )(_In_ HANDLE hThread, _In_ PWSTR * ppszThreadDescription); + static auto getThreadDescription = reinterpret_cast(::GetProcAddress(::GetModuleHandleA("Kernel32.dll"), "GetThreadDescription")); + + if (!getThreadDescription) { + std::ostringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); + } else { + PWSTR data; + HRESULT hr = getThreadDescription(GetCurrentThread(), &data); + if (SUCCEEDED(hr) && data[0] != '\0') { + char threadName[MAX_PATH]; + size_t numCharsConverted; + errno_t charResult = wcstombs_s(&numCharsConverted, threadName, data, MAX_PATH - 1); + if (charResult == 0) { + LocalFree(data); + std::ostringstream ss; + ss << threadName; + return ss.str(); + } else { + if (data) { + LocalFree(data); + } + return to_string((uint64_t) GetCurrentThreadId()); + } + } else { + if (data) { + LocalFree(data); + } + return to_string((uint64_t) GetCurrentThreadId()); + } + } +#else + if (!thread_name.empty()) { + return thread_name; + } + std::ostringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +#endif +} + +bool setThreadAffinity(int i) { +#if (defined(__linux) || defined(__linux__)) && !defined(ANDROID) + cpu_set_t mask; + CPU_ZERO(&mask); + if (i >= 0) { + CPU_SET(i, &mask); + } else { + for (auto j = 0u; j < thread::hardware_concurrency(); ++j) { + CPU_SET(j, &mask); + } + } + if (!pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask)) { + return true; + } + WarnL << "pthread_setaffinity_np failed: " << get_uv_errmsg(); +#endif + return false; +} + +#ifndef HAS_CXA_DEMANGLE +// We only support some compilers that support __cxa_demangle. +// TODO: Checks if Android NDK has fixed this issue or not. +#if defined(__ANDROID__) && (defined(__i386__) || defined(__x86_64__)) +#define HAS_CXA_DEMANGLE 0 +#elif (__GNUC__ >= 4 || (__GNUC__ >= 3 && __GNUC_MINOR__ >= 4)) && \ + !defined(__mips__) +#define HAS_CXA_DEMANGLE 1 +#elif defined(__clang__) && !defined(_MSC_VER) +#define HAS_CXA_DEMANGLE 1 +#else +#define HAS_CXA_DEMANGLE 0 +#endif +#endif +#if HAS_CXA_DEMANGLE +#include +#endif + +// Demangle a mangled symbol name and return the demangled name. +// If 'mangled' isn't mangled in the first place, this function +// simply returns 'mangled' as is. +// +// This function is used for demangling mangled symbol names such as +// '_Z3bazifdPv'. It uses abi::__cxa_demangle() if your compiler has +// the API. Otherwise, this function simply returns 'mangled' as is. +// +// Currently, we support only GCC 3.4.x or later for the following +// reasons. +// +// - GCC 2.95.3 doesn't have cxxabi.h +// - GCC 3.3.5 and ICC 9.0 have a bug. Their abi::__cxa_demangle() +// returns junk values for non-mangled symbol names (ex. function +// names in C linkage). For example, +// abi::__cxa_demangle("main", 0, 0, &status) +// returns "unsigned long" and the status code is 0 (successful). +// +// Also, +// +// - MIPS is not supported because abi::__cxa_demangle() is not defined. +// - Android x86 is not supported because STLs don't define __cxa_demangle +// +string demangle(const char *mangled) { + int status = 0; + char *demangled = nullptr; +#if HAS_CXA_DEMANGLE + demangled = abi::__cxa_demangle(mangled, nullptr, nullptr, &status); +#endif + string out; + if (status == 0 && demangled) { // Demangling succeeeded. + out.append(demangled); + delete [] demangled; // 开启asan后,用free会卡死 + } else { + out.append(mangled); + } + return out; +} + +string getEnv(const string &key) { + auto ekey = key.c_str(); + if (*ekey == '$') { + ++ekey; + } + auto value = *ekey ? getenv(ekey) : nullptr; + return value ? value : ""; +} + + +void Creator::onDestoryException(const type_info &info, const exception &ex) { + ErrorL << "Invoke " << demangle(info.name()) << "::onDestory throw a exception: " << ex.what(); +} + +} // namespace turbo \ No newline at end of file diff --git a/turbo/network/util/util.h b/turbo/network/util/util.h new file mode 100644 index 00000000..aa1a776b --- /dev/null +++ b/turbo/network/util/util.h @@ -0,0 +1,443 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_UTIL_H_ +#define TURBO_NETWORK_UTIL_UTIL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "turbo/network/util/function_traits.h" + +#if defined(_WIN32) +#undef FD_SETSIZE +//修改默认64为1024路 +#define FD_SETSIZE 1024 +#include +#pragma comment (lib,"WS2_32") +#else +#include +#include +#include +#include +#endif // defined(_WIN32) + +#if defined(__APPLE__) +#include "TargetConditionals.h" +#if TARGET_IPHONE_SIMULATOR +#define OS_IPHONE +#elif TARGET_OS_IPHONE +#define OS_IPHONE +#endif +#endif //__APPLE__ + +#define INSTANCE_IMP(class_name, ...) \ +class_name &class_name::Instance() { \ + static std::shared_ptr s_instance(new class_name(__VA_ARGS__)); \ + static class_name &s_instance_ref = *s_instance; \ + return s_instance_ref; \ +} + +namespace turbo { + +#define StrPrinter ::turbo::_StrPrinter() +class _StrPrinter : public std::string { +public: + _StrPrinter() {} + + template + _StrPrinter& operator <<(T && data) { + _stream << std::forward(data); + this->std::string::operator=(_stream.str()); + return *this; + } + + std::string operator <<(std::ostream&(*f)(std::ostream&)) const { + return *this; + } + +private: + std::stringstream _stream; +}; + +//禁止拷贝基类 +class noncopyable { +protected: + noncopyable() {} + ~noncopyable() {} +private: + //禁止拷贝 + noncopyable(const noncopyable &that) = delete; + noncopyable(noncopyable &&that) = delete; + noncopyable &operator=(const noncopyable &that) = delete; + noncopyable &operator=(noncopyable &&that) = delete; +}; + +#ifndef CLASS_FUNC_TRAITS +#define CLASS_FUNC_TRAITS(func_name) \ +template \ +constexpr bool Has_##func_name(decltype(&T::on##func_name) /*unused*/) { \ + using RET = typename function_traits::return_type; \ + using FuncType = RET (T::*)(ARGS...); \ + return std::is_same::value; \ +} \ +\ +template \ +constexpr bool Has_##func_name(...) { \ + return false; \ +} \ +\ +template \ +static void InvokeFunc_##func_name(typename std::enable_if(nullptr), T>::type &obj, ARGS ...args) {} \ +\ +template\ +static typename function_traits::return_type InvokeFunc_##func_name(typename std::enable_if(nullptr), T>::type &obj, ARGS ...args) {\ + return obj.on##func_name(std::forward(args)...);\ +} +#endif //CLASS_FUNC_TRAITS + +#ifndef CLASS_FUNC_INVOKE +#define CLASS_FUNC_INVOKE(T, obj, func_name, ...) InvokeFunc_##func_name(obj, ##__VA_ARGS__) +#endif //CLASS_FUNC_INVOKE + +CLASS_FUNC_TRAITS(Destory) +CLASS_FUNC_TRAITS(Create) + +/** + * 对象安全的构建和析构,构建后执行onCreate函数,析构前执行onDestory函数 + * 在函数onCreate和onDestory中可以执行构造或析构中不能调用的方法,比如说shared_from_this或者虚函数 + * @warning onDestory函数确保参数个数为0;否则会被忽略调用 + */ +class Creator { +public: + /** + * 创建对象,用空参数执行onCreate和onDestory函数 + * @param args 对象构造函数参数列表 + * @return args对象的智能指针 + */ + template + static std::shared_ptr create(ArgsType &&...args) { + std::shared_ptr ret(new C(std::forward(args)...), [](C *ptr) { + try { + CLASS_FUNC_INVOKE(C, *ptr, Destory); + } catch (std::exception &ex){ + onDestoryException(typeid(C), ex); + } + delete ptr; + }); + CLASS_FUNC_INVOKE(C, *ret, Create); + return ret; + } + + /** + * 创建对象,用指定参数执行onCreate函数 + * @param args 对象onCreate函数参数列表 + * @warning args参数类型和个数必须与onCreate函数类型匹配(不可忽略默认参数),否则会由于模板匹配失败导致忽略调用 + * @return args对象的智能指针 + */ + template + static std::shared_ptr create2(ArgsType &&...args) { + std::shared_ptr ret(new C(), [](C *ptr) { + try { + CLASS_FUNC_INVOKE(C, *ptr, Destory); + } catch (std::exception &ex){ + onDestoryException(typeid(C), ex); + } + delete ptr; + }); + CLASS_FUNC_INVOKE(C, *ret, Create, std::forward(args)...); + return ret; + } + +private: + static void onDestoryException(const std::type_info &info, const std::exception &ex); + +private: + Creator() = default; + ~Creator() = default; +}; + +template +class ObjectStatistic{ +public: + ObjectStatistic(){ + ++getCounter(); + } + + ~ObjectStatistic(){ + --getCounter(); + } + + static size_t count(){ + return getCounter().load(); + } + +private: + static std::atomic & getCounter(); +}; + +#define StatisticImp(Type) \ + template<> \ + std::atomic& ObjectStatistic::getCounter(){ \ + static std::atomic instance(0); \ + return instance; \ + } + +std::string makeRandStr(int sz, bool printable = true); +std::string hexdump(const void *buf, size_t len); +std::string hexmem(const void* buf, size_t len); +std::string exePath(bool isExe = true); +std::string exeDir(bool isExe = true); +std::string exeName(bool isExe = true); + +std::vector split(const std::string& s, const char *delim); +//去除前后的空格、回车符、制表符... +std::string& trim(std::string &s,const std::string &chars=" \r\n\t"); +std::string trim(std::string &&s,const std::string &chars=" \r\n\t"); +// string转小写 +std::string &strToLower(std::string &str); +std::string strToLower(std::string &&str); +// string转大写 +std::string &strToUpper(std::string &str); +std::string strToUpper(std::string &&str); +//替换子字符串 +void replace(std::string &str, const std::string &old_str, const std::string &new_str, std::string::size_type b_pos = 0) ; +//判断是否为ip +bool isIP(const char *str); +//字符串是否以xx开头 +bool start_with(const std::string &str, const std::string &substr); +//字符串是否以xx结尾 +bool end_with(const std::string &str, const std::string &substr); +//拼接格式字符串 +template +std::string str_format(const std::string &format, Args... args) { + + // Calculate the buffer size + auto size_buf = snprintf(nullptr, 0, format.c_str(), args ...) + 1; + // Allocate the buffer +#if __cplusplus >= 201703L + // C++17 + auto buf = std::make_unique(size_buf); +#else + // C++11 + std:: unique_ptr buf(new(std::nothrow) char[size_buf]); +#endif + // Check if the allocation is successful + if (buf == nullptr) { + return {}; + } + // Fill the buffer with formatted string + auto result = snprintf(buf.get(), size_buf, format.c_str(), args ...); + // Return the formatted string + return std::string(buf.get(), buf.get() + result); +} + +#ifndef bzero +#define bzero(ptr,size) memset((ptr),0,(size)); +#endif //bzero + +#if defined(ANDROID) +template +std::string to_string(T value){ + std::ostringstream os ; + os << std::forward(value); + return os.str() ; +} +#endif//ANDROID + +#if defined(_WIN32) +int gettimeofday(struct timeval *tp, void *tzp); +void usleep(int micro_seconds); +void sleep(int second); +int vasprintf(char **strp, const char *fmt, va_list ap); +int asprintf(char **strp, const char *fmt, ...); +const char *strcasestr(const char *big, const char *little); + +#if !defined(strcasecmp) + #define strcasecmp _stricmp +#endif + +#if !defined(strncasecmp) +#define strncasecmp _strnicmp +#endif + +#ifndef ssize_t + #ifdef _WIN64 + #define ssize_t int64_t + #else + #define ssize_t int32_t + #endif +#endif +#endif //WIN32 + +/** + * 获取时间差, 返回值单位为秒 + */ +long getGMTOff(); + +/** + * 获取1970年至今的毫秒数 + * @param system_time 是否为系统时间(系统时间可以回退),否则为程序启动时间(不可回退) + */ +uint64_t getCurrentMillisecond(bool system_time = false); + +/** + * 获取1970年至今的微秒数 + * @param system_time 是否为系统时间(系统时间可以回退),否则为程序启动时间(不可回退) + */ +uint64_t getCurrentMicrosecond(bool system_time = false); + +/** + * 获取时间字符串 + * @param fmt 时间格式,譬如%Y-%m-%d %H:%M:%S + * @return 时间字符串 + */ +std::string getTimeStr(const char *fmt,time_t time = 0); + +/** + * 根据unix时间戳获取本地时间 + * @param sec unix时间戳 + * @return tm结构体 + */ +struct tm getLocalTime(time_t sec); + +/** + * 设置线程名 + */ +void setThreadName(const char *name); + +/** + * 获取线程名 + */ +std::string getThreadName(); + +/** + * 设置当前线程cpu亲和性 + * @param i cpu索引,如果为-1,那么取消cpu亲和性 + * @return 是否成功,目前只支持linux + */ +bool setThreadAffinity(int i); + +/** + * 根据typeid(class).name()获取类名 + */ +std::string demangle(const char *mangled); + +/** + * 获取环境变量内容,以'$'开头 + */ +std::string getEnv(const std::string &key); + +// 可以保存任意的对象 +class Any { +public: + using Ptr = std::shared_ptr; + + Any() = default; + ~Any() = default; + + Any(const Any &that) = default; + Any(Any &&that) { + _type = that._type; + _data = std::move(that._data); + } + + Any &operator=(const Any &that) = default; + Any &operator=(Any &&that) { + _type = that._type; + _data = std::move(that._data); + return *this; + } + + template + void set(ArgsType &&...args) { + _type = &typeid(T); + _data.reset(new T(std::forward(args)...), [](void *ptr) { delete (T *)ptr; }); + } + + template + void set(std::shared_ptr data) { + if (data) { + _type = &typeid(T); + _data = std::move(data); + } else { + reset(); + } + } + + template + T &get(bool safe = true) { + if (!_data) { + throw std::invalid_argument("Any is empty"); + } + if (safe && !is()) { + throw std::invalid_argument("Any::get(): " + demangle(_type->name()) + " unable cast to " + demangle(typeid(T).name())); + } + return *((T *)_data.get()); + } + + template + const T &get(bool safe = true) const { + return const_cast(*this).get(safe); + } + + template + bool is() const { + return _type && typeid(T) == *_type; + } + + operator bool() const { return _data.operator bool(); } + bool empty() const { return !bool(); } + + void reset() { + _type = nullptr; + _data = nullptr; + } + + Any &operator=(nullptr_t) { + reset(); + return *this; + } + + std::string type_name() const { + if (!_type) { + return ""; + } + return demangle(_type->name()); + } + +private: + const std::type_info* _type = nullptr; + std::shared_ptr _data; +}; + +// 用于保存一些外加属性 +class AnyStorage : public std::unordered_map { +public: + AnyStorage() = default; + ~AnyStorage() = default; + using Ptr = std::shared_ptr; +}; + +} // namespace turbo +#endif /* TURBO_NETWORK_UTIL_UTIL_H_ */ + + + diff --git a/turbo/network/util/uv_errno.cc b/turbo/network/util/uv_errno.cc new file mode 100644 index 00000000..ec04fa71 --- /dev/null +++ b/turbo/network/util/uv_errno.cc @@ -0,0 +1,198 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "turbo/network/util/uv_errno.h" +#include +#include +#include + +#if defined(_WIN32) +#define FD_SETSIZE 1024 //修改默认64为1024路 +#include +#include +#else +#include +#endif // defined(_WIN32) + + +namespace turbo { + +static const char *uv__unknown_err_code(int err) { + static char buf[32]; + snprintf(buf, sizeof(buf), "Unknown system error %d", err); + return buf; +} + +#define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name; +const char *uv_err_name(int err) { + switch (err) { + UV_ERRNO_MAP(UV_ERR_NAME_GEN) + } + return uv__unknown_err_code(err); +} +#undef UV_ERR_NAME_GEN + +#define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg; +const char *uv_strerror(int err) { + switch (err) { + UV_ERRNO_MAP(UV_STRERROR_GEN) + } + return uv__unknown_err_code(err); +} +#undef UV_STRERROR_GEN + +int uv_translate_posix_error(int err) { + if (err <= 0) { + return err; + } + switch (err) { + //为了兼容windows/unix平台,信号EINPROGRESS ,EAGAIN,EWOULDBLOCK,ENOBUFS 全部统一成EAGAIN处理 + case ENOBUFS://在mac系统下实测发现会有此信号发生 + case EINPROGRESS: + case EWOULDBLOCK: err = EAGAIN; break; + default: break; + } + return -err; +} + +int get_uv_error(bool netErr) { +#if defined(_WIN32) + auto errCode = netErr ? WSAGetLastError() : GetLastError(); + if (errCode == WSANOTINITIALISED) { + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + WSAStartup(wVersionRequested, &wsaData); + static std::atomic try_count { 0 }; + if (++try_count == 1) { + std::cerr << "win socket not startuped, now start it and try again" << std::endl; + } else if (try_count == 10) { + std::cerr << "win socket start failed, now force exit" << std::endl; + exit(-1); + } + return UV_EINTR; + } + switch (errCode) { + case ERROR_NOACCESS: return UV_EACCES; + case WSAEACCES: return UV_EACCES; +#if defined(ERROR_ELEVATION_REQUIRED) + case ERROR_ELEVATION_REQUIRED: return UV_EACCES; +#endif //ERROR_ELEVATION_REQUIRED + case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; + case WSAEADDRINUSE: return UV_EADDRINUSE; + case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; + case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT; + case WSAEWOULDBLOCK: return UV_EAGAIN; + case WSAEALREADY: return UV_EALREADY; + case ERROR_INVALID_FLAGS: return UV_EBADF; + case ERROR_INVALID_HANDLE: return UV_EBADF; + case ERROR_LOCK_VIOLATION: return UV_EBUSY; + case ERROR_PIPE_BUSY: return UV_EBUSY; + case ERROR_SHARING_VIOLATION: return UV_EBUSY; + case ERROR_OPERATION_ABORTED: return UV_ECANCELED; + case WSAEINTR: return UV_ECANCELED; + case ERROR_NO_UNICODE_TRANSLATION: return UV_ECHARSET; + case ERROR_CONNECTION_ABORTED: return UV_ECONNABORTED; + case WSAECONNABORTED: return UV_ECONNABORTED; + case ERROR_CONNECTION_REFUSED: return UV_ECONNREFUSED; + case WSAECONNREFUSED: return UV_ECONNREFUSED; + case ERROR_NETNAME_DELETED: return UV_ECONNRESET; + case WSAECONNRESET: return UV_ECONNRESET; + case ERROR_ALREADY_EXISTS: return UV_EEXIST; + case ERROR_FILE_EXISTS: return UV_EEXIST; + case ERROR_BUFFER_OVERFLOW: return UV_EFAULT; + case WSAEFAULT: return UV_EFAULT; + case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH; + case WSAEHOSTUNREACH: return UV_EHOSTUNREACH; + case ERROR_INSUFFICIENT_BUFFER: return UV_EINVAL; + case ERROR_INVALID_DATA: return UV_EINVAL; + case ERROR_INVALID_PARAMETER: return UV_EINVAL; +#if defined(ERROR_SYMLINK_NOT_SUPPORTED) + case ERROR_SYMLINK_NOT_SUPPORTED: return UV_EINVAL; +#endif //ERROR_SYMLINK_NOT_SUPPORTED + case WSAEINVAL: return UV_EINVAL; + case WSAEPFNOSUPPORT: return UV_EINVAL; + case WSAESOCKTNOSUPPORT: return UV_EINVAL; + case ERROR_BEGINNING_OF_MEDIA: return UV_EIO; + case ERROR_BUS_RESET: return UV_EIO; + case ERROR_CRC: return UV_EIO; + case ERROR_DEVICE_DOOR_OPEN: return UV_EIO; + case ERROR_DEVICE_REQUIRES_CLEANING: return UV_EIO; + case ERROR_DISK_CORRUPT: return UV_EIO; + case ERROR_EOM_OVERFLOW: return UV_EIO; + case ERROR_FILEMARK_DETECTED: return UV_EIO; + case ERROR_GEN_FAILURE: return UV_EIO; + case ERROR_INVALID_BLOCK_LENGTH: return UV_EIO; + case ERROR_IO_DEVICE: return UV_EIO; + case ERROR_NO_DATA_DETECTED: return UV_EIO; + case ERROR_NO_SIGNAL_SENT: return UV_EIO; + case ERROR_OPEN_FAILED: return UV_EIO; + case ERROR_SETMARK_DETECTED: return UV_EIO; + case ERROR_SIGNAL_REFUSED: return UV_EIO; + case WSAEISCONN: return UV_EISCONN; + case ERROR_CANT_RESOLVE_FILENAME: return UV_ELOOP; + case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE; + case WSAEMFILE: return UV_EMFILE; + case WSAEMSGSIZE: return UV_EMSGSIZE; + case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG; + case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH; + case WSAENETUNREACH: return UV_ENETUNREACH; + case WSAENOBUFS: return UV_ENOBUFS; + case ERROR_BAD_PATHNAME: return UV_ENOENT; + case ERROR_DIRECTORY: return UV_ENOENT; + case ERROR_FILE_NOT_FOUND: return UV_ENOENT; + case ERROR_INVALID_NAME: return UV_ENOENT; + case ERROR_INVALID_DRIVE: return UV_ENOENT; + case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT; + case ERROR_MOD_NOT_FOUND: return UV_ENOENT; + case ERROR_PATH_NOT_FOUND: return UV_ENOENT; + case WSAHOST_NOT_FOUND: return UV_ENOENT; + case WSANO_DATA: return UV_ENOENT; + case ERROR_NOT_ENOUGH_MEMORY: return UV_ENOMEM; + case ERROR_OUTOFMEMORY: return UV_ENOMEM; + case ERROR_CANNOT_MAKE: return UV_ENOSPC; + case ERROR_DISK_FULL: return UV_ENOSPC; + case ERROR_EA_TABLE_FULL: return UV_ENOSPC; + case ERROR_END_OF_MEDIA: return UV_ENOSPC; + case ERROR_HANDLE_DISK_FULL: return UV_ENOSPC; + case ERROR_NOT_CONNECTED: return UV_ENOTCONN; + case WSAENOTCONN: return UV_ENOTCONN; + case ERROR_DIR_NOT_EMPTY: return UV_ENOTEMPTY; + case WSAENOTSOCK: return UV_ENOTSOCK; + case ERROR_NOT_SUPPORTED: return UV_ENOTSUP; + case ERROR_BROKEN_PIPE: return UV_EOF; + case ERROR_ACCESS_DENIED: return UV_EPERM; + case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM; + case ERROR_BAD_PIPE: return UV_EPIPE; + case ERROR_NO_DATA: return UV_EPIPE; + case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE; + case WSAESHUTDOWN: return UV_EPIPE; + case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT; + case ERROR_WRITE_PROTECT: return UV_EROFS; + case ERROR_SEM_TIMEOUT: return UV_ETIMEDOUT; + case WSAETIMEDOUT: return UV_ETIMEDOUT; + case ERROR_NOT_SAME_DEVICE: return UV_EXDEV; + case ERROR_INVALID_FUNCTION: return UV_EISDIR; + case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG; + default: return errCode; + } +#else + return uv_translate_posix_error(errno); +#endif // defined(_WIN32) +} + +const char *get_uv_errmsg(bool netErr) { + return uv_strerror(get_uv_error(netErr)); +} + +}//namespace turbo \ No newline at end of file diff --git a/turbo/network/util/uv_errno.h b/turbo/network/util/uv_errno.h new file mode 100644 index 00000000..1f048813 --- /dev/null +++ b/turbo/network/util/uv_errno.h @@ -0,0 +1,517 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_NETWORK_UTIL_UV_ERROR_H_ +#define TURBO_NETWORK_UTIL_UV_ERROR_H_ + +#include + +#define UV__EOF (-4095) +#define UV__UNKNOWN (-4094) + +#define UV__EAI_ADDRFAMILY (-3000) +#define UV__EAI_AGAIN (-3001) +#define UV__EAI_BADFLAGS (-3002) +#define UV__EAI_CANCELED (-3003) +#define UV__EAI_FAIL (-3004) +#define UV__EAI_FAMILY (-3005) +#define UV__EAI_MEMORY (-3006) +#define UV__EAI_NODATA (-3007) +#define UV__EAI_NONAME (-3008) +#define UV__EAI_OVERFLOW (-3009) +#define UV__EAI_SERVICE (-3010) +#define UV__EAI_SOCKTYPE (-3011) +#define UV__EAI_BADHINTS (-3013) +#define UV__EAI_PROTOCOL (-3014) + +/* Only map to the system errno on non-Windows platforms. It's apparently +* a fairly common practice for Windows programmers to redefine errno codes. +*/ +#if defined(E2BIG) && !defined(_WIN32) +# define UV__E2BIG (-E2BIG) +#else +# define UV__E2BIG (-4093) +#endif + +#if defined(EACCES) && !defined(_WIN32) +# define UV__EACCES (-EACCES) +#else +# define UV__EACCES (-4092) +#endif + +#if defined(EADDRINUSE) && !defined(_WIN32) +# define UV__EADDRINUSE (-EADDRINUSE) +#else +# define UV__EADDRINUSE (-4091) +#endif + +#if defined(EADDRNOTAVAIL) && !defined(_WIN32) +# define UV__EADDRNOTAVAIL (-EADDRNOTAVAIL) +#else +# define UV__EADDRNOTAVAIL (-4090) +#endif + +#if defined(EAFNOSUPPORT) && !defined(_WIN32) +# define UV__EAFNOSUPPORT (-EAFNOSUPPORT) +#else +# define UV__EAFNOSUPPORT (-4089) +#endif + +#if defined(EAGAIN) && !defined(_WIN32) +# define UV__EAGAIN (-EAGAIN) +#else +# define UV__EAGAIN (-4088) +#endif + +#if defined(EALREADY) && !defined(_WIN32) +# define UV__EALREADY (-EALREADY) +#else +# define UV__EALREADY (-4084) +#endif + +#if defined(EBADF) && !defined(_WIN32) +# define UV__EBADF (-EBADF) +#else +# define UV__EBADF (-4083) +#endif + +#if defined(EBUSY) && !defined(_WIN32) +# define UV__EBUSY (-EBUSY) +#else +# define UV__EBUSY (-4082) +#endif + +#if defined(ECANCELED) && !defined(_WIN32) +# define UV__ECANCELED (-ECANCELED) +#else +# define UV__ECANCELED (-4081) +#endif + +#if defined(ECHARSET) && !defined(_WIN32) +# define UV__ECHARSET (-ECHARSET) +#else +# define UV__ECHARSET (-4080) +#endif + +#if defined(ECONNABORTED) && !defined(_WIN32) +# define UV__ECONNABORTED (-ECONNABORTED) +#else +# define UV__ECONNABORTED (-4079) +#endif + +#if defined(ECONNREFUSED) && !defined(_WIN32) +# define UV__ECONNREFUSED (-ECONNREFUSED) +#else +# define UV__ECONNREFUSED (-4078) +#endif + +#if defined(ECONNRESET) && !defined(_WIN32) +# define UV__ECONNRESET (-ECONNRESET) +#else +# define UV__ECONNRESET (-4077) +#endif + +#if defined(EDESTADDRREQ) && !defined(_WIN32) +# define UV__EDESTADDRREQ (-EDESTADDRREQ) +#else +# define UV__EDESTADDRREQ (-4076) +#endif + +#if defined(EEXIST) && !defined(_WIN32) +# define UV__EEXIST (-EEXIST) +#else +# define UV__EEXIST (-4075) +#endif + +#if defined(EFAULT) && !defined(_WIN32) +# define UV__EFAULT (-EFAULT) +#else +# define UV__EFAULT (-4074) +#endif + +#if defined(EHOSTUNREACH) && !defined(_WIN32) +# define UV__EHOSTUNREACH (-EHOSTUNREACH) +#else +# define UV__EHOSTUNREACH (-4073) +#endif + +#if defined(EINTR) && !defined(_WIN32) +# define UV__EINTR (-EINTR) +#else +# define UV__EINTR (-4072) +#endif + +#if defined(EINVAL) && !defined(_WIN32) +# define UV__EINVAL (-EINVAL) +#else +# define UV__EINVAL (-4071) +#endif + +#if defined(EIO) && !defined(_WIN32) +# define UV__EIO (-EIO) +#else +# define UV__EIO (-4070) +#endif + +#if defined(EISCONN) && !defined(_WIN32) +# define UV__EISCONN (-EISCONN) +#else +# define UV__EISCONN (-4069) +#endif + +#if defined(EISDIR) && !defined(_WIN32) +# define UV__EISDIR (-EISDIR) +#else +# define UV__EISDIR (-4068) +#endif + +#if defined(ELOOP) && !defined(_WIN32) +# define UV__ELOOP (-ELOOP) +#else +# define UV__ELOOP (-4067) +#endif + +#if defined(EMFILE) && !defined(_WIN32) +# define UV__EMFILE (-EMFILE) +#else +# define UV__EMFILE (-4066) +#endif + +#if defined(EMSGSIZE) && !defined(_WIN32) +# define UV__EMSGSIZE (-EMSGSIZE) +#else +# define UV__EMSGSIZE (-4065) +#endif + +#if defined(ENAMETOOLONG) && !defined(_WIN32) +# define UV__ENAMETOOLONG (-ENAMETOOLONG) +#else +# define UV__ENAMETOOLONG (-4064) +#endif + +#if defined(ENETDOWN) && !defined(_WIN32) +# define UV__ENETDOWN (-ENETDOWN) +#else +# define UV__ENETDOWN (-4063) +#endif + +#if defined(ENETUNREACH) && !defined(_WIN32) +# define UV__ENETUNREACH (-ENETUNREACH) +#else +# define UV__ENETUNREACH (-4062) +#endif + +#if defined(ENFILE) && !defined(_WIN32) +# define UV__ENFILE (-ENFILE) +#else +# define UV__ENFILE (-4061) +#endif + +#if defined(ENOBUFS) && !defined(_WIN32) +# define UV__ENOBUFS (-ENOBUFS) +#else +# define UV__ENOBUFS (-4060) +#endif + +#if defined(ENODEV) && !defined(_WIN32) +# define UV__ENODEV (-ENODEV) +#else +# define UV__ENODEV (-4059) +#endif + +#if defined(ENOENT) && !defined(_WIN32) +# define UV__ENOENT (-ENOENT) +#else +# define UV__ENOENT (-4058) +#endif + +#if defined(ENOMEM) && !defined(_WIN32) +# define UV__ENOMEM (-ENOMEM) +#else +# define UV__ENOMEM (-4057) +#endif + +#if defined(ENONET) && !defined(_WIN32) +# define UV__ENONET (-ENONET) +#else +# define UV__ENONET (-4056) +#endif + +#if defined(ENOSPC) && !defined(_WIN32) +# define UV__ENOSPC (-ENOSPC) +#else +# define UV__ENOSPC (-4055) +#endif + +#if defined(ENOSYS) && !defined(_WIN32) +# define UV__ENOSYS (-ENOSYS) +#else +# define UV__ENOSYS (-4054) +#endif + +#if defined(ENOTCONN) && !defined(_WIN32) +# define UV__ENOTCONN (-ENOTCONN) +#else +# define UV__ENOTCONN (-4053) +#endif + +#if defined(ENOTDIR) && !defined(_WIN32) +# define UV__ENOTDIR (-ENOTDIR) +#else +# define UV__ENOTDIR (-4052) +#endif + +#if defined(ENOTEMPTY) && !defined(_WIN32) +# define UV__ENOTEMPTY (-ENOTEMPTY) +#else +# define UV__ENOTEMPTY (-4051) +#endif + +#if defined(ENOTSOCK) && !defined(_WIN32) +# define UV__ENOTSOCK (-ENOTSOCK) +#else +# define UV__ENOTSOCK (-4050) +#endif + +#if defined(ENOTSUP) && !defined(_WIN32) +# define UV__ENOTSUP (-ENOTSUP) +#else +# define UV__ENOTSUP (-4049) +#endif + +#if defined(EPERM) && !defined(_WIN32) +# define UV__EPERM (-EPERM) +#else +# define UV__EPERM (-4048) +#endif + +#if defined(EPIPE) && !defined(_WIN32) +# define UV__EPIPE (-EPIPE) +#else +# define UV__EPIPE (-4047) +#endif + +#if defined(EPROTO) && !defined(_WIN32) +# define UV__EPROTO (-EPROTO) +#else +# define UV__EPROTO (-4046) +#endif + +#if defined(EPROTONOSUPPORT) && !defined(_WIN32) +# define UV__EPROTONOSUPPORT (-EPROTONOSUPPORT) +#else +# define UV__EPROTONOSUPPORT (-4045) +#endif + +#if defined(EPROTOTYPE) && !defined(_WIN32) +# define UV__EPROTOTYPE (-EPROTOTYPE) +#else +# define UV__EPROTOTYPE (-4044) +#endif + +#if defined(EROFS) && !defined(_WIN32) +# define UV__EROFS (-EROFS) +#else +# define UV__EROFS (-4043) +#endif + +#if defined(ESHUTDOWN) && !defined(_WIN32) +# define UV__ESHUTDOWN (-ESHUTDOWN) +#else +# define UV__ESHUTDOWN (-4042) +#endif + +#if defined(ESPIPE) && !defined(_WIN32) +# define UV__ESPIPE (-ESPIPE) +#else +# define UV__ESPIPE (-4041) +#endif + +#if defined(ESRCH) && !defined(_WIN32) +# define UV__ESRCH (-ESRCH) +#else +# define UV__ESRCH (-4040) +#endif + +#if defined(ETIMEDOUT) && !defined(_WIN32) +# define UV__ETIMEDOUT (-ETIMEDOUT) +#else +# define UV__ETIMEDOUT (-4039) +#endif + +#if defined(ETXTBSY) && !defined(_WIN32) +# define UV__ETXTBSY (-ETXTBSY) +#else +# define UV__ETXTBSY (-4038) +#endif + +#if defined(EXDEV) && !defined(_WIN32) +# define UV__EXDEV (-EXDEV) +#else +# define UV__EXDEV (-4037) +#endif + +#if defined(EFBIG) && !defined(_WIN32) +# define UV__EFBIG (-EFBIG) +#else +# define UV__EFBIG (-4036) +#endif + +#if defined(ENOPROTOOPT) && !defined(_WIN32) +# define UV__ENOPROTOOPT (-ENOPROTOOPT) +#else +# define UV__ENOPROTOOPT (-4035) +#endif + +#if defined(ERANGE) && !defined(_WIN32) +# define UV__ERANGE (-ERANGE) +#else +# define UV__ERANGE (-4034) +#endif + +#if defined(ENXIO) && !defined(_WIN32) +# define UV__ENXIO (-ENXIO) +#else +# define UV__ENXIO (-4033) +#endif + +#if defined(EMLINK) && !defined(_WIN32) +# define UV__EMLINK (-EMLINK) +#else +# define UV__EMLINK (-4032) +#endif + +/* EHOSTDOWN is not visible on BSD-like systems when _POSIX_C_SOURCE is +* defined. Fortunately, its value is always 64 so it's possible albeit +* icky to hard-code it. +*/ +#if defined(EHOSTDOWN) && !defined(_WIN32) +# define UV__EHOSTDOWN (-EHOSTDOWN) +#elif defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) +# define UV__EHOSTDOWN (-64) +#else +# define UV__EHOSTDOWN (-4031) +#endif + +#if defined(EREMOTEIO) && !defined(_WIN32) +# define UV__EREMOTEIO (-EREMOTEIO) +#else +# define UV__EREMOTEIO (-4030) +#endif + + +#define UV_ERRNO_MAP(XX) \ + XX(E2BIG, "argument list too long") \ + XX(EACCES, "permission denied") \ + XX(EADDRINUSE, "address already in use") \ + XX(EADDRNOTAVAIL, "address not available") \ + XX(EAFNOSUPPORT, "address family not supported") \ + XX(EAGAIN, "resource temporarily unavailable") \ + XX(EAI_ADDRFAMILY, "address family not supported") \ + XX(EAI_AGAIN, "temporary failure") \ + XX(EAI_BADFLAGS, "bad ai_flags value") \ + XX(EAI_BADHINTS, "invalid value for hints") \ + XX(EAI_CANCELED, "request canceled") \ + XX(EAI_FAIL, "permanent failure") \ + XX(EAI_FAMILY, "ai_family not supported") \ + XX(EAI_MEMORY, "out of memory") \ + XX(EAI_NODATA, "no address") \ + XX(EAI_NONAME, "unknown node or service") \ + XX(EAI_OVERFLOW, "argument buffer overflow") \ + XX(EAI_PROTOCOL, "resolved protocol is unknown") \ + XX(EAI_SERVICE, "service not available for socket type") \ + XX(EAI_SOCKTYPE, "socket type not supported") \ + XX(EALREADY, "connection already in progress") \ + XX(EBADF, "bad file descriptor") \ + XX(EBUSY, "resource busy or locked") \ + XX(ECANCELED, "operation canceled") \ + XX(ECHARSET, "invalid Unicode character") \ + XX(ECONNABORTED, "software caused connection abort") \ + XX(ECONNREFUSED, "connection refused") \ + XX(ECONNRESET, "connection reset by peer") \ + XX(EDESTADDRREQ, "destination address required") \ + XX(EEXIST, "file already exists") \ + XX(EFAULT, "bad address in system call argument") \ + XX(EFBIG, "file too large") \ + XX(EHOSTUNREACH, "host is unreachable") \ + XX(EINTR, "interrupted system call") \ + XX(EINVAL, "invalid argument") \ + XX(EIO, "i/o error") \ + XX(EISCONN, "socket is already connected") \ + XX(EISDIR, "illegal operation on a directory") \ + XX(ELOOP, "too many symbolic links encountered") \ + XX(EMFILE, "too many open files") \ + XX(EMSGSIZE, "message too long") \ + XX(ENAMETOOLONG, "name too long") \ + XX(ENETDOWN, "network is down") \ + XX(ENETUNREACH, "network is unreachable") \ + XX(ENFILE, "file table overflow") \ + XX(ENOBUFS, "no buffer space available") \ + XX(ENODEV, "no such device") \ + XX(ENOENT, "no such file or directory") \ + XX(ENOMEM, "not enough memory") \ + XX(ENONET, "machine is not on the network") \ + XX(ENOPROTOOPT, "protocol not available") \ + XX(ENOSPC, "no space left on device") \ + XX(ENOSYS, "function not implemented") \ + XX(ENOTCONN, "socket is not connected") \ + XX(ENOTDIR, "not a directory") \ + XX(ENOTEMPTY, "directory not empty") \ + XX(ENOTSOCK, "socket operation on non-socket") \ + XX(ENOTSUP, "operation not supported on socket") \ + XX(EPERM, "operation not permitted") \ + XX(EPIPE, "broken pipe") \ + XX(EPROTO, "protocol error") \ + XX(EPROTONOSUPPORT, "protocol not supported") \ + XX(EPROTOTYPE, "protocol wrong type for socket") \ + XX(ERANGE, "result too large") \ + XX(EROFS, "read-only file system") \ + XX(ESHUTDOWN, "cannot send after transport endpoint shutdown") \ + XX(ESPIPE, "invalid seek") \ + XX(ESRCH, "no such process") \ + XX(ETIMEDOUT, "connection timed out") \ + XX(ETXTBSY, "text file is busy") \ + XX(EXDEV, "cross-device link not permitted") \ + XX(UNKNOWN, "unknown error") \ + XX(EOF, "end of file") \ + XX(ENXIO, "no such device or address") \ + XX(EMLINK, "too many links") \ + XX(EHOSTDOWN, "host is down") \ + XX(EREMOTEIO, "remote I/O error") \ + +namespace turbo { + +typedef enum { +#define XX(code, _) UV_ ## code = UV__ ## code, + UV_ERRNO_MAP(XX) +#undef XX + UV_ERRNO_MAX = UV__EOF - 1 +} uv_errno_t; + +const char *uv_err_name(int err); +const char *uv_strerror(int err); +int uv_translate_posix_error(int err); +//netErr参数在windows平台下才有效 +int get_uv_error(bool netErr = true); +//netErr参数在windows平台下才有效 +const char *get_uv_errmsg(bool netErr = true); + +}//namespace turbo + +#endif /* TURBO_NETWORK_UTIL_UV_ERRNO_H_ */ \ No newline at end of file diff --git a/turbo/network/web_framework.h b/turbo/network/web_framework.h new file mode 100644 index 00000000..6ff81dfe --- /dev/null +++ b/turbo/network/web_framework.h @@ -0,0 +1,54 @@ +// Copyright 2023 The turbo Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef TURBO_WEB_FRAMEWORK_H_ +#define TURBO_WEB_FRAMEWORK_H_ + +#include "turbo/network/network/buffer.h" +#include "turbo/network/network/buffer_sock.h" +#include "turbo/network/network/server.h" +#include "turbo/network/network/session.h" +#include "turbo/network/network/socket.h" +#include "turbo/network/network/sock_util.h" +#include "turbo/network/network/tcp_client.h" +#include "turbo/network/network/tcp_server.h" +#include "turbo/network/network/udp_server.h" +#include "turbo/network/poller/event_poller.h" +#include "turbo/network/poller/pipe.h" +#include "turbo/network/poller/pipe_wrap.h" +#include "turbo/network/poller/select_wrap.h" +#include "turbo/network/poller/timer.h" +#include "turbo/network/thread/semaphore.h" +#include "turbo/network/thread/task_executor.h" +#include "turbo/network/thread/task_queue.h" +#include "turbo/network/thread/thread_group.h" +#include "turbo/network/thread/thread_pool.h" +#include "turbo/network/thread/work_pool.h" +#include "turbo/network/util/file.h" +#include "turbo/network/util/function_traits.h" +#include "turbo/network/util/list.h" +#include "turbo/network/util/local_time.h" +#include "turbo/network/util/logger.h" +#include "turbo/network/util/mini.h" +#include "turbo/network/util/notice_center.h" +#include "turbo/network/util/once_token.h" +#include "turbo/network/util/resource_pool.h" +#include "turbo/network/util/speed_statistic.h" +#include "turbo/network/util/ssl_box.h" +#include "turbo/network/util/ssl_util.h" +#include "turbo/network/util/time_ticker.h" +#include "turbo/network/util/util.h" +#include "turbo/network/util/uv_errno.h" + +#endif //TURBO_WEB_FRAMEWORK_H_ diff --git a/turbo/network/win32/getopt.c b/turbo/network/win32/getopt.c new file mode 100644 index 00000000..9cc5a00f --- /dev/null +++ b/turbo/network/win32/getopt.c @@ -0,0 +1,761 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __STDC__ +# ifndef const +# define const +# endif +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in . */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include +#include "tailor.h" + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +#define BAD_OPTION '\0' +int optopt = BAD_OPTION; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#include + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ + +#define my_index strchr +#define my_strlen strlen +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#if __STDC__ || defined(PROTO) +extern char *getenv(const char *name); +extern int strcmp (const char *s1, const char *s2); + +#if defined(__MINGW32_MAJOR_VERSION) +extern int strncmp(const char *_Str1,const char *_Str2,size_t _MaxCount); +#else +extern int strncmp(const char *s1, const char *s2, size_t n); +#endif // +static int my_strlen(const char *s); +static char *my_index (const char *str, int chr); +#else +extern char *getenv (); +#endif + +static int +my_strlen (str) + const char *str; +{ + int n = 0; + while (*str++) + n++; + return n; +} + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. + + To perform the swap, we first reverse the order of all elements. So + all options now come before all non options, but they are in the + wrong order. So we put back the options and non options in original + order by reversing them again. For example: + original input: a b c -x -y + reverse all: -y -x c b a + reverse options: -x -y c b a + reverse non options: -x -y a b c +*/ + +#if __STDC__ || defined(PROTO) +static void exchange (char **argv); +#endif + +static void +exchange (argv) + char **argv; +{ + char *temp, **first, **last; + + /* Reverse all the elements [first_nonopt, optind) */ + first = &argv[first_nonopt]; + last = &argv[optind-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + /* Put back the options in order */ + first = &argv[first_nonopt]; + first_nonopt += (optind - last_nonopt); + last = &argv[first_nonopt - 1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } + + /* Put back the non options in order */ + first = &argv[first_nonopt]; + last_nonopt = optind; + last = &argv[last_nonopt-1]; + while (first < last) { + temp = *first; *first = *last; *last = temp; first++; last--; + } +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return BAD_OPTION after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return BAD_OPTION. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound = 0; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == my_strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += my_strlen (nextchar); + optind++; + return BAD_OPTION; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += my_strlen (nextchar); + return BAD_OPTION; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += my_strlen (nextchar); + return optstring[0] == ':' ? ':' : BAD_OPTION; + } + } + nextchar += my_strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return BAD_OPTION; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return BAD_OPTION; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = BAD_OPTION; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case BAD_OPTION: + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + diff --git a/turbo/network/win32/getopt.h b/turbo/network/win32/getopt.h new file mode 100644 index 00000000..735018d0 --- /dev/null +++ b/turbo/network/win32/getopt.h @@ -0,0 +1,128 @@ +/* Declarations for getopt. + Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +//#if __STDC__ || defined(PROTO) +//#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +//#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +/* extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); */ +//#else /* not __STDC__ */ +//extern int getopt (); +//extern int getopt_long (); +//extern int getopt_long_only (); + +//extern int _getopt_internal (); +//#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */ + diff --git a/turbo/network/win32/tailor.h b/turbo/network/win32/tailor.h new file mode 100644 index 00000000..7b179e5a --- /dev/null +++ b/turbo/network/win32/tailor.h @@ -0,0 +1,329 @@ +/* tailor.h -- target dependent definitions + * Copyright (C) 1992-1993 Jean-loup Gailly. + * This is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License, see the file COPYING. + */ + +/* The target dependent definitions should be defined here only. + * The target dependent functions should be defined in tailor.c. + */ + +/* $Id: tailor.h,v 0.18 1993/06/14 19:32:20 jloup Exp $ */ + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif + +#if defined(__OS2__) && !defined(OS2) +# define OS2 +#endif + +#if defined(OS2) && defined(MSDOS) /* MS C under OS/2 */ +# undef MSDOS +#endif + +#ifdef MSDOS +# ifdef __GNUC__ + /* DJGPP version 1.09+ on MS-DOS. + * The DJGPP 1.09 stat() function must be upgraded before gzip will + * fully work. + * No need for DIRENT, since defines POSIX_SOURCE which + * implies DIRENT. + */ +# define near +# else +# define MAXSEG_64K +# ifdef __TURBOC__ +# define NO_OFF_T +# ifdef __BORLANDC__ +# define DIRENT +# else +# define NO_UTIME +# endif +# else /* MSC */ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# endif +# endif +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 128 +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# define NO_SIZE_CHECK +# define casemap(c) tolow(c) /* Force file names to lower case */ +# include +# define OS_CODE 0x00 +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# if !defined(NO_ASM) && !defined(ASMV) +# define ASMV +# endif +#else +# define near +#endif + +#ifdef OS2 +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 260 +# ifdef OS2FAT +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define casemap(c) tolow(c) +# endif +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# include +# define OS_CODE 0x06 +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# ifdef _MSC_VER +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define MAXSEG_64K +# undef near +# define near _near +# endif +# ifdef __EMX__ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define DIRENT +# define EXPAND(argc,argv) \ + {_response(&argc, &argv); _wildcard(&argc, &argv);} +# endif +# ifdef __BORLANDC__ +# define DIRENT +# endif +# ifdef __ZTC__ +# define NO_DIR +# define NO_UTIME_H +# include +# define EXPAND(argc,argv) \ + {response_expand(&argc, &argv);} +# endif +#endif + +#ifdef WIN32 /* Windows NT */ +# define HAVE_SYS_UTIME_H +# define NO_UTIME_H +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 260 +# define NO_CHOWN +# define PROTO +# define STDC_HEADERS +# define SET_BINARY_MODE(fd) setmode(fd, O_BINARY) +# include +# include +# ifdef NTFAT +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define casemap(c) tolow(c) /* Force file names to lower case */ +# endif +# define OS_CODE 0x0b +#endif + +#ifdef MSDOS +# ifdef __TURBOC__ +# include +# define DYN_ALLOC + /* Turbo C 2.0 does not accept static allocations of large arrays */ + void * fcalloc (unsigned items, unsigned size); + void fcfree (void *ptr); +# else /* MSC */ +# include +# define fcalloc(nitems,itemsize) halloc((long)(nitems),(itemsize)) +# define fcfree(ptr) hfree(ptr) +# endif +#else +# ifdef MAXSEG_64K +# define fcalloc(items,size) calloc((items),(size)) +# else +# define fcalloc(items,size) malloc((size_t)(items)*(size_t)(size)) +# endif +# define fcfree(ptr) free(ptr) +#endif + +#if defined(VAXC) || defined(VMS) +# define PATH_SEP ']' +# define PATH_SEP2 ':' +# define SUFFIX_SEP ';' +# define NO_MULTIPLE_DOTS +# define Z_SUFFIX "-gz" +# define RECORD_IO 1 +# define casemap(c) tolow(c) +# define OS_CODE 0x02 +# define OPTIONS_VAR "GZIP_OPT" +# define STDC_HEADERS +# define NO_UTIME +# define EXPAND(argc,argv) vms_expand_args(&argc,&argv); +# include +# define unlink delete +# ifdef VAXC +# define NO_FCNTL_H +# include +# endif +#endif + +#ifdef AMIGA +# define PATH_SEP2 ':' +# define STDC_HEADERS +# define OS_CODE 0x01 +# define ASMV +# ifdef __GNUC__ +# define DIRENT +# define HAVE_UNISTD_H +# else /* SASC */ +# define NO_STDIN_FSTAT +# define SYSDIR +# define NO_SYMLINK +# define NO_CHOWN +# define NO_FCNTL_H +# include /* for read() and write() */ +# define direct dirent + extern void _expand_args(int *argc, char ***argv); +# define EXPAND(argc,argv) _expand_args(&argc,&argv); +# undef O_BINARY /* disable useless --ascii option */ +# endif +#endif + +#if defined(ATARI) || defined(atarist) +# ifndef STDC_HEADERS +# define STDC_HEADERS +# define HAVE_UNISTD_H +# define DIRENT +# endif +# define ASMV +# define OS_CODE 0x05 +# ifdef TOSFS +# define PATH_SEP2 '\\' +# define PATH_SEP3 ':' +# define MAX_PATH_LEN 128 +# define NO_MULTIPLE_DOTS +# define MAX_EXT_CHARS 3 +# define Z_SUFFIX "z" +# define NO_CHOWN +# define casemap(c) tolow(c) /* Force file names to lower case */ +# define NO_SYMLINK +# endif +#endif + +#ifdef MACOS +# define PATH_SEP ':' +# define DYN_ALLOC +# define PROTO +# define NO_STDIN_FSTAT +# define NO_CHOWN +# define NO_UTIME +# define chmod(file, mode) (0) +# define OPEN(name, flags, mode) open(name, flags) +# define OS_CODE 0x07 +# ifdef MPW +# define isatty(fd) ((fd) <= 2) +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define PATH_SEP '>' +# define STDC_HEADERS +# define NO_MEMORY_H +# define NO_UTIME_H +# define NO_UTIME +# define NO_CHOWN +# define NO_STDIN_FSTAT +# define NO_SIZE_CHECK +# define NO_SYMLINK +# define RECORD_IO 1 +# define casemap(c) tolow(c) /* Force file names to lower case */ +# define put_char(c) put_byte((c) & 0x7F) +# define get_char(c) ascii2pascii(get_byte()) +# define OS_CODE 0x0F /* temporary, subject to change */ +# ifdef SIGTERM +# undef SIGTERM /* We don't want a signal handler for SIGTERM */ +# endif +#endif + +#if defined(pyr) && !defined(NOMEMCPY) /* Pyramid */ +# define NOMEMCPY /* problem with overlapping copies */ +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifndef unix +# define NO_ST_INO /* don't rely on inode numbers */ +#endif + + + /* Common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef PATH_SEP +# define PATH_SEP '/' +#endif + +#ifndef casemap +# define casemap(c) (c) +#endif + +#ifndef OPTIONS_VAR +# define OPTIONS_VAR "GZIP" +#endif + +#ifndef Z_SUFFIX +# define Z_SUFFIX ".gz" +#endif + +#ifdef MAX_EXT_CHARS +# define MAX_SUFFIX MAX_EXT_CHARS +#else +# define MAX_SUFFIX 30 +#endif + +#ifndef MAKE_LEGAL_NAME +# ifdef NO_MULTIPLE_DOTS +# define MAKE_LEGAL_NAME(name) make_simple_name(name) +# else +# define MAKE_LEGAL_NAME(name) +# endif +#endif + +#ifndef MIN_PART +# define MIN_PART 3 + /* keep at least MIN_PART chars between dots in a file name. */ +#endif + +#ifndef EXPAND +# define EXPAND(argc,argv) +#endif + +#ifndef RECORD_IO +# define RECORD_IO 0 +#endif + +#ifndef SET_BINARY_MODE +# define SET_BINARY_MODE(fd) +#endif + +#ifndef OPEN +# define OPEN(name, flags, mode) open(name, flags, mode) +#endif + +#ifndef get_char +# define get_char() get_byte() +#endif + +#ifndef put_char +# define put_char(c) put_byte(c) +#endif +