├── third_party ├── .clang-tidy └── Repository.cmake ├── .codechecker.clang-tidy.args ├── ci ├── requirements.txt ├── format.sh ├── do_ci.ps1 └── do_ci.sh ├── .codechecker.skipfile ├── CPPLINT.cfg ├── tools ├── tools.macro.cmake ├── script │ ├── startup_benchmark_shm.sh │ ├── valgrind_memcheck.sh │ ├── startup_benchmark_io_stream.sh │ └── cppcheck.sh ├── CMakeLists.txt ├── show_shm_channel.cpp ├── benchmark_shm_channel_send.cpp ├── benchmark_shm_channel_recv.cpp ├── benchmark_io_stream_channel_recv.cpp └── benchmark_io_stream_channel_send.cpp ├── sample ├── sample.custom-macro.cmake ├── CMakeLists.txt └── sample_usage_01.cpp ├── .cmake-format.yaml ├── codecov.yml ├── .gitmodules ├── include ├── libatbus.h ├── include.macro.cmake ├── detail │ ├── libatbus_adapter_libuv.h │ ├── libatbus_protocol.fbs │ ├── libatbus_config.h.in │ ├── libatbus_error.h │ ├── libatbus_channel_types.h │ ├── libatbus_channel_export.h │ └── buffer.h ├── atbus_connection_context.h ├── libatbus_protocol.h ├── atbus_message_handler.h └── libatbus_protocol.proto ├── .github ├── codeql │ └── codeql-config.yml └── workflows │ ├── stale.yml │ └── main.yml ├── docs ├── experience │ ├── test_shm_delete.cpp │ └── test_shm_create.cpp ├── Schedule.md ├── Usage.md ├── Build.md ├── Benchmark.md └── README.md ├── .clang-format ├── .gitignore ├── CHANGELOG.md ├── .codechecker.yaml ├── LICENSE ├── project └── cmake │ ├── FetchDependeny.cmake │ └── ProjectBuildOption.cmake ├── test ├── CMakeLists.txt └── case │ ├── atbus_test_utils.h │ ├── atbus_test_utils.cpp │ ├── atbus_node_setup_test.cpp │ ├── libatbus_error_test.cpp │ └── atbus_node_relationship_test.cpp ├── BOOST_LICENSE_1_0.txt ├── atframework └── Repository.cmake ├── src ├── atbus_connection_context.cpp ├── libatbus.macro.cmake ├── channel_utility.cpp ├── libatbus_protocol.cpp └── CMakeLists.txt ├── .gitattributes ├── NODE_S_LICENSE ├── libatbus-config.cmake.in ├── README.md ├── .vscode └── settings.json ├── CMakeLists.txt ├── .clang-tidy.light └── .clang-tidy /third_party/.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: "-*" 2 | -------------------------------------------------------------------------------- /.codechecker.clang-tidy.args: -------------------------------------------------------------------------------- 1 | --allow-no-checks 2 | -------------------------------------------------------------------------------- /ci/requirements.txt: -------------------------------------------------------------------------------- 1 | cmake-format>=0.6.13 2 | PyYAML>=5.4.1 3 | -------------------------------------------------------------------------------- /.codechecker.skipfile: -------------------------------------------------------------------------------- 1 | -*/third_party/install/* 2 | -*/third_party/packages/* 3 | -------------------------------------------------------------------------------- /CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | linelength=120 2 | filter=-build/c++11,-build/c++14,-runtime/references 3 | exclude=third_party -------------------------------------------------------------------------------- /tools/tools.macro.cmake: -------------------------------------------------------------------------------- 1 | # =========== tools =========== 2 | set(PROJECT_TOOLS_BAS_DIR ${CMAKE_CURRENT_LIST_DIR}) 3 | set(PROJECT_TOOLS_INC_DIR ${PROJECT_TOOLS_BAS_DIR}) 4 | set(PROJECT_TOOLS_SRC_DIR ${PROJECT_TOOLS_BAS_DIR}) 5 | -------------------------------------------------------------------------------- /sample/sample.custom-macro.cmake: -------------------------------------------------------------------------------- 1 | # =========== sample =========== 2 | set(PROJECT_SAMPLE_BAS_DIR ${CMAKE_CURRENT_LIST_DIR}) 3 | set(PROJECT_SAMPLE_INC_DIR ${PROJECT_SAMPLE_BAS_DIR}) 4 | set(PROJECT_SAMPLE_SRC_DIR ${PROJECT_SAMPLE_BAS_DIR}) 5 | -------------------------------------------------------------------------------- /.cmake-format.yaml: -------------------------------------------------------------------------------- 1 | # @see https://cmake-format.readthedocs.io/en/latest/configuration.html for more details 2 | 3 | format: 4 | line_width: 120 5 | tab_size: 2 6 | use_tabchars: false 7 | line_ending: unix 8 | 9 | markup: 10 | first_comment_is_literal: True 11 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "**/sample/**" 3 | - "**/test/**" 4 | - "**/tools/**" 5 | - "**/build_jobs_coverage/CMakeFiles/**" 6 | - "**/docs/**" 7 | - "**/include/libatbus_protocol.pb.h" 8 | - "**/src/libatbus_protocol.pb.cc" 9 | - "**/third_party/**" 10 | - "**/atframework/**" -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "atframework/atframe_utils"] 2 | path = atframework/atframe_utils 3 | url = https://github.com/atframework/atframe_utils.git 4 | branch = main 5 | [submodule "atframework/cmake-toolset"] 6 | path = atframework/cmake-toolset 7 | url = https://github.com/atframework/cmake-toolset.git 8 | branch = main 9 | -------------------------------------------------------------------------------- /third_party/Repository.cmake: -------------------------------------------------------------------------------- 1 | project_third_party_include_port("compression/import.cmake") 2 | project_third_party_include_port("libuv/libuv.cmake") 3 | project_third_party_include_port("fmtlib/fmtlib.cmake") 4 | project_third_party_include_port("abseil-cpp/abseil-cpp.cmake") 5 | project_third_party_include_port("protobuf/protobuf.cmake") 6 | -------------------------------------------------------------------------------- /include/libatbus.h: -------------------------------------------------------------------------------- 1 | /** 2 | * libatbus.h 3 | * 4 | * Created on: 2014年8月11日 5 | * Author: owent 6 | */ 7 | 8 | #pragma once 9 | 10 | #ifndef LIBATBUS_H 11 | # define LIBATBUS_H 12 | 13 | # pragma once 14 | 15 | # include "atbus_message_handler.h" 16 | # include "atbus_node.h" 17 | 18 | #endif /* LIBATBUS_H_ */ 19 | -------------------------------------------------------------------------------- /.github/codeql/codeql-config.yml: -------------------------------------------------------------------------------- 1 | languages: 2 | - cpp 3 | queries: 4 | - name: Security And Quality 5 | uses: security-and-quality 6 | # "paths"/"paths-ignore" fields of the config only have effect for JavaScript, Python, and Ruby 7 | # paths: 8 | # - include 9 | # - src 10 | # paths-ignore: 11 | # - third_party 12 | # - test 13 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Mark and close stale issues" 2 | on: 3 | schedule: 4 | - cron: "30 2 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v6 11 | with: 12 | stale-issue-message: "This issue was marked as stale due to lack of activity." 13 | days-before-issue-stale: 90 14 | exempt-issue-labels: "do-not-stale" 15 | -------------------------------------------------------------------------------- /tools/script/startup_benchmark_shm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ADDRESS=12345679 ; 4 | UNIT_SIZE=1024 ; 5 | SHM_SIZE=16777216 ; # 16MB 6 | 7 | if [ $# -ge 1 ]; then 8 | ADDRESS="$1"; 9 | fi 10 | 11 | if [ $# -ge 2 ]; then 12 | UNIT_SIZE=$2 ; 13 | fi 14 | 15 | ./benchmark_shm_channel_recv $ADDRESS $UNIT_SIZE $SHM_SIZE > recv-shm.log 2>&1 & 16 | 17 | sleep 2; 18 | 19 | for ((i=1;i<=16;++i)); do 20 | ./benchmark_shm_channel_send $ADDRESS $UNIT_SIZE $SHM_SIZE > send-shm-$i.log 2>&1 & 21 | done 22 | 23 | -------------------------------------------------------------------------------- /tools/script/valgrind_memcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # valgrind --vgdb=full --vgdb-error=0 --leak-check=full --tool=memcheck --show-leak-kinds=all --log-file=memcheck.log --malloc-fill=0x5E "$@"; 4 | # valgrind --vgdb=yes --vgdb-error=0 --leak-check=full --tool=memcheck --show-leak-kinds=all --log-file=memcheck.log --malloc-fill=0x5E "$@"; 5 | # then, run: gdb [executable-file] --eval-command="target remote | vgdb" 6 | valgrind --vgdb=yes --leak-check=full --tool=memcheck --show-leak-kinds=all --log-file=memcheck.log "$@"; -------------------------------------------------------------------------------- /docs/experience/test_shm_delete.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include /* For O_* constants */ 3 | #include 4 | #include /* For mode constants */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main() { 11 | // glibc接口的shm_unlink不允许子目录 12 | // int res = shm_unlink("/libatbus-test-shm.bus"); 13 | int res = unlink("/dev/shm/libatbus/test/shm.bus"); 14 | if (res < 0) { 15 | printf("shm_unlink failed, err: %d, %s", errno, strerror(errno)); 16 | return 1; 17 | } 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | ColumnLimit: 120 5 | DerivePointerAlignment: true 6 | PointerAlignment: Left 7 | 8 | # Only sort headers in each include block 9 | SortIncludes: true 10 | IncludeBlocks: Preserve 11 | 12 | # Ident #if/#else/#endif: 13 | # #if defined(WIN) 14 | # # include 15 | # #else 16 | # # include 17 | # #endif 18 | IndentPPDirectives: AfterHash 19 | 20 | IfMacros: ["UTIL_LIKELY_IF", "UTIL_UNLIKELY_IF"] 21 | --- 22 | 23 | --- 24 | Language: Proto 25 | BasedOnStyle: Google 26 | ColumnLimit: 120 27 | --- 28 | 29 | -------------------------------------------------------------------------------- /tools/script/startup_benchmark_io_stream.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ADDRESS="ipv6://::1:16389"; 4 | UNIT_SIZE=1024 ; 5 | LIMIT_SIZE=4194304 ; 6 | STATIC_LIMIT_NUM=1024 ; 7 | 8 | if [ $# -ge 1 ]; then 9 | ADDRESS="$1"; 10 | fi 11 | 12 | if [ $# -ge 2 ]; then 13 | UNIT_SIZE=$2 ; 14 | fi 15 | 16 | if [ $# -ge 3 ]; then 17 | LIMIT_SIZE=$3 ; 18 | fi 19 | 20 | if [ $# -ge 4 ]; then 21 | STATIC_LIMIT_NUM=$4 ; 22 | fi 23 | 24 | ./benchmark_io_stream_channel_recv "$ADDRESS" $UNIT_SIZE > recv-ios.log 2>&1 & 25 | 26 | sleep 2; 27 | 28 | for ((i=1;i<=16;++i)); do 29 | ./benchmark_io_stream_channel_send "$ADDRESS" $UNIT_SIZE $LIMIT_SIZE $STATIC_LIMIT_NUM > send-ios-$i.log 2>&1 & 30 | done 31 | 32 | -------------------------------------------------------------------------------- /tools/script/cppcheck.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"; 4 | SCRIPT_DIR="$( readlink -f $SCRIPT_DIR )"; 5 | 6 | PROJECT_DIR="$( cd "$SCRIPT_DIR/../.." && pwd )"; 7 | 8 | WORK_DIR="$PWD"; 9 | 10 | cd "$PROJECT_DIR"; 11 | 12 | cppcheck . --enable=all --xml --xml-version=2 --inconclusive --std=c11 --std=c++14 --std=posix -I "$PROJECT_DIR/third_party/atframe_utils/repo/include" -I "$PROJECT_DIR/third_party/protobuf/prebuilt/"*"/include" $@ 2>"$WORK_DIR/cppcheck.xml"; 13 | 14 | which cppcheck-htmlreport; 15 | if [ 0 -eq $? ]; then 16 | mkdir -p "$WORK_DIR/cppcheck.html"; 17 | cppcheck-htmlreport --title=libatbus.cppcheck --file="$WORK_DIR/cppcheck.xml" --report-dir="$WORK_DIR/cppcheck.html" --source-dir=. --source-encoding=utf-8 ; 18 | fi 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | 23 | # Build directory 24 | build/* 25 | build_* 26 | 27 | # project files 28 | /.idea 29 | /.vs 30 | 31 | # generated file 32 | /include/detail/libatbus_config.h 33 | /include/detail/libatbus_protocol_generated.h 34 | /include/libatbus_protocol.pb.h 35 | /include/libatbus_protocol.pb 36 | /src/libatbus_protocol.pb.cc 37 | 38 | /atframework/cmake-toolset 39 | /atframework/atframe_utils 40 | 41 | # prebuilt directory 42 | /third_party/install 43 | /third_party/packages 44 | -------------------------------------------------------------------------------- /include/include.macro.cmake: -------------------------------------------------------------------------------- 1 | # =========== include - macro =========== 2 | set(PROJECT_LIBATBUS_ROOT_INC_DIR ${CMAKE_CURRENT_LIST_DIR}) 3 | 4 | set(PROJECT_LIBATBUS_GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/_generated") 5 | file(MAKE_DIRECTORY "${PROJECT_LIBATBUS_GENERATED_DIR}/include/detail") 6 | file(MAKE_DIRECTORY "${PROJECT_LIBATBUS_GENERATED_DIR}/src") 7 | file(MAKE_DIRECTORY "${PROJECT_LIBATBUS_GENERATED_DIR}/temp") 8 | 9 | # define CONF from cmake to c macro 10 | configure_file("${CMAKE_CURRENT_LIST_DIR}/detail/libatbus_config.h.in" 11 | "${PROJECT_LIBATBUS_GENERATED_DIR}/temp/libatbus_config.h" @ONLY) 12 | 13 | execute_process( 14 | COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_LIBATBUS_GENERATED_DIR}/temp/libatbus_config.h" 15 | "${PROJECT_LIBATBUS_GENERATED_DIR}/include/detail") 16 | -------------------------------------------------------------------------------- /include/detail/libatbus_adapter_libuv.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by owent on 2015/9/15. 3 | // 4 | 5 | #ifndef LIBATBUS_LIBATBUS_ADAPTER_LIBUV_H 6 | #define LIBATBUS_LIBATBUS_ADAPTER_LIBUV_H 7 | 8 | #pragma once 9 | 10 | #include "uv.h" 11 | 12 | #include "detail/libatbus_config.h" 13 | 14 | ATBUS_MACRO_NAMESPACE_BEGIN 15 | namespace adapter { 16 | using loop_t = uv_loop_t; 17 | using poll_t = uv_poll_t; 18 | using stream_t = uv_stream_t; 19 | using pipe_t = uv_pipe_t; 20 | using tty_t = uv_tty_t; 21 | using tcp_t = uv_tcp_t; 22 | using handle_t = uv_handle_t; 23 | using timer_t = uv_timer_t; 24 | using shutdown_t = uv_shutdown_t; 25 | 26 | using fd_t = uv_os_fd_t; 27 | 28 | enum run_mode_t { 29 | RUN_DEFAULT = UV_RUN_DEFAULT, 30 | RUN_ONCE = UV_RUN_ONCE, 31 | RUN_NOWAIT = UV_RUN_NOWAIT, 32 | }; 33 | } // namespace adapter 34 | ATBUS_MACRO_NAMESPACE_END 35 | 36 | #endif // LIBATBUS_LIBATBUS_ADAPTER_LIBUV_H 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | ============ 3 | 4 | 2.1.0 5 | ------------ 6 | 7 | 1. 重构构建系统 8 | 2. 增加代码规范工具 9 | 3. 重构CI配置 10 | 11 | 2.0.0 12 | ------------ 13 | 14 | 这是一个比较大规模的重构,并且协议层向前不兼容,API除协议数据读取外基本兼容 15 | 16 | 1. 移除对msgpack的依赖,使用protobuf作为内部交互协议 17 | 2. 增加access_token的支持,防止误操作 18 | 3. 增加subnet功能,可以支持自身node的id不在自己管理的子节点集合中 19 | 4. 重构父节点范围管理的结构,现在更清晰易懂一些 20 | 5. 增加(共享)内存通道的版本号功能,增加协议版本号功能,用于跨版本兼容性检查 21 | 6. 连接层面也增加错误计数,如果超出容忍值直接断开连接 22 | 7. 支持字符串路径的共享内存(使用: ```shm_open/ftruncate/mmap/munmap/shm_unlink/close/fstat``` 来管理),支持字符串命名的共享内存(长度限定为NAME_MAX(255)) 23 | 8. 增加大量错误流程的单元测试,优化Unix Sock的单元测试 24 | 9. 优化工程脚本,逐步使用Modern CMake规范管理依赖链。 25 | 10. 增加对符号隐藏、符号导出和Windows下dll的支持。现在GCC/Clang/MSVC使用相同的符号可见性和导出规则。 26 | 11. 规范版本号规则:第一位变化表示有向前不兼容的变更;第二位表示有功能增加,向前兼容,第三位表示BUG修复和优化 27 | 12. 增加协议版本号机制,用于多个由atbus版本组成的系统中,可以根据最低要求版本号和当前版本号来决定是否互联(比如新的atproxy中不再会尝试连接不支持的版本) 28 | 29 | 1.1.0 30 | ------------ 31 | 32 | 第一个线上使用过的版本 33 | -------------------------------------------------------------------------------- /.codechecker.yaml: -------------------------------------------------------------------------------- 1 | # CodeChecker check --config ../.codechecker.yaml -i ../.codechecker.skipfile -l compile_commands.json -j 30 -o codechecker-result || true 2 | analyzer: 3 | # Disable unused cases 4 | - --analyzers 5 | - clang-tidy 6 | # - clangsa # 这个PCH有适配问题, 先关闭 7 | 8 | # Allow .clang-tidy 9 | - "--analyzer-config" 10 | - "clang-tidy:take-config-from-directory=true" 11 | - "clang-tidy:cc-verbatim-args-file=.codechecker.clang-tidy.args" 12 | 13 | # - --enable=profile:extreme 14 | # - --disable=boost 15 | # 16 | # # Many identifiers in MX framework start with _ 17 | # - --disable=clang-diagnostic-reserved-identifier 18 | # - --disable=clang-diagnostic-reserved-macro-identifier 19 | # - --disable=clang-diagnostic-unused-parameter 20 | 21 | # # For ccc-analyzer/clangsa 22 | # - --disable=alpha.clone.CloneChecker 23 | # - --disable=alpha.cplusplus.IteratorRange 24 | 25 | # PCH support 26 | # - --ctu 27 | # - --ctu-ast-mode=load-from-pch 28 | -------------------------------------------------------------------------------- /tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | echowithcolor(COLOR GREEN "-- Configure Tools ${CMAKE_CURRENT_LIST_DIR}") 2 | 3 | # ============ TOOLS - [...] ============ 4 | file(GLOB TOOLS_SRC_LIST ${PROJECT_TOOLS_SRC_DIR}/*.cpp ${PROJECT_TOOLS_SRC_DIR}/*.cc ${PROJECT_TOOLS_SRC_DIR}/*.c 5 | ${PROJECT_TOOLS_SRC_DIR}/*.cxx) 6 | 7 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/tools") 8 | 9 | foreach(TOOLS_SRC_FILE IN LISTS TOOLS_SRC_LIST) 10 | get_filename_component(TOOLS_SRC_BIN_NAME "${TOOLS_SRC_FILE}" NAME_WE) 11 | 12 | add_executable("${TOOLS_SRC_BIN_NAME}" ${TOOLS_SRC_FILE}) 13 | target_link_libraries("${TOOLS_SRC_BIN_NAME}" atframework::atbus) 14 | target_include_directories("${TOOLS_SRC_BIN_NAME}" PRIVATE "$") 15 | 16 | set_property(TARGET "${TOOLS_SRC_BIN_NAME}" PROPERTY FOLDER "atframework/tools/atbus") 17 | 18 | install( 19 | TARGETS "${TOOLS_SRC_BIN_NAME}" 20 | RUNTIME DESTINATION tools 21 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 22 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 23 | 24 | endforeach() 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 atframework 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /project/cmake/FetchDependeny.cmake: -------------------------------------------------------------------------------- 1 | include_guard(GLOBAL) 2 | 3 | find_package(Git REQUIRED) 4 | 5 | set(ATFRAMEWORK_CMAKE_TOOLSET_DIR 6 | "${PROJECT_SOURCE_DIR}/atframework/cmake-toolset" 7 | CACHE PATH "PATH to cmake-toolset") 8 | 9 | if(NOT ATFRAMEWORK_CMAKE_TOOLSET_EXECUTE_PROCESS_OUTPUT_OPTIONS) 10 | unset(ATFRAMEWORK_CMAKE_TOOLSET_EXECUTE_PROCESS_OUTPUT_OPTIONS) 11 | if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.15") 12 | list(APPEND ATFRAMEWORK_CMAKE_TOOLSET_EXECUTE_PROCESS_OUTPUT_OPTIONS COMMAND_ECHO STDOUT) 13 | endif() 14 | if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") 15 | list(APPEND ATFRAMEWORK_CMAKE_TOOLSET_EXECUTE_PROCESS_OUTPUT_OPTIONS ECHO_OUTPUT_VARIABLE ECHO_ERROR_VARIABLE) 16 | endif() 17 | endif() 18 | 19 | if(NOT EXISTS "${ATFRAMEWORK_CMAKE_TOOLSET_DIR}/Import.cmake") 20 | execute_process( 21 | COMMAND ${GIT_EXECUTABLE} submodule update --depth 100 --recommend-shallow -f --init -- atframework/cmake-toolset 22 | WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" ${ATFRAMEWORK_CMAKE_TOOLSET_EXECUTE_PROCESS_OUTPUT_OPTIONS}) 23 | set(ATFRAMEWORK_CMAKE_TOOLSET_DIR 24 | "${PROJECT_SOURCE_DIR}/atframework/cmake-toolset" 25 | CACHE PATH "PATH to cmake-toolset" FORCE) 26 | endif() 27 | 28 | include("${ATFRAMEWORK_CMAKE_TOOLSET_DIR}/Import.cmake") 29 | -------------------------------------------------------------------------------- /sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | echowithcolor(COLOR GREEN "-- Configure ${CMAKE_CURRENT_LIST_DIR}") 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/sample.custom-macro.cmake") 4 | 5 | # ============ sample - [...] ============ 6 | 7 | file( 8 | GLOB SAMPLE_SRC_LIST 9 | RELATIVE "${PROJECT_SAMPLE_SRC_DIR}" 10 | ${PROJECT_SAMPLE_SRC_DIR}/*.cpp ${PROJECT_SAMPLE_SRC_DIR}/*.cc ${PROJECT_SAMPLE_SRC_DIR}/*.c 11 | ${PROJECT_SAMPLE_SRC_DIR}/*.cxx) 12 | 13 | set(CMAKE_BUILD_RPATH "${CMAKE_INSTALL_RPATH}") 14 | if(NOT (WIN32 AND BUILD_SHARED_LIBS)) 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/sample") 16 | endif() 17 | 18 | foreach(SAMPLE_SRC_FILE IN LISTS SAMPLE_SRC_LIST) 19 | get_filename_component(SAMPLE_SRC_BIN_NAME ${SAMPLE_SRC_FILE} NAME_WE) 20 | set(SAMPLE_SRC_BIN_NAME "atbus_${SAMPLE_SRC_BIN_NAME}") 21 | 22 | add_executable(${SAMPLE_SRC_BIN_NAME} ${SAMPLE_SRC_FILE}) 23 | target_link_libraries(${SAMPLE_SRC_BIN_NAME} atframework::atbus) 24 | 25 | target_compile_options(${SAMPLE_SRC_BIN_NAME} PRIVATE ${PROJECT_LIBATBUS_PRIVATE_COMPILE_OPTIONS}) 26 | 27 | set_property(TARGET ${SAMPLE_SRC_BIN_NAME} PROPERTY FOLDER "atframework/sample/atbus") 28 | if(MSVC) 29 | add_target_properties(${SAMPLE_SRC_BIN_NAME} LINK_FLAGS /NODEFAULTLIB:library) 30 | endif() 31 | endforeach() 32 | -------------------------------------------------------------------------------- /docs/Schedule.md: -------------------------------------------------------------------------------- 1 | # Schedule List 2 | 3 | 1. endpoint短线重连后的重发机制 4 | 1.1 endpoint断开后短期内不移除(pending_endpoint_gc_list 也采用定时器,影响单元测试的析构判定,也需要做相应变更) 5 | 1.2 custom command和forward协议增加和endpoint绑定的sequence,用于对重发的消息回调去重(其他协议不需要去重) 6 | 1.3 根据hash code、hostname、pid允许重置和endpoint绑定的sequence 7 | 1.4 endpoint握手仅连接最快可访问的数据链接(ios连接需要排队挨个重试),需要防止正在连接导致的误判,需要处理正在连接过程中GC生效的问题 8 | 1.5 ios channel允许重绑定写出缓冲区管理器,和endpoint内共享。 9 | 1.6 buffer_manager增加atomic的private_guard用于CAS操作处理正在写出的独占connection。connection释放时要解除占用。endpoint定期检查独占有效性(防止死锁) 10 | 1.7 connection握手成功后需要开始尝试重发和endpoint内共享的缓冲区 11 | 1.8 ios channel在写出完成的会调用发现需要关闭connection或正在关闭connection不再清理buffer_manager 12 | 1.9 增加ios channel拆包接口,用于处理如果endpoint丢弃时有些包需要通知发送失败的回调+单元测试 13 | 1.10 兄弟节点endpoint在进入gc列表时定期自动重连(父子节点会由子节点自动重连)(node非CLOSING时) 14 | 1.11 已绑定endpoint的connection在收到注册协议时要检查endpoint信息是否和原来一致。不一致说明同地址换endpoint了,需要移除endpoint的无效地址缓存,并重置connection的binding_ 15 | 1.12 已绑定endpoint的connection在收到注册协议时要检查endpoint的数据节点是否和上报地址匹配,不一致说明同地址换endpoint了,移除endpoint的无效地址缓存,并移除无效的connection后需要重新发起endpoint的数据通道连接流程 16 | 1.13 endpoint的数据通道全部离线后不再下线,而改为进入GC检查列表后尝试重新发起endpoint的数据通道连接流程 17 | 1.14 endpoint的控制通道全部离线后不再下线,而改为进入GC检查列表后尝试重新发起endpoint的控制通道连接流程 18 | 1.15 ios channel切换到shm/mem channel后发送的data/cmd消息要重排序,并且要处理shm/mem channel拥塞后的自动重发 19 | 1.16 增加消息超时机制,用于处理重发和GC机制联动导致的消息事件重复触发,forward_response事件增加不确定是否成功的错误码 20 | -------------------------------------------------------------------------------- /tools/show_shm_channel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include "config/compiler_features.h" 16 | #include "detail/libatbus_channel_export.h" 17 | 18 | #if defined(ATBUS_CHANNEL_SHM) 19 | int main(int argc, char *argv[]) { 20 | if (argc < 2) { 21 | printf("usage: %s [need node info: 0 or 1] [node data size]\n", argv[0]); 22 | return 0; 23 | } 24 | 25 | using namespace atbus::channel; 26 | shm_channel *channel = nullptr; 27 | long need_node_info = 0; 28 | size_t need_node_data = 0; 29 | 30 | if (argc > 2) { 31 | need_node_info = strtol(argv[2], nullptr, 10); 32 | } 33 | 34 | if (argc > 3) { 35 | need_node_data = (size_t)strtol(argv[3], nullptr, 10); 36 | } 37 | 38 | int res = shm_attach(argv[1], 0, &channel, nullptr); 39 | if (res < 0) { 40 | fprintf(stderr, "shm_attach for %s failed, ret: %d\n", argv[1], res); 41 | return res; 42 | } 43 | 44 | shm_show_channel(channel, std::cout, !!need_node_info, need_node_data); 45 | return 0; 46 | } 47 | #else 48 | int main() { 49 | puts("shm channel disabled"); 50 | return 0; 51 | } 52 | #endif -------------------------------------------------------------------------------- /ci/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | find . -type f \ 4 | -regex "^./third_party/packages/.*" -prune \ 5 | -o -regex "^./third_party/install/.*" -prune \ 6 | -o -regex "^./build_jobs_.*" -prune \ 7 | -o -regex "^./atframework/cmake-toolset/.*" -prune \ 8 | -o -regex "^./atframework/atframe_utils/.*" -prune \ 9 | -o -name "*.cmake" -print \ 10 | -o -name "*.cmake.in" -print \ 11 | -o -name 'CMakeLists.txt' -print \ 12 | | xargs cmake-format -i 13 | 14 | find . -type f \ 15 | -regex "^./third_party/packages/.*" -prune \ 16 | -o -regex "^./third_party/install/.*" -prune \ 17 | -o -regex "^./build_jobs_.*" -prune \ 18 | -o -regex "^./atframework/cmake-toolset/.*" -prune \ 19 | -o -regex "^./atframework/atframe_utils/.*" -prune \ 20 | -o -name "*.h" -print \ 21 | -o -name "*.hpp" -print \ 22 | -o -name "*.cxx" -print \ 23 | -o -name '*.cpp' -print \ 24 | -o -name '*.cc' -print \ 25 | -o -name '*.c' -print \ 26 | | xargs -r -n 32 clang-format -i --style=file --fallback-style=none 27 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | echowithcolor(COLOR GREEN "-- Configure Unit Test ${CMAKE_CURRENT_LIST_DIR}") 2 | 3 | include("${PROJECT_TEST_BAS_DIR}/test.build_bin.cmake") 4 | 5 | file( 6 | GLOB_RECURSE 7 | PROJECT_TEST_SRC_LIST 8 | ${PROJECT_TEST_SRC_DIR}/app/*.cpp 9 | ${PROJECT_TEST_SRC_DIR}/frame/*.h 10 | ${PROJECT_TEST_SRC_DIR}/frame/*.cpp 11 | ${CMAKE_CURRENT_LIST_DIR}/*.hpp 12 | ${CMAKE_CURRENT_LIST_DIR}/*.h 13 | ${CMAKE_CURRENT_LIST_DIR}/*.c 14 | ${CMAKE_CURRENT_LIST_DIR}/*.cpp 15 | ${CMAKE_CURRENT_LIST_DIR}/*.cc 16 | ${CMAKE_CURRENT_LIST_DIR}/*.cxx) 17 | source_group_by_dir(PROJECT_TEST_SRC_LIST) 18 | 19 | # ============ test - coroutine test frame ============ 20 | set(CMAKE_BUILD_RPATH "${CMAKE_INSTALL_RPATH}") 21 | if(NOT (WIN32 AND BUILD_SHARED_LIBS)) 22 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/test") 23 | endif() 24 | 25 | atframe_add_test_executable(atbus_unit_test ${PROJECT_TEST_SRC_LIST}) 26 | 27 | target_compile_options(atbus_unit_test PRIVATE ${PROJECT_LIBATBUS_PRIVATE_COMPILE_OPTIONS}) 28 | target_link_libraries(atbus_unit_test atframework::atbus) 29 | 30 | target_include_directories(atbus_unit_test PRIVATE "$") 31 | 32 | add_test(NAME "libatbus.unit_test" COMMAND "$") 33 | set_tests_properties("libatbus.unit_test" PROPERTIES LABELS "libatbus;libatbus.unit_test") 34 | -------------------------------------------------------------------------------- /BOOST_LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /atframework/Repository.cmake: -------------------------------------------------------------------------------- 1 | set(ATFRAMEWORK_ATFRAME_UTILS_REPO_DIR 2 | "${PROJECT_SOURCE_DIR}/atframework/atframe_utils" 3 | CACHE PATH "PATH to atframe_utils") 4 | 5 | macro(ATFRAMEWORK_ATFRAME_UTILS_POPULATE) 6 | if(NOT EXISTS "${ATFRAMEWORK_ATFRAME_UTILS_REPO_DIR}/CMakeLists.txt") 7 | execute_process( 8 | COMMAND ${GIT_EXECUTABLE} submodule update --depth 100 --recommend-shallow -f --init -- atframework/atframe_utils 9 | WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" ${ATFRAMEWORK_CMAKE_TOOLSET_EXECUTE_PROCESS_OUTPUT_OPTIONS}) 10 | set(ATFRAMEWORK_ATFRAME_UTILS_REPO_DIR 11 | "${PROJECT_SOURCE_DIR}/atframework/atframe_utils" 12 | CACHE PATH "PATH to atframe_utils" FORCE) 13 | endif() 14 | endmacro() 15 | 16 | if(TARGET atframe_utils) 17 | set(ATFRAMEWORK_ATFRAME_UTILS_LINK_NAME atframe_utils) 18 | elseif(TARGET atframework::atframe_utils) 19 | set(ATFRAMEWORK_ATFRAME_UTILS_LINK_NAME atframework::atframe_utils) 20 | else() 21 | set(ATFRAMEWORK_ATFRAME_UTILS_LINK_NAME atframe_utils) 22 | if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/_deps/${ATFRAMEWORK_ATFRAME_UTILS_LINK_NAME}") 23 | file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/_deps/${ATFRAMEWORK_ATFRAME_UTILS_LINK_NAME}") 24 | endif() 25 | atframework_atframe_utils_populate() 26 | add_subdirectory("${ATFRAMEWORK_ATFRAME_UTILS_REPO_DIR}" 27 | "${CMAKE_CURRENT_BINARY_DIR}/_deps/${ATFRAMEWORK_ATFRAME_UTILS_LINK_NAME}") 28 | endif() 29 | -------------------------------------------------------------------------------- /include/atbus_connection_context.h: -------------------------------------------------------------------------------- 1 | // Copyright 2026 atframework 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "detail/libatbus_config.h" 11 | 12 | namespace atframework { 13 | namespace atbus { 14 | namespace protocol { 15 | class message_body; 16 | } // namespace protocol 17 | } // namespace atbus 18 | } // namespace atframework 19 | 20 | ATBUS_MACRO_NAMESPACE_BEGIN 21 | 22 | class ATFW_UTIL_SYMBOL_VISIBLE connection_context final { 23 | UTIL_DESIGN_PATTERN_NOCOPYABLE(connection_context) 24 | UTIL_DESIGN_PATTERN_NOMOVABLE(connection_context) 25 | 26 | public: 27 | using ptr_t = atfw::util::memory::strong_rc_ptr; 28 | 29 | private: 30 | struct ctor_guard_type {}; 31 | 32 | public: 33 | ATBUS_MACRO_API connection_context(ctor_guard_type &); 34 | ATBUS_MACRO_API ~connection_context(); 35 | 36 | static ATBUS_MACRO_API ptr_t create(); 37 | 38 | ATBUS_MACRO_API size_t padding_size(size_t origin_size) const noexcept; 39 | 40 | ATBUS_MACRO_API int pack_body(const protocol::message_body &body, size_t expect_size, gsl::span buffer, 41 | size_t &used_size) noexcept; 42 | 43 | ATBUS_MACRO_API int unpack_body(protocol::message_body &body, size_t expect_size, 44 | gsl::span buffer) noexcept; 45 | 46 | private: 47 | }; 48 | ATBUS_MACRO_NAMESPACE_END 49 | -------------------------------------------------------------------------------- /test/case/atbus_test_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UNITTEST_LIBATBUS_TEST_UTILS_H_ 2 | #define UNITTEST_LIBATBUS_TEST_UTILS_H_ 3 | 4 | #pragma once 5 | 6 | #include "uv.h" 7 | 8 | void unit_test_tick_handle(uv_timer_t *handle); 9 | void unit_test_timeout_handle(uv_timer_t *handle); 10 | void unit_test_setup_exit(uv_loop_t *ev, uint64_t timeout_ms = 30000); 11 | 12 | struct unit_test_libuv_wait_manager { 13 | unit_test_libuv_wait_manager(uv_loop_t *ev, uint64_t timeout_ms, uint64_t tick_ms, bool print_error); 14 | ~unit_test_libuv_wait_manager(); 15 | 16 | void run(uv_loop_t *ev); 17 | 18 | uv_timer_t timeout_timer_; 19 | uv_timer_t tick_timer_; 20 | bool tick_enabled_; 21 | bool print_error_; 22 | bool is_timeout_; 23 | }; 24 | 25 | #define UNITTEST_WAIT_UNTIL(ev, conndition, timeout_ms, tick_ms) \ 26 | for (unit_test_libuv_wait_manager libuv_wait_mgr(ev, timeout_ms, tick_ms, true); \ 27 | !libuv_wait_mgr.is_timeout_ && !(conndition); libuv_wait_mgr.run(ev)) \ 28 | if (!libuv_wait_mgr.is_timeout_) 29 | 30 | #define UNITTEST_WAIT_IF(ev, conndition, timeout_ms, tick_ms) \ 31 | for (unit_test_libuv_wait_manager libuv_wait_mgr(ev, timeout_ms, tick_ms, true); \ 32 | !libuv_wait_mgr.is_timeout_ && (conndition); libuv_wait_mgr.run(ev)) \ 33 | if (!libuv_wait_mgr.is_timeout_) 34 | 35 | #define UNITTEST_WAIT_MS(ev, timeout_ms, tick_ms) \ 36 | for (unit_test_libuv_wait_manager libuv_wait_mgr(ev, timeout_ms, tick_ms, false); !libuv_wait_mgr.is_timeout_; \ 37 | libuv_wait_mgr.run(ev)) \ 38 | if (!libuv_wait_mgr.is_timeout_) 39 | 40 | #endif -------------------------------------------------------------------------------- /src/atbus_connection_context.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2025 atframework 2 | 3 | #include "atbus_connection_context.h" 4 | 5 | #include "detail/libatbus_error.h" 6 | #include "libatbus_protocol.h" 7 | 8 | ATBUS_MACRO_NAMESPACE_BEGIN 9 | 10 | ATBUS_MACRO_API connection_context::connection_context(ctor_guard_type &) {} 11 | 12 | ATBUS_MACRO_API connection_context::~connection_context() {} 13 | 14 | ATBUS_MACRO_API connection_context::ptr_t connection_context::create() { 15 | ctor_guard_type guard; 16 | return atfw::util::memory::make_strong_rc(guard); 17 | } 18 | 19 | ATBUS_MACRO_API size_t connection_context::padding_size(size_t origin_size) const noexcept { 20 | // TODO: cipher padding 21 | // TODO: compress padding 22 | return origin_size; 23 | } 24 | 25 | ATBUS_MACRO_API int connection_context::pack_body(const protocol::message_body &body, size_t expect_size, 26 | gsl::span buffer, size_t &used_size) noexcept { 27 | if (expect_size > buffer.size()) { 28 | return EN_ATBUS_ERR_BUFF_LIMIT; 29 | } 30 | 31 | used_size = body.ByteSizeLong(); 32 | if (used_size != expect_size) { 33 | return EN_ATBUS_ERR_INVALID_SIZE; 34 | } 35 | 36 | // TODO: cipher encrypt 37 | // TODO: compress 38 | 39 | body.SerializeWithCachedSizesToArray(reinterpret_cast(buffer.data())); 40 | return EN_ATBUS_ERR_SUCCESS; 41 | } 42 | 43 | ATBUS_MACRO_API int connection_context::unpack_body(protocol::message_body &body, size_t expect_size, 44 | gsl::span buffer) noexcept { 45 | if (expect_size > buffer.size()) { 46 | return EN_ATBUS_ERR_BUFF_LIMIT; 47 | } 48 | 49 | // TODO: uncompress 50 | 51 | // TODO: cipher decrypt 52 | 53 | if (!body.ParseFromArray(reinterpret_cast(buffer.data()), static_cast(expect_size))) { 54 | return EN_ATBUS_ERR_UNPACK; 55 | } 56 | 57 | return EN_ATBUS_ERR_SUCCESS; 58 | } 59 | 60 | ATBUS_MACRO_NAMESPACE_END 61 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h text working-tree-encoding=UTF-8 eol=lf 2 | *.hpp text working-tree-encoding=UTF-8 eol=lf 3 | *.cpp text working-tree-encoding=UTF-8 eol=lf 4 | *.cxx text working-tree-encoding=UTF-8 eol=lf 5 | *.cc text working-tree-encoding=UTF-8 eol=lf 6 | *.sh text encoding=UTF-8 eol=lf 7 | *.sh.mako text encoding=UTF-8 eol=lf 8 | *.sh.in text encoding=UTF-8 eol=lf 9 | *.py text encoding=UTF-8 eol=lf 10 | *.mako.py text encoding=UTF-8 eol=lf 11 | *.py.in text encoding=UTF-8 eol=lf 12 | *.py.mako text encoding=UTF-8 eol=lf 13 | *.md text encoding=UTF-8-BOM eol=crlf 14 | *.rst text encoding=UTF-8-BOM eol=crlf 15 | *.bat text encoding=UTF-8-BOM eol=crlf 16 | *.bat.in text encoding=UTF-8-BOM eol=crlf 17 | *.ps1 text encoding=UTF-8-BOM eol=crlf 18 | *.ps1.in text encoding=UTF-8-BOM eol=crlf 19 | *.tar.* filter=lfs diff=lfs merge=lfs -text 20 | *.tar filter=lfs diff=lfs merge=lfs -text 21 | *.gz filter=lfs diff=lfs merge=lfs -text 22 | *.xz filter=lfs diff=lfs merge=lfs -text 23 | *.7z filter=lfs diff=lfs merge=lfs -text 24 | *.tgz filter=lfs diff=lfs merge=lfs -text 25 | *.zip filter=lfs diff=lfs merge=lfs -text 26 | *.a filter=lfs diff=lfs merge=lfs -text 27 | *.so filter=lfs diff=lfs merge=lfs -text 28 | *.so.* filter=lfs diff=lfs merge=lfs -text 29 | *.lib filter=lfs diff=lfs merge=lfs -text 30 | *.dylib filter=lfs diff=lfs merge=lfs -text 31 | *.dll filter=lfs diff=lfs merge=lfs -text 32 | *.doc filter=lfs diff=lfs merge=lfs -text 33 | *.docx filter=lfs diff=lfs merge=lfs -text 34 | *.xls filter=lfs diff=lfs merge=lfs -text 35 | *.xlsx filter=lfs diff=lfs merge=lfs -text 36 | *.ppt filter=lfs diff=lfs merge=lfs -text 37 | *.pptx filter=lfs diff=lfs merge=lfs -text 38 | *.potx filter=lfs diff=lfs merge=lfs -text 39 | *.pdf filter=lfs diff=lfs merge=lfs -text 40 | *.png filter=lfs diff=lfs merge=lfs -text 41 | *.ico filter=lfs diff=lfs merge=lfs -text 42 | *.jpg filter=lfs diff=lfs merge=lfs -text 43 | *.psd filter=lfs diff=lfs merge=lfs -text 44 | *.jar filter=lfs diff=lfs merge=lfs -text 45 | *.exe filter=lfs diff=lfs merge=lfs -text 46 | *.7z filter=lfs diff=lfs merge=lfs -text 47 | *.gz filter=lfs diff=lfs merge=lfs -text 48 | -------------------------------------------------------------------------------- /NODE_S_LICENSE: -------------------------------------------------------------------------------- 1 | libuv is part of the Node project: http://nodejs.org/ 2 | libuv may be distributed alone under Node's license: 3 | 4 | ==== 5 | 6 | Copyright Joyent, Inc. and other Node contributors. All rights reserved. 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to 9 | deal in the Software without restriction, including without limitation the 10 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | IN THE SOFTWARE. 24 | 25 | ==== 26 | 27 | This license applies to all parts of libuv that are not externally 28 | maintained libraries. 29 | 30 | The externally maintained libraries used by libuv are: 31 | 32 | - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. 33 | 34 | - inet_pton and inet_ntop implementations, contained in src/inet.c, are 35 | copyright the Internet Systems Consortium, Inc., and licensed under the ISC 36 | license. 37 | 38 | - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three 39 | clause BSD license. 40 | 41 | - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile 42 | Communications AB. Three clause BSD license. 43 | 44 | - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design 45 | Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement 46 | n° 289016). Three clause BSD license. 47 | -------------------------------------------------------------------------------- /src/libatbus.macro.cmake: -------------------------------------------------------------------------------- 1 | # =========== libatbus/src =========== 2 | set(PROJECT_LIBATBUS_ROOT_SRC_DIR ${CMAKE_CURRENT_LIST_DIR}) 3 | 4 | set(PROJECT_LIBATBUS_LIB_LINK "atbus") 5 | set(PROJECT_LIBATBUS_EXPORT_NAME ${PROJECT_NAME}-target) 6 | 7 | # include("${PROJECT_LIBATBUS_ROOT_SRC_DIR}/XXX.cmake") 8 | 9 | add_custom_command( 10 | OUTPUT "${PROJECT_LIBATBUS_GENERATED_DIR}/include/libatbus_protocol.pb.h" 11 | "${PROJECT_LIBATBUS_GENERATED_DIR}/src/libatbus_protocol.pb.cc" 12 | "${PROJECT_LIBATBUS_GENERATED_DIR}/libatbus_protocol.pb" 13 | COMMAND 14 | ${ATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_BIN_PROTOC} --proto_path ${PROJECT_LIBATBUS_ROOT_INC_DIR} -o 15 | "${PROJECT_LIBATBUS_GENERATED_DIR}/temp/libatbus_protocol.pb" 16 | "--cpp_out=dllexport_decl=ATBUS_MACRO_PROTOCOL_API:${PROJECT_LIBATBUS_GENERATED_DIR}/temp/" 17 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/libatbus_protocol.proto" 18 | COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_LIBATBUS_GENERATED_DIR}/temp/libatbus_protocol.pb.cc" 19 | "${PROJECT_LIBATBUS_GENERATED_DIR}/src/libatbus_protocol.pb.cc" 20 | COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_LIBATBUS_GENERATED_DIR}/temp/libatbus_protocol.pb.h" 21 | "${PROJECT_LIBATBUS_GENERATED_DIR}/include/libatbus_protocol.pb.h" 22 | COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_LIBATBUS_GENERATED_DIR}/temp/libatbus_protocol.pb" 23 | "${PROJECT_LIBATBUS_GENERATED_DIR}/libatbus_protocol.pb" 24 | COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${PROJECT_SOURCE_DIR}/third_party/.clang-tidy" 25 | "${PROJECT_LIBATBUS_GENERATED_DIR}/" 26 | DEPENDS "${PROJECT_LIBATBUS_ROOT_INC_DIR}/libatbus_protocol.proto" 27 | "${ATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_BIN_PROTOC}" 28 | COMMENT 29 | "Generate ${PROJECT_LIBATBUS_GENERATED_DIR}/include/libatbus_protocol.pb.h, ${PROJECT_LIBATBUS_GENERATED_DIR}/src/libatbus_protocol.pb.cc and ${PROJECT_LIBATBUS_GENERATED_DIR}/libatbus_protocol.pb" 30 | ) 31 | 32 | add_custom_target("atbus-generate-protocol" SOURCES "${PROJECT_LIBATBUS_GENERATED_DIR}/include/libatbus_protocol.pb.h" 33 | "${PROJECT_LIBATBUS_GENERATED_DIR}/src/libatbus_protocol.pb.cc") 34 | 35 | set_property(TARGET "atbus-generate-protocol" PROPERTY FOLDER "atframework/atbus") 36 | -------------------------------------------------------------------------------- /test/case/atbus_test_utils.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "atbus_test_utils.h" 3 | #include "frame/test_macros.h" 4 | 5 | void unit_test_tick_handle(uv_timer_t *handle) { uv_stop(handle->loop); } 6 | 7 | void unit_test_timeout_handle(uv_timer_t *handle) { 8 | uv_stop(handle->loop); 9 | 10 | unit_test_libuv_wait_manager *mgr = reinterpret_cast(handle->data); 11 | if (nullptr == mgr || mgr->print_error_) { 12 | CASE_MSG_ERROR() << CASE_MSG_FCOLOR(RED) << "wait timeout." << std::endl; 13 | } 14 | 15 | if (nullptr != mgr) { 16 | mgr->is_timeout_ = true; 17 | } 18 | } 19 | 20 | void unit_test_setup_exit(uv_loop_t *ev, uint64_t timeout_ms) { 21 | size_t left_tick = timeout_ms / 8; 22 | while (left_tick > 0 && UV_EBUSY == uv_loop_close(ev)) { 23 | // loop 8 times 24 | for (int i = 0; i < 8; ++i) { 25 | uv_run(ev, UV_RUN_NOWAIT); 26 | } 27 | CASE_THREAD_SLEEP_MS(8); 28 | 29 | --left_tick; 30 | } 31 | 32 | CASE_EXPECT_NE(left_tick, 0); 33 | } 34 | 35 | static void unit_test_timer_close_handle(uv_handle_t *handle) { 36 | handle->data = nullptr; 37 | uv_stop(handle->loop); 38 | } 39 | 40 | unit_test_libuv_wait_manager::unit_test_libuv_wait_manager(uv_loop_t *ev, uint64_t timeout_ms, uint64_t tick_ms, 41 | bool print_error) 42 | : tick_enabled_(false), print_error_(print_error), is_timeout_(false) { 43 | uv_timer_init(ev, &timeout_timer_); 44 | 45 | if (tick_ms > 0) { 46 | if (0 == uv_timer_init(ev, &tick_timer_)) { 47 | tick_enabled_ = true; 48 | } 49 | } 50 | tick_timer_.data = this; 51 | timeout_timer_.data = this; 52 | 53 | if (0 != uv_timer_start(&timeout_timer_, unit_test_timeout_handle, timeout_ms, 0)) { 54 | timeout_timer_.data = nullptr; 55 | } 56 | 57 | if (tick_enabled_) { 58 | if (0 != uv_timer_start(&tick_timer_, unit_test_tick_handle, tick_ms, tick_ms)) { 59 | timeout_timer_.data = nullptr; 60 | tick_enabled_ = false; 61 | } 62 | } 63 | } 64 | 65 | unit_test_libuv_wait_manager::~unit_test_libuv_wait_manager() { 66 | uv_loop_t *ev = timeout_timer_.loop; 67 | uv_timer_stop(&timeout_timer_); 68 | 69 | uv_close(reinterpret_cast(&timeout_timer_), unit_test_timer_close_handle); 70 | 71 | if (tick_enabled_) { 72 | uv_timer_stop(&tick_timer_); 73 | uv_close(reinterpret_cast(&tick_timer_), unit_test_timer_close_handle); 74 | } else { 75 | tick_timer_.data = nullptr; 76 | } 77 | 78 | while (nullptr != timeout_timer_.data || nullptr != tick_timer_.data) { 79 | uv_run(ev, UV_RUN_DEFAULT); 80 | } 81 | } 82 | 83 | void unit_test_libuv_wait_manager::run(uv_loop_t *ev) { uv_run(ev, UV_RUN_ONCE); } -------------------------------------------------------------------------------- /libatbus-config.cmake.in: -------------------------------------------------------------------------------- 1 | #[=======================================================================[.rst: 2 | libatbus-config.cmake 3 | --------------------- 4 | 5 | Find the native libatbus includes and library. 6 | 7 | 8 | Result Variables 9 | ^^^^^^^^^^^^^^^^ 10 | 11 | This module defines the following variables: 12 | 13 | ``Libatbus_INCLUDE_DIRS`` 14 | Where to find detail/libatbus_config.h , etc. 15 | ``Libatbus_PROTOCOL_DIRS`` 16 | Where to find libatbus_protocol.proto , etc. 17 | ``Libatbus_LIBRARY_DIRS`` 18 | Where to find (lib)atbus.(a/so/lib/dll/dylib), etc. 19 | ``Libatbus_LIBRARIES`` 20 | List of libraries when using libatbus. 21 | ``Libatbus_FOUND`` 22 | True if libatbus found. 23 | ``Libatbus_VERSION`` 24 | Full version of libatbus 25 | 26 | 27 | The following :prop_tgt:`IMPORTED` targets are also defined: 28 | 29 | ``atframework::atbus`` 30 | The libatbus library 31 | 32 | ============================================================================= 33 | Copyright 2020 OWenT. 34 | 35 | Distributed under the OSI-approved BSD License (the "License"); 36 | see accompanying file Copyright.txt for details. 37 | 38 | This software is distributed WITHOUT ANY WARRANTY; without even the 39 | implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 40 | See the License for more information. 41 | ============================================================================= 42 | (To distribute this file outside of CMake, substitute the full License text for 43 | the above reference.) 44 | 45 | #]=======================================================================] 46 | 47 | set(${CMAKE_FIND_PACKAGE_NAME}_VERSION "@LIBATBUS_VERSION@") 48 | 49 | @PACKAGE_INIT@ 50 | 51 | # ###################################################################################################################### 52 | # libatbus source dir 53 | set(${CMAKE_FIND_PACKAGE_NAME}_SOURCE_DIR "@PROJECT_SOURCE_DIR@") 54 | 55 | set_and_check(${CMAKE_FIND_PACKAGE_NAME}_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@") 56 | set_and_check(${CMAKE_FIND_PACKAGE_NAME}_LIBRARY_DIRS "@PACKAGE_CMAKE_INSTALL_LIBDIR@") 57 | set_and_check(${CMAKE_FIND_PACKAGE_NAME}_PROTOCOL_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@") 58 | 59 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_LIBATBUS_EXPORT_NAME@.cmake") 60 | if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@PROJECT_LIBATBUS_EXPORT_NAME@.cmake") 61 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_LIBATBUS_EXPORT_NAME@.cmake") 62 | endif() 63 | # Normal search. 64 | set(${CMAKE_FIND_PACKAGE_NAME}_LIBRARIES atframework::atbus) 65 | 66 | # handle the QUIETLY and REQUIRED arguments and set LIBATBUS_FOUND to TRUE if all listed variables are TRUE 67 | include("FindPackageHandleStandardArgs") 68 | find_package_handle_standard_args( 69 | ${CMAKE_FIND_PACKAGE_NAME} 70 | FOUND_VAR ${CMAKE_FIND_PACKAGE_NAME}_FOUND 71 | REQUIRED_VARS ${CMAKE_FIND_PACKAGE_NAME}_INCLUDE_DIRS ${CMAKE_FIND_PACKAGE_NAME}_LIBRARIES) 72 | 73 | if(${CMAKE_FIND_PACKAGE_NAME}_FOUND) 74 | set(LIBATBUS_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_FOUND}) 75 | endif() 76 | 77 | # check_required_components(${CMAKE_FIND_PACKAGE_NAME}) 78 | -------------------------------------------------------------------------------- /src/channel_utility.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2025 atframework 2 | /** 3 | * @brief 所有channel文件的模式均为 c + channel
4 | * 使用c的模式是为了简单、结构清晰并且避免异常
5 | * 附带c++的部分是为了避免命名空间污染并且c++的跨平台适配更加简单 6 | */ 7 | 8 | #include 9 | 10 | #include "common/string_oprs.h" 11 | 12 | #include "detail/libatbus_channel_export.h" 13 | 14 | ATBUS_MACRO_NAMESPACE_BEGIN 15 | namespace channel { 16 | ATBUS_MACRO_API bool make_address(const char *in, channel_address_t &addr) { 17 | addr.address = in; 18 | 19 | // 获取协议 20 | size_t scheme_end = addr.address.find_first_of("://"); 21 | if (addr.address.npos == scheme_end) { 22 | return false; 23 | } 24 | 25 | addr.scheme = addr.address.substr(0, scheme_end); 26 | size_t port_end = addr.address.find_last_of(":"); 27 | addr.port = 0; 28 | if (addr.address.npos != port_end && port_end >= scheme_end + 3) { 29 | UTIL_STRFUNC_SSCANF(addr.address.c_str() + port_end + 1, "%d", &addr.port); 30 | } 31 | 32 | // 截取域名 33 | addr.host = 34 | addr.address.substr(scheme_end + 3, (port_end == addr.address.npos) ? port_end : port_end - scheme_end - 3); 35 | 36 | return true; 37 | } 38 | 39 | ATBUS_MACRO_API void make_address(const char *scheme, const char *host, int port, channel_address_t &addr) { 40 | addr.scheme = scheme; 41 | addr.host = host; 42 | addr.port = port; 43 | addr.address.reserve(addr.scheme.size() + addr.host.size() + 4 + 8); 44 | addr.address = addr.scheme + "://" + addr.host; 45 | 46 | if (port > 0) { 47 | char port_str[16] = {0}; 48 | UTIL_STRFUNC_SNPRINTF(port_str, sizeof(port_str), "%d", port); 49 | addr.address += ":"; 50 | addr.address += &port_str[0]; 51 | } 52 | } 53 | 54 | ATBUS_MACRO_API bool is_duplex_address(const char *in) { 55 | if (nullptr == in || !(*in)) { 56 | return false; 57 | } 58 | 59 | return false == is_simplex_address(in); 60 | } 61 | 62 | ATBUS_MACRO_API bool is_simplex_address(const char *in) { 63 | if (nullptr == in || !(*in)) { 64 | return false; 65 | } 66 | 67 | if (0 == UTIL_STRFUNC_STRNCASE_CMP("mem:", in, 4)) { 68 | return true; 69 | } 70 | 71 | if (0 == UTIL_STRFUNC_STRNCASE_CMP("shm:", in, 4)) { 72 | return true; 73 | } 74 | 75 | return false; 76 | } 77 | 78 | ATBUS_MACRO_API bool is_local_host_address(const char *in) { 79 | if (nullptr == in || !(*in)) { 80 | return false; 81 | } 82 | 83 | if (is_local_process_address(in)) { 84 | return true; 85 | } 86 | 87 | if (0 == UTIL_STRFUNC_STRNCASE_CMP("shm:", in, 4)) { 88 | return true; 89 | } 90 | 91 | if (0 == UTIL_STRFUNC_STRNCASE_CMP("unix:", in, 5)) { 92 | return true; 93 | } 94 | 95 | return false; 96 | } 97 | 98 | ATBUS_MACRO_API bool is_local_process_address(const char *in) { 99 | if (nullptr == in || !(*in)) { 100 | return false; 101 | } 102 | 103 | if (0 == UTIL_STRFUNC_STRNCASE_CMP("mem:", in, 4)) { 104 | return true; 105 | } 106 | 107 | return false; 108 | } 109 | } // namespace channel 110 | ATBUS_MACRO_NAMESPACE_END 111 | -------------------------------------------------------------------------------- /project/cmake/ProjectBuildOption.cmake: -------------------------------------------------------------------------------- 1 | # 默认配置选项 2 | # ###################################################################################################################### 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/FetchDependeny.cmake") 5 | include(IncludeDirectoryRecurse) 6 | include(EchoWithColor) 7 | 8 | # atbus 选项 9 | set(ATBUS_MACRO_BUSID_TYPE 10 | "uint64_t" 11 | CACHE STRING "busid type") 12 | set(ATBUS_MACRO_DATA_NODE_SIZE 13 | 256 14 | CACHE STRING "node size of (shared) memory channel(must be power of 2)") 15 | set(ATBUS_MACRO_DATA_ALIGN_SIZE 16 | 16 17 | CACHE STRING "memory align size, most architecture require to padding to 16") 18 | set(ATBUS_MACRO_DATA_MAX_PROTECT_SIZE 19 | 16384 20 | CACHE STRING "max protected node size for mem/shm channel") 21 | 22 | # By now, other component in io_stream_connection cost 472 bytes, make_shared will also cost some memory. we hope one 23 | # connection will cost no more than 8KB, so 100K connections will cost no more than 800MB memory so we use 7KB for small 24 | # message buffer, and left about 500 Bytes in future use. This can be 512 or smaller (but not smaller than 32), but in 25 | # most server environment, memory is cheap and there are only few connections between server and server. 26 | set(ATBUS_MACRO_DATA_SMALL_SIZE 27 | 7168 28 | CACHE STRING 29 | "small message buffer for io_stream channel(used to reduce memory copy when there are many small messages)") 30 | 31 | set(ATBUS_MACRO_HUGETLB_SIZE 32 | 4194304 33 | CACHE STRING "huge page size in shared memory channel(unused now)") 34 | set(ATBUS_MACRO_MESSAGE_LIMIT 35 | 2097152 36 | CACHE STRING "message size hard limit") 37 | set(ATBUS_MACRO_MAX_FRAME_HEADER 38 | 1024 39 | CACHE STRING "message header size limit") 40 | set(ATBUS_MACRO_CONNECTION_CONFIRM_TIMEOUT 41 | 30 42 | CACHE STRING "connection confirm timeout") 43 | set(ATBUS_MACRO_CONNECTION_BACKLOG 44 | 256 45 | CACHE STRING "tcp backlog") 46 | set(ATBUS_MACRO_SHM_MEM_CHANNEL_LENGTH 47 | 167510016 48 | CACHE STRING "channel size for shm/mem channel") 49 | set(ATBUS_MACRO_IOS_SEND_BUFFER_LENGTH 50 | 4194304 51 | CACHE STRING "send buffer size for iostream channel") 52 | 53 | math(EXPR ATFRAMEWORK_ATBUS_ABI_NUMBER "${LIBATBUS_VERSION_MAJOR}*1000+${LIBATBUS_VERSION_MINOR}") 54 | set(ATFRAMEWORK_ATBUS_ABI_TAG "v${ATFRAMEWORK_ATBUS_ABI_NUMBER}" CACHE STRING "libatbus ABI tag") 55 | 56 | option(ATBUS_MACRO_ABORT_ON_PROTECTED_ERROR "abort when any inner error found." OFF) 57 | 58 | option(ATFRAMEWORK_USE_DYNAMIC_LIBRARY "Build and linking with dynamic libraries." OFF) 59 | 60 | # libuv选项 61 | set(LIBUV_ROOT 62 | "" 63 | CACHE STRING "libuv root directory") 64 | 65 | # 测试配置选项 66 | set(GTEST_ROOT 67 | "" 68 | CACHE STRING "GTest root directory") 69 | set(BOOST_ROOT 70 | "" 71 | CACHE STRING "Boost root directory") 72 | option(PROJECT_TEST_ENABLE_BOOST_UNIT_TEST "Enable boost unit test." OFF) 73 | 74 | # find if we have Unix Sock 75 | include(CheckIncludeFiles) 76 | check_include_files("sys/un.h;sys/socket.h" ATBUS_MACRO_WITH_UNIX_SOCK) 77 | -------------------------------------------------------------------------------- /include/libatbus_protocol.h: -------------------------------------------------------------------------------- 1 | /** 2 | * libatbus.h 3 | * 4 | * Created on: 2014年8月11日 5 | * Author: owent 6 | */ 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | #include "detail/libatbus_config.h" 18 | 19 | // clang-format off 20 | # include "config/compiler/protobuf_prefix.h" 21 | // clang-format on 22 | 23 | #include "google/protobuf/arena.h" 24 | 25 | #include "libatbus_protocol.pb.h" // NOLINT 26 | 27 | // clang-format off 28 | # include "config/compiler/protobuf_suffix.h" 29 | // clang-format on 30 | 31 | ATBUS_MACRO_NAMESPACE_BEGIN 32 | struct ATFW_UTIL_SYMBOL_VISIBLE message_inplace { 33 | ::atframework::atbus::protocol::message_head head; 34 | ::atframework::atbus::protocol::message_body body; 35 | 36 | message_inplace(const message_inplace&) = delete; 37 | message_inplace& operator=(const message_inplace&) = delete; 38 | 39 | message_inplace() = default; 40 | message_inplace(message_inplace&&) = default; 41 | message_inplace& operator=(message_inplace&&) = default; 42 | }; 43 | 44 | using message_body_type = ::atframework::atbus::protocol::message_body::MessageTypeCase; 45 | class ATFW_UTIL_SYMBOL_VISIBLE message { 46 | public: 47 | ATBUS_MACRO_API message(const ::google::protobuf::ArenaOptions& options); 48 | ATBUS_MACRO_API message(std::unique_ptr<::google::protobuf::Arena>&& input_arena); 49 | 50 | ATBUS_MACRO_API message(message&&); 51 | ATBUS_MACRO_API message& operator=(message&&); 52 | ATBUS_MACRO_API ~message(); 53 | 54 | message(const message&) = delete; 55 | message& operator=(const message&) = delete; 56 | 57 | ATBUS_MACRO_API ::atframework::atbus::protocol::message_head& mutable_head(); 58 | 59 | ATBUS_MACRO_API ::atframework::atbus::protocol::message_body& mutable_body(); 60 | 61 | ATBUS_MACRO_API ::atfw::util::nostd::nullable get_head() 62 | const noexcept; 63 | 64 | ATBUS_MACRO_API ::atfw::util::nostd::nullable get_body() 65 | const noexcept; 66 | 67 | ATBUS_MACRO_API const ::atframework::atbus::protocol::message_head& head() const noexcept; 68 | 69 | ATBUS_MACRO_API const ::atframework::atbus::protocol::message_body& body() const noexcept; 70 | 71 | ATBUS_MACRO_API std::string get_head_debug_string() const; 72 | 73 | ATBUS_MACRO_API std::string get_body_debug_string() const; 74 | 75 | ATBUS_MACRO_API message_body_type get_body_type() const noexcept; 76 | 77 | ATBUS_MACRO_API std::string get_unpack_error_message() const noexcept; 78 | 79 | private: 80 | std::unique_ptr<::google::protobuf::Arena> arena_; 81 | std::unique_ptr inplace_cache_; 82 | ::atfw::util::nostd::nullable<::atframework::atbus::protocol::message_head*> head_; 83 | ::atfw::util::nostd::nullable<::atframework::atbus::protocol::message_body*> body_; 84 | }; 85 | ATBUS_MACRO_NAMESPACE_END 86 | 87 | #define ATBUS_MACRO_RESERVED_SIZE 1024 88 | 89 | #ifndef ATBUS_MACRO_PROTOBUF_NAMESPACE_ID 90 | # define ATBUS_MACRO_PROTOBUF_NAMESPACE_ID google::protobuf 91 | #endif 92 | -------------------------------------------------------------------------------- /sample/sample_usage_01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | int main() { 12 | #if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS 13 | // 初始化默认配置 14 | atbus::node::conf_t conf; 15 | atbus::node::default_conf(&conf); 16 | 17 | // 子域的范围设为16(后16位都是子节点), id_prefix=0 等于使用endpoint自己的ID 18 | conf.subnets.push_back(atbus::endpoint_subnet_conf(0, 16)); 19 | 20 | // 初始化libuv事件分发器 21 | uv_loop_t ev_loop; 22 | uv_loop_init(&ev_loop); 23 | 24 | // 指定事件分发器 25 | conf.ev_loop = &ev_loop; 26 | 27 | { 28 | // 创建两个通信节点 29 | atbus::node::ptr_t node1 = atbus::node::create(); 30 | atbus::node::ptr_t node2 = atbus::node::create(); 31 | 32 | // 初始化 33 | node1->init(0x12345678, &conf); // BUS ID=0x12345678, 0x1234XXXX 都是子节点 34 | node2->init(0x12356789, &conf); // BUS ID=0x12356789, 0x1235XXXX 都是子节点 35 | // 所以这两个都是兄弟节点 36 | 37 | // 各自监听地址 38 | node1->listen("ipv4://127.0.0.1:16387"); 39 | node2->listen("ipv4://127.0.0.1:16388"); 40 | 41 | // 启动初始化 42 | node1->start(); 43 | node2->start(); 44 | 45 | // 启动连接 46 | node1->connect("ipv4://127.0.0.1:16388"); 47 | 48 | // 等待连接成功 49 | // 如果是拥有共同父节点的兄弟节点不能这么判定,因为在第一次发消息前是不会建立连接的 50 | for (int i = 0; i < 512; ++i) { 51 | if (node2->is_endpoint_available(node1->get_id()) && node1->is_endpoint_available(node2->get_id())) { 52 | break; 53 | } 54 | 55 | uv_run(conf.ev_loop, UV_RUN_ONCE); 56 | 57 | // 定期执行proc函数,用于处理内部定时器 58 | // 第一个参数是Unix时间戳(秒),第二个参数是微秒 59 | node1->proc(time(nullptr), 0); 60 | node2->proc(time(nullptr), 0); 61 | } 62 | 63 | // 设置接收到消息后的回调函数 64 | bool recved = false; 65 | node2->set_on_recv_handle([&recved](const atbus::node &n, const atbus::endpoint *ep, const atbus::connection *conn, 66 | const atbus::message &, const void *buffer, size_t len) { 67 | if (nullptr != ep && nullptr != conn) { 68 | std::cout << "atbus node 0x" << std::ios::hex << n.get_id() << " receive data from 0x" << std::ios::hex 69 | << ep->get_id() << "(connection: " << conn->get_address().address << "): "; 70 | } 71 | std::cout.write(reinterpret_cast(buffer), static_cast(len)); 72 | std::cout << std::endl; 73 | recved = true; 74 | return 0; 75 | }); 76 | 77 | // 发送数据 78 | std::string send_data = "hello world!"; 79 | node1->send_data(node2->get_id(), 0, send_data.data(), send_data.size()); 80 | 81 | // 等待发送完成 82 | while (!recved) { 83 | uv_run(conf.ev_loop, UV_RUN_ONCE); 84 | 85 | // 定期执行proc函数,用于处理内部定时器 86 | // 第一个参数是Unix时间戳(秒),第二个参数是微秒 87 | node1->proc(time(nullptr), 0); 88 | node2->proc(time(nullptr), 0); 89 | } 90 | 91 | // 析构时会自动关闭所持有的资源 92 | } 93 | 94 | // 关闭libuv事件分发器 95 | while (UV_EBUSY == uv_loop_close(&ev_loop)) { 96 | uv_run(&ev_loop, UV_RUN_ONCE); 97 | } 98 | #else 99 | std::cout << "lambda not supported, ignore this sample" << std::endl; 100 | #endif 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libatbus 2 | 3 | 用于搭建高性能、全异步(a)、树形结构(t)的BUS消息系统的跨平台框架库 4 | 5 | [![ci-badge]][ci-link] [![codecov badge]][codecov status] 6 | 7 | [ci-badge]: https://github.com/atframework/libatbus/actions/workflows/main.yml/badge.svg "Github action build status" 8 | [ci-link]: https://github.com/atframework/libatbus/actions/workflows/main.yml "Github action build status" 9 | [codecov badge]: https://codecov.io/gh/atframework/libatbus/branch/main/graph/badge.svg 10 | [codecov status]: https://codecov.io/gh/atframework/libatbus 11 | 12 | ## CI Job Matrix 13 | 14 | | Target System | Toolchain | Note | 15 | | ------------- | ------------------ | --------------------- | 16 | | Linux | GCC | 17 | | Linux | Clang | With libc++ | 18 | | MinGW64 | GCC | Dynamic linking | 19 | | Windows | Visual Studio 2022 | Static linking | 20 | | Windows | Visual Studio 2022 | Dynamic linking | 21 | | macOS | AppleClang | With libc++ | 22 | 23 | ## 依赖 24 | 25 | + 支持c++17的编译器 26 | > + GCC: 7.1 及以上 27 | > + Clang: 7 及以上 28 | > + VC: VS2022 及以上 29 | 30 | + [cmake](https://cmake.org/download/) 3.24.0 以上 31 | 32 | ## 设计初衷和要点 33 | 34 | 1. **[扩展性]** 根据很多业务的需要,预留足够长的ID段(64位),用以给不同的ID段做不同类型的业务区分。 35 | > 现有很多框架都是32位(比如腾讯的tbus和云风的[skynet](https://github.com/cloudwu/skynet)),在服务类型比较多的时候必须小心设计服务ID,以免冲突。 36 | > 37 | > 当然也有考虑到以后可能会扩展为带Hash的字符串,所以在编译选项上做了预留。但是目前还是uint64_t 38 | 39 | 2. **[高性能]** 同物理机之间可以直接使用共享内存通信,大幅提高消息分发的性能。跨物理机之间会使用tcp通信。并且这个通信方式的选择是完全自动并透明的(尽可能选择更快的方式发送消息),业务层完全不需要关心。 40 | 3. **[跨平台]** 拥有不同习惯的Developer可以使用自己熟悉的工具,提高开发速度 41 | 4. **[动态路由]** 父子节点间会至少保持一条连接,自动断线重连。同父的兄弟节点之间完全按需建立连接。并且这个过程都是自动完成的,不需要提前初始化。 42 | > 同样,新增节点和移除节点也不需要做什么特别的初始化操作。不会影响到已经在线上的服务。 43 | 44 | 5. **[低消耗]** 采用无锁队列,提高CPU性能。(共享)内存通道支持多端写入,一端读取,减少内存浪费。 45 | > 如果N个节点两两互联,每个节点可以只拥有一个(共享)内存通道。即总共只有N个通道,内存消耗为N*每个通道内存占用 46 | > 47 | > 一些其他的系统(比如tbus和我们目前的服务器框架)如果N个节点两两互联,每两个节点之间都要创建(共享)内存通道。即总共只有N*N个通道,内存消耗为N*N*每个通道内存占用。非常浪费 48 | 49 | 6. **[简化设计]** 根据一些实际的项目情况,把父子节点间的关系限定为Bus ID的后N位有包含关系,类似路由器的路由表的策略。 50 | > 比如 0x12345678 可以控制的子节点有16位(0x12345678/16),那么0x12340000-0x1234FFFF都可以注册为它的子节点。 51 | > 52 | > 如同IP协议中 192.168.1.1/24 路由表能管理 192.168.1.0-192.168.1.255 一样。当然这里的24指前24位,而前面提到的16指后16位。 53 | > 54 | > 这么简化以后很多节点关系维护和通信都能简单很多并且能提高性能。 55 | 56 | ## 环境准备和构建流程 57 | 58 | 使用cmake标准构建方式,默认的编译目标为Debug版本,详见 [使用(编译)流程](docs/Build.md) 59 | 60 | **注意: 默认的编译选项是Debug模式,压测和正式环境请使用 cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo 源码目录 [其他选项] 编译(相当于gcc -O2 -g -ggdb -DNDEBUG -Wall -Werror或MSVC /O2)** 61 | 62 | **注意: Windows下私有共享内存不允许跨进程共享,公有共享内存必须有管理员权限。如果Windows下出现初始化共享内存错误请使用管理员权限运行。** 63 | 64 | ## 使用示例 65 | 66 | 简要的使用示例见 [使用示例](docs/Usage.md) 67 | 68 | 更加详细的请参考单元测试和[tools](tools)目录内的代码 69 | 70 | ## Benchmark 71 | 72 | 压力测试和对比见[docs/Benchmark.md](docs/Benchmark.md) 73 | 74 | ## 支持工具 75 | 76 | Linux下 GCC编译安装脚本(支持离线编译安装): 77 | 78 | 1. [GCC](https://github.com/owent-utils/bash-shell/tree/master/GCC%20Installer) 79 | 2. [LLVM & Clang](https://github.com/owent-utils/bash-shell/tree/master/LLVM%26Clang%20Installer) 80 | 81 | ## LICENSE 82 | 83 | + libatbus 采用[MIT License](LICENSE) 84 | + Flatbuffers 采用[Apache License, Version 2.0](LICENSE-Apache.txt) 85 | + libuv 采用[Node's license协议](NODE_S_LICENSE)(类似MIT License) 86 | -------------------------------------------------------------------------------- /include/detail/libatbus_protocol.fbs: -------------------------------------------------------------------------------- 1 | 2 | namespace atbus.protocol; 3 | 4 | // --gen-mutable --gen-name-strings --no-includes --natural-utf8 --allow-non-utf8 5 | 6 | enum ATBUS_PROTOCOL_CONST : byte { 7 | ATBUS_PROTOCOL_VERSION = 2, 8 | } 9 | 10 | enum ATBUS_PROTOCOL_COMPACT : byte { 11 | ATBUS_PROTOCOL_MINIMAL_VERSION = 2, // minimal protocol version supported 12 | } 13 | 14 | enum ATBUS_FORWARD_DATA_FLAG_TYPE : uint32 (bit_flags) { 15 | REQUIRE_RSP = 1 16 | } 17 | 18 | table custom_command_argv { 19 | arg: [ubyte] (id: 0); 20 | } 21 | 22 | table custom_command_data { 23 | from : uint64 (id: 0); 24 | commands : [custom_command_argv] (id: 1); 25 | access_keys : [access_data] (id: 2); // ID: 2 26 | } 27 | 28 | table forward_data { 29 | from : uint64 (id: 0); 30 | to : uint64 (id: 1); 31 | router : [uint64] (id: 2); 32 | content : [ubyte] (id: 3); 33 | flags : uint32 (id: 4); 34 | } 35 | 36 | table access_data { 37 | token_salt : uint32 (id: 0); 38 | token_hash1: uint64 (id: 1); 39 | token_hash2: uint64 (id: 2); 40 | } 41 | 42 | table channel_data { 43 | address: string (id: 0); // ID: 0 44 | } 45 | 46 | table node_data { 47 | bus_id : uint64 (id: 0); // ID: 0 48 | overwrite : bool (id: 1); // ID: 1 49 | flags : bool (id: 2); // ID: 2 50 | children_id_mask : uint64 (id: 3); // ID: 3 51 | children_id_prefix : uint64 (id: 4); // ID: 4(0 for using bus_id) 52 | children : [node_data] (id: 5); // ID: 5 53 | } 54 | 55 | table node_tree { 56 | nodes: [node_data] (id: 0); 57 | } 58 | 59 | table ping_data { 60 | time_point: int64 (id: 0); // ID: 0 61 | } 62 | 63 | table register_data { 64 | bus_id : uint64 (id: 0); // ID: 0 65 | pid : int32 (id: 1); // ID: 1 66 | hostname : string (id: 2); // ID: 2 67 | channels : [channel_data] (id: 3); // ID: 3 68 | children_id_mask : uint32 (id: 4); // ID: 4 69 | children_id_prefix : uint64 (id: 5); // ID: 5(0 for using bus_id) 70 | flags : uint32 (id: 6); // ID: 6 71 | access_keys : [access_data] (id: 7); // ID: 7 72 | } 73 | 74 | table connection_data { 75 | address: channel_data (id: 0); // ID: 0 76 | } 77 | 78 | union message_body { 79 | // invalid_body : string (id: 0); 80 | custom_command_req : custom_command_data = 1, 81 | custom_command_rsp : custom_command_data = 2, 82 | data_transform_req : forward_data = 3, 83 | data_transform_rsp : forward_data = 4, 84 | node_sync_req : node_tree = 5, 85 | node_sync_rsp : node_tree = 6, 86 | node_register_req : register_data = 7, 87 | node_register_rsp : register_data = 8, 88 | node_connect_sync : connection_data = 10, 89 | node_ping_req : ping_data = 11, 90 | node_pong_rsp : ping_data = 12, 91 | } 92 | 93 | table message_head { 94 | version : int32 (id: 0); 95 | type : int32 (id: 1); 96 | ret : int32 (id: 2); 97 | sequence : uint64 (id: 3); 98 | source_bus_id : uint64 (id: 4); 99 | } 100 | 101 | table msg { 102 | head: message_head (id: 0); 103 | body: message_body (id: 2); // id: 1 is implicitly added for body case by flatc 104 | } 105 | 106 | root_type msg; 107 | -------------------------------------------------------------------------------- /test/case/atbus_node_setup_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "atbus_test_utils.h" 20 | #include "frame/test_macros.h" 21 | 22 | #include 23 | 24 | #ifdef CRYPTO_CIPHER_ENABLED 25 | CASE_TEST_EVENT_ON_START(unit_test_event_on_start_setup_openssl) { 26 | atfw::util::crypto::cipher::init_global_algorithm(); 27 | } 28 | 29 | CASE_TEST_EVENT_ON_EXIT(unit_test_event_on_exit_close_openssl) { 30 | atfw::util::crypto::cipher::cleanup_global_algorithm(); 31 | } 32 | #endif 33 | 34 | CASE_TEST_EVENT_ON_START(unit_test_event_on_start_ignore_sigpipe) { 35 | #ifndef WIN32 36 | signal(SIGPIPE, SIG_IGN); // close stdin, stdout or stderr 37 | signal(SIGTSTP, SIG_IGN); // close tty 38 | signal(SIGTTIN, SIG_IGN); // tty input 39 | signal(SIGTTOU, SIG_IGN); // tty output 40 | #endif 41 | } 42 | 43 | CASE_TEST_EVENT_ON_EXIT(unit_test_event_on_exit_shutdown_protobuf) { 44 | ATBUS_MACRO_PROTOBUF_NAMESPACE_ID::ShutdownProtobufLibrary(); 45 | } 46 | 47 | CASE_TEST_EVENT_ON_EXIT(unit_test_event_on_exit_close_libuv) { 48 | int finish_event = 2048; 49 | while (0 != uv_loop_alive(uv_default_loop()) && finish_event-- > 0) { 50 | uv_run(uv_default_loop(), UV_RUN_NOWAIT); 51 | } 52 | uv_loop_close(uv_default_loop()); 53 | } 54 | 55 | #ifndef _WIN32 56 | static int node_msg_test_on_log(const atfw::util::log::log_formatter::caller_info_t &, const char *content, 57 | size_t content_size) { 58 | gsl::string_view log_data{content, content_size}; 59 | CASE_MSG_INFO() << log_data << std::endl; 60 | return 0; 61 | } 62 | static void setup_atbus_node_logger(atbus::node &n) { 63 | n.get_logger()->set_level(atfw::util::log::log_formatter::level_t::LOG_LW_DEBUG); 64 | n.get_logger()->clear_sinks(); 65 | n.get_logger()->add_sink(node_msg_test_on_log); 66 | } 67 | 68 | // 主动reset流程测试 69 | // 正常首发数据测试 70 | CASE_TEST(atbus_node_setup, override_listen_path) { 71 | atbus::node::conf_t conf; 72 | atbus::node::default_conf(&conf); 73 | conf.overwrite_listen_path = false; 74 | 75 | uv_loop_t ev_loop; 76 | uv_loop_init(&ev_loop); 77 | 78 | conf.ev_loop = &ev_loop; 79 | 80 | { 81 | atbus::node::ptr_t node1 = atbus::node::create(); 82 | atbus::node::ptr_t node2 = atbus::node::create(); 83 | atbus::node::ptr_t node3 = atbus::node::create(); 84 | setup_atbus_node_logger(*node1); 85 | setup_atbus_node_logger(*node2); 86 | setup_atbus_node_logger(*node3); 87 | 88 | CASE_EXPECT_EQ(EN_ATBUS_ERR_NOT_INITED, node1->start()); 89 | CASE_EXPECT_EQ(EN_ATBUS_ERR_NOT_INITED, node2->start()); 90 | CASE_EXPECT_EQ(EN_ATBUS_ERR_NOT_INITED, node3->start()); 91 | 92 | node1->init(0x12345678, &conf); 93 | node2->init(0x12356789, &conf); 94 | conf.overwrite_listen_path = true; 95 | node3->init(0x12367890, &conf); 96 | 97 | CASE_EXPECT_EQ(EN_ATBUS_ERR_SUCCESS, node1->listen("unix:///tmp/atbus-unit-test-overwrite-unix.sock")); 98 | CASE_EXPECT_EQ(EN_ATBUS_ERR_PIPE_LOCK_PATH_FAILED, 99 | node2->listen("unix:///tmp/atbus-unit-test-overwrite-unix.sock")); 100 | CASE_EXPECT_EQ(EN_ATBUS_ERR_SUCCESS, node3->listen("unix:///tmp/atbus-unit-test-overwrite-unix.sock")); 101 | } 102 | 103 | unit_test_setup_exit(&ev_loop); 104 | } 105 | #endif 106 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.configureSettings": { 3 | "BUILD_SHARED_LIBS": "YES", 4 | "PROJECT_ENABLE_UNITTEST": "YES", 5 | "PROJECT_ENABLE_SAMPLE": "YES", 6 | "PROJECT_ENABLE_TOOLS": "YES", 7 | "ATBUS_MACRO_ABORT_ON_PROTECTED_ERROR": "YES" 8 | // "CMAKE_CXX_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", 9 | // "CMAKE_C_FLAGS": "-fsanitize=address -fno-omit-frame-pointer", 10 | // "CMAKE_EXE_LINKER_FLAGS=": "-static-libsan" 11 | }, 12 | "cmake.configureEnvironment": { 13 | "CPRINTF_MODE": "none" 14 | }, 15 | "cmake.buildEnvironment": { 16 | "CPRINTF_MODE": "none" 17 | }, 18 | "[cpp]": { 19 | "editor.formatOnSave": false 20 | }, 21 | "files.associations": { 22 | "*.ejs": "html", 23 | "*.h.in": "cpp", 24 | "*.h.cpp": "cpp", 25 | "*.ps1.in": "powershell", 26 | "*.sh.in": "shellscript", 27 | "*.bat.in": "bat", 28 | "array": "cpp", 29 | "atomic": "cpp", 30 | "*.tcc": "cpp", 31 | "bitset": "cpp", 32 | "cctype": "cpp", 33 | "chrono": "cpp", 34 | "clocale": "cpp", 35 | "cmath": "cpp", 36 | "cstddef": "cpp", 37 | "cstdint": "cpp", 38 | "cstdio": "cpp", 39 | "cstdlib": "cpp", 40 | "cstring": "cpp", 41 | "ctime": "cpp", 42 | "cwchar": "cpp", 43 | "cwctype": "cpp", 44 | "deque": "cpp", 45 | "list": "cpp", 46 | "unordered_map": "cpp", 47 | "unordered_set": "cpp", 48 | "vector": "cpp", 49 | "exception": "cpp", 50 | "string_view": "cpp", 51 | "fstream": "cpp", 52 | "functional": "cpp", 53 | "initializer_list": "cpp", 54 | "iomanip": "cpp", 55 | "iosfwd": "cpp", 56 | "iostream": "cpp", 57 | "istream": "cpp", 58 | "limits": "cpp", 59 | "memory": "cpp", 60 | "new": "cpp", 61 | "numeric": "cpp", 62 | "optional": "cpp", 63 | "ostream": "cpp", 64 | "ratio": "cpp", 65 | "sstream": "cpp", 66 | "stdexcept": "cpp", 67 | "streambuf": "cpp", 68 | "system_error": "cpp", 69 | "thread": "cpp", 70 | "cinttypes": "cpp", 71 | "tuple": "cpp", 72 | "type_traits": "cpp", 73 | "utility": "cpp", 74 | "typeinfo": "cpp", 75 | "__config": "cpp", 76 | "__nullptr": "cpp", 77 | "algorithm": "cpp", 78 | "cstdarg": "cpp", 79 | "condition_variable": "cpp", 80 | "iterator": "cpp", 81 | "map": "cpp", 82 | "memory_resource": "cpp", 83 | "regex": "cpp", 84 | "set": "cpp", 85 | "string": "cpp", 86 | "mutex": "cpp", 87 | "__bit_reference": "cpp", 88 | "__debug": "cpp", 89 | "__functional_base": "cpp", 90 | "__hash_table": "cpp", 91 | "__locale": "cpp", 92 | "__mutex_base": "cpp", 93 | "__split_buffer": "cpp", 94 | "__string": "cpp", 95 | "__threading_support": "cpp", 96 | "__tree": "cpp", 97 | "__tuple": "cpp", 98 | "ios": "cpp", 99 | "locale": "cpp", 100 | "queue": "cpp", 101 | "stack": "cpp", 102 | "xstring": "cpp", 103 | "xutility": "cpp", 104 | "*.inc": "cpp", 105 | "xmemory": "cpp", 106 | "random": "cpp", 107 | "concepts": "cpp", 108 | "forward_list": "cpp", 109 | "xfacet": "cpp", 110 | "xhash": "cpp", 111 | "xiosbase": "cpp", 112 | "xlocale": "cpp", 113 | "xlocbuf": "cpp", 114 | "xlocinfo": "cpp", 115 | "xlocmes": "cpp", 116 | "xlocmon": "cpp", 117 | "xlocnum": "cpp", 118 | "xloctime": "cpp", 119 | "xstddef": "cpp", 120 | "xtr1common": "cpp", 121 | "xtree": "cpp", 122 | "compare": "cpp" 123 | }, 124 | "C_Cpp.default.configurationProvider": "vector-of-bool.cmake-tools" 125 | } -------------------------------------------------------------------------------- /include/detail/libatbus_config.h.in: -------------------------------------------------------------------------------- 1 | // Copyright atframework 2 | // This file is generated by cmake, please don't edit it 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #cmakedefine LIBATBUS_VERSION_MAJOR @LIBATBUS_VERSION_MAJOR@ 11 | #ifndef LIBATBUS_VERSION_MAJOR 12 | #define LIBATBUS_VERSION_MAJOR 0 13 | #endif 14 | #cmakedefine LIBATBUS_VERSION_MINOR @LIBATBUS_VERSION_MINOR@ 15 | #ifndef LIBATBUS_VERSION_MINOR 16 | #define LIBATBUS_VERSION_MINOR 0 17 | #endif 18 | #cmakedefine LIBATBUS_VERSION_PATCH @LIBATBUS_VERSION_PATCH@ 19 | #ifndef LIBATBUS_VERSION_PATCH 20 | #define LIBATBUS_VERSION_PATCH 0 21 | #endif 22 | #cmakedefine LIBATBUS_VERSION "@LIBATBUS_VERSION@" 23 | 24 | #cmakedefine ATBUS_MACRO_BUSID_TYPE @ATBUS_MACRO_BUSID_TYPE@ 25 | #cmakedefine ATBUS_MACRO_MESSAGE_LIMIT @ATBUS_MACRO_MESSAGE_LIMIT@ 26 | #cmakedefine ATBUS_MACRO_MAX_FRAME_HEADER @ATBUS_MACRO_MAX_FRAME_HEADER@ 27 | #cmakedefine ATBUS_MACRO_CONNECTION_CONFIRM_TIMEOUT @ATBUS_MACRO_CONNECTION_CONFIRM_TIMEOUT@ 28 | #cmakedefine ATBUS_MACRO_CONNECTION_BACKLOG @ATBUS_MACRO_CONNECTION_BACKLOG@ 29 | #cmakedefine ATBUS_MACRO_DATA_SMALL_SIZE @ATBUS_MACRO_DATA_SMALL_SIZE@ 30 | 31 | #cmakedefine ATBUS_MACRO_DATA_NODE_SIZE @ATBUS_MACRO_DATA_NODE_SIZE@ 32 | #cmakedefine ATBUS_MACRO_DATA_ALIGN_SIZE @ATBUS_MACRO_DATA_ALIGN_SIZE@ 33 | #cmakedefine ATBUS_MACRO_DATA_MAX_PROTECT_SIZE @ATBUS_MACRO_DATA_MAX_PROTECT_SIZE@ 34 | 35 | #cmakedefine ATBUS_MACRO_HUGETLB_SIZE @ATBUS_MACRO_HUGETLB_SIZE@ 36 | 37 | #cmakedefine ATBUS_MACRO_SHM_MEM_CHANNEL_LENGTH @ATBUS_MACRO_SHM_MEM_CHANNEL_LENGTH@ 38 | #cmakedefine ATBUS_MACRO_IOS_SEND_BUFFER_LENGTH @ATBUS_MACRO_IOS_SEND_BUFFER_LENGTH@ 39 | #cmakedefine01 ATBUS_MACRO_ABORT_ON_PROTECTED_ERROR 40 | 41 | 42 | #include 43 | #include 44 | 45 | #if defined(__cplusplus) && __cplusplus >= 201103L 46 | #define ATBUS_MACRO_ENABLE_STATIC_ASSERT 1 47 | #elif defined(_MSC_VER) && _MSC_VER >= 1600 48 | #define ATBUS_MACRO_ENABLE_STATIC_ASSERT 1 49 | #endif 50 | 51 | #cmakedefine ATBUS_MACRO_WITH_UNIX_SOCK @ATBUS_MACRO_WITH_UNIX_SOCK@ 52 | 53 | #ifndef ATBUS_MACRO_API 54 | # if defined(ATBUS_MACRO_API_NATIVE) && ATBUS_MACRO_API_NATIVE 55 | # if defined(ATBUS_MACRO_API_DLL) && ATBUS_MACRO_API_DLL 56 | # define ATBUS_MACRO_API ATFW_UTIL_SYMBOL_EXPORT 57 | # else 58 | # define ATBUS_MACRO_API ATFW_UTIL_SYMBOL_VISIBLE 59 | # endif 60 | # else 61 | # if defined(ATBUS_MACRO_API_DLL) && ATBUS_MACRO_API_DLL 62 | # define ATBUS_MACRO_API ATFW_UTIL_SYMBOL_IMPORT 63 | # else 64 | # define ATBUS_MACRO_API ATFW_UTIL_SYMBOL_VISIBLE 65 | # endif 66 | # endif 67 | #endif 68 | #define ATBUS_MACRO_API_HEAD_ONLY ATFW_UTIL_SYMBOL_VISIBLE 69 | #define ATBUS_MACRO_API_C(R) extern "C" ATBUS_MACRO_API R __cdecl 70 | 71 | #ifndef ATBUS_MACRO_PROTOCOL_API 72 | # if defined(ATBUS_MACRO_API_NATIVE) && ATBUS_MACRO_API_NATIVE 73 | # if defined(ATBUS_MACRO_API_DLL) && ATBUS_MACRO_API_DLL 74 | # define ATBUS_MACRO_PROTOCOL_API ATFW_UTIL_SYMBOL_IMPORT 75 | # else 76 | # define ATBUS_MACRO_PROTOCOL_API ATFW_UTIL_SYMBOL_VISIBLE 77 | # endif 78 | # else 79 | # define ATBUS_MACRO_PROTOCOL_API ATBUS_MACRO_API 80 | # endif 81 | #endif 82 | 83 | #define ATBUS_MACRO_NAMESPACE_BEGIN namespace atframework { namespace atbus { inline namespace @ATFRAMEWORK_ATBUS_ABI_TAG@ { 84 | #define ATBUS_MACRO_NAMESPACE_END } } } 85 | #define ATBUS_MACRO_NAMESPACE_ID atframework::atbus::@ATFRAMEWORK_ATBUS_ABI_TAG@ 86 | #define ATBUS_MACRO_NAMESPACE "atframework::atbus::@ATFRAMEWORK_ATBUS_ABI_TAG@" 87 | 88 | namespace atframework { 89 | namespace atbus {} 90 | } // namespace atframework 91 | 92 | namespace atbus = atframework::atbus; 93 | 94 | #ifndef ATFRAMEWORK_NAMESPACE_SHORT_ALIAS 95 | # define ATFRAMEWORK_NAMESPACE_SHORT_ALIAS 96 | namespace atfw = atframework; 97 | #endif 98 | 99 | -------------------------------------------------------------------------------- /test/case/libatbus_error_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "frame/test_macros.h" 7 | 8 | namespace { 9 | 10 | template 11 | static std::basic_string unit_test_ascii_to_basic_string(const char *s) { 12 | std::basic_string out; 13 | if (nullptr == s) { 14 | return out; 15 | } 16 | for (; '\0' != *s; ++s) { 17 | out.push_back(static_cast(*s)); 18 | } 19 | return out; 20 | } 21 | 22 | } // namespace 23 | 24 | CASE_TEST(libatbus_error, strerror_known_success) { 25 | const std::string &msg = libatbus_strerror(EN_ATBUS_ERR_SUCCESS); 26 | CASE_EXPECT_EQ(std::string("EN_ATBUS_ERR_SUCCESS(0): success"), msg); 27 | 28 | // Known errors are cached globally, repeated lookups should return the same object reference. 29 | const std::string &msg2 = libatbus_strerror(EN_ATBUS_ERR_SUCCESS); 30 | CASE_EXPECT_EQ(&msg, &msg2); 31 | } 32 | 33 | CASE_TEST(libatbus_error, strerror_known_samples) { 34 | CASE_EXPECT_EQ(std::string("EN_ATBUS_ERR_PARAMS(-1): ATBUS parameter error"), libatbus_strerror(EN_ATBUS_ERR_PARAMS)); 35 | CASE_EXPECT_EQ(std::string("EN_ATBUS_ERR_NOT_READY(-607): not ready"), libatbus_strerror(EN_ATBUS_ERR_NOT_READY)); 36 | CASE_EXPECT_EQ(std::string("EN_ATBUS_ERR_PIPE_ADDR_TOO_LONG(-504): pipe address too long"), 37 | libatbus_strerror(EN_ATBUS_ERR_PIPE_ADDR_TOO_LONG)); 38 | } 39 | 40 | CASE_TEST(libatbus_error, strerror_unknown_thread_local_cache) { 41 | #if defined(__GNUC__) && !defined(__clang__) && !defined(__apple_build_version__) 42 | # pragma GCC diagnostic push 43 | # pragma GCC diagnostic ignored "-Wconversion" 44 | #elif defined(__clang__) || defined(__apple_build_version__) 45 | # pragma clang diagnostic push 46 | # pragma clang diagnostic ignored "-Wconversion" 47 | #endif 48 | const ATBUS_ERROR_TYPE code1 = static_cast(12345); 49 | const ATBUS_ERROR_TYPE code2 = static_cast(12346); 50 | #if defined(__GNUC__) && !defined(__clang__) && !defined(__apple_build_version__) 51 | # pragma GCC diagnostic pop 52 | #elif defined(__clang__) || defined(__apple_build_version__) 53 | # pragma clang diagnostic pop 54 | #endif 55 | 56 | const std::string &msg1 = libatbus_strerror(code1); 57 | CASE_EXPECT_EQ(std::string("ATBUS_ERROR_TYPE(12345): unknown"), msg1); 58 | 59 | // Unknown errors are cached per-thread. The object address should be stable in the same thread. 60 | const std::string &msg2 = libatbus_strerror(code2); 61 | CASE_EXPECT_EQ(&msg1, &msg2); 62 | CASE_EXPECT_EQ(std::string("ATBUS_ERROR_TYPE(12346): unknown"), msg2); 63 | } 64 | 65 | CASE_TEST(libatbus_error, wstrerror_known_and_unknown) { 66 | const std::wstring &known = libatbus_wstrerror(EN_ATBUS_ERR_SUCCESS); 67 | CASE_EXPECT_TRUE(known == std::wstring(L"EN_ATBUS_ERR_SUCCESS(0): success")); 68 | 69 | const std::wstring &unknown = libatbus_wstrerror(static_cast(42)); 70 | CASE_EXPECT_TRUE(unknown == std::wstring(L"ATBUS_ERROR_TYPE(42): unknown")); 71 | } 72 | 73 | #ifdef __cpp_unicode_characters 74 | CASE_TEST(libatbus_error, u16_u32_strerror) { 75 | { 76 | const std::u16string &msg = libatbus_u16strerror(EN_ATBUS_ERR_SUCCESS); 77 | CASE_EXPECT_TRUE(msg == unit_test_ascii_to_basic_string("EN_ATBUS_ERR_SUCCESS(0): success")); 78 | } 79 | 80 | { 81 | const std::u32string &msg = libatbus_u32strerror(static_cast(7)); 82 | CASE_EXPECT_TRUE(msg == unit_test_ascii_to_basic_string("ATBUS_ERROR_TYPE(7): unknown")); 83 | } 84 | } 85 | #endif 86 | 87 | #if defined(__cpp_char8_t) && (__cpp_char8_t >= 201811L) && (!defined(_HAS_CHAR8_T) || _HAS_CHAR8_T) 88 | CASE_TEST(libatbus_error, u8_strerror) { 89 | const std::basic_string &msg = libatbus_u8strerror(EN_ATBUS_ERR_SUCCESS); 90 | CASE_EXPECT_TRUE(msg == unit_test_ascii_to_basic_string("EN_ATBUS_ERR_SUCCESS(0): success")); 91 | } 92 | #endif 93 | -------------------------------------------------------------------------------- /docs/Usage.md: -------------------------------------------------------------------------------- 1 | 使用示例 2 | ====== 3 | 4 | + 配置里的 subnets 决定了子节点的BUS ID范围,规则类似路由器。 5 | > 比如 mask_bits=8,id_prefix=0x12345678, 则0x12345600-0x123456FF都是它的子节点,0x12345778则是它的兄弟节点 6 | 7 | + 父子节点之间会自动断线重连 8 | + 拥有相同父节点的兄弟节点之间会自动按需断线重连 9 | + 没有相同父节点的兄弟节点之间**不会自动断线重连**。建议使用心跳或其他机制重新执行***connect***api来维持在线信息 10 | 11 | 12 | 监听和连接的地址目前支持: 13 | 14 | 1. ipv4://IP地址:端口 15 | 2. ipv6://IP地址:端口 16 | 3. unix://Unix Socket地址(仅Unix like系统下有效) 17 | 4. dns://域名:端口 18 | 5. shm://共享内存Key(整数,仅本机通信有效,支持16进制或10进制表示,比如 shm://0x1234FF00 或 shm://305463040) 19 | 6. mem://内存地址(整数,仅本机通信有效,支持16进制或10进制表示,内存通道必须先分配好。比如 mem://0x1234FF00 或 mem://305463040) 20 | 21 | 最简单的完整代码流程如下: 22 | ```cpp 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | #include 32 | #include 33 | 34 | int main() { 35 | // 初始化默认配置 36 | atbus::node::conf_t conf; 37 | atbus::node::default_conf(&conf); 38 | 39 | // 子域的范围设为16(后16位都是子节点), id_prefix=0 等于使用endpoint自己的ID 40 | conf.subnets.push_back(atbus::endpoint_subnet_conf(0, 16)); 41 | 42 | // 初始化libuv事件分发器 43 | uv_loop_t ev_loop; 44 | uv_loop_init(&ev_loop); 45 | 46 | // 指定事件分发器 47 | conf.ev_loop = &ev_loop; 48 | 49 | { 50 | // 创建两个通信节点 51 | atbus::node::ptr_t node1 = atbus::node::create(); 52 | atbus::node::ptr_t node2 = atbus::node::create(); 53 | 54 | // 初始化 55 | node1->init(0x12345678, &conf); // BUS ID=0x12345678, 0x1234XXXX 都是子节点 56 | node2->init(0x12356789, &conf); // BUS ID=0x12356789, 0x1235XXXX 都是子节点 57 | // 所以这两个都是兄弟节点 58 | 59 | // 各自监听地址 60 | node1->listen("ipv4://127.0.0.1:16387"); 61 | node2->listen("ipv4://127.0.0.1:16388"); 62 | 63 | // 启动初始化 64 | node1->start(); 65 | node2->start(); 66 | 67 | // 启动连接 68 | node1->connect("ipv4://127.0.0.1:16388"); 69 | 70 | // 等待连接成功 71 | // 如果是拥有共同父节点的兄弟节点不能这么判定,因为在第一次发消息前是不会建立连接的 72 | for (int i = 0; i < 512; ++i) { 73 | if (node2->is_endpoint_available(node1->get_id()) && node1->is_endpoint_available(node2->get_id())) { 74 | break; 75 | } 76 | 77 | uv_run(conf.ev_loop, UV_RUN_ONCE); 78 | 79 | // 定期执行proc函数,用于处理内部定时器 80 | // 第一个参数是Unix时间戳(秒),第二个参数是微秒 81 | node1->proc(time(nullptr), 0); 82 | node2->proc(time(nullptr), 0); 83 | } 84 | 85 | // 设置接收到消息后的回调函数 86 | bool recved = false; 87 | node2->set_on_recv_handle([&recved](const atbus::node &n, const atbus::endpoint *ep, const atbus::connection *conn, 88 | const atbus::msg_t &m, const void *buffer, size_t len) { 89 | if (nullptr != ep && nullptr != conn) { 90 | std::cout << "atbus node 0x" << std::ios::hex << n.get_id() << " receive data from 0x" << std::ios::hex << ep->get_id() 91 | << "(connection: " << conn->get_address().address << "): "; 92 | } 93 | std::cout.write(reinterpret_cast(buffer), len); 94 | std::cout << std::endl; 95 | recved = true; 96 | return 0; 97 | }); 98 | 99 | 100 | // 发送数据 101 | std::string send_data = "hello world!"; 102 | node1->send_data(node2->get_id(), 0, send_data.data(), send_data.size()); 103 | 104 | // 等待发送完成 105 | while (!recved) { 106 | uv_run(conf.ev_loop, UV_RUN_ONCE); 107 | 108 | // 定期执行proc函数,用于处理内部定时器 109 | // 第一个参数是Unix时间戳(秒),第二个参数是微秒 110 | node1->proc(time(nullptr), 0); 111 | node2->proc(time(nullptr), 0); 112 | } 113 | 114 | // 析构时会自动关闭所持有的资源 115 | } 116 | 117 | // 关闭libuv事件分发器 118 | while (UV_EBUSY == uv_loop_close(&ev_loop)) { 119 | uv_run(&ev_loop, UV_RUN_ONCE); 120 | } 121 | return 0; 122 | } 123 | ``` 124 | 125 | 编译时请包含msgpack、libuv和libatbus的include目录,链接atbus、atframe_utils和libuv 126 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.24.0) 2 | 3 | if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.27.0") 4 | cmake_policy(SET CMP0144 NEW) 5 | endif() 6 | 7 | enable_testing() 8 | 9 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 10 | 11 | project( 12 | libatbus 13 | VERSION "3.0.0" 14 | HOMEPAGE_URL "https://github.com/atframework/libatbus" 15 | LANGUAGES C CXX) 16 | 17 | if(NOT DEFINED __COMPILER_OPTION_LOADED) 18 | if(MSVC) 19 | string(REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 20 | string(REGEX REPLACE "/GR-?" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 21 | else() 22 | string(REGEX REPLACE "-f(no-)?exceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 23 | string(REGEX REPLACE "-f(no-)?rtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 24 | endif() 25 | endif() 26 | 27 | set(LIBATBUS_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") 28 | set(LIBATBUS_VERSION_MINOR "${PROJECT_VERSION_MINOR}") 29 | set(LIBATBUS_VERSION_PATCH "${PROJECT_VERSION_PATCH}") 30 | set(LIBATBUS_VERSION "${PROJECT_VERSION}") 31 | 32 | include("${CMAKE_CURRENT_LIST_DIR}/project/cmake/ProjectBuildOption.cmake") 33 | include("${CMAKE_CURRENT_LIST_DIR}/third_party/Repository.cmake") 34 | include("${CMAKE_CURRENT_LIST_DIR}/atframework/Repository.cmake") 35 | 36 | unset(PROJECT_LIBATBUS_PRIVATE_COMPILE_OPTIONS) 37 | 38 | # ###################################################################################################################### 39 | # 导入项目配置 导入所有 macro 定义 40 | include("${CMAKE_CURRENT_LIST_DIR}/include/include.macro.cmake") 41 | include("${CMAKE_CURRENT_LIST_DIR}/src/libatbus.macro.cmake") 42 | include("${CMAKE_CURRENT_LIST_DIR}/tools/tools.macro.cmake") 43 | 44 | # 导入工程项目 45 | set(PROJECT_LIBATBUS_PUBLIC_LINK_NAMES 46 | ${ATFRAMEWORK_ATFRAME_UTILS_LINK_NAME} ${ATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_LINK_NAME} 47 | ${ATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LIBUV_LINK_NAME}) 48 | 49 | if(COMPILER_STRICT_EXTRA_CFLAGS) 50 | list(APPEND PROJECT_LIBATBUS_PRIVATE_COMPILE_OPTIONS ${COMPILER_STRICT_EXTRA_CFLAGS}) 51 | endif() 52 | 53 | if(COMPILER_STRICT_CFLAGS) 54 | list(APPEND PROJECT_LIBATBUS_PRIVATE_COMPILE_OPTIONS ${COMPILER_STRICT_CFLAGS}) 55 | endif() 56 | 57 | if(COMPILER_STRICT_RECOMMEND_EXTRA_CFLAGS) 58 | list(APPEND PROJECT_LIBATBUS_PRIVATE_COMPILE_OPTIONS ${COMPILER_STRICT_RECOMMEND_EXTRA_CFLAGS}) 59 | endif() 60 | 61 | add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/src") 62 | 63 | if(PROJECT_ENABLE_SAMPLE) 64 | add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/sample") 65 | endif() 66 | 67 | if(PROJECT_ENABLE_UNITTEST OR BUILD_TESTING) 68 | atframework_atframe_utils_populate() 69 | include("${ATFRAMEWORK_ATFRAME_UTILS_REPO_DIR}/test/test.utils-macro.cmake") 70 | add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/test") 71 | endif() 72 | 73 | if(PROJECT_ENABLE_TOOLS) 74 | add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/tools") 75 | endif() 76 | 77 | # 生成文档和导入配置 78 | 79 | # Install configuration 80 | set(CMAKE_INSTALL_CMAKEDIR 81 | "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" 82 | CACHE STRING "Directory relative to CMAKE_INSTALL to install the cmake configuration files") 83 | 84 | include(CMakePackageConfigHelpers) 85 | set(INCLUDE_INSTALL_DIR include) 86 | 87 | file(MAKE_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}") 88 | 89 | configure_package_config_file( 90 | "${CMAKE_CURRENT_LIST_DIR}/libatbus-config.cmake.in" 91 | "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/libatbus-config.cmake" 92 | INSTALL_DESTINATION ${CMAKE_INSTALL_CMAKEDIR} 93 | PATH_VARS LIBATBUS_VERSION INCLUDE_INSTALL_DIR CMAKE_INSTALL_LIBDIR PROJECT_SOURCE_DIR 94 | NO_CHECK_REQUIRED_COMPONENTS_MACRO) 95 | 96 | write_basic_package_version_file( 97 | "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/libatbus-config-version.cmake" 98 | VERSION ${LIBATBUS_VERSION} 99 | COMPATIBILITY SameMajorVersion) 100 | 101 | install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/libatbus-config.cmake" 102 | "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/libatbus-config-version.cmake" 103 | DESTINATION ${CMAKE_INSTALL_CMAKEDIR}) 104 | 105 | export( 106 | EXPORT ${PROJECT_LIBATBUS_EXPORT_NAME} 107 | NAMESPACE atframework:: 108 | FILE "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/cmake/${PROJECT_NAME}/${PROJECT_LIBATBUS_EXPORT_NAME}.cmake") 109 | install( 110 | EXPORT ${PROJECT_LIBATBUS_EXPORT_NAME} 111 | NAMESPACE "atframework::" 112 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 113 | -------------------------------------------------------------------------------- /src/libatbus_protocol.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2025 atframework 2 | 3 | #include "libatbus_protocol.h" 4 | 5 | #include 6 | 7 | ATBUS_MACRO_NAMESPACE_BEGIN 8 | 9 | ATBUS_MACRO_API message::message(const ::google::protobuf::ArenaOptions& options) 10 | : arena_(std::make_unique<::google::protobuf::Arena>(options)), head_(nullptr), body_(nullptr) { 11 | // TODO(owent): 可以池化arena的初始block以减少内存分配和碎片 12 | } 13 | 14 | ATBUS_MACRO_API message::message(std::unique_ptr<::google::protobuf::Arena>&& input_arena) 15 | : arena_(std::move(input_arena)), head_(nullptr), body_(nullptr) {} 16 | 17 | ATBUS_MACRO_API message::message(message&& other) 18 | : arena_(std::move(other.arena_)), 19 | inplace_cache_(std::move(other.inplace_cache_)), 20 | head_(std::move(other.head_)), 21 | body_(std::move(other.body_)) {} 22 | 23 | ATBUS_MACRO_API message& message::operator=(message&& other) { 24 | if (this != &other) { 25 | arena_ = std::move(other.arena_); 26 | inplace_cache_ = std::move(other.inplace_cache_); 27 | head_ = std::move(other.head_); 28 | body_ = std::move(other.body_); 29 | } 30 | 31 | return *this; 32 | } 33 | 34 | ATBUS_MACRO_API message::~message() {} 35 | 36 | ATBUS_MACRO_API ::atframework::atbus::protocol::message_head& message::mutable_head() { 37 | if ATFW_UTIL_LIKELY_CONDITION (head_ != nullptr) { 38 | return *head_; 39 | } 40 | 41 | if (arena_ == nullptr) { 42 | if (!inplace_cache_) { 43 | inplace_cache_ = std::make_unique(); 44 | } 45 | 46 | return inplace_cache_->head; 47 | } 48 | 49 | #if defined(PROTOBUF_VERSION) && PROTOBUF_VERSION >= 5027000 50 | head_ = ::google::protobuf::Arena::Create<::atframework::atbus::protocol::message_head>(arena_.get()); 51 | #else 52 | head_ = ::google::protobuf::Arena::CreateMessage<::atframework::atbus::protocol::message_head>(arena_.get()); 53 | #endif 54 | return *head_; 55 | } 56 | 57 | ATBUS_MACRO_API ::atframework::atbus::protocol::message_body& message::mutable_body() { 58 | if ATFW_UTIL_LIKELY_CONDITION (body_ != nullptr) { 59 | return *body_; 60 | } 61 | 62 | if (arena_ == nullptr) { 63 | if (!inplace_cache_) { 64 | inplace_cache_ = gsl::make_unique(); 65 | } 66 | 67 | return inplace_cache_->body; 68 | } 69 | 70 | #if defined(PROTOBUF_VERSION) && PROTOBUF_VERSION >= 5027000 71 | body_ = ::google::protobuf::Arena::Create<::atframework::atbus::protocol::message_body>(arena_.get()); 72 | #else 73 | body_ = ::google::protobuf::Arena::CreateMessage<::atframework::atbus::protocol::message_body>(ararena_ena.get()); 74 | #endif 75 | return *body_; 76 | } 77 | 78 | ATBUS_MACRO_API ::atfw::util::nostd::nullable message::get_head() 79 | const noexcept { 80 | return head_; 81 | } 82 | 83 | ATBUS_MACRO_API ::atfw::util::nostd::nullable message::get_body() 84 | const noexcept { 85 | return body_; 86 | } 87 | 88 | ATBUS_MACRO_API const ::atframework::atbus::protocol::message_head& message::head() const noexcept { 89 | if (head_ == nullptr) { 90 | return ::atframework::atbus::protocol::message_head::default_instance(); 91 | } 92 | 93 | return *head_; 94 | } 95 | 96 | ATBUS_MACRO_API const ::atframework::atbus::protocol::message_body& message::body() const noexcept { 97 | if (body_ == nullptr) { 98 | return ::atframework::atbus::protocol::message_body::default_instance(); 99 | } 100 | 101 | return *body_; 102 | } 103 | 104 | ATBUS_MACRO_API std::string message::get_head_debug_string() const { 105 | if (head_ != nullptr) { 106 | return head_->DebugString(); 107 | } 108 | 109 | return {}; 110 | } 111 | 112 | ATBUS_MACRO_API std::string message::get_body_debug_string() const { 113 | if (body_ != nullptr) { 114 | return body_->DebugString(); 115 | } 116 | 117 | return {}; 118 | } 119 | 120 | ATBUS_MACRO_API message_body_type message::get_body_type() const noexcept { 121 | if (body_ == nullptr) { 122 | return message_body_type::MESSAGE_TYPE_NOT_SET; 123 | } 124 | 125 | return body_->message_type_case(); 126 | } 127 | 128 | ATBUS_MACRO_API std::string message::get_unpack_error_message() const noexcept { 129 | if (body_ != nullptr) { 130 | return body_->InitializationErrorString(); 131 | } 132 | 133 | if (head_ != nullptr) { 134 | return head_->InitializationErrorString(); 135 | } 136 | 137 | return {}; 138 | } 139 | 140 | ATBUS_MACRO_NAMESPACE_END 141 | -------------------------------------------------------------------------------- /.clang-tidy.light: -------------------------------------------------------------------------------- 1 | Checks: 2 | - 'clang-diagnostic-*' 3 | - 'clang-analyzer-*' 4 | - 'bugprone-*' 5 | - 'performance-*' 6 | - '-performance-unnecessary-copy-initialization' 7 | - '-performance-enum-size' 8 | - '-bugprone-misplaced-widening-cast' 9 | - '-bugprone-branch-clone' 10 | # 虽然按规范下划线开头的都是保留字,但是写通用库的时候常见使用私有的保留字来减少冲突 11 | - '-bugprone-reserved-identifier' 12 | # 过于常见的用法而忽略 13 | - '-bugprone-easily-swappable-parameters' # 参数容易被隐式转换而被调用者传错顺序 14 | - '-performance-move-const-arg' # 无意义的move可用于标记生命周期 15 | # 重复报告 16 | - '-bugprone-narrowing-conversions' # 和 clang-diagnostic-sign-conversion/clang-diagnostic-shorten-64-to-32 重复 17 | - '-bugprone-switch-missing-default-case' # 由 fallthrough 告警管理,某些设计模式通过宏实现,无法通过注释排除 18 | # 老代码大量使用,以后处理 19 | - '-bugprone-multi-level-implicit-pointer-conversion' 20 | - '-bugprone-macro-parentheses' 21 | WarningsAsErrors: '' 22 | HeaderFileExtensions: 23 | - '' 24 | - h 25 | - hh 26 | - hpp 27 | - hxx 28 | ImplementationFileExtensions: 29 | - c 30 | - cc 31 | - cpp 32 | - cxx 33 | HeaderFilterRegex: ".*" 34 | ExcludeHeaderFilterRegex: "\\.pb\\.h$" 35 | FormatStyle: none 36 | CheckOptions: 37 | cert-arr39-c.WarnOnSizeOfCompareToConstant: 'false' 38 | cert-arr39-c.WarnOnSizeOfConstant: 'false' 39 | cert-arr39-c.WarnOnSizeOfIntegerExpression: 'false' 40 | cert-arr39-c.WarnOnSizeOfPointer: 'false' 41 | cert-arr39-c.WarnOnSizeOfPointerToAggregate: 'false' 42 | cert-arr39-c.WarnOnSizeOfThis: 'false' 43 | cert-dcl16-c.NewSuffixes: 'L;LL;LU;LLU' 44 | cert-err33-c.AllowCastToVoid: 'true' 45 | cert-err33-c.CheckedFunctions: '^::aligned_alloc;^::asctime_s;^::at_quick_exit;^::atexit;^::bsearch;^::bsearch_s;^::btowc;^::c16rtomb;^::c32rtomb;^::calloc;^::clock;^::cnd_broadcast;^::cnd_init;^::cnd_signal;^::cnd_timedwait;^::cnd_wait;^::ctime_s;^::fclose;^::fflush;^::fgetc;^::fgetpos;^::fgets;^::fgetwc;^::fopen;^::fopen_s;^::fprintf;^::fprintf_s;^::fputc;^::fputs;^::fputwc;^::fputws;^::fread;^::freopen;^::freopen_s;^::fscanf;^::fscanf_s;^::fseek;^::fsetpos;^::ftell;^::fwprintf;^::fwprintf_s;^::fwrite;^::fwscanf;^::fwscanf_s;^::getc;^::getchar;^::getenv;^::getenv_s;^::gets_s;^::getwc;^::getwchar;^::gmtime;^::gmtime_s;^::localtime;^::localtime_s;^::malloc;^::mbrtoc16;^::mbrtoc32;^::mbsrtowcs;^::mbsrtowcs_s;^::mbstowcs;^::mbstowcs_s;^::memchr;^::mktime;^::mtx_init;^::mtx_lock;^::mtx_timedlock;^::mtx_trylock;^::mtx_unlock;^::printf_s;^::putc;^::putwc;^::raise;^::realloc;^::remove;^::rename;^::scanf;^::scanf_s;^::setlocale;^::setvbuf;^::signal;^::snprintf;^::snprintf_s;^::sprintf;^::sprintf_s;^::sscanf;^::sscanf_s;^::strchr;^::strerror_s;^::strftime;^::strpbrk;^::strrchr;^::strstr;^::strtod;^::strtof;^::strtoimax;^::strtok;^::strtok_s;^::strtol;^::strtold;^::strtoll;^::strtoul;^::strtoull;^::strtoumax;^::strxfrm;^::swprintf;^::swprintf_s;^::swscanf;^::swscanf_s;^::thrd_create;^::thrd_detach;^::thrd_join;^::thrd_sleep;^::time;^::timespec_get;^::tmpfile;^::tmpfile_s;^::tmpnam;^::tmpnam_s;^::tss_create;^::tss_get;^::tss_set;^::ungetc;^::ungetwc;^::vfprintf;^::vfprintf_s;^::vfscanf;^::vfscanf_s;^::vfwprintf;^::vfwprintf_s;^::vfwscanf;^::vfwscanf_s;^::vprintf_s;^::vscanf;^::vscanf_s;^::vsnprintf;^::vsnprintf_s;^::vsprintf;^::vsprintf_s;^::vsscanf;^::vsscanf_s;^::vswprintf;^::vswprintf_s;^::vswscanf;^::vswscanf_s;^::vwprintf_s;^::vwscanf;^::vwscanf_s;^::wcrtomb;^::wcschr;^::wcsftime;^::wcspbrk;^::wcsrchr;^::wcsrtombs;^::wcsrtombs_s;^::wcsstr;^::wcstod;^::wcstof;^::wcstoimax;^::wcstok;^::wcstok_s;^::wcstol;^::wcstold;^::wcstoll;^::wcstombs;^::wcstombs_s;^::wcstoul;^::wcstoull;^::wcstoumax;^::wcsxfrm;^::wctob;^::wctrans;^::wctype;^::wmemchr;^::wprintf_s;^::wscanf;^::wscanf_s;' 46 | cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: 'false' 47 | cert-str34-c.DiagnoseSignedUnsignedCharComparisons: 'false' 48 | cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: 'true' 49 | google-readability-braces-around-statements.ShortStatementLines: '1' 50 | google-readability-function-size.StatementThreshold: '800' 51 | google-readability-namespace-comments.ShortNamespaceLines: '10' 52 | google-readability-namespace-comments.SpacesBeforeComments: '2' 53 | llvm-else-after-return.WarnOnConditionVariables: 'false' 54 | llvm-else-after-return.WarnOnUnfixable: 'false' 55 | llvm-qualified-auto.AddConstToQualified: 'false' 56 | bugprone-lambda-function-name.IgnoreMacros: 'true' 57 | bugprone-signed-char-misuse.CharTypdefsToIgnore: 'signed char' 58 | bugprone-implicit-widening-of-multiplication-result.IgnoreConstantIntExpr: 'true' 59 | readability-function-cognitive-complexity.Threshold: '100' 60 | SystemHeaders: false 61 | 62 | -------------------------------------------------------------------------------- /tools/benchmark_shm_channel_send.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef _MSC_VER 15 | 16 | # include 17 | # include 18 | 19 | #else 20 | # pragma comment(lib, "Ws2_32.lib") 21 | #endif 22 | 23 | #include 24 | #include "config/compiler_features.h" 25 | #include "detail/libatbus_channel_export.h" 26 | 27 | #ifdef max 28 | # undef max 29 | #endif 30 | 31 | #if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS && defined(ATBUS_CHANNEL_SHM) 32 | 33 | static int test_get_pid() { 34 | # ifdef _MSC_VER 35 | return _getpid(); 36 | # else 37 | return getpid(); 38 | # endif 39 | } 40 | 41 | int main(int argc, char *argv[]) { 42 | if (argc < 2) { 43 | printf("usage: %s [max unit size] [shm size]\n", argv[0]); 44 | return 0; 45 | } 46 | 47 | using namespace atbus::channel; 48 | size_t max_n = 1024; 49 | if (argc > 2) max_n = (size_t)strtol(argv[2], nullptr, 10); 50 | 51 | size_t buffer_len = 64 * 1024 * 1024; // 64MB 52 | if (argc > 3) buffer_len = (size_t)strtol(argv[3], nullptr, 10); 53 | 54 | shm_channel *channel = nullptr; 55 | 56 | int res = shm_attach(argv[1], buffer_len, &channel, nullptr); 57 | if (res < 0) { 58 | fprintf(stderr, "shm_attach failed, ret: %d\n", res); 59 | return res; 60 | } 61 | 62 | srand(static_cast(time(nullptr))); 63 | 64 | size_t sum_send_len = 0; 65 | size_t sum_send_times = 0; 66 | size_t sum_send_full = 0; 67 | size_t sum_send_err = 0; 68 | char sum_seq = (char)rand() + 1; 69 | int pid = test_get_pid(); 70 | 71 | if (!sum_seq) { 72 | ++sum_seq; 73 | } 74 | // 创建写线程 75 | std::thread *write_threads = 76 | new std::thread([&sum_send_len, &sum_send_times, &sum_send_full, &sum_send_err, &sum_seq, pid, max_n, channel] { 77 | char *buf_pool = new char[max_n * sizeof(size_t)]; 78 | int sleep_msec = 8; 79 | 80 | while (true) { 81 | size_t n = rand() % max_n; // 最大 4K-8K的包 82 | if (0 == n) n = 1; // 保证一定有数据包,保证收发次数一致 83 | 84 | *((int *)buf_pool) = pid; 85 | memset(buf_pool + sizeof(int), (int)sum_seq, n * sizeof(size_t) - sizeof(int)); 86 | int res = shm_send(channel, buf_pool, n * sizeof(size_t)); 87 | 88 | if (res) { 89 | if (EN_ATBUS_ERR_BUFF_LIMIT == res) { 90 | ++sum_send_full; 91 | } else { 92 | ++sum_send_err; 93 | 94 | std::pair last_action = shm_last_action(); 95 | fprintf(stderr, "shm_send error, ret code: %d. start: %d, end: %d\n", res, (int)last_action.first, 96 | (int)last_action.second); 97 | } 98 | 99 | std::this_thread::sleep_for(std::chrono::milliseconds(sleep_msec)); 100 | if (sleep_msec < 32) { 101 | sleep_msec *= 2; 102 | } 103 | 104 | } else { 105 | ++sum_send_times; 106 | sum_send_len += n * sizeof(size_t); 107 | 108 | ++sum_seq; 109 | if (!sum_seq) { 110 | ++sum_seq; 111 | } 112 | 113 | if (sleep_msec > 8) sleep_msec /= 2; 114 | } 115 | } 116 | 117 | delete[] buf_pool; 118 | }); 119 | 120 | // 检查状态 121 | int secs = 0; 122 | char unit_desc[][4] = {"B", "KB", "MB", "GB"}; 123 | size_t unit_devi[] = {1UL, 1UL << 10, 1UL << 20, 1UL << 30}; 124 | size_t unit_index = 0; 125 | 126 | while (true) { 127 | ++secs; 128 | // std::chrono::seconds dura(60); 129 | std::this_thread::sleep_for(std::chrono::milliseconds(60000)); 130 | 131 | while (sum_send_len / unit_devi[unit_index] > 1024 && unit_index < sizeof(unit_devi) / sizeof(size_t) - 1) 132 | ++unit_index; 133 | 134 | while (sum_send_len / unit_devi[unit_index] <= 1024 && unit_index > 0) --unit_index; 135 | 136 | std::cout << "[ RUNNING ] NO." << secs << " m" << std::endl 137 | << "[ RUNNING ] send(" << sum_send_times << " times, " << (sum_send_len / unit_devi[unit_index]) << " " 138 | << unit_desc[unit_index] << ") " << "full " << sum_send_full << " times, err " << sum_send_err << " times" 139 | << std::endl 140 | << std::endl; 141 | } 142 | 143 | write_threads->join(); 144 | delete write_threads; 145 | 146 | return 0; 147 | } 148 | 149 | #else 150 | 151 | int main() { 152 | std::cerr << "this benckmark code require your compiler support lambda and c++11/thread" << std::endl; 153 | return 0; 154 | } 155 | 156 | #endif -------------------------------------------------------------------------------- /ci/do_ci.ps1: -------------------------------------------------------------------------------- 1 | $PSDefaultParameterValues['*:Encoding'] = 'UTF-8' 2 | 3 | $OutputEncoding = [System.Text.UTF8Encoding]::new() 4 | 5 | $SCRIPT_DIR = Split-Path -Parent $MyInvocation.MyCommand.Definition 6 | $WORK_DIR = Get-Location 7 | 8 | if ($IsWindows) { 9 | # See https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd 10 | New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" ` 11 | -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force 12 | 13 | function Invoke-Environment { 14 | param 15 | ( 16 | [Parameter(Mandatory = $true)] 17 | [string] $Command 18 | ) 19 | cmd /c "$Command > nul 2>&1 && set" | . { process { 20 | if ($_ -match '^([^=]+)=(.*)') { 21 | [System.Environment]::SetEnvironmentVariable($matches[1], $matches[2]) 22 | } 23 | } } 24 | } 25 | $vswhere = "${Env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe" 26 | $vsInstallationPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath 27 | $winSDKDir = $(Get-ItemPropertyValue -Path "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0" -Name "InstallationFolder") 28 | if ([string]::IsNullOrEmpty($winSDKDir)) { 29 | $winSDKDir = "${Env:ProgramFiles(x86)}/Windows Kits/10/Include/" 30 | } 31 | else { 32 | $winSDKDir = "$winSDKDir/Include/" 33 | } 34 | foreach ($sdk in $(Get-ChildItem $winSDKDir | Sort-Object -Property Name)) { 35 | if ($sdk.Name -match "[0-9]+\.[0-9]+\.[0-9\.]+") { 36 | $selectWinSDKVersion = $sdk.Name 37 | } 38 | } 39 | if (!(Test-Path Env:WindowsSDKVersion)) { 40 | $Env:WindowsSDKVersion = $selectWinSDKVersion 41 | } 42 | # Maybe using $selectWinSDKVersion = "10.0.18362.0" for better compatible 43 | Write-Output "Window SDKs:(Latest: $selectWinSDKVersion)" 44 | foreach ($sdk in $(Get-ChildItem $winSDKDir | Sort-Object -Property Name)) { 45 | Write-Output " - $sdk" 46 | } 47 | } 48 | 49 | Set-Location "$SCRIPT_DIR/.." 50 | $RUN_MODE = $args[0] 51 | 52 | if ( $RUN_MODE -eq "msvc.2019+.test" ) { 53 | Invoke-Environment "call ""$vsInstallationPath/VC/Auxiliary/Build/vcvars64.bat""" 54 | New-Item -Path "build_jobs_ci" -ItemType "directory" -Force 55 | Set-Location "build_jobs_ci" 56 | & cmake ".." "-G" "$Env:CMAKE_GENERATOR" "-A" $Env:CMAKE_PLATFORM "-DBUILD_SHARED_LIBS=$ENV:BUILD_SHARED_LIBS" ` 57 | "-DPROJECT_ENABLE_UNITTEST=ON" "-DPROJECT_ENABLE_SAMPLE=ON" "-DPROJECT_ENABLE_TOOLS=ON" "-DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON" ` 58 | "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_ALLOW_SHARED_LIBS=OFF" ` 59 | "-DCMAKE_SYSTEM_VERSION=$selectWinSDKVersion" "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 60 | if ( $LastExitCode -ne 0 ) { 61 | exit $LastExitCode 62 | } 63 | 64 | $ALL_DLL_FILES = Get-ChildItem -Path "../third_party/install/*.dll" -Recurse 65 | $ALL_DLL_DIRS = $(foreach ($dll_file in $ALL_DLL_FILES) { 66 | $dll_file.Directory.FullName 67 | }) | Sort-Object | Get-Unique 68 | $Env:PATH = ($ALL_DLL_DIRS -Join [IO.Path]::PathSeparator) + [IO.Path]::PathSeparator + $Env:PATH 69 | Write-Output "PATH=$Env:PATH" 70 | 71 | & cmake --build . --config $Env:CONFIGURATION 72 | if ( $LastExitCode -ne 0 ) { 73 | exit $LastExitCode 74 | } 75 | & ctest . -V -C $Env:CONFIGURATION -L libatbus.unit_test 76 | if ( $LastExitCode -ne 0 ) { 77 | exit $LastExitCode 78 | } 79 | } 80 | elseif ( $RUN_MODE -eq "msvc.2017.test" ) { 81 | Invoke-Environment "call ""$vsInstallationPath/VC/Auxiliary/Build/vcvars64.bat""" 82 | New-Item -Path "build_jobs_ci" -ItemType "directory" -Force 83 | Set-Location "build_jobs_ci" 84 | & cmake ".." "-G" "$Env:CMAKE_GENERATOR" "-DBUILD_SHARED_LIBS=$Env:BUILD_SHARED_LIBS" "-DPROJECT_ENABLE_UNITTEST=ON" ` 85 | "-DPROJECT_ENABLE_SAMPLE=ON" "-DPROJECT_ENABLE_TOOLS=ON" "-DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON" ` 86 | "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_ALLOW_SHARED_LIBS=OFF" ` 87 | "-DCMAKE_SYSTEM_VERSION=$selectWinSDKVersion" "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 88 | if ( $LastExitCode -ne 0 ) { 89 | exit $LastExitCode 90 | } 91 | 92 | $ALL_DLL_FILES = Get-ChildItem -Path "../third_party/install/*.dll" -Recurse 93 | $ALL_DLL_DIRS = $(foreach ($dll_file in $ALL_DLL_FILES) { 94 | $dll_file.Directory.FullName 95 | }) | Sort-Object | Get-Unique 96 | $Env:PATH = ($ALL_DLL_DIRS -Join [IO.Path]::PathSeparator) + [IO.Path]::PathSeparator + $Env:PATH 97 | Write-Output "PATH=$Env:PATH" 98 | 99 | & cmake --build . --config $Env:CONFIGURATION 100 | if ( $LastExitCode -ne 0 ) { 101 | exit $LastExitCode 102 | } 103 | & ctest . -V -C $Env:CONFIGURATION -L libatbus.unit_test 104 | if ( $LastExitCode -ne 0 ) { 105 | exit $LastExitCode 106 | } 107 | } 108 | 109 | Set-Location $WORK_DIR 110 | -------------------------------------------------------------------------------- /docs/experience/test_shm_create.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include /* For O_* constants */ 3 | #include 4 | #include /* For mode constants */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main() { 12 | // GNU libc (>=2.19) validates the shared memory name. Specifically, the shared memory object MUST now be at the root 13 | // of the shmfs mount point. 14 | // @see 15 | // https://stackoverflow.com/questions/23145458/shm-open-fails-with-einval-when-creating-shared-memory-in-subdirectory-of-dev 16 | // @see https://sourceware.org/git/?p=glibc.git;a=commit;h=b20de2c3d9d751eb259c321426188eefc64fcbe9 17 | // @see https://sourceware.org/bugzilla/show_bug.cgi?id=16274 18 | // link with -lrt 19 | // int test_fd = shm_open("/dev/shm/libatbus-test-shm.bus", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | 20 | // S_IWOTH); glibc接口的shm_unlink不允许子目录 21 | #if 22 | 23 | mkdir("/dev/shm/libatbus", S_IRWXU | S_IRWXG | S_IRWXO); 24 | mkdir("/dev/shm/libatbus/test", S_IRWXU | S_IRWXG | S_IRWXO); 25 | int test_fd = openat(AT_FDCWD, "/dev/shm/libatbus/test/shm.bus", O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 26 | S_IRWXU | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 27 | if (test_fd < 0) { 28 | printf("shm_open failed, err: %d, %s\n", errno, strerror(errno)); 29 | return 1; 30 | } 31 | 32 | struct stat statbuf; 33 | if (0 != fstat(test_fd, &statbuf)) { 34 | printf("fstat failed, err: %d, %s\n", errno, strerror(errno)); 35 | close(test_fd); 36 | return 1; 37 | } 38 | 39 | puts("================================ read ================================"); 40 | printf("stat.dev %lld\n", static_cast(statbuf.st_dev)); /* ID of device containing file */ 41 | printf("stat.ino %lld\n", static_cast(statbuf.st_ino)); /* Inode number */ 42 | printf("stat.mode %lld\n", static_cast(statbuf.st_mode)); /* File type and mode */ 43 | printf("stat.nlink %lld\n", static_cast(statbuf.st_nlink)); /* Number of hard links */ 44 | printf("stat.uid %lld\n", static_cast(statbuf.st_uid)); /* User ID of owner */ 45 | printf("stat.gid %lld\n", static_cast(statbuf.st_gid)); /* Group ID of owner */ 46 | printf("stat.dev %lld\n", static_cast(statbuf.st_rdev)); /* Device ID (if special file) */ 47 | printf("stat.off %lld\n", static_cast(statbuf.st_size)); /* Total size, in bytes */ 48 | printf("stat.blksize %lld\n", static_cast(statbuf.st_blksize)); /* Block size for filesystem I/O */ 49 | printf("stat.blkcnt %lld\n", static_cast(statbuf.st_blocks)); /* Number of 512B blocks allocated */ 50 | 51 | if (statbuf.st_size <= 0) { 52 | if (0 != ftruncate(test_fd, 2 * 1024 * 1024)) { 53 | printf("ftruncate failed, err: %d, %s\n", errno, strerror(errno)); 54 | close(test_fd); 55 | return 1; 56 | } 57 | 58 | if (0 != fstat(test_fd, &statbuf)) { 59 | printf("fstat failed, err: %d, %s\n", errno, strerror(errno)); 60 | close(test_fd); 61 | return 1; 62 | } 63 | 64 | puts("================================ create ================================"); 65 | printf("stat.dev %lld\n", static_cast(statbuf.st_dev)); /* ID of device containing file */ 66 | printf("stat.ino %lld\n", static_cast(statbuf.st_ino)); /* Inode number */ 67 | printf("stat.mode %lld\n", static_cast(statbuf.st_mode)); /* File type and mode */ 68 | printf("stat.nlink %lld\n", static_cast(statbuf.st_nlink)); /* Number of hard links */ 69 | printf("stat.uid %lld\n", static_cast(statbuf.st_uid)); /* User ID of owner */ 70 | printf("stat.gid %lld\n", static_cast(statbuf.st_gid)); /* Group ID of owner */ 71 | printf("stat.dev %lld\n", static_cast(statbuf.st_rdev)); /* Device ID (if special file) */ 72 | printf("stat.off %lld\n", static_cast(statbuf.st_size)); /* Total size, in bytes */ 73 | printf("stat.blksize %lld\n", static_cast(statbuf.st_blksize)); /* Block size for filesystem I/O */ 74 | printf("stat.blkcnt %lld\n", static_cast(statbuf.st_blocks)); /* Number of 512B blocks allocated */ 75 | } 76 | 77 | int shmflag = MAP_SHARED; 78 | 79 | # ifdef __linux__ 80 | shmflag |= MAP_NORESERVE; 81 | # endif 82 | 83 | puts("================================ mmap ================================"); 84 | void* access_data = mmap(nullptr, statbuf.st_size, PROT_READ | PROT_WRITE, shmflag, test_fd, 0); 85 | if (MAP_FAILED == access_data) { 86 | printf("mmap failed, err: %d, %s\n", errno, strerror(errno)); 87 | close(test_fd); 88 | return 1; 89 | } 90 | 91 | for (unsigned int i = 0; i < statbuf.st_size; ++i) { 92 | (*((unsigned char*)access_data + i)) = (unsigned char)(i & 0xFF); 93 | } 94 | 95 | if (0 != munmap(access_data, statbuf.st_size)) { 96 | printf("munmap failed, err: %d, %s\n", errno, strerror(errno)); 97 | close(test_fd); 98 | return 1; 99 | } 100 | 101 | close(test_fd); 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /include/atbus_message_handler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2026 atframework 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "detail/libatbus_config.h" 14 | 15 | namespace atframework { 16 | namespace atbus { 17 | namespace protocol { 18 | class access_data; 19 | class crypto_handshake_data; 20 | class custom_command_data; 21 | } // namespace protocol 22 | } // namespace atbus 23 | } // namespace atframework 24 | 25 | ATBUS_MACRO_NAMESPACE_BEGIN 26 | 27 | class message; 28 | class connection_context; 29 | 30 | class node; 31 | class endpoint; 32 | class connection; 33 | 34 | struct message_handler { 35 | using handler_fn_t = int (*)(node &n, connection *conn, message &&, int status, int errcode); 36 | 37 | static ATBUS_MACRO_API int unpack_message(connection_context &conn_ctx, message &target, 38 | gsl::span data); 39 | 40 | static ATBUS_MACRO_API void finish_message(connection_context &conn_ctx, message &source, int32_t protocol_version); 41 | 42 | static ATBUS_MACRO_API int pack_message(connection_context &conn_ctx, const message &source, 43 | gsl::span buffer, size_t &used_size); 44 | 45 | static ATBUS_MACRO_API int dispatch_message(node &n, connection *conn, message &&, int status, int errcode); 46 | 47 | static ATBUS_MACRO_API const char *get_body_name(int body_case); 48 | 49 | static ATBUS_MACRO_API void generate_access_data(::atframework::atbus::protocol::access_data &ad, uint64_t bus_id, 50 | uint64_t nince1, uint64_t nince2, 51 | gsl::span> access_tokens, 52 | const ::atframework::atbus::protocol::crypto_handshake_data &hd); 53 | 54 | static ATBUS_MACRO_API void generate_access_data(::atframework::atbus::protocol::access_data &ad, uint64_t bus_id, 55 | uint64_t nince1, uint64_t nince2, 56 | gsl::span> access_tokens, 57 | const ::atframework::atbus::protocol::custom_command_data &csarg); 58 | 59 | static ATBUS_MACRO_API std::string make_access_data_plaintext( 60 | uint64_t bus_id, const ::atframework::atbus::protocol::access_data &ad, 61 | const ::atframework::atbus::protocol::crypto_handshake_data &hd); 62 | 63 | static ATBUS_MACRO_API std::string make_access_data_plaintext( 64 | uint64_t bus_id, const ::atframework::atbus::protocol::access_data &ad, 65 | const ::atframework::atbus::protocol::custom_command_data &csarg); 66 | 67 | static ATBUS_MACRO_API std::string calculate_access_data_signature( 68 | const ::atframework::atbus::protocol::access_data &ad, gsl::span access_token, 69 | atfw::util::nostd::string_view plaintext); 70 | 71 | static ATBUS_MACRO_API int send_ping(node &n, connection &conn, uint64_t seq); 72 | 73 | static ATBUS_MACRO_API int send_reg(int32_t msg_id, node &n, connection &conn, int32_t ret_code, uint64_t seq); 74 | 75 | static ATBUS_MACRO_API int send_transfer_rsp(node &n, message &&, int32_t ret_code); 76 | 77 | static ATBUS_MACRO_API int send_custom_cmd_rsp(node &n, connection *conn, const std::list &rsp_data, 78 | int32_t type, int32_t ret_code, uint64_t sequence, 79 | uint64_t from_bus_id); 80 | 81 | static ATBUS_MACRO_API int send_node_connect_sync(node &n, uint64_t direct_from_bus_id, endpoint &dst_ep); 82 | 83 | static ATBUS_MACRO_API int send_message(node &n, connection &conn, message &msg); 84 | 85 | // ========================= 接收handle ========================= 86 | static ATBUS_MACRO_API int on_recv_data_transfer_req(node &n, connection *conn, message &&, int status, int errcode); 87 | static ATBUS_MACRO_API int on_recv_data_transfer_rsp(node &n, connection *conn, message &&, int status, int errcode); 88 | 89 | static ATBUS_MACRO_API int on_recv_custom_cmd_req(node &n, connection *conn, message &&, int status, int errcode); 90 | static ATBUS_MACRO_API int on_recv_custom_cmd_rsp(node &n, connection *conn, message &&, int status, int errcode); 91 | 92 | static ATBUS_MACRO_API int on_recv_node_sync_req(node &n, connection *conn, message &&, int status, int errcode); 93 | static ATBUS_MACRO_API int on_recv_node_sync_rsp(node &n, connection *conn, message &&, int status, int errcode); 94 | static ATBUS_MACRO_API int on_recv_node_reg_req(node &n, connection *conn, message &&, int status, int errcode); 95 | static ATBUS_MACRO_API int on_recv_node_reg_rsp(node &n, connection *conn, message &&, int status, int errcode); 96 | static ATBUS_MACRO_API int on_recv_node_conn_syn(node &n, connection *conn, message &&, int status, int errcode); 97 | static ATBUS_MACRO_API int on_recv_node_ping(node &n, connection *conn, message &&, int status, int errcode); 98 | static ATBUS_MACRO_API int on_recv_node_pong(node &n, connection *conn, message &&, int status, int errcode); 99 | }; 100 | ATBUS_MACRO_NAMESPACE_END 101 | -------------------------------------------------------------------------------- /include/libatbus_protocol.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package atframework.atbus.protocol; 4 | 5 | option go_package = "github.com/atframework/libatbus-go/protocol"; 6 | option optimize_for = SPEED; 7 | // option optimize_for = LITE_RUNTIME; 8 | // option optimize_for = CODE_SIZE; 9 | // --cpp_out=lite:,--cpp_out= 10 | option cc_enable_arenas = true; 11 | 12 | enum ATBUS_PROTOCOL_CONST { 13 | option allow_alias = true; 14 | ATBUS_PROTOCOL_CONST_UNKNOWN = 0; 15 | ATBUS_PROTOCOL_VERSION = 3; 16 | ATBUS_PROTOCOL_MINIMAL_VERSION = 3; // minimal protocol version supported 17 | } 18 | 19 | enum ATBUS_FORWARD_DATA_FLAG_TYPE { 20 | FORWARD_DATA_FLAG_NONE = 0; 21 | // all flags must be power of 2 22 | FORWARD_DATA_FLAG_REQUIRE_RSP = 1; 23 | } 24 | 25 | enum ATBUS_ACCESS_DATA_ALGORITHM_TYPE { 26 | ATBUS_ACCESS_DATA_ALGORITHM_HMAC_SHA256 = 0; 27 | } 28 | 29 | enum ATBUS_CRYPTO_ALGORITHM_TYPE { 30 | ATBUS_CRYPTO_ALGORITHM_NONE = 0; 31 | ATBUS_CRYPTO_ALGORITHM_XXTEA = 1; 32 | ATBUS_CRYPTO_ALGORITHM_AES_128_CBC = 11; // PKCS#7 33 | ATBUS_CRYPTO_ALGORITHM_AES_192_CBC = 12; // PKCS#7 34 | ATBUS_CRYPTO_ALGORITHM_AES_256_CBC = 13; // PKCS#7 35 | ATBUS_CRYPTO_ALGORITHM_AES_128_GCM = 14; // AEAD 36 | ATBUS_CRYPTO_ALGORITHM_AES_192_GCM = 15; // AEAD 37 | ATBUS_CRYPTO_ALGORITHM_AES_256_GCM = 16; // AEAD 38 | 39 | ATBUS_CRYPTO_ALGORITHM_CHACHA20 = 31; 40 | ATBUS_CRYPTO_ALGORITHM_CHACHA20_POLY1305_IETF = 32; // AEAD 41 | ATBUS_CRYPTO_ALGORITHM_XCHACHA20_POLY1305_IETF = 33; // AEAD 42 | } 43 | 44 | enum ATBUS_CRYPTO_KEY_EXCHANGE_TYPE { 45 | ATBUS_CRYPTO_KEY_EXCHANGE_NONE = 0; 46 | ATBUS_CRYPTO_KEY_EXCHANGE_X25519 = 1; // x25519 47 | ATBUS_CRYPTO_KEY_EXCHANGE_SECP256R1 = 2; // secp256r1, p-256 48 | ATBUS_CRYPTO_KEY_EXCHANGE_SECP384R1 = 3; // secp384r1, p-384 49 | ATBUS_CRYPTO_KEY_EXCHANGE_SECP521R1 = 4; // secp521r1, p-521 50 | } 51 | 52 | message crypto_handshake_data { 53 | uint64 sequence = 1; // used to prevent duplication 54 | ATBUS_CRYPTO_KEY_EXCHANGE_TYPE type = 2; 55 | repeated ATBUS_CRYPTO_ALGORITHM_TYPE algorithms = 3; // available cryptographic algorithms 56 | bytes params = 4; 57 | bytes public_key = 5; 58 | } 59 | 60 | message custom_command_argv { 61 | bytes arg = 1; 62 | } 63 | 64 | message custom_command_data { 65 | uint64 from = 1; 66 | repeated custom_command_argv commands = 2; 67 | access_data access_key = 3; 68 | } 69 | 70 | message forward_data { 71 | uint64 from = 1; 72 | uint64 to = 2; 73 | repeated uint64 router = 3; 74 | bytes content = 4; 75 | uint32 flags = 5; 76 | } 77 | 78 | message access_data { 79 | ATBUS_ACCESS_DATA_ALGORITHM_TYPE algorithm = 1; 80 | int64 timestamp = 2; // unix timestamp 81 | 82 | // 128bits nonce is enough 83 | // - TLS 1.3: 96 bits 84 | // - IPSec: 64 bits 85 | // - JWT: 128 bits 86 | // - UUID v4: 128 bits 87 | uint64 nonce1 = 3; // random 88 | uint64 nonce2 = 4; // random 89 | 90 | // signature=HMAC.sha256(":-::{register_data.crypto_handshake.type}:{sha256(register_data.crypto_handshake.params+register_data.crypto_handshake.public_key)}") 91 | // signature=HMAC.sha256(":-:") without encryption 92 | // signature=HMAC.sha256(":-::{sha256(custom_command_data.commands.arg)}") 93 | // abs(timestamp-current time)<=300 94 | repeated bytes signature = 9; 95 | } 96 | 97 | message channel_data { 98 | string address = 1; 99 | } 100 | 101 | message node_data { 102 | uint64 bus_id = 1; 103 | bool overwrite = 2; 104 | bool flags = 3; 105 | uint64 children_id_mask = 4; 106 | uint64 children_id_prefix = 5; 107 | repeated node_data children = 6; 108 | } 109 | 110 | message node_tree { 111 | repeated node_data nodes = 1; 112 | } 113 | 114 | message ping_data { 115 | int64 time_point = 1; 116 | crypto_handshake_data crypto_handshake = 2; 117 | } 118 | 119 | message subnet_range { 120 | uint64 id_prefix = 1; 121 | uint32 mask_bits = 2; // suffix 122 | } 123 | 124 | message register_data { 125 | uint64 bus_id = 1; 126 | int32 pid = 2; 127 | string hostname = 3; 128 | repeated channel_data channels = 4; 129 | uint32 flags = 7; 130 | access_data access_key = 8; 131 | repeated subnet_range subnets = 9; 132 | string hash_code = 10; 133 | crypto_handshake_data crypto_handshake = 11; 134 | } 135 | 136 | message connection_data { 137 | channel_data address = 1; 138 | } 139 | 140 | message message_head_crypto { 141 | // IV/nonce for crypto, must be unique for each encryption 142 | // If not set, just used the default rotation/nonce rule of the algorithm 143 | bytes iv = 1; 144 | 145 | // AAD for AEAD 146 | bytes aad = 11; 147 | // tag length for AEAD 148 | uint32 tag_len = 12; 149 | } 150 | 151 | message message_head { 152 | int32 version = 1; 153 | int32 type = 2; 154 | sint32 result_code = 3; 155 | uint64 sequence = 4; 156 | uint64 source_bus_id = 5; 157 | message_head_crypto crypto = 6; 158 | 159 | // body size without padding 160 | uint64 body_size = 9; 161 | } 162 | 163 | message message_body { 164 | oneof message_type { 165 | custom_command_data custom_command_req = 11; 166 | custom_command_data custom_command_rsp = 12; 167 | forward_data data_transform_req = 13; 168 | forward_data data_transform_rsp = 14; 169 | node_tree node_sync_req = 15; 170 | node_tree node_sync_rsp = 16; 171 | register_data node_register_req = 17; 172 | register_data node_register_rsp = 18; 173 | connection_data node_connect_sync = 20; 174 | ping_data node_ping_req = 21; 175 | ping_data node_pong_rsp = 22; 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /tools/benchmark_shm_channel_recv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include "config/compiler_features.h" 16 | 17 | #include "common/string_oprs.h" 18 | 19 | #if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS && defined(ATBUS_CHANNEL_SHM) 20 | 21 | static inline char *get_pid_cur(int pid) { 22 | static std::vector > res; 23 | for (size_t i = 0; i < res.size(); ++i) { 24 | if (res[i].first == pid) { 25 | return &res[i].second; 26 | } 27 | } 28 | 29 | res.push_back(std::pair(pid, 0)); 30 | return &res.back().second; 31 | } 32 | 33 | int main(int argc, char *argv[]) { 34 | if (argc < 2) { 35 | printf("usage: %s [max unit size] [shm size]\n", argv[0]); 36 | return 0; 37 | } 38 | 39 | using namespace atbus::channel; 40 | size_t max_n = 1024; 41 | if (argc > 2) max_n = (size_t)strtol(argv[2], nullptr, 10); 42 | 43 | size_t buffer_len = 64 * 1024 * 1024; // 64MB 44 | if (argc > 3) buffer_len = (size_t)strtol(argv[3], nullptr, 10); 45 | 46 | shm_channel *channel = nullptr; 47 | 48 | int res = shm_init(argv[1], buffer_len, &channel, nullptr); 49 | if (res < 0) { 50 | fprintf(stderr, "shm_init failed, ret: %d\n", res); 51 | return res; 52 | } 53 | 54 | srand(static_cast(time(nullptr))); 55 | 56 | size_t sum_recv_len = 0; 57 | size_t sum_recv_times = 0; 58 | size_t sum_recv_err = 0; 59 | size_t sum_data_err = 0; 60 | 61 | // 创建读线程 62 | std::thread *read_threads = 63 | new std::thread([&sum_recv_len, &sum_recv_times, &sum_recv_err, &sum_data_err, max_n, channel] { 64 | char *buf_pool = new char[max_n * sizeof(size_t)]; 65 | char *buf_check = new char[max_n * sizeof(size_t)]; 66 | 67 | bool is_last_tick_faild = false; 68 | while (true) { 69 | size_t n = 0; // 最大 4K-8K的包 70 | int res = shm_recv(channel, buf_pool, sizeof(size_t) * max_n, &n); 71 | 72 | if (res) { 73 | if (EN_ATBUS_ERR_NO_DATA != res) { 74 | std::pair last_action = shm_last_action(); 75 | fprintf(stderr, "shm_recv error, ret code: %d. start: %d, end: %d\n", res, (int)last_action.first, 76 | (int)last_action.second); 77 | ++sum_recv_err; 78 | 79 | if (!is_last_tick_faild) { 80 | shm_show_channel(channel, std::cout, true, 24); 81 | } 82 | is_last_tick_faild = true; 83 | } else { 84 | std::this_thread::sleep_for(std::chrono::milliseconds(128)); 85 | } 86 | } else { 87 | ++sum_recv_times; 88 | sum_recv_len += n; 89 | 90 | int pid = *((int *)buf_pool); 91 | char *val_check = get_pid_cur(pid); 92 | 93 | if (0 == *val_check) { 94 | *val_check = *(buf_pool + sizeof(int)); 95 | } else { 96 | ++(*val_check); 97 | if (0 == *val_check) { 98 | ++(*val_check); 99 | } 100 | } 101 | 102 | memset(buf_check + sizeof(int), (int)*val_check, n - sizeof(int)); 103 | int cmp_res = memcmp(buf_pool + sizeof(int), buf_check + sizeof(int), n - sizeof(int)); 104 | if (0 == cmp_res) { 105 | is_last_tick_faild = false; 106 | } else { 107 | std::cerr << "pid: " << pid << " expected data is "; 108 | atfw::util::string::dumphex(val_check, 1, std::cerr); 109 | std::cerr << ", but real is "; 110 | atfw::util::string::dumphex(buf_pool + sizeof(int), 1, std::cerr); 111 | std::cerr << std::endl; 112 | *val_check = *(buf_pool + sizeof(int)); 113 | 114 | ++sum_data_err; 115 | 116 | if (!is_last_tick_faild) { 117 | shm_show_channel(channel, std::cout, true, 24); 118 | } 119 | is_last_tick_faild = true; 120 | } 121 | } 122 | } 123 | 124 | delete[] buf_pool; 125 | delete[] buf_check; 126 | }); 127 | 128 | // 检查状态 129 | int secs = 0; 130 | char unit_desc[][4] = {"B", "KB", "MB", "GB"}; 131 | size_t unit_devi[] = {1UL, 1UL << 10, 1UL << 20, 1UL << 30}; 132 | size_t unit_index = 0; 133 | 134 | while (true) { 135 | ++secs; 136 | std::this_thread::sleep_for(std::chrono::milliseconds(60000)); 137 | 138 | while (sum_recv_len / unit_devi[unit_index] > 1024 && unit_index < sizeof(unit_devi) / sizeof(size_t) - 1) 139 | ++unit_index; 140 | 141 | while (sum_recv_len / unit_devi[unit_index] <= 1024 && unit_index > 0) --unit_index; 142 | 143 | std::cout << "[ RUNNING ] NO." << secs << " m" << std::endl 144 | << "[ RUNNING ] recv(" << sum_recv_times << " times, " << (sum_recv_len / unit_devi[unit_index]) << " " 145 | << unit_desc[unit_index] << ") " << "recv err " << sum_recv_err << " times, data valid failed " 146 | << sum_data_err << " times" << std::endl 147 | << std::endl; 148 | 149 | shm_show_channel(channel, std::cout, false, 0); 150 | } 151 | 152 | read_threads->join(); 153 | delete read_threads; 154 | 155 | return 0; 156 | } 157 | 158 | #else 159 | 160 | int main() { 161 | std::cerr << "this benckmark code require your compiler support lambda and c++11/thread" << std::endl; 162 | return 0; 163 | } 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /docs/Build.md: -------------------------------------------------------------------------------- 1 | # 使用(编译)流程 2 | 3 | ## 依赖工具集合库 4 | 5 | + 支持c++0x或c++11的编译器(为了代码尽量简洁,特别是少做无意义的平台兼容,依赖部分 C11和C++11的功能,所以不支持过低版本的编译器) 6 | 7 | > + GCC: 4.8 及以上 8 | > + Clang: 7.0 及以上 9 | > + MSVC: VS 2019 及以上 10 | 11 | + [cmake](https://cmake.org/download/) 3.24.0 以上 12 | + [protobuf](https://github.com/protocolbuffers/protobuf) 13 | 14 | > 用于协议打解包,仅使用头文件 15 | 16 | + [libuv](http://libuv.org/)(用于网络通道) 17 | 18 | > 用于协议打解包,仅使用头文件 19 | > 20 | > 手动编译libuv则需要额外的软件集,基本有: gcc/clang/msvc, autoconf, automake, make, pthread, m4 21 | 22 | + [atframe_utils](https://github.com/atframework/atframe_utils)(基础公共代码,检测到不存在会自动下载) 23 | + (可选)[python](http://python.org/) (周边工具集) 24 | + (可选)tar, curl, wget: 如果使用内置的脚本自动构建依赖的库,则这些是必要的工具 25 | 26 | ## 环境准备(开发环境最小依赖) 27 | 28 | ### Windows + MSVC 29 | 30 | 1. [cmake](https://cmake.org/download/) 31 | 2. [visual studio](https://www.visualstudio.com) 32 | 3. [libuv](http://dist.libuv.org/dist) 33 | 4. 执行 mkdir build && cd build && cmake .. -G "Visual Studio 15 2017 Win64" -DLIBUV_ROOT=[libuv安装目录] -DFlatbuffers_ROOT=[flatbuffers的安装目录] 34 | 5. 编译打开Visual Studio编译或 cmake --build . --config RelWithDebInfo 35 | 36 | 详情可参考 [Appceyor CI](../appveyor.yml) 脚本 37 | 38 | 或者也可以选择使用nmake编译。 39 | 40 | **生成目标(-G 选项的值)请按自己所在环境手动修改,否则会使用默认的配置,MSVC的默认会生成x86版本的工程文件** 41 | 42 | 上面最后一条命令可以根据实际环境修改参数,这里只提供一个示例 43 | 44 | ### Windows + MinGW(msys2) 45 | 46 | 1. 安装[Msys2](http://msys2.github.io/) 47 | 2. Msys2里安装依赖组件 48 | > ```bash 49 | > for pkg_name in git m4 curl wget tar autoconf automake mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain mingw-w64-x86_64-libtool mingw-w64-i686-libtool python; do pacman -S --noconfirm --needed $pkg_name; done 50 | > ``` 51 | > 具体可以根据 编译目标裁剪,这里把32位和64位依赖库都安装上了 52 | 53 | 3. MinGW shell 下执行cmake构建命令 54 | > 注意一定是要MingGW的shell(libuv不支持msys shell),msys2默认开启的是msys shell 55 | > 56 | > 通常情况下可以通过以下方式打开MingGW shell 57 | > + C:\msys64\mingw32.exe -- 32位 58 | > + C:\msys64\mingw64.exe -- 64位 59 | > 60 | > 请指定生成 MSYS Makefiles 工程文件(-G "MSYS Makefiles") 61 | 62 | 其他MinGW环境请自行安装依赖库 63 | 64 | 详情可参考 [Appceyor CI](../appveyor.yml) 脚本 65 | 66 | ### Linux 67 | 1. gcc 4.4及以上 68 | 2. autoconf 69 | 3. gdb 70 | 4. valgrind 71 | 5. curl 72 | 6. wget 73 | 7. tar 74 | 8. m4 75 | 9. cmake 76 | 10. automake 77 | 11. make 78 | 12. git 79 | 80 | 以上请用Linux发行版的包管理器安装,然后正常使用cmake即可 81 | 82 | 比如CentOS: 83 | 84 | ```bash 85 | yum install gcc gcc-c++ autoconf automake gdb valgrind curl wget tar m4 automake make git; 86 | wget -c "https://cmake.org/files/v3.9/cmake-3.9.4-Linux-x86_64.sh" -O "cmake-Linux-x86_64.sh"; 87 | bash cmake-Linux-x86_64.sh --skip-license --prefix=/usr ; 88 | ``` 89 | 90 | 详情可参考 [Travis CI](../.travis.yml) 脚本 91 | 92 | ### OSX 93 | 94 | 1. 安装 [brew](http://brew.sh/) 95 | 2. 安装 xcode 96 | 3. sudo brew install gcc gdb autoconf automake make curl wget tar m4 cmake git 97 | 98 | 详情可参考 [Travis CI](../.travis.yml) 脚本 99 | 100 | 编译和构建 101 | ------ 102 | 103 | ### 编译选项 104 | 105 | 除了cmake标准编译选项外,libatbus还提供一些额外选项 106 | 107 | + CMAKE_BUILD_TYPE (默认: Debug): 构建类型,目前**默认是Debug**方式构建。生产环境建议使用**-DCMAKE_BUILD_TYPE=RelWithDebInfo**(相当于gcc -O2 -g -ggdb -DNDEBUG) 108 | + LIBUV_ROOT: 手动指定libuv的安装目录 109 | + Flatbuffers_ROOT: 手动指定flatbuffers的安装目录,需要符合cmake的find_package规则,使用flatbuffers官方提供的config文件,且必须编译lib和flatc 110 | + CMAKE_MSVC_RUNTIME (默认: MD): 使用MSVC编译时默认使用MD/MDd运行时,如果需要尽可能一处依赖并使用MT请把这个值设为MT 111 | + PROJECT_ENABLE_SAMPLE (默认: NO): 是否编译Sample代码 112 | + PROJECT_ENABLE_UNITTEST (默认: NO): 是否编译单元测试代码 113 | + PROJECT_ENABLE_TOOLS (默认: NO): 是否编译工具集(主要是压力测试工具) 114 | + ============= 以上选项根据实际环境配置,以下选项不建议修改 ============= 115 | + ATBUS_MACRO_BUSID_TYPE (默认: uint64_t): busid的类型,建议不要设置成大于64位,否则需要修改protocol目录内的busid类型,并且重新生成协议文件 116 | + ATBUS_MACRO_DATA_NODE_SIZE (默认: 256): atbus的内存通道node大小(必须是2的倍数) 117 | + ATBUS_MACRO_DATA_ALIGN_SIZE (默认: 16): atbus的内存内存块对齐大小,大多数某些架构要求对齐到16 118 | + ATBUS_MACRO_DATA_SMALL_SIZE (默认: 3072): 流通道小数据块大小(用于优化减少内存拷贝) 119 | + ATBUS_MACRO_HUGETLB_SIZE (默认: 4194304): 大页表分页大小(用于优化共享内存分页,此功能暂时关闭,所以并不生效) 120 | + ATBUS_MACRO_MESSAGE_LIMIT (默认: 2097152): 默认消息体大小限制 121 | + ATBUS_MACRO_CONNECTION_CONFIRM_TIMEOUT (默认: 30): 默认连接确认时限 122 | + ATBUS_MACRO_CONNECTION_BACKLOG (默认: 128): 默认握手队列的最大连接数 123 | + ATBUS_MACRO_SHM_MEM_CHANNEL_LENGTH (默认: 8388608): 共享内存和内存通道的默认大小 124 | + ATBUS_MACRO_IOS_SEND_BUFFER_LENGTH (默认: 4194304): IO流(tcp/unix sock)发送通道的默认大小 125 | + GTEST_ROOT: 使用GTest单元测试框架 126 | + BOOST_ROOT: 设置Boost库根目录 127 | + PROJECT_TEST_ENABLE_BOOST_UNIT_TEST: 使用Boost.Test单元测试框架(如果GTEST_ROOT和此项都不设置,则使用内置单元测试框架) 128 | 129 | ### 示例构建脚本(MinGW64) 130 | 131 | ```bash 132 | git clone --depth=1 "https://github.com/atframework/libatbus.git" 133 | mkdir libatbus/build; 134 | cd libatbus/build; 135 | # cmake .. -G "MSYS Makefiles" [其他可选项] 136 | cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local/libatbus; 137 | cmake --build .; # 或 make 138 | cmake --build . --target install; # 或 make install 139 | ``` 140 | 141 | ### 运行和测试 142 | 143 | ```bash 144 | # MSVC请在构建目录下使用以下命令运行单元测试,其中Debug和之气按编译时的--config选项内容保持一致 145 | ctest . -V -C Debug 146 | # Unix like环境(包括mingw)在构建目录运行下面命令即可 147 | ctest . -V 148 | ``` 149 | 150 | 单元测试会生成在[构建目录]/test下,sample会生成在[构建目录]/sample下,工具程序(比如压力测试程序)会生成在[构建目录]/tools下。并且不会被install 151 | 152 | cmake --build . --target install(或者make构建系统的make install) 仅会安装include目录和编译好的libs 153 | 154 | **注意:** Windows下MSVC直接安装的libuv会链接dll库,但是构建脚本不会把dll拷贝到执行目录,所以直接运行可能会出错。这时候把libuv.dll手动拷贝到执行目录即可 155 | 156 | 附加说明 157 | ------ 158 | 159 | ### 目录结构说明 160 | 161 | + third_party: 外部组件(不一定是依赖项) 162 | + docs: 文档目录 163 | + include: 导出lib的包含文件(注意不导出的内部接口后文件会直接在src目录里) 164 | + project: 工程工具和配置文件集 165 | + protocol: 协议描述文件目录 166 | + sample: 使用示例目录,每一个cpp文件都是一个完全独立的例子 167 | + src: 源文件和内部接口申明目录 168 | + test: 测试框架及测试用例目录 169 | -------------------------------------------------------------------------------- /docs/Benchmark.md: -------------------------------------------------------------------------------- 1 | 2 | Benchmark - Run On 2016-07-07 3 | ====== 4 | 环境 5 | ------ 6 | + 环境: CentOS 7.1, GCC 4.8.5 7 | + CPU: Xeon E3-1230 v2 3.30GHz*8 (sender和receiver都只用一个核心) 8 | + 内存: 24GB (这是总内存,具体使用数根据配置不同而不同) 9 | + 网络: 千兆网卡 * 1 10 | + 编译选项: -O2 -g -DNDEBUG -ggdb -Wall -Werror -Wno-unused-local-typedefs -std=gnu++11 -D_POSIX_MT_ 11 | + 配置选项: -DATBUS_MACRO_BUSID_TYPE=uint64_t -DATBUS_MACRO_CONNECTION_BACKLOG=128 -DATBUS_MACRO_CONNECTION_CONFIRM_TIMEOUT=30 -DATBUS_MACRO_DATA_ALIGN_TYPE=uint64_t -DATBUS_MACRO_DATA_NODE_SIZE=128 -DATBUS_MACRO_DATA_SMALL_SIZE=3072 -DATBUS_MACRO_HUGETLB_SIZE=4194304 -DATBUS_MACRO_MSG_LIMIT=65536 12 | 13 | 14 | 测试项 | 连接数 | 包长度 | CPU消耗 | 内存消耗 | 吞吐量 | QPS 15 | ----------------------------------------|----------------|---------------------|-----------------|--------------|--------------|--------------- 16 | Linux+本地回环+ipv6+静态缓冲区 | 1 | 8-16384字节 | 90%/100% | 5.8MB/24MB | 601MB/s | 95K/s 17 | Linux+本地回环+ipv6+静态缓冲区 | 1 | 8-128字节(模拟ping包) | 48%/100% | 5.8MB/27MB | 163MB/s | 2822K/s 18 | Linux+本地回环+ipv6+动态缓冲区(ptmalloc) | 1 | 8-16384字节 | 90%/100% | 5.8MB/24MB | 607MB/s | 96K/s 19 | Linux+本地回环+ipv6+动态缓冲区(ptmalloc) | 1 | 8-128字节(模拟ping包) | 48%/100% | 5.8MB/27MB | 165MB/s | 2857K/s 20 | Linux+共享内存 | 1 | 8-16384字节 | 98%/98% | 74MB/74MB | 1.56GB/s | 199K/s 21 | Linux+共享内存 | 1 | 8-128字节(模拟ping包) | 100%/83% | 74MB/74MB | 303MB/s | 5253K/s 22 | 23 | 24 | 压力测试说明 25 | ------ 26 | 27 | 1. 动态缓冲区指发送者发送缓冲区使用malloc和free来保存发送中的数据,静态缓冲区指使用内置的内存池来缓存。 28 | 2. 由于发送者的CPU消耗会高于接收者(接收者没有任何逻辑,发送者会有一次随机数操作),所以动态缓冲区时其实内存会持续增加。(内存碎片原因,实际测试过程中3分钟由20MB涨到28MB) 29 | 3. Windows下除了不支持Unix Socket外,共享内存和ipv4/ipv6连接都是支持的,但是没有跑压力测试。 30 | 31 | *PS: 目前没写多连接测试工具,后面有空再写吧* 32 | 33 | 测试命令如下(需要把tools/script里的脚本拷贝到[构建目录]/tools内) 34 | ------ 35 | 36 | ```bash 37 | # Linux+本地回环+ipv6+静态缓冲区:8-16384字节 测试命令 38 | ./startup_benchmark_io_stream.sh ipv6://::1:16389 2048 4194304 8096 39 | 40 | # Linux+本地回环+ipv6+静态缓冲区:8-128字节(模拟ping包) 测试命令 41 | ./startup_benchmark_io_stream.sh ipv6://::1:16389 16 4194304 8096 42 | 43 | # Linux+本地回环+动态缓冲区(ptmalloc):8-16384字节 测试命令 44 | ./startup_benchmark_io_stream.sh ipv6://::1:16389 2048 4194304 0 45 | 46 | # Linux+本地回环+动态缓冲区(ptmalloc):8-128字节(模拟ping包) 测试命令 47 | ./startup_benchmark_io_stream.sh ipv6://::1:16389 16 4194304 0 48 | 49 | # Linux+共享内存:8-16384字节 测试命令 50 | ./startup_benchmark_shm.sh 12345679 2048 51 | 52 | # Linux+共享内存:8-128字节(模拟ping包) 测试命令 53 | ./startup_benchmark_shm.sh 12345679 16 54 | ``` 55 | 56 | 57 | 对比tsf4g性能测试报告 - Run On 2014-01-14 58 | ------ 59 | + 环境: tlinux 1.0.7 (based on CentOS 6.2), GCC 4.8.2, gperftools 2.1(启用tcmalloc和cpu profile) 60 | + CPU: Xeon X3440 2.53GHz*8 61 | + 内存: 8GB (这是总内存,具体使用数根据配置不同而不同) 62 | + 网络: 千兆网卡 * 1 63 | + 编译选项: -O2 -g -DNDEBUG -ggdb -Wall -Werror -Wno-unused-local-typedefs -std=gnu++11 -D_POSIX_MT_ 64 | + 配置选项: 无 65 | 66 | 测试项 | 连接数 | 包长度 | CPU消耗 | 内存消耗 | 吞吐量 | QPS 67 | ----------------------------------------|--------------------|---------------------|-----------------|--------------|--------------|--------------- 68 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 16KB | 13%/100% | 280MB | 86.4MB/s | 5.4K/s 69 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 8KB | 13%/100% | 280MB | 96MB/s | 12K/s 70 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 4KB | 13%/100% | 280MB | 92MB/s | 23K/s 71 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 2KB | 15%/100% | 280MB | 88MB/s | 44K/s 72 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 1KB | 16%/100% | 280MB | 82MB/s | 82K/s 73 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 512字节 | 22%/100% | 280MB | 79.5MB/s | 159K/s 74 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 256字节 | 33%/100% | 280MB | 73.5MB/s | 294K/s 75 | Linux+跨机器转发+ipv4 | 2(仅一个连接压力测试) | 128字节 | 50%/100% | 280MB | 65.75MB/s | 526K/s 76 | Linux+共享内存 | 3(仅一个连接压力测试) | 32KB | 100%/100% | 280MB | 3.06GB/s | 98K/s 77 | Linux+共享内存 | 3(仅一个连接压力测试) | 16KB | 61%/71% | 280MB | 1.59GB/s | 102K/s 78 | Linux+共享内存 | 3(仅一个连接压力测试) | 8KB | 36%/70% | 280MB | 1.27GB/s | 163K/s 79 | Linux+共享内存 | 3(仅一个连接压力测试) | 4KB | 40%/73% | 280MB | 1.30MB/s | 333K/s 80 | Linux+共享内存 | 3(仅一个连接压力测试) | 2KB | 43%/93% | 280MB | 1.08GB/s | 556K/s 81 | Linux+共享内存 | 3(仅一个连接压力测试) | 1KB | 54%/100% | 280MB | 977MB/s | 1000K/s 82 | Linux+共享内存 | 3(仅一个连接压力测试) | 512字节 | 44%/100% | 280MB | 610MB/s | 1250K/s 83 | Linux+共享内存 | 3(仅一个连接压力测试) | 256字节 | 42%/100% | 280MB | 305MB/s | 1250K/s 84 | Linux+共享内存 | 3(仅一个连接压力测试) | 128字节 | 42%/100% | 280MB | 174MB/s | 1429K/s 85 | 86 | 87 | 对比结果分析: 88 | 89 | 1. atbus的吞吐量基本上高于tbus 90 | 2. [共享内存] QPS方面,atbus比tbus的的发送端性能高,接收端相近 91 | -------------------------------------------------------------------------------- /tools/benchmark_io_stream_channel_recv.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include "detail/libatbus_channel_export.h" 13 | 14 | struct run_config { 15 | size_t max_n; 16 | size_t limit_size; 17 | size_t limit_static_num; 18 | 19 | // stats 20 | 21 | size_t sum_recv_len; 22 | size_t sum_recv_times; 23 | size_t sum_recv_full; 24 | size_t sum_recv_err; 25 | }; 26 | 27 | run_config conf; 28 | 29 | static void recv_callback(atbus::channel::io_stream_channel *channel, // 事件触发的channel 30 | atbus::channel::io_stream_connection *connection, // 事件触发的连接 31 | int status, // libuv传入的转态码 32 | void *buff, // 额外参数(不同事件不同含义) 33 | size_t s // 额外参数长度 34 | ) { 35 | assert(channel); 36 | assert(connection); 37 | 38 | if (0 != status) { 39 | fprintf(stderr, "recv callback error, ret code: %d. %s: %s\n", status, uv_err_name(channel->error_code), 40 | uv_strerror(channel->error_code)); 41 | 42 | ++conf.sum_recv_err; 43 | return; 44 | } 45 | 46 | size_t checked = 0; 47 | // check value 48 | if (s > 0) { 49 | size_t len = s / sizeof(size_t); 50 | bool passed = true; 51 | size_t *data_arr = reinterpret_cast(buff); 52 | checked = data_arr[0]; 53 | for (size_t i = 1; passed && i < len; ++i) { 54 | passed = data_arr[i] == checked; 55 | } 56 | 57 | if (passed && 0 != reinterpret_cast(connection->data)) { 58 | passed = checked == reinterpret_cast(connection->data); 59 | } 60 | 61 | if (!passed) { 62 | ++conf.sum_recv_err; 63 | std::cerr << "recv callback check error" << std::endl; 64 | } else { 65 | ++conf.sum_recv_times; 66 | conf.sum_recv_len += s; 67 | } 68 | 69 | ++checked; 70 | } 71 | 72 | const_cast(connection)->data = reinterpret_cast(checked); 73 | } 74 | 75 | static void stat_callback(uv_timer_t * /*handle*/) { 76 | static int secs = 0; 77 | static char unit_desc[][4] = {"B", "KB", "MB", "GB"}; 78 | static size_t unit_devi[] = {1UL, 1UL << 10, 1UL << 20, 1UL << 30}; 79 | static size_t unit_index = 0; 80 | 81 | ++secs; 82 | 83 | while (conf.sum_recv_len / unit_devi[unit_index] > 1024 && unit_index < sizeof(unit_devi) / sizeof(size_t) - 1) 84 | ++unit_index; 85 | 86 | while (conf.sum_recv_len / unit_devi[unit_index] <= 1024 && unit_index > 0) --unit_index; 87 | 88 | std::cout << "[ RUNNING ] NO." << secs << " m" << std::endl 89 | << "[ RUNNING ] recv(" << conf.sum_recv_times << " times, " << (conf.sum_recv_len / unit_devi[unit_index]) 90 | << " " << unit_desc[unit_index] << ") " << "full " << conf.sum_recv_full << " times, err " 91 | << conf.sum_recv_err << " times" << std::endl 92 | << std::endl; 93 | 94 | std::cout.flush(); 95 | std::cerr.flush(); 96 | } 97 | 98 | static void closed_callback(EXPLICIT_UNUSED_ATTR atbus::channel::io_stream_channel *channel, // 事件触发的channel 99 | EXPLICIT_UNUSED_ATTR atbus::channel::io_stream_connection *connection, // 事件触发的连接 100 | int /*status*/, // libuv传入的转态码 101 | void *, // 额外参数(不同事件不同含义) 102 | size_t // 额外参数长度 103 | ) { 104 | assert(channel); 105 | assert(connection); 106 | 107 | // 除listen外的最后一个连接 108 | if (channel->conn_pool.size() <= 2) uv_stop(channel->ev_loop); 109 | } 110 | 111 | int main(int argc, char *argv[]) { 112 | if (argc < 2) { 113 | printf("usage: %s
[max unit size] [limit number]\n", argv[0]); 114 | return 0; 115 | } 116 | 117 | using namespace atbus::channel; 118 | 119 | if (argc > 2) 120 | conf.max_n = (size_t)strtol(argv[2], nullptr, 10); 121 | else 122 | conf.max_n = 1024; 123 | 124 | conf.limit_size = sizeof(size_t) * conf.max_n; // 64KB 125 | 126 | if (argc > 3) 127 | conf.limit_static_num = (size_t)strtol(argv[3], nullptr, 10); 128 | else 129 | conf.limit_static_num = 2; // default 130 | 131 | conf.sum_recv_len = 0; 132 | conf.sum_recv_times = 0; 133 | conf.sum_recv_full = 0; 134 | conf.sum_recv_err = 0; 135 | 136 | io_stream_conf cfg; 137 | io_stream_init_configure(&cfg); 138 | cfg.recv_buffer_max_size = 139 | conf.limit_size + atbus::detail::buffer_block::full_size(cfg.recv_buffer_limit_size) * conf.limit_static_num + 140 | atbus::detail::buffer_block::padding_size(1); // 预留一个对齐单位的空区域 141 | cfg.recv_buffer_static = conf.limit_static_num; 142 | 143 | io_stream_channel channel; 144 | io_stream_init(&channel, uv_default_loop(), &cfg); 145 | channel.evt.callbacks[io_stream_callback_evt_t::EN_FN_RECVED] = recv_callback; 146 | channel.evt.callbacks[io_stream_callback_evt_t::EN_FN_DISCONNECTED] = closed_callback; 147 | 148 | channel_address_t addr; 149 | make_address(argv[1], addr); 150 | 151 | if (io_stream_listen(&channel, addr, nullptr, nullptr, 0) < 0) { 152 | std::cerr << "listen to " << argv[1] << " failed." << uv_err_name(channel.error_code) << ":" 153 | << uv_strerror(channel.error_code) << std::endl; 154 | io_stream_close(&channel); 155 | return -1; 156 | } 157 | 158 | uv_timer_t stat; 159 | uv_timer_init(uv_default_loop(), &stat); 160 | uv_timer_start(&stat, stat_callback, 60000, 60000); 161 | 162 | int ret = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 163 | io_stream_close(&channel); 164 | return ret; 165 | } 166 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: "main" 2 | 3 | on: # @see https://help.github.com/en/articles/events-that-trigger-workflows#webhook-events 4 | push: 5 | branches: # Array of patterns that match refs/heads 6 | - master # Push events on master branch 7 | - main 8 | pull_request: 9 | branches: [main] 10 | 11 | jobs: 12 | format: 13 | name: Format 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | - name: CI Job 19 | shell: bash 20 | run: | 21 | bash ci/do_ci.sh format ; 22 | unix_build: # job id, can be any string 23 | name: Unix Build 24 | # This job runs on Linux 25 | strategy: 26 | matrix: 27 | include: 28 | - os: ubuntu-latest 29 | triplet: x64-linux 30 | cc: gcc 31 | - os: ubuntu-latest 32 | triplet: x64-linux 33 | cc: clang-latest 34 | - os: macos-latest 35 | triplet: x64-osx 36 | cc: clang-latest 37 | runs-on: ${{ matrix.os }} 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | - name: Generate cache key 42 | shell: bash 43 | run: git submodule > '.github/.cache-key' 44 | - name: Cache packages 45 | uses: actions/cache@v4 46 | with: 47 | path: | 48 | third_party/install 49 | key: ${{ matrix.os }}-${{ matrix.cc }}-${{ hashFiles('.github/.cache-key') }} 50 | - name: Build & Test 51 | shell: bash 52 | env: 53 | USE_CC: ${{ matrix.cc }} 54 | VCPKG_TARGET_TRIPLET: ${{ matrix.triplet }} 55 | run: | 56 | # The OpenSSL config package in apple ci job is break 57 | if [ -e /opt/homebrew/lib/cmake/OpenSSL ]; then 58 | rm -rf /opt/homebrew/lib/cmake/OpenSSL || true 59 | fi 60 | if [[ "x$USE_CC" =~ xclang.* ]]; then 61 | bash ci/do_ci.sh clang.test ; 62 | else 63 | bash ci/do_ci.sh ssl.openssl ; 64 | fi 65 | vs2022_build: # job id, can be any string 66 | name: "Visual Studio 2022 Build" 67 | strategy: 68 | matrix: 69 | include: 70 | - os: windows-latest 71 | generator: "Visual Studio 17 2022" 72 | build_shared_libs: "ON" 73 | platform: x64 74 | - os: windows-latest 75 | generator: "Visual Studio 17 2022" 76 | build_shared_libs: "OFF" 77 | platform: x64 78 | runs-on: ${{ matrix.os }} 79 | steps: 80 | - name: Checkout 81 | uses: actions/checkout@v4 82 | - name: Generate cache key 83 | shell: bash 84 | run: git submodule > '.github/.cache-key' 85 | - name: Cache packages 86 | uses: actions/cache@v4 87 | with: 88 | path: | 89 | third_party/install 90 | key: ${{ matrix.os }}-shared-${{ matrix.build_shared_libs }}-${{ hashFiles('.github/.cache-key') }} 91 | - name: Build & Test 92 | shell: pwsh 93 | env: 94 | CMAKE_GENERATOR: ${{ matrix.generator }} 95 | CMAKE_PLATFORM: ${{ matrix.platform }} 96 | BUILD_SHARED_LIBS: ${{ matrix.build_shared_libs }} 97 | CONFIGURATION: RelWithDebInfo 98 | run: | 99 | pwsh ci/do_ci.ps1 "msvc.2019+.test" ; 100 | # mingw_build: # job id, can be any string 101 | # name: MinGW Build 102 | # strategy: 103 | # matrix: 104 | # include: 105 | # - os: windows-latest 106 | # build_shared_libs: "ON" 107 | # runs-on: ${{ matrix.os }} 108 | # steps: 109 | # - name: Checkout 110 | # uses: actions/checkout@v4 111 | # - name: Generate cache key 112 | # shell: bash 113 | # run: git submodule > '.github/.cache-key' 114 | # - name: Cache packages 115 | # uses: actions/cache@v4 116 | # with: 117 | # path: | 118 | # third_party/install 119 | # C:/msys64/var/cache/pacman/pkg 120 | # key: ${{ matrix.os }}-mingw-shared-${{ matrix.build_shared_libs }}-${{ hashFiles('.github/.cache-key') }} 121 | # - name: Build & Test 122 | # shell: bash 123 | # env: 124 | # BUILD_SHARED_LIBS: ${{ matrix.build_shared_libs }} 125 | # run: | 126 | # C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -lc "ci/do_ci.sh msys2.mingw.test" 127 | codeql: # job id, can be any string 128 | name: CodeQL 129 | # This job runs on Linux 130 | strategy: 131 | matrix: 132 | include: 133 | - os: ubuntu-latest 134 | triplet: x64-linux 135 | cc: gcc 136 | gcov_flags: "--coverage -fprofile-arcs -ftest-coverage" 137 | runs-on: ${{ matrix.os }} 138 | steps: 139 | - name: Checkout 140 | uses: actions/checkout@v4 141 | - name: Generate cache key 142 | shell: bash 143 | run: git submodule > '.github/.cache-key' 144 | - name: Cache packages 145 | uses: actions/cache@v4 146 | with: 147 | path: | 148 | third_party/install 149 | key: ${{ matrix.os }}-coverage-${{ hashFiles('.github/.cache-key') }} 150 | - name: Configure 151 | shell: bash 152 | env: 153 | USE_CC: ${{ matrix.cc }} 154 | VCPKG_TARGET_TRIPLET: ${{ matrix.triplet }} 155 | GCOV_FLAGS: "${{ matrix.gcov_flags }}" 156 | run: | 157 | bash ci/do_ci.sh codeql.configure 158 | - name: Initialize CodeQL 159 | uses: github/codeql-action/init@v3 160 | with: 161 | config-file: ./.github/codeql/codeql-config.yml 162 | - name: Build 163 | shell: bash 164 | env: 165 | USE_CC: ${{ matrix.cc }} 166 | run: | 167 | bash ci/do_ci.sh codeql.build 168 | - name: Perform CodeQL Analysis 169 | uses: github/codeql-action/analyze@v3 170 | with: 171 | category: "/language:cpp" 172 | -------------------------------------------------------------------------------- /test/case/atbus_node_relationship_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common/string_oprs.h" 8 | 9 | #ifdef max 10 | # undef max 11 | #endif 12 | 13 | #ifdef min 14 | # undef min 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | #include "frame/test_macros.h" 21 | 22 | #if 0 // Unit Test for flatbuffers implements 23 | CASE_TEST(atbus_node_rela, basic_test) { 24 | std::vector packed_buffer; 25 | char test_buffer[] = "hello world!"; 26 | 27 | { 28 | ::flatbuffers::FlatBufferBuilder fbb; 29 | 30 | uint64_t self_id = 0x12345678; 31 | uint32_t flags = 0; 32 | flags |= atbus::protocol::ATBUS_FORWARD_DATA_FLAG_TYPE_REQUIRE_RSP; 33 | 34 | fbb.Finish(::atframework::atbus::protocol::Createmsg(fbb, 35 | ::atframework::atbus::protocol::Createmsg_head(fbb, ::atframework::atbus::protocol::ATBUS_PROTOCOL_CONST_ATBUS_PROTOCOL_VERSION, 36 | 123, 0, 9876543210, self_id), 37 | ::atframework::atbus::protocol::msg_body_data_transform_req, 38 | ::atframework::atbus::protocol::Createforward_data(fbb, 0x123456789, 0x987654321, fbb.CreateVector(&self_id, 1), 39 | fbb.CreateVector(reinterpret_cast(test_buffer), sizeof(test_buffer)), 40 | flags) 41 | .Union()) 42 | 43 | ); 44 | packed_buffer.assign(reinterpret_cast(fbb.GetBufferPointer()), 45 | reinterpret_cast(fbb.GetBufferPointer()) + fbb.GetSize()); 46 | std::stringstream so; 47 | atfw::util::string::serialization(packed_buffer.data(), packed_buffer.size(), so); 48 | CASE_MSG_INFO() << "flatbuffers encoded(size=" << packed_buffer.size() << "): " << so.str() << std::endl; 49 | } 50 | 51 | { 52 | ::flatbuffers::Verifier msg_verify(reinterpret_cast(&packed_buffer[0]), packed_buffer.size()); 53 | CASE_EXPECT_TRUE(::atframework::atbus::protocol::VerifymsgBuffer(msg_verify)); 54 | const ::atframework::atbus::protocol::msg* m = ::atframework::atbus::protocol::Getmsg(&packed_buffer[0]); 55 | 56 | CASE_EXPECT_EQ(::atframework::atbus::protocol::msg_body_data_transform_req, m->body_type()); 57 | CASE_EXPECT_EQ(123, m->head()->type()); 58 | CASE_EXPECT_EQ(0, m->head()->ret()); 59 | CASE_EXPECT_EQ(9876543210, m->head()->sequence()); 60 | CASE_EXPECT_EQ(0x12345678, m->head()->source_bus_id()); 61 | 62 | CASE_EXPECT_EQ(0x123456789, m->body_as_data_transform_req()->from()); 63 | CASE_EXPECT_EQ(0x987654321, m->body_as_data_transform_req()->to()); 64 | CASE_EXPECT_EQ(0x12345678, m->body_as_data_transform_req()->router()->Get(0)); 65 | CASE_EXPECT_EQ( 66 | 0, UTIL_STRFUNC_STRNCMP(test_buffer, reinterpret_cast(m->body_as_data_transform_req()->content()->data()), sizeof(test_buffer))); 67 | } 68 | } 69 | #endif 70 | 71 | CASE_TEST(atbus_node_rela, copy_conf) { 72 | atbus::node::conf_t c1; 73 | atbus::node::conf_t c2(c1); 74 | 75 | atbus::node::default_conf((atbus::node::conf_t*)nullptr); 76 | atbus::node::default_conf((atbus::node::start_conf_t*)nullptr); 77 | } 78 | 79 | CASE_TEST(atbus_node_rela, child_endpoint_opr) { 80 | atbus::node::conf_t conf; 81 | atbus::node::default_conf(&conf); 82 | conf.subnets.push_back(atbus::endpoint_subnet_conf(0, 16)); 83 | conf.subnets.push_back(atbus::endpoint_subnet_conf(0x22345679, 16)); 84 | 85 | atbus::node::ptr_t node = atbus::node::create(); 86 | node->init(0x12345678, &conf); 87 | 88 | std::vector ep_subnets; 89 | ep_subnets.push_back(atbus::endpoint_subnet_conf(0, 8)); 90 | atbus::endpoint::ptr_t ep = 91 | atbus::endpoint::create(node.get(), 0x12345679, ep_subnets, node->get_pid(), node->get_hostname()); 92 | // 插入到末尾 93 | CASE_EXPECT_EQ(0, node->add_endpoint(ep)); 94 | CASE_EXPECT_EQ(1, node->get_routes().size()); 95 | 96 | // 插入到中间 97 | ep = atbus::endpoint::create(node.get(), 0x12345589, ep_subnets, node->get_pid(), node->get_hostname()); 98 | CASE_EXPECT_EQ(0, node->add_endpoint(ep)); 99 | CASE_EXPECT_EQ(2, node->get_routes().size()); 100 | 101 | // 新端点子域冲突-父子关系 102 | ep_subnets.clear(); 103 | ep_subnets.push_back(atbus::endpoint_subnet_conf(0, 4)); 104 | ep = atbus::endpoint::create(node.get(), 0x12345680, ep_subnets, node->get_pid(), node->get_hostname()); 105 | CASE_EXPECT_EQ(EN_ATBUS_ERR_PARAMS, node->add_endpoint(atbus::endpoint::ptr_t())); 106 | CASE_EXPECT_EQ(EN_ATBUS_ERR_ATNODE_MASK_CONFLICT, node->add_endpoint(ep)); 107 | 108 | // 新端点子域冲突-子父关系 109 | ep_subnets.clear(); 110 | ep_subnets.push_back(atbus::endpoint_subnet_conf(0, 12)); 111 | ep = atbus::endpoint::create(node.get(), 0x12345780, ep_subnets, node->get_pid(), node->get_hostname()); 112 | CASE_EXPECT_EQ(EN_ATBUS_ERR_ATNODE_MASK_CONFLICT, node->add_endpoint(ep)); 113 | ep = atbus::endpoint::create(node.get(), 0x12345480, ep_subnets, node->get_pid(), node->get_hostname()); 114 | CASE_EXPECT_EQ(EN_ATBUS_ERR_ATNODE_MASK_CONFLICT, node->add_endpoint(ep)); 115 | 116 | // 新端点子域冲突-ID不同子域相同 117 | ep_subnets.clear(); 118 | ep_subnets.push_back(atbus::endpoint_subnet_conf(0, 8)); 119 | ep = atbus::endpoint::create(node.get(), 0x12345680, ep_subnets, node->get_pid(), node->get_hostname()); 120 | CASE_EXPECT_EQ(EN_ATBUS_ERR_ATNODE_MASK_CONFLICT, node->add_endpoint(ep)); 121 | 122 | // 跨域添加节点 - 成功 123 | ep = atbus::endpoint::create(node.get(), 0x22345679, ep_subnets, node->get_pid(), node->get_hostname()); 124 | CASE_EXPECT_EQ(0, node->add_endpoint(ep)); 125 | 126 | // 跨域添加节点 - 范围不完全覆盖 127 | ep_subnets.push_back(atbus::endpoint_subnet_conf(0x32345680, 8)); 128 | ep = atbus::endpoint::create(node.get(), 0x22345680, ep_subnets, node->get_pid(), node->get_hostname()); 129 | CASE_EXPECT_EQ(EN_ATBUS_ERR_ATNODE_MASK_CONFLICT, node->add_endpoint(ep)); 130 | 131 | CASE_EXPECT_EQ(3, node->get_routes().size()); 132 | 133 | // 移除失败-找不到 134 | CASE_EXPECT_EQ(EN_ATBUS_ERR_ATNODE_NOT_FOUND, node->remove_endpoint(0x12345680)); 135 | // 移除成功 136 | CASE_EXPECT_EQ(EN_ATBUS_ERR_SUCCESS, node->remove_endpoint(0x12345589)); 137 | CASE_EXPECT_EQ(EN_ATBUS_ERR_SUCCESS, node->remove_endpoint(0x22345679)); 138 | } 139 | -------------------------------------------------------------------------------- /include/detail/libatbus_error.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBATBUS_DETAIL_LIBATBUS_ERROR_H 2 | #define LIBATBUS_DETAIL_LIBATBUS_ERROR_H 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include "detail/libatbus_config.h" 11 | 12 | enum ATBUS_ERROR_TYPE { 13 | EN_ATBUS_ERR_SUCCESS = 0, 14 | 15 | EN_ATBUS_ERR_PARAMS = -1, 16 | EN_ATBUS_ERR_INNER = -2, 17 | EN_ATBUS_ERR_NO_DATA = -3, // 无数据 18 | EN_ATBUS_ERR_BUFF_LIMIT = -4, // 缓冲区不足 19 | EN_ATBUS_ERR_MALLOC = -5, // 分配失败 20 | EN_ATBUS_ERR_SCHEME = -6, // 协议错误 21 | EN_ATBUS_ERR_BAD_DATA = -7, // 数据校验不通过 22 | EN_ATBUS_ERR_INVALID_SIZE = -8, // 数据大小异常 23 | EN_ATBUS_ERR_NOT_INITED = -9, // 未初始化 24 | EN_ATBUS_ERR_ALREADY_INITED = -10, // 已填充初始数据 25 | EN_ATBUS_ERR_ACCESS_DENY = -11, // 不允许的操作 26 | EN_ATBUS_ERR_UNPACK = -12, // 解包失败 27 | EN_ATBUS_ERR_PACK = -13, // 打包失败 28 | EN_ATBUS_ERR_UNSUPPORTED_VERSION = -14, // 版本不受支持 29 | EN_ATBUS_ERR_CLOSING = -15, // 正在关闭或已关闭 30 | EN_ATBUS_ERR_ALGORITHM_NOT_SUPPORT = -16, // 算法不受支持 31 | EN_ATBUS_ERR_MESSAGE_NOT_FINISH_YET = -17, // 消息尚未调用finish 32 | 33 | EN_ATBUS_ERR_ATNODE_NOT_FOUND = -65, // 查找不到目标节点 34 | EN_ATBUS_ERR_ATNODE_INVALID_ID = -66, // 不可用的ID 35 | EN_ATBUS_ERR_ATNODE_NO_CONNECTION = -67, // 无可用连接 36 | EN_ATBUS_ERR_ATNODE_FAULT_TOLERANT = -68, // 超出容错值 37 | EN_ATBUS_ERR_ATNODE_INVALID_MSG = -69, // 错误的消息 38 | EN_ATBUS_ERR_ATNODE_BUS_ID_NOT_MATCH = -70, // Bus ID不匹配 39 | EN_ATBUS_ERR_ATNODE_TTL = -71, // ttl限制 40 | EN_ATBUS_ERR_ATNODE_MASK_CONFLICT = -72, // 域范围错误或冲突 41 | EN_ATBUS_ERR_ATNODE_ID_CONFLICT = -73, // ID冲突 42 | EN_ATBUS_ERR_ATNODE_SRC_DST_IS_SAME = -75, // 发送源和发送目标不能相同 43 | 44 | EN_ATBUS_ERR_CHANNEL_SIZE_TOO_SMALL = -101, 45 | EN_ATBUS_ERR_CHANNEL_BUFFER_INVALID = -102, // 缓冲区错误(已被其他模块使用或检测冲突) 46 | EN_ATBUS_ERR_CHANNEL_ADDR_INVALID = -103, // 地址错误 47 | EN_ATBUS_ERR_CHANNEL_CLOSING = -104, // 正在关闭 48 | EN_ATBUS_ERR_CHANNEL_NOT_SUPPORT = -105, // 不支持的通道 49 | EN_ATBUS_ERR_CHANNEL_UNSUPPORTED_VERSION = -106, // 通道版本不受支持 50 | EN_ATBUS_ERR_CHANNEL_ALIGN_SIZE_MISMATCH = -107, // 对齐参数不一致 51 | EN_ATBUS_ERR_CHANNEL_ARCH_SIZE_T_MISMATCH = -108, // 架构size_t不匹配 52 | 53 | EN_ATBUS_ERR_NODE_BAD_BLOCK_NODE_NUM = -202, // 发现写坏的数据块 - 节点数量错误 54 | EN_ATBUS_ERR_NODE_BAD_BLOCK_BUFF_SIZE = -203, // 发现写坏的数据块 - 节点数量错误 55 | EN_ATBUS_ERR_NODE_BAD_BLOCK_WSEQ_ID = -204, // 发现写坏的数据块 - 写操作序列错误 56 | EN_ATBUS_ERR_NODE_BAD_BLOCK_CSEQ_ID = -205, // 发现写坏的数据块 - 检查操作序列错误 57 | 58 | EN_ATBUS_ERR_NODE_TIMEOUT = -211, // 操作超时 59 | 60 | EN_ATBUS_ERR_SHM_GET_FAILED = -301, // 连接共享内存出错,具体错误原因可以查看errno或类似的位置 61 | EN_ATBUS_ERR_SHM_NOT_FOUND = -302, // 共享内存未找到 62 | EN_ATBUS_ERR_SHM_CLOSE_FAILED = -303, // 移除共享内存出错,具体错误原因可以查看errno或类似的位置 63 | EN_ATBUS_ERR_SHM_PATH_INVALID = -304, // 共享内存地址错误 64 | EN_ATBUS_ERR_SHM_MAP_FAILED = -305, // 共享内存地址映射错误 65 | 66 | EN_ATBUS_ERR_SOCK_BIND_FAILED = -401, // 绑定地址或端口失败 67 | EN_ATBUS_ERR_SOCK_LISTEN_FAILED = -402, // 监听失败 68 | EN_ATBUS_ERR_SOCK_CONNECT_FAILED = -403, // 连接失败 69 | 70 | EN_ATBUS_ERR_PIPE_BIND_FAILED = -501, // 绑定地址或端口失败 71 | EN_ATBUS_ERR_PIPE_LISTEN_FAILED = -502, // 监听失败 72 | EN_ATBUS_ERR_PIPE_CONNECT_FAILED = -503, // 连接失败 73 | EN_ATBUS_ERR_PIPE_ADDR_TOO_LONG = -504, // 地址路径过长,绝对路径长度要小于 sizeof(sockaddr_un.sun_path) - 1 74 | EN_ATBUS_ERR_PIPE_REMOVE_FAILED = -505, // 删除老socket失败 75 | EN_ATBUS_ERR_PIPE_PATH_EXISTS = -506, // 该地址已被占用 76 | EN_ATBUS_ERR_PIPE_LOCK_PATH_FAILED = -507, // 锁地址失败 77 | 78 | EN_ATBUS_ERR_DNS_GETADDR_FAILED = -601, // DNS解析失败 79 | EN_ATBUS_ERR_CONNECTION_NOT_FOUND = -602, // 找不到连接 80 | EN_ATBUS_ERR_WRITE_FAILED = -603, // 底层API写失败 81 | EN_ATBUS_ERR_READ_FAILED = -604, // 底层API读失败 82 | EN_ATBUS_ERR_EV_RUN = -605, // 底层API事件循环失败 83 | EN_ATBUS_ERR_NO_LISTEN = -606, // 尚未监听(绑定) 84 | EN_ATBUS_ERR_NOT_READY = -607, // 未准备好(没有握手完成) 85 | 86 | EN_ATBUS_ERR_MIN = -999, 87 | }; 88 | 89 | template 90 | struct libatbus_strerror_helper; 91 | 92 | ATBUS_MACRO_API const std::basic_string &libatbus_strerror(ATBUS_ERROR_TYPE errcode) noexcept; 93 | template <> 94 | struct libatbus_strerror_helper { 95 | ATFW_UTIL_FORCEINLINE static const std::basic_string &get(ATBUS_ERROR_TYPE errcode) noexcept { 96 | return libatbus_strerror(errcode); 97 | } 98 | }; 99 | 100 | ATBUS_MACRO_API const std::basic_string &libatbus_wstrerror(ATBUS_ERROR_TYPE errcode) noexcept; 101 | template <> 102 | struct libatbus_strerror_helper { 103 | ATFW_UTIL_FORCEINLINE static const std::basic_string &get(ATBUS_ERROR_TYPE errcode) noexcept { 104 | return libatbus_wstrerror(errcode); 105 | } 106 | }; 107 | 108 | #ifdef __cpp_unicode_characters 109 | ATBUS_MACRO_API const std::basic_string &libatbus_u16strerror(ATBUS_ERROR_TYPE errcode) noexcept; 110 | template <> 111 | struct libatbus_strerror_helper { 112 | ATFW_UTIL_FORCEINLINE static const std::basic_string &get(ATBUS_ERROR_TYPE errcode) noexcept { 113 | return libatbus_u16strerror(errcode); 114 | } 115 | }; 116 | ATBUS_MACRO_API const std::basic_string &libatbus_u32strerror(ATBUS_ERROR_TYPE errcode) noexcept; 117 | template <> 118 | struct libatbus_strerror_helper { 119 | ATFW_UTIL_FORCEINLINE static const std::basic_string &get(ATBUS_ERROR_TYPE errcode) noexcept { 120 | return libatbus_u32strerror(errcode); 121 | } 122 | }; 123 | #endif 124 | #ifdef __cpp_char8_t 125 | ATBUS_MACRO_API const std::basic_string &libatbus_u8strerror(ATBUS_ERROR_TYPE errcode) noexcept; 126 | template <> 127 | struct libatbus_strerror_helper { 128 | ATFW_UTIL_FORCEINLINE static const std::basic_string &get(ATBUS_ERROR_TYPE errcode) noexcept { 129 | return libatbus_u8strerror(errcode); 130 | } 131 | }; 132 | #endif 133 | 134 | ATFRAMEWORK_UTILS_STRING_FWAPI_NAMESPACE_BEGIN 135 | template 136 | struct ATFW_UTIL_SYMBOL_VISIBLE formatter<::ATBUS_ERROR_TYPE, CharT> 137 | : formatter, CharT> { 138 | template 139 | auto format(ATBUS_ERROR_TYPE const &val, FormatContext &ctx) const -> decltype(ctx.out()) { 140 | return formatter, CharT>::format( 141 | ATFRAMEWORK_UTILS_STRING_FWAPI_NAMESPACE_ID::basic_string_view{ 142 | libatbus_strerror_helper::get(val)}, 143 | ctx); 144 | } 145 | }; 146 | ATFRAMEWORK_UTILS_STRING_FWAPI_NAMESPACE_END 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /include/detail/libatbus_channel_types.h: -------------------------------------------------------------------------------- 1 | /** 2 | * libatbus_channel_types.h 3 | * 4 | * Created on: 2014年8月13日 5 | * Author: owent 6 | */ 7 | #ifndef LIBATBUS_CHANNEL_TYPES_H 8 | #define LIBATBUS_CHANNEL_TYPES_H 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "lock/seq_alloc.h" 22 | 23 | #include "detail/libatbus_config.h" 24 | 25 | #include "buffer.h" 26 | #include "detail/libatbus_adapter_libuv.h" 27 | 28 | #if defined(__ANDROID__) 29 | #elif defined(__APPLE__) 30 | # if __dest_os == __mac_os_x 31 | # include 32 | # include 33 | 34 | # define ATBUS_CHANNEL_SHM 1 35 | # endif 36 | #elif defined(__unix__) 37 | # include 38 | # include 39 | 40 | # define ATBUS_CHANNEL_SHM 1 41 | #else 42 | # include 43 | 44 | # define ATBUS_CHANNEL_SHM 1 45 | #endif 46 | 47 | ATBUS_MACRO_NAMESPACE_BEGIN 48 | namespace channel { 49 | // utility functions 50 | struct ATBUS_MACRO_API_HEAD_ONLY channel_address_t { 51 | std::string address; // 主机完整地址,比如:ipv4://127.0.0.1:8123 或 unix:///tmp/atbut.sock 52 | std::string scheme; // 协议名称,比如:ipv4 或 unix 53 | std::string host; // 主机地址,比如:127.0.0.1 或 /tmp/atbut.sock 54 | int port; // 端口。(仅网络连接有效) 55 | }; 56 | 57 | // memory channel 58 | struct mem_channel; 59 | struct mem_conf; 60 | 61 | struct ATBUS_MACRO_API_HEAD_ONLY mem_stats_block_error { 62 | // 统计信息 63 | size_t write_check_sequence_failed_count; // 写完后校验操作序号错误 64 | size_t write_retry_count; // 写操作内部重试次数 65 | 66 | size_t read_bad_node_count; // 读到的错误数据节点数量 67 | size_t read_bad_block_count; // 读到的错误数据块数量 68 | size_t read_write_timeout_count; // 读到的写超时保护数量 69 | size_t read_check_block_size_failed_count; // 读到的数据块长度检查错误数量 70 | size_t read_check_node_size_failed_count; // 读到的数据节点和长度检查错误数量 71 | size_t read_check_hash_failed_count; // 读到的数据hash值检查错误数量 72 | }; 73 | 74 | #ifdef ATBUS_CHANNEL_SHM 75 | // shared memory channel 76 | struct shm_channel; 77 | struct shm_conf; 78 | 79 | using shm_stats_block_error = mem_stats_block_error; 80 | #endif 81 | 82 | // stream channel(tcp,pipe(unix socket) and etc. udp is not a stream) 83 | struct io_stream_connection; 84 | struct io_stream_channel; 85 | using io_stream_callback_t = void (*)(io_stream_channel *channel, // 事件触发的channel 86 | io_stream_connection *connection, // 事件触发的连接 87 | int status, // libuv传入的转态码 88 | void *, // 额外参数(不同事件不同含义) 89 | size_t s // 额外参数长度 90 | ); 91 | 92 | struct ATBUS_MACRO_API_HEAD_ONLY io_stream_callback_evt_t { 93 | enum mem_fn_t { 94 | EN_FN_ACCEPTED = 0, 95 | EN_FN_CONNECTED, // 连接或listen成功 96 | EN_FN_DISCONNECTED, 97 | EN_FN_RECVED, 98 | EN_FN_WRITEN, 99 | MAX 100 | }; 101 | // 回调函数 102 | io_stream_callback_t callbacks[MAX]; 103 | }; 104 | 105 | // 以下不是POD类型,所以不得不暴露出来 106 | struct ATBUS_MACRO_API_HEAD_ONLY io_stream_connection { 107 | enum flag_t { 108 | EN_CF_LISTEN = 0, 109 | EN_CF_CONNECT, 110 | EN_CF_ACCEPT, 111 | EN_CF_WRITING, 112 | EN_CF_CLOSING, 113 | EN_CF_MAX, 114 | }; 115 | 116 | channel_address_t addr; 117 | std::shared_ptr handle; // 流设备 118 | adapter::fd_t fd; // 文件描述符 119 | 120 | enum status_t { EN_ST_CREATED = 0, EN_ST_CONNECTED, EN_ST_DISCONNECTING, EN_ST_DISCONNECTED }; 121 | status_t status; // 状态 122 | int flags; // flag 123 | io_stream_channel *channel; 124 | 125 | // 事件响应 126 | io_stream_callback_evt_t evt; 127 | io_stream_callback_t act_disc_cbk; // 主动关闭连接的回调(为了减少额外分配而采用的缓存策略) 128 | 129 | // 数据区域 130 | // 读数据缓冲区(两种Buffer管理方式,一种动态,一种静态) 131 | /** 132 | * @note 由于大多数数据包都比较小 133 | * 当数据包比较小时和动态直接放在动态int的数据包一起,这样可以减少内存拷贝次数 134 | */ 135 | ::atframework::atbus::detail::buffer_manager read_buffers; 136 | 137 | struct read_head_t { 138 | char buffer[ATBUS_MACRO_DATA_SMALL_SIZE]; // varint数据暂存区和小数据包存储区 139 | size_t len; // varint数据暂存区和小数据包存储区已使用长度 140 | }; 141 | read_head_t read_head; 142 | ::atframework::atbus::detail::buffer_manager write_buffers; // 写数据缓冲区(两种Buffer管理方式,一种动态,一种静态) 143 | 144 | // 自定义数据区域 145 | void *data; 146 | }; 147 | 148 | struct ATBUS_MACRO_API_HEAD_ONLY io_stream_conf { 149 | time_t keepalive; 150 | 151 | bool is_noblock; 152 | bool is_nodelay; 153 | size_t send_buffer_static; 154 | size_t recv_buffer_static; 155 | size_t send_buffer_max_size; 156 | size_t send_buffer_limit_size; 157 | size_t recv_buffer_max_size; 158 | size_t recv_buffer_limit_size; 159 | 160 | int backlog; // backlog indicates the number of connections the kernel might queue 161 | 162 | time_t confirm_timeout; 163 | size_t max_read_net_eagain_count; 164 | size_t max_read_check_block_size_failed_count; 165 | size_t max_read_check_hash_failed_count; 166 | }; 167 | 168 | struct ATBUS_MACRO_API_HEAD_ONLY io_stream_channel { 169 | enum flag_t { 170 | EN_CF_IS_LOOP_OWNER = 0, 171 | EN_CF_CLOSING, 172 | EN_CF_IN_CALLBACK, 173 | EN_CF_MAX, 174 | }; 175 | 176 | adapter::loop_t *ev_loop; 177 | int flags; 178 | 179 | io_stream_conf conf; 180 | 181 | using conn_pool_t = std::unordered_map>; 182 | conn_pool_t conn_pool; 183 | using conn_gc_pool_t = std::unordered_map>; 184 | conn_gc_pool_t conn_gc_pool; 185 | 186 | // 事件响应 187 | io_stream_callback_evt_t evt; 188 | 189 | int error_code; // 记录外部的错误码 190 | // 统计信息 191 | atfw::util::lock::seq_alloc_u32 active_reqs; // 正在进行的req数量 192 | size_t read_net_eagain_count; // 读到的网络重试错误数量 193 | size_t read_check_block_size_failed_count; // 读到的数据块长度检查错误数量 194 | size_t read_check_hash_failed_count; // 读到的数据hash检查错误数量 195 | 196 | // 自定义数据区域 197 | void *data; 198 | }; 199 | 200 | #define ATBUS_CHANNEL_IOS_CHECK_FLAG(f, v) (0 != ((f) & (1 << (v)))) 201 | #define ATBUS_CHANNEL_IOS_SET_FLAG(f, v) (f) |= (1 << (v)) 202 | #define ATBUS_CHANNEL_IOS_UNSET_FLAG(f, v) (f) &= ~(1 << (v)) 203 | #define ATBUS_CHANNEL_IOS_CLEAR_FLAG(f) (f) = 0 204 | 205 | #define ATBUS_CHANNEL_REQ_START(channel) (channel)->active_reqs.inc() 206 | #define ATBUS_CHANNEL_REQ_ACTIVE(channel) ((channel)->active_reqs.get() > 0) 207 | 208 | #define ATBUS_CHANNEL_REQ_END(channel) \ 209 | assert(ATBUS_CHANNEL_REQ_ACTIVE(channel)); \ 210 | (channel)->active_reqs.dec() 211 | } // namespace channel 212 | ATBUS_MACRO_NAMESPACE_END 213 | 214 | #endif /* LIBATBUS_CHANNEL_EXPORT_H_ */ 215 | -------------------------------------------------------------------------------- /include/detail/libatbus_channel_export.h: -------------------------------------------------------------------------------- 1 | /** 2 | * libatbus_channel_export.h 3 | * 4 | * Created on: 2014年8月13日 5 | * Author: owent 6 | */ 7 | 8 | #pragma once 9 | 10 | #ifndef LIBATBUS_CHANNEL_EXPORT_H 11 | # define LIBATBUS_CHANNEL_EXPORT_H 12 | 13 | # pragma once 14 | 15 | # include 16 | # include 17 | # include 18 | # include 19 | # include 20 | 21 | # include "detail/libatbus_config.h" 22 | 23 | # include "detail/libatbus_adapter_libuv.h" 24 | 25 | # include "detail/libatbus_channel_types.h" 26 | 27 | ATBUS_MACRO_NAMESPACE_BEGIN 28 | namespace channel { 29 | // utility functions 30 | ATBUS_MACRO_API bool make_address(const char *in, channel_address_t &addr); 31 | ATBUS_MACRO_API void make_address(const char *scheme, const char *host, int port, channel_address_t &addr); 32 | 33 | /** 34 | * @brief If it's a duplex address, means both enpoint has a connection to receive and send data 35 | * @param in address , start with unix:/ipv4:/ipv6:/dns:/shm: and etc. 36 | * @return true if it's a duplex address 37 | */ 38 | ATBUS_MACRO_API bool is_duplex_address(const char *in); 39 | 40 | /** 41 | * @brief If it's a simplex address, means the other node has no connection and can only receive data 42 | * @param in address , start with unix:/ipv4:/ipv6:/dns:/shm: and etc. 43 | * @return true if it's a simplex address 44 | */ 45 | ATBUS_MACRO_API bool is_simplex_address(const char *in); 46 | 47 | /** 48 | * @brief If it's a address that can only be connected by nodes on the same machine 49 | * @param in address , start with unix:/ipv4:/ipv6:/dns:/shm: and etc. 50 | * @return true if it's a address that can only be connected by nodes on the same machine 51 | */ 52 | ATBUS_MACRO_API bool is_local_host_address(const char *in); 53 | 54 | /** 55 | * @brief If it's a address that can only be connected by nodes on the same process 56 | * @param in address , start with unix:/ipv4:/ipv6:/dns:/shm: and etc. 57 | * @return true it's a address that can only be connected by nodes on the same process 58 | */ 59 | ATBUS_MACRO_API bool is_local_process_address(const char *in); 60 | 61 | // memory channel 62 | ATBUS_MACRO_API int mem_configure_set_write_timeout(mem_channel *channel, uint64_t ms); 63 | ATBUS_MACRO_API uint64_t mem_configure_get_write_timeout(mem_channel *channel); 64 | ATBUS_MACRO_API int mem_configure_set_write_retry_times(mem_channel *channel, size_t times); 65 | ATBUS_MACRO_API size_t mem_configure_get_write_retry_times(mem_channel *channel); 66 | ATBUS_MACRO_API uint16_t mem_info_get_version(mem_channel *channel); 67 | ATBUS_MACRO_API uint16_t mem_info_get_align_size(mem_channel *channel); 68 | ATBUS_MACRO_API uint16_t mem_info_get_host_size(mem_channel *channel); 69 | 70 | ATBUS_MACRO_API int mem_attach(void *buf, size_t len, mem_channel **channel, const mem_conf *conf); 71 | ATBUS_MACRO_API int mem_init(void *buf, size_t len, mem_channel **channel, const mem_conf *conf); 72 | ATBUS_MACRO_API int mem_send(mem_channel *channel, const void *buf, size_t len); 73 | ATBUS_MACRO_API int mem_recv(mem_channel *channel, void *buf, size_t len, size_t *recv_size); 74 | ATBUS_MACRO_API std::pair mem_last_action(); 75 | ATBUS_MACRO_API void mem_show_channel(mem_channel *channel, std::ostream &out, bool need_node_status, 76 | size_t need_node_data); 77 | 78 | ATBUS_MACRO_API void mem_stats_get_error(mem_channel *channel, mem_stats_block_error &out); 79 | 80 | # ifdef ATBUS_CHANNEL_SHM 81 | // shared memory channel 82 | ATBUS_MACRO_API int shm_configure_set_write_timeout(shm_channel *channel, uint64_t ms); 83 | ATBUS_MACRO_API uint64_t shm_configure_get_write_timeout(shm_channel *channel); 84 | ATBUS_MACRO_API int shm_configure_set_write_retry_times(shm_channel *channel, size_t times); 85 | ATBUS_MACRO_API size_t shm_configure_get_write_retry_times(shm_channel *channel); 86 | ATBUS_MACRO_API uint16_t shm_info_get_version(shm_channel *channel); 87 | ATBUS_MACRO_API uint16_t shm_info_get_align_size(shm_channel *channel); 88 | ATBUS_MACRO_API uint16_t shm_info_get_host_size(shm_channel *channel); 89 | 90 | /** 91 | * @brief shm_attach/shm_init/shm_close with shm_path 92 | * @param shm_path shm_path can be a number(means shared memory key) or a path begin with '/' 93 | * @note shm_path can only contains one '/' and the length shoud not extend 255 according to POSIX 94 | * On Windows, we will add prefix of "Global\\" for shm_path, so the length of shm_path can 95 | * not be grater than 248 96 | * @see http://man7.org/linux/man-pages/man3/shm_open.3.html 97 | * @see https://linux.die.net/man/3/shm_open 98 | * @see https://man.openbsd.org/shm_open.3 99 | */ 100 | ATBUS_MACRO_API int shm_attach(const char *shm_path, size_t len, shm_channel **channel, const shm_conf *conf); 101 | ATBUS_MACRO_API int shm_init(const char *shm_path, size_t len, shm_channel **channel, const shm_conf *conf); 102 | ATBUS_MACRO_API int shm_close(const char *shm_path); 103 | ATBUS_MACRO_API int shm_send(shm_channel *channel, const void *buf, size_t len); 104 | ATBUS_MACRO_API int shm_recv(shm_channel *channel, void *buf, size_t len, size_t *recv_size); 105 | ATBUS_MACRO_API std::pair shm_last_action(); 106 | ATBUS_MACRO_API void shm_show_channel(shm_channel *channel, std::ostream &out, bool need_node_status, 107 | size_t need_node_data); 108 | 109 | ATBUS_MACRO_API void shm_stats_get_error(shm_channel *channel, shm_stats_block_error &out); 110 | # endif 111 | 112 | // stream channel(tcp,pipe(unix socket) and etc. udp is not a stream) 113 | ATBUS_MACRO_API void io_stream_init_configure(io_stream_conf *conf); 114 | 115 | ATBUS_MACRO_API int io_stream_init(io_stream_channel *channel, adapter::loop_t *ev_loop, const io_stream_conf *conf); 116 | 117 | // it will block and wait for all connections are disconnected success. 118 | ATBUS_MACRO_API int io_stream_close(io_stream_channel *channel); 119 | 120 | ATBUS_MACRO_API int io_stream_run(io_stream_channel *channel, adapter::run_mode_t mode = adapter::RUN_NOWAIT); 121 | 122 | ATBUS_MACRO_API int io_stream_listen(io_stream_channel *channel, const channel_address_t &addr, 123 | io_stream_callback_t callback, void *priv_data, size_t priv_size); 124 | 125 | ATBUS_MACRO_API int io_stream_connect(io_stream_channel *channel, const channel_address_t &addr, 126 | io_stream_callback_t callback, void *priv_data, size_t priv_size); 127 | 128 | ATBUS_MACRO_API int io_stream_disconnect(io_stream_channel *channel, io_stream_connection *connection, 129 | io_stream_callback_t callback); 130 | ATBUS_MACRO_API int io_stream_disconnect_fd(io_stream_channel *channel, adapter::fd_t fd, 131 | io_stream_callback_t callback); 132 | ATBUS_MACRO_API int io_stream_try_write(io_stream_connection *connection); 133 | ATBUS_MACRO_API int io_stream_send(io_stream_connection *connection, const void *buf, size_t len); 134 | ATBUS_MACRO_API size_t io_stream_get_max_unix_socket_length(); 135 | 136 | ATBUS_MACRO_API void io_stream_show_channel(io_stream_channel *channel, std::ostream &out); 137 | } // namespace channel 138 | ATBUS_MACRO_NAMESPACE_END 139 | 140 | #endif /* LIBATBUS_CHANNEL_EXPORT_H_ */ 141 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ### 编译选项 2 | 除了cmake标准编译选项外,libatbus还提供一些额外选项 3 | 4 | + ATBUS_MACRO_BUSID_TYPE: busid的类型(默认: uint64_t),建议不要设置成大于64位,否则需要修改protocol目录内的busid类型,并且重新生成协议文件 5 | + GTEST_ROOT: 使用GTest单元测试框架 6 | + BOOST_ROOT: 设置Boost库根目录 7 | + PROJECT_TEST_ENABLE_BOOST_UNIT_TEST: 使用Boost.Test单元测试框架(如果GTEST_ROOT和此项都不设置,则使用内置单元测试框架) 8 | 9 | 10 | ## 开发文档 11 | ### 目录结构说明 12 | 13 | + third_party: 外部组件(不一定是依赖项) 14 | + docs: 文档目录 15 | + include: 导出lib的包含文件(注意不导出的内部接口后文件会直接在src目录里) 16 | + project: 工程工具和配置文件集 17 | + protocol: 协议描述文件目录 18 | + sample: 使用示例目录,每一个cpp文件都是一个完全独立的例子 19 | + src: 源文件和内部接口申明目录 20 | + test: 测试框架及测试用例目录 21 | 22 | ### 关于 #pragma once 23 | 由于目标平台和环境的编译器均已支持 #pragma once 功能,故而所有源代码直接使用这个关键字,以提升编译速度。 24 | 25 | 详见:[pragma once](http://zh.wikipedia.org/wiki/Pragma_once) 26 | 27 | 28 | ### 内存通道设计 29 | 单多写状态 30 | ``` 31 | ▼ ▼ 32 | -------------------WWWW####WWWW###---------------- 33 | △ 34 | ``` 35 | |长度| 节点头结构 | 说明 | 36 | |---|------------------------------|------------------| 37 | |1B | 标记 flag |是否写完、是否是头节点| 38 | |1B | 写权限(原子操作) | | 39 | |4B | 首读时间(毫秒) |最大容忍误差是49天| 40 | 41 | 42 | **内存通道结构(内存和共享内存)** 43 | 44 | 所有消息对齐到size_t的大小 45 | |4K通道头|数据节点头*数据节点个数|数据区| 46 | 47 | ```cpp 48 | // 通道头 49 | struct mem_channel { 50 | // 数据节点 51 | size_t node_size; /** 每个节点的size **/ 52 | size_t node_size_bin_power; // (用于优化算法) node_size = 1 << node_size_bin_power 53 | size_t node_count; /** 数据节点个数 **/ 54 | 55 | // [atomic_read_cur, atomic_write_cur) 内的数据块都是已使用的数据块 56 | // atomic_write_cur指向的数据块一定是空块,故而必然有一个node的空洞 57 | // c11的stdatomic.h在很多编译器不支持并且还有些潜规则(gcc 不能使用-fno-builtin 和 -march=xxx),故而使用c++版本 58 | volatile std::atomic atomic_read_cur; // std::atomic也是POD类型 59 | volatile std::atomic atomic_write_cur; // std::atomic也是POD类型 60 | 61 | // 第一次读到正在写入数据的时间 62 | uint32_t first_failed_writing_time; /** 第一次读到正在写节点的时间,用于跳过错误写 **/ 63 | 64 | volatile std::atomic atomic_operation_seq; // 操作序列号(用于保证只有一个接收者) 65 | 66 | // 配置 67 | mem_conf conf; 68 | size_t area_channel_offset; /** 地址偏移: channel **/ 69 | size_t area_head_offset; /** 地址偏移: 数据节点头 **/ 70 | size_t area_data_offset; /** 地址偏移: 数据区 **/ 71 | size_t area_end_offset; /** 地址偏移: 使用的缓冲区尾部 **/ 72 | 73 | // 统计信息 74 | size_t block_bad_count; // 读取到坏块次数 75 | size_t block_timeout_count; // 读取到写入超时块次数 76 | size_t node_bad_count; // 读取到坏node次数 77 | }; 78 | 79 | // 配置数据结构 80 | struct mem_conf { 81 | size_t protect_node_count; /** 保护节点个数:用于降低冲突概率 **/ 82 | size_t protect_memory_size; /** 保护内存大小:用于降低冲突概率 **/ 83 | uint64_t conf_send_timeout_ms; /** 发送超时阀值:用于降低冲突概率 **/ 84 | 85 | // TODO 接收端校验号(用于保证只有一个接收者) 86 | volatile std::atomic atomic_receiver_identify; 87 | }; 88 | ``` 89 | 90 | **写数据步骤:** 91 | 92 | 1. 获取写游标,比较读游标,判断是否有空间 93 | 2. 分配操作序号 94 | 3. 顺序设置操作序号,交换0序号块,失败则返回空间不足 95 | 4. 逆序写数据,设置写完状态 96 | 97 | 98 | **读数据步骤:** 99 | 100 | 1. 获取读游标,比较写游标,判断是否有数据 101 | 2. 分配操作序号 102 | 2. 获取第一个节点是否准备完成状态 103 | 3. 如果不是完成状态尝试设置首读时间,如果首读超出阀值则认为写错误。此时reset脏节点(非头且不到写游标节点)后移动读游标 104 | 105 | 106 | **关于冲突:** 107 | 108 | 1. **读-读冲突:**只考虑单点读,没有这个问题。 109 | 2. **读-写冲突:**head有写完毕标记位,当写数据块准备完毕时才开始读。 110 | 3. **写-写冲突:**写游标是原子操作,每个节点写缓冲区独立。如果两个节点同时写一个块,则只有一个能写成功。如果写序列中任意块写失败,则整体返回空间不足,写失败。(防止读失败后释放的内存被重新写导致写冲突) 111 | 4. **写进程崩溃:**会产生赃数据块,即写完标记永远是未写完。这时候可以利用上上面提到的第一次读取时间。如果是0,则取当前时间赋值,否则如果超出容忍值,就视为赃数据块。取时间可以使用clock函数(Linux下实测每次执行消耗约160ns),也可以用汇编直接提取CPU时钟。一般情况下系统应该在数百次读取无数据后休眠至少一个时间片的时间(Linux下一般最少有4ms),这时候写进程还没写完基本可以认为是出现赃数据。 112 | 5. **读进程崩溃:**移动读游标是最后的操作,下次启动时可以继续,不会丢失数据 113 | 114 | **写-读失败-写覆盖问题:** 115 | 116 | 有一个目前无法解决的是**写-读失败-写覆盖**的问题。这个问题比较难处理,而且发生情况很少。为了这个偶现的问题增加锁和复杂的错误处理逻辑我认为是很不值得的,所以这里采用一些措施来提早发现问题。 117 | 118 | + **第一个措施**是增加一个保护区,当剩于空间不足某个阀值时直接返回空间不足 119 | + **第二个措施**是增加一个简单的校验码,当校验不通过时返回错误 120 | 121 | 在设置合理的情况下这两个措施基本能保证数据不出错(如果设置合理,再出错的概率按某人的说法就是,硬件也会出错坏掉的啊) 122 | 123 | **共享内存通道压力测试** 124 | 1个读进程,5个写进程 125 | 读进程满负荷运行3小时,接收数据3390712433次,接收数据12933GB,出现9次数据坏块错误,无数据校验错误 126 | 出错率低于3.7亿分之一 127 | 128 | 129 | ### 网络通道设计 130 | 131 | 网络消息收发走socket协议,然而由于**socket是一对一**的,所以需要对消息接收做一个汇总操作。另外网络通道由于不是使用预分配的内存,所以还需要一个回收操作。 132 | 133 | 消息接收的汇总聚合需要IO复用的支持,为了简化网络层跨平台适配,我们直接使用libuv。 134 | 135 | 另外网络通道和内存通道还有几个不同的地方: 136 | 137 | 1. 网络通道并不是一个真实的通道,所以**逻辑上接收通道(通过init创建)不能发送数据**,**发送通道(通过attach创建)不能用于接收数据**。这种情况下,获取数据时需要带回socket标识,用以做安全性控制。 138 | 2. 由于接收通道和发送通道socket是分离的,意味着一个节点可能会有多个接收通道。 139 | 3. 由于每条连接的接收端不一样,所以每个接收socket需要有自己的缓冲区。 140 | 4. 需要处理被动断线和断线重连的问题。而断线重连时也需要区分连接是否能成功的不同逻辑。 141 | 5. 发送接口除了发送成功和发送失败以外还有一个**发送中的状态**。(由于MTU分片,有些数据一次发不完,需要一点一点地发) 142 | 143 | 根据以上特点,主要设计思路如下: 144 | 145 | 1. 每个**数据节点**拥有一个libuv的context,用于异步分发fd事件。 146 | 2. 每个**数据节点**需要有一个连接池,连接池内的连接需要保存一些连接数据,包含发送缓冲区、接收缓冲区、状态等等。 147 | 3. 连接的发缓冲区有两种形式,一种是固定缓冲区(固定大小,固定个数)。另一种是动态缓冲区,动态缓冲区会频繁malloc,所以最好要使用jemalloc之类的内存分配器,并且动态缓冲区是一个链表,要有最大上限。 148 | 4. 数据发送、接收需要统计数据。 149 | 5. 断开连接时需要能够通过析构逻辑释放所有缓冲区和待发送项,并且回调失败接口。 150 | 6. 网络出现问题(包含超时、断开等)需要进行重连逻辑,但是重连逻辑需要有重试次数限制。立即重试失败则会有定时重试机制。 151 | 7. 网络断开事件中要清理节点的连接信息。 152 | 8. 如果数据很小能直接发掉,就直接发送(直接进入系统socket缓冲区),不需要过缓冲区(当然当前缓冲区必须为空)。发送接口防止多线程要加锁(先全加自旋锁,后面可以考虑抽空移植BOOST中对线程支持的判定,有多线程支持时加自旋锁)。 153 | 154 | ```cpp 155 | // Sock通道状态 156 | struct sock_status_t { 157 | enum type { 158 | INIT = 0, 159 | CONNECTING, 160 | CONNECTED, 161 | }; 162 | }; 163 | 164 | // Sock通道头 165 | struct io_stream_channel { 166 | std::string host; // 主机地址 167 | uint16_t port; // 端口 168 | int fd; // socket设备描述符/HANDLE 169 | int status; // 状态 170 | 171 | // 数据区域 172 | buffer_manager write_buffers; // 写数据缓冲区(两种Buffer管理方式,一种动态,一种静态) 173 | buffer_manager read_buffers; // 读数据缓冲区(两种Buffer管理方式,一种动态,一种静态) 174 | 175 | // 回调函数 176 | connect_callback_t on_connect; 177 | disconnect_callback_t on_disconnect; 178 | recv_callback_t on_recv; 179 | 180 | // 统计信息 181 | size_t block_bad_count; // 读取到坏块次数 182 | size_t block_timeout_count; // 读取到写入超时块次数 183 | size_t node_bad_count; // 读取到坏node次数 184 | }; 185 | ``` 186 | 187 | ### 数据节点 188 | 189 | 数据节点从接收通道上从逻辑区分可以有**命令通道**、**数据通道**。控制命令优先走**命令通道**,数据收发走**数据通道**。并且每种逻辑通道都可以是上面提到的任意N种类型。不过**libatbus**对通道的收发类型不做明确限制,而是根据协议来判定。 190 | 191 | **libatbus**会把IO流通道,accept的节点作为命令通道发送节点。数据通道另外发起连接。而对于内存或共享内存节点。不区分数据通道或命令通道。 192 | 193 | 数据节点的发送通道可以多种多样,但是KEY都是节点的ID。Value里包含连接信息,并且能根据连接信息来判定怎么建立连接或者如何发送数据。 194 | 195 | 对单个数据节点的操作必须是单线程的。包含节点更新、获取、查找等。 196 | 197 | 另外libatbus不规定通信模式(不像zeromq一样必须指定一种通信模式)。所以基本没有回包一说。但是因为存在网络延迟发送和发送过程,所以会出现发送失败的问题。 198 | 199 | 然而,在最极端的条件下,即便TCP连接的底层接口返回发送成功,也不能保证对端能正确收到(因为是异步接口并且底层可能发送到一半连接断开并且重试失败),能保证的只是对方收到的情况下的顺序和内容。 200 | 为了尽可能的抛出网络问题,调用发送接口时。我们在尝试重连的时候直接向上层直接返回错误。其他情况下, EAGAIN和EWOULDBLOCK、EINTR则直接重试,其他错误直接返回错误。 201 | 202 | 连接协议使用类似zeromq的方式。具体实施规则如下: 203 | 204 | + TCP网络连接: ipv4://IP:端口, ipv6://IP:端口, dns://域名或IP:端口 205 | + Unix Socket连接: unix://文件名路径 (如果是绝对路径,比如/tmp/atbus.sock的完整路径是 unit:///tmp/atbus.sock) 206 | + 共享内存连接: shm://共享内存Key 207 | + 堆内存连接: mem://名称 208 | 209 | 内部协议类型: 210 | 1. 转发协议 211 | 2. 节点树同步协议 212 | 3. 注册协议 213 | 4. 建立连接协议 214 | 5. Ping协议 215 | -------------------------------------------------------------------------------- /tools/benchmark_io_stream_channel_send.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include "detail/libatbus_channel_export.h" 13 | 14 | struct run_config { 15 | size_t max_n; 16 | size_t limit_size; 17 | size_t limit_static_num; 18 | size_t *buff_pool; 19 | 20 | // stats 21 | size_t pending_send; 22 | size_t sum_send_len; 23 | size_t sum_send_times; 24 | size_t sum_send_full; 25 | size_t sum_send_err; 26 | size_t sum_seq; 27 | }; 28 | 29 | run_config conf; 30 | 31 | static void send_data(atbus::channel::io_stream_connection *connection); 32 | 33 | static void connect_callback(atbus::channel::io_stream_channel *channel, // 事件触发的channel 34 | atbus::channel::io_stream_connection *connection, // 事件触发的连接 35 | int status, // libuv传入的转态码 36 | void *, // 额外参数(不同事件不同含义) 37 | size_t // 额外参数长度 38 | ) { 39 | if (0 != status) { 40 | std::cerr << "connect failed, statue: " << status << std::endl; 41 | std::cerr << uv_err_name(channel->error_code) << ":" << uv_strerror(channel->error_code) << std::endl; 42 | uv_stop(channel->ev_loop); 43 | return; 44 | } 45 | assert(connection); 46 | send_data(const_cast(connection)); 47 | } 48 | 49 | static void sended_callback(atbus::channel::io_stream_channel *channel, // 事件触发的channel 50 | atbus::channel::io_stream_connection *connection, // 事件触发的连接 51 | int status, // libuv传入的转态码 52 | void *, // 额外参数(不同事件不同含义) 53 | size_t s // 额外参数长度 54 | ) { 55 | assert(channel); 56 | assert(connection); 57 | 58 | --conf.pending_send; 59 | 60 | if (0 != status) { 61 | fprintf(stderr, "io_stream_send callback error, ret code: %d. %s: %s\n", status, uv_err_name(channel->error_code), 62 | uv_strerror(channel->error_code)); 63 | 64 | ++conf.sum_send_err; 65 | return; 66 | } 67 | 68 | ++conf.sum_send_times; 69 | conf.sum_send_len += s; 70 | send_data(const_cast(connection)); 71 | } 72 | 73 | static void closed_callback(EXPLICIT_UNUSED_ATTR atbus::channel::io_stream_channel *channel, // 事件触发的channel 74 | EXPLICIT_UNUSED_ATTR atbus::channel::io_stream_connection *connection, // 事件触发的连接 75 | int /*status*/, // libuv传入的转态码 76 | void *, // 额外参数(不同事件不同含义) 77 | size_t // 额外参数长度 78 | ) { 79 | assert(channel); 80 | assert(connection); 81 | 82 | uv_stop(channel->ev_loop); 83 | } 84 | 85 | static void stat_callback(uv_timer_t * /*handle*/) { 86 | static int secs = 0; 87 | static char unit_desc[][4] = {"B", "KB", "MB", "GB"}; 88 | static size_t unit_devi[] = {1UL, 1UL << 10, 1UL << 20, 1UL << 30}; 89 | static size_t unit_index = 0; 90 | 91 | ++secs; 92 | 93 | while (conf.sum_send_len / unit_devi[unit_index] > 1024 && unit_index < sizeof(unit_devi) / sizeof(size_t) - 1) 94 | ++unit_index; 95 | 96 | while (conf.sum_send_len / unit_devi[unit_index] <= 1024 && unit_index > 0) --unit_index; 97 | 98 | std::cout << "[ RUNNING ] NO." << secs << " m" << std::endl 99 | << "[ RUNNING ] send(" << conf.sum_send_times << " times, " << (conf.sum_send_len / unit_devi[unit_index]) 100 | << " " << unit_desc[unit_index] << ") " << "full " << conf.sum_send_full << " times, err " 101 | << conf.sum_send_err << " times" << std::endl 102 | << std::endl; 103 | 104 | std::cout.flush(); 105 | std::cerr.flush(); 106 | } 107 | 108 | int main(int argc, char *argv[]) { 109 | if (argc < 2) { 110 | printf("usage: %s
[max unit size] [limit size] [limit number]\n", argv[0]); 111 | return 0; 112 | } 113 | 114 | using namespace atbus::channel; 115 | 116 | if (argc > 2) 117 | conf.max_n = (size_t)strtol(argv[2], nullptr, 10); 118 | else 119 | conf.max_n = 1024; 120 | 121 | conf.buff_pool = new size_t[conf.max_n]; 122 | assert(conf.buff_pool); 123 | 124 | if (argc > 3) 125 | conf.limit_size = (size_t)strtol(argv[3], nullptr, 10); 126 | else 127 | conf.limit_size = 64 * 1024; // 64KB 128 | 129 | if (argc > 4) 130 | conf.limit_static_num = (size_t)strtol(argv[4], nullptr, 10); 131 | else 132 | conf.limit_static_num = 0; // dynamic 133 | 134 | srand(static_cast(time(nullptr))); 135 | conf.pending_send = 0; 136 | conf.sum_send_len = 0; 137 | conf.sum_send_times = 0; 138 | conf.sum_send_full = 0; 139 | conf.sum_send_err = 0; 140 | conf.sum_seq = ((size_t)rand() << (sizeof(size_t) * 4)); 141 | 142 | io_stream_conf cfg; 143 | io_stream_init_configure(&cfg); 144 | cfg.send_buffer_max_size = conf.limit_size; 145 | cfg.send_buffer_static = conf.limit_static_num; 146 | 147 | io_stream_channel channel; 148 | io_stream_init(&channel, uv_default_loop(), &cfg); 149 | channel.evt.callbacks[io_stream_callback_evt_t::EN_FN_WRITEN] = sended_callback; 150 | channel.evt.callbacks[io_stream_callback_evt_t::EN_FN_DISCONNECTED] = closed_callback; 151 | 152 | channel_address_t addr; 153 | make_address(argv[1], addr); 154 | 155 | if (io_stream_connect(&channel, addr, connect_callback, nullptr, 0) < 0) { 156 | std::cerr << "connect to " << argv[1] << " failed." << uv_err_name(channel.error_code) << ":" 157 | << uv_strerror(channel.error_code) << std::endl; 158 | io_stream_close(&channel); 159 | delete[] conf.buff_pool; 160 | return -1; 161 | } 162 | 163 | uv_timer_t stat; 164 | uv_timer_init(uv_default_loop(), &stat); 165 | uv_timer_start(&stat, stat_callback, 60000, 60000); 166 | 167 | int ret = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 168 | io_stream_close(&channel); 169 | 170 | delete[] conf.buff_pool; 171 | return ret; 172 | } 173 | 174 | static void send_data(atbus::channel::io_stream_connection *connection) { 175 | using namespace atbus::channel; 176 | 177 | assert(connection); 178 | // 尝试5次,逐渐填满数据 179 | for (int try_times = 0; try_times < 2; ++try_times) { 180 | size_t n = rand() % conf.max_n; // 最大 4K-8K的包 181 | if (0 == n) n = 1; // 保证一定有数据包,保证收发次数一致 182 | 183 | for (size_t i = 0; i < n; ++i) { 184 | conf.buff_pool[i] = conf.sum_seq; 185 | } 186 | 187 | int res = io_stream_send(connection, conf.buff_pool, n * sizeof(size_t)); 188 | if (res) { 189 | if (EN_ATBUS_ERR_BUFF_LIMIT == res) { 190 | ++conf.sum_send_full; 191 | break; 192 | } else { 193 | ++conf.sum_send_err; 194 | fprintf(stderr, "io_stream_send error, ret code: %d. %s: %s\n", res, 195 | uv_err_name(connection->channel->error_code), uv_strerror(connection->channel->error_code)); 196 | } 197 | } else { 198 | ++conf.pending_send; 199 | ++conf.sum_seq; 200 | } 201 | } 202 | } -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: 2 | - "clang-diagnostic-*" 3 | - "clang-analyzer-*" 4 | - "bugprone-*" 5 | # - 'concurrency-*' 6 | - "misc-*" 7 | - "modernize-*" 8 | - "cppcoreguidelines-*" 9 | - "performance-*" 10 | - "portability-*" 11 | - "readability-*" 12 | - "-misc-non-private-member-variables-in-classes" 13 | - "-misc-unused-parameters" 14 | - "-modernize-use-trailing-return-type" 15 | - "-modernize-avoid-c-arrays" 16 | - "-modernize-type-traits" 17 | - "-modernize-use-auto" 18 | - "-modernize-concat-nested-namespaces" 19 | - "-modernize-use-constraints" 20 | - "-portability-avoid-pragma-once" 21 | - "-cppcoreguidelines-avoid-do-while" 22 | - "-cppcoreguidelines-pro-type-vararg" 23 | - "-cppcoreguidelines-pro-bounds-pointer-arithmetic" 24 | - "-cppcoreguidelines-pro-type-static-cast-downcast" 25 | - "-cppcoreguidelines-pro-type-reinterpret-cast" 26 | - "-cppcoreguidelines-pro-type-union-access" 27 | - "-cppcoreguidelines-avoid-c-arrays" 28 | - "-cppcoreguidelines-owning-memory" 29 | - "-cppcoreguidelines-rvalue-reference-param-not-moved" 30 | - "-cppcoreguidelines-pro-bounds-array-to-pointer-decay" 31 | - "-cppcoreguidelines-pro-bounds-constant-array-index" 32 | - "-performance-unnecessary-copy-initialization" 33 | - "-performance-enum-size" 34 | - "-readability-identifier-length" 35 | - "-readability-identifier-length" 36 | - "-readability-avoid-const-params-in-decls" 37 | - "-readability-redundant-access-specifiers" 38 | - "-readability-avoid-unconditional-preprocessor-if" 39 | - "-readability-redundant-inline-specifier" 40 | - "-readability-container-contains" 41 | - "-readability-use-std-min-max" 42 | - "-readability-static-definition-in-anonymous-namespace" 43 | - "-readability-named-parameter" 44 | - "-readability-simplify-boolean-expr" 45 | - "-bugprone-misplaced-widening-cast" 46 | - "-bugprone-branch-clone" 47 | # 虽然按规范下划线开头的都是保留字,但是写通用库的时候常见使用私有的保留字来减少冲突 48 | - "-bugprone-reserved-identifier" 49 | # 算法库常用magic number 50 | - "-readability-magic-numbers" 51 | # 过于常见的用法而忽略 52 | - "-bugprone-easily-swappable-parameters" # 参数容易被隐式转换而被调用者传错顺序 53 | - "-misc-no-recursion" # 递归调用 54 | - "-performance-move-const-arg" # 无意义的move可用于标记生命周期 55 | - "-cppcoreguidelines-pro-type-const-cast" # 使用const_cast的地方通常都是故意使用的 56 | - "-modernize-use-nodiscard" 57 | # 重复报告 58 | - "-bugprone-narrowing-conversions" # 和 clang-diagnostic-sign-conversion/clang-diagnostic-shorten-64-to-32 重复 59 | - "-bugprone-switch-missing-default-case" # 由 fallthrough 告警管理,某些设计模式通过宏实现,无法通过注释排除 60 | # 存在误判,先关闭 61 | - "-readability-make-member-function-const" 62 | - "-cppcoreguidelines-special-member-functions" 63 | # 由于C++标准版本兼容性考虑而忽略的checks 64 | - "-modernize-use-ranges" 65 | - "-modernize-use-starts-ends-with" 66 | # 以下检查由于性能问题被关闭(@see https://github.com/llvm/llvm-project/blob/main/clang-tools-extra/clangd/TidyFastChecks.inc) 67 | - "-misc-confusable-identifiers" 68 | - "-misc-const-correctness" 69 | - "-misc-header-include-cycle" 70 | - "-altera-id-dependent-backward-branch" 71 | - "-readability-container-size-empty" 72 | # RT类型和用于SHM内数据类型必须保持Trivially Copyable,所以不能使用member-init和某些不必要的lint 73 | - "-cppcoreguidelines-use-default-member-init" 74 | - "-modernize-use-equals-default" 75 | - "-modernize-use-default-member-init" 76 | # 以下是某些老代码的checkes,不影响功能 77 | - "-cppcoreguidelines-avoid-magic-numbers" 78 | - "-cppcoreguidelines-macro-usage" 79 | - "-cppcoreguidelines-macro-to-enum" 80 | - "-modernize-macro-to-enum" 81 | - "-readability-isolate-declaration" 82 | - "-readability-uppercase-literal-suffix" 83 | WarningsAsErrors: "" 84 | HeaderFileExtensions: 85 | - "" 86 | - h 87 | - hh 88 | - hpp 89 | - hxx 90 | ImplementationFileExtensions: 91 | - c 92 | - cc 93 | - cpp 94 | - cxx 95 | HeaderFilterRegex: ".*" 96 | ExcludeHeaderFilterRegex: "\\.pb\\.h$" 97 | FormatStyle: none 98 | CheckOptions: 99 | cert-arr39-c.WarnOnSizeOfCompareToConstant: "false" 100 | cert-arr39-c.WarnOnSizeOfConstant: "false" 101 | cert-arr39-c.WarnOnSizeOfIntegerExpression: "false" 102 | cert-arr39-c.WarnOnSizeOfPointer: "false" 103 | cert-arr39-c.WarnOnSizeOfPointerToAggregate: "false" 104 | cert-arr39-c.WarnOnSizeOfThis: "false" 105 | cert-dcl16-c.NewSuffixes: "L;LL;LU;LLU" 106 | cert-err33-c.AllowCastToVoid: "true" 107 | cert-err33-c.CheckedFunctions: "^::aligned_alloc;^::asctime_s;^::at_quick_exit;^::atexit;^::bsearch;^::bsearch_s;^::btowc;^::c16rtomb;^::c32rtomb;^::calloc;^::clock;^::cnd_broadcast;^::cnd_init;^::cnd_signal;^::cnd_timedwait;^::cnd_wait;^::ctime_s;^::fclose;^::fflush;^::fgetc;^::fgetpos;^::fgets;^::fgetwc;^::fopen;^::fopen_s;^::fprintf;^::fprintf_s;^::fputc;^::fputs;^::fputwc;^::fputws;^::fread;^::freopen;^::freopen_s;^::fscanf;^::fscanf_s;^::fseek;^::fsetpos;^::ftell;^::fwprintf;^::fwprintf_s;^::fwrite;^::fwscanf;^::fwscanf_s;^::getc;^::getchar;^::getenv;^::getenv_s;^::gets_s;^::getwc;^::getwchar;^::gmtime;^::gmtime_s;^::localtime;^::localtime_s;^::malloc;^::mbrtoc16;^::mbrtoc32;^::mbsrtowcs;^::mbsrtowcs_s;^::mbstowcs;^::mbstowcs_s;^::memchr;^::mktime;^::mtx_init;^::mtx_lock;^::mtx_timedlock;^::mtx_trylock;^::mtx_unlock;^::printf_s;^::putc;^::putwc;^::raise;^::realloc;^::remove;^::rename;^::scanf;^::scanf_s;^::setlocale;^::setvbuf;^::signal;^::snprintf;^::snprintf_s;^::sprintf;^::sprintf_s;^::sscanf;^::sscanf_s;^::strchr;^::strerror_s;^::strftime;^::strpbrk;^::strrchr;^::strstr;^::strtod;^::strtof;^::strtoimax;^::strtok;^::strtok_s;^::strtol;^::strtold;^::strtoll;^::strtoul;^::strtoull;^::strtoumax;^::strxfrm;^::swprintf;^::swprintf_s;^::swscanf;^::swscanf_s;^::thrd_create;^::thrd_detach;^::thrd_join;^::thrd_sleep;^::time;^::timespec_get;^::tmpfile;^::tmpfile_s;^::tmpnam;^::tmpnam_s;^::tss_create;^::tss_get;^::tss_set;^::ungetc;^::ungetwc;^::vfprintf;^::vfprintf_s;^::vfscanf;^::vfscanf_s;^::vfwprintf;^::vfwprintf_s;^::vfwscanf;^::vfwscanf_s;^::vprintf_s;^::vscanf;^::vscanf_s;^::vsnprintf;^::vsnprintf_s;^::vsprintf;^::vsprintf_s;^::vsscanf;^::vsscanf_s;^::vswprintf;^::vswprintf_s;^::vswscanf;^::vswscanf_s;^::vwprintf_s;^::vwscanf;^::vwscanf_s;^::wcrtomb;^::wcschr;^::wcsftime;^::wcspbrk;^::wcsrchr;^::wcsrtombs;^::wcsrtombs_s;^::wcsstr;^::wcstod;^::wcstof;^::wcstoimax;^::wcstok;^::wcstok_s;^::wcstol;^::wcstold;^::wcstoll;^::wcstombs;^::wcstombs_s;^::wcstoul;^::wcstoull;^::wcstoumax;^::wcsxfrm;^::wctob;^::wctrans;^::wctype;^::wmemchr;^::wprintf_s;^::wscanf;^::wscanf_s;" 108 | cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField: "false" 109 | cert-str34-c.DiagnoseSignedUnsignedCharComparisons: "false" 110 | cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: "true" 111 | google-readability-braces-around-statements.ShortStatementLines: "1" 112 | google-readability-function-size.StatementThreshold: "800" 113 | google-readability-namespace-comments.ShortNamespaceLines: "10" 114 | google-readability-namespace-comments.SpacesBeforeComments: "2" 115 | llvm-else-after-return.WarnOnConditionVariables: "false" 116 | llvm-else-after-return.WarnOnUnfixable: "false" 117 | llvm-qualified-auto.AddConstToQualified: "false" 118 | bugprone-lambda-function-name.IgnoreMacros: "true" 119 | bugprone-signed-char-misuse.CharTypdefsToIgnore: "signed char" 120 | bugprone-implicit-widening-of-multiplication-result.IgnoreConstantIntExpr: "true" 121 | readability-function-cognitive-complexity.Threshold: "100" 122 | readability-implicit-bool-conversion.AllowIntegerConditions: "true" 123 | misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic: "true" 124 | SystemHeaders: false 125 | -------------------------------------------------------------------------------- /include/detail/buffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by owent on 2015/8/11. 3 | // 4 | 5 | #ifndef LIBATBUS_BUFFER_H 6 | #define LIBATBUS_BUFFER_H 7 | 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "detail/libatbus_config.h" 16 | 17 | #include "design_pattern/nomovable.h" 18 | #include "design_pattern/noncopyable.h" 19 | 20 | ATBUS_MACRO_NAMESPACE_BEGIN 21 | namespace detail { 22 | namespace fn { 23 | ATBUS_MACRO_API void *buffer_next(void *pointer, size_t step); 24 | ATBUS_MACRO_API const void *buffer_next(const void *pointer, size_t step); 25 | 26 | ATBUS_MACRO_API void *buffer_prev(void *pointer, size_t step); 27 | ATBUS_MACRO_API const void *buffer_prev(const void *pointer, size_t step); 28 | 29 | ATBUS_MACRO_API size_t buffer_offset(const void *l, const void *r); 30 | 31 | /** 32 | * @brief try to read a dynamic int from buffer 33 | * @param out output integer 34 | * @param pointer buffer address 35 | * @param s buffer size 36 | * @note encoding: like protobuf varint, first bit means more or last byte, big endian, padding right 37 | * @note can not used with signed integer 38 | * @return how much bytes the integer cost, 0 if failed 39 | **/ 40 | ATBUS_MACRO_API size_t read_vint(uint64_t &out, const void *pointer, size_t s); 41 | 42 | /** 43 | * @brief try to write a dynamic int to buffer 44 | * @param in input integer 45 | * @param pointer buffer address 46 | * @param s buffer size 47 | * @note encoding: like protobuf varint, first bit means more or last byte, big endian, padding right 48 | * @note can not used with signed integer 49 | * @return how much bytes the integer cost, 0 if failed 50 | **/ 51 | ATBUS_MACRO_API size_t write_vint(uint64_t in, void *pointer, size_t s); 52 | } // namespace fn 53 | 54 | class buffer_manager; 55 | 56 | /** 57 | * @brief buffer block, not thread safe 58 | */ 59 | class buffer_block { 60 | public: 61 | ATBUS_MACRO_API void *data(); 62 | ATBUS_MACRO_API const void *data() const; 63 | ATBUS_MACRO_API void *raw_data(); 64 | ATBUS_MACRO_API const void *raw_data() const; 65 | 66 | ATBUS_MACRO_API size_t size() const; 67 | 68 | ATBUS_MACRO_API size_t raw_size() const; 69 | 70 | ATBUS_MACRO_API void *pop(size_t s); 71 | 72 | ATBUS_MACRO_API size_t instance_size() const; 73 | 74 | public: 75 | /** alloc and init buffer_block **/ 76 | static ATBUS_MACRO_API buffer_block *malloc(size_t s); 77 | 78 | /** destroy and free buffer_block **/ 79 | static ATBUS_MACRO_API void free(buffer_block *p); 80 | 81 | /** 82 | * @brief init buffer_block as specify address 83 | * @param pointer data address 84 | * @param s data max size 85 | * @param bs buffer size 86 | * @return unused data address 87 | **/ 88 | static ATBUS_MACRO_API void *create(void *pointer, size_t s, size_t bs); 89 | 90 | /** init buffer_block as specify address **/ 91 | static ATBUS_MACRO_API void *destroy(buffer_block *p); 92 | 93 | static ATBUS_MACRO_API size_t padding_size(size_t s); 94 | static ATBUS_MACRO_API size_t head_size(size_t s); 95 | static ATBUS_MACRO_API size_t full_size(size_t s); 96 | 97 | private: 98 | friend class buffer_manager; 99 | size_t size_; 100 | size_t used_; 101 | void *pointer_; 102 | }; 103 | 104 | /** 105 | * @brief buffer block manager, not thread safe 106 | */ 107 | class buffer_manager { 108 | public: 109 | struct limit_t { 110 | size_t cost_number_; 111 | size_t cost_size_; 112 | 113 | size_t limit_number_; 114 | size_t limit_size_; 115 | }; 116 | 117 | UTIL_DESIGN_PATTERN_NOCOPYABLE(buffer_manager) 118 | UTIL_DESIGN_PATTERN_NOMOVABLE(buffer_manager) 119 | 120 | public: 121 | ATBUS_MACRO_API buffer_manager(); 122 | ATBUS_MACRO_API ~buffer_manager(); 123 | 124 | ATBUS_MACRO_API const limit_t &limit() const; 125 | 126 | /** 127 | * @brief set limit when in dynamic mode 128 | * @param max_size size limit of dynamic, set 0 if unlimited 129 | * @param max_number number limit of dynamic, set 0 if unlimited 130 | * @return true on success 131 | */ 132 | ATBUS_MACRO_API bool set_limit(size_t max_size, size_t max_number); 133 | 134 | ATBUS_MACRO_API buffer_block *front(); 135 | 136 | ATBUS_MACRO_API int front(void *&pointer, size_t &nread, size_t &nwrite); 137 | 138 | ATBUS_MACRO_API buffer_block *back(); 139 | 140 | ATBUS_MACRO_API int back(void *&pointer, size_t &nread, size_t &nwrite); 141 | 142 | ATBUS_MACRO_API int push_back(void *&pointer, size_t s); 143 | 144 | ATBUS_MACRO_API int push_front(void *&pointer, size_t s); 145 | 146 | ATBUS_MACRO_API int pop_back(size_t s, bool free_unwritable = true); 147 | 148 | ATBUS_MACRO_API int pop_front(size_t s, bool free_unwritable = true); 149 | 150 | /** 151 | * @brief append buffer and merge to the tail of the last buffer block 152 | * @note if manager is empty now, just like push_back 153 | * @param pointer output the writable buffer address 154 | * @param s buffer size 155 | * @return 0 or error code 156 | */ 157 | ATBUS_MACRO_API int merge_back(void *&pointer, size_t s); 158 | 159 | /** 160 | * @brief append buffer and merge to the tail of the first buffer block 161 | * @note if manager is empty now, just like push_front 162 | * @param pointer output the writable buffer address 163 | * @param s buffer size 164 | * @return 0 or error code 165 | */ 166 | ATBUS_MACRO_API int merge_front(void *&pointer, size_t s); 167 | 168 | ATBUS_MACRO_API bool empty() const; 169 | 170 | ATBUS_MACRO_API void reset(); 171 | 172 | /** 173 | * @brief set dynamic mode(use malloc when push buffer) or static mode(malloc a huge buffer at once) 174 | * @param max_size circle buffer size when static mode, 0 when dynamic mode 175 | * @param max_number buffer number when static mode 176 | * @note this api will clear buffer data already exists 177 | */ 178 | ATBUS_MACRO_API void set_mode(size_t max_size, size_t max_number); 179 | 180 | ATBUS_MACRO_API bool is_static_mode() const; 181 | ATBUS_MACRO_API bool is_dynamic_mode() const; 182 | 183 | private: 184 | buffer_block *static_front(); 185 | 186 | buffer_block *static_back(); 187 | 188 | int static_push_back(void *&pointer, size_t s); 189 | 190 | int static_push_front(void *&pointer, size_t s); 191 | 192 | int static_pop_back(size_t s, bool free_unwritable); 193 | 194 | int static_pop_front(size_t s, bool free_unwritable); 195 | 196 | int static_merge_back(void *&pointer, size_t s); 197 | 198 | int static_merge_front(void *&pointer, size_t s); 199 | 200 | bool static_empty() const; 201 | 202 | buffer_block *dynamic_front(); 203 | 204 | buffer_block *dynamic_back(); 205 | 206 | int dynamic_push_back(void *&pointer, size_t s); 207 | 208 | int dynamic_push_front(void *&pointer, size_t s); 209 | 210 | int dynamic_pop_back(size_t s, bool free_unwritable); 211 | 212 | int dynamic_pop_front(size_t s, bool free_unwritable); 213 | 214 | int dynamic_merge_back(void *&pointer, size_t s); 215 | 216 | int dynamic_merge_front(void *&pointer, size_t s); 217 | 218 | bool dynamic_empty() const; 219 | 220 | private: 221 | struct static_buffer_t { 222 | void *buffer_; 223 | size_t size_; 224 | 225 | size_t head_; 226 | size_t tail_; 227 | std::vector circle_index_; 228 | }; 229 | 230 | static_buffer_t static_buffer_; 231 | std::list dynamic_buffer_; 232 | 233 | limit_t limit_; 234 | }; 235 | } // namespace detail 236 | ATBUS_MACRO_NAMESPACE_END 237 | 238 | #endif // LIBATBUS_BUFFER_H 239 | -------------------------------------------------------------------------------- /ci/do_ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(cd "$(dirname $0)" && pwd)/.." 4 | 5 | set -ex 6 | 7 | if [[ -z "$CONFIGURATION" ]]; then 8 | CONFIGURATION=RelWithDebInfo 9 | fi 10 | 11 | if [[ "x$USE_CC" == "xclang-latest" ]]; then 12 | echo '#include 13 | int main() { std::cout<<"Hello"; }' >test-libc++.cpp 14 | SELECT_CLANG_VERSION="" 15 | SELECT_CLANG_HAS_LIBCXX=1 16 | clang -x c++ -stdlib=libc++ test-libc++.cpp -lc++ -lc++abi || SELECT_CLANG_HAS_LIBCXX=0 17 | if [[ $SELECT_CLANG_HAS_LIBCXX -eq 0 ]]; then 18 | CURRENT_CLANG_VERSION=$(clang -x c /dev/null -dM -E | grep __clang_major__ | awk '{print $NF}') 19 | for ((i = $CURRENT_CLANG_VERSION + 5; $i >= $CURRENT_CLANG_VERSION; --i)); do 20 | SELECT_CLANG_HAS_LIBCXX=1 21 | SELECT_CLANG_VERSION="-$i" 22 | clang$SELECT_CLANG_VERSION -x c++ -stdlib=libc++ test-libc++.cpp -lc++ -lc++abi || SELECT_CLANG_HAS_LIBCXX=0 23 | if [[ $SELECT_CLANG_HAS_LIBCXX -eq 1 ]]; then 24 | break 25 | fi 26 | done 27 | fi 28 | SELECT_CLANGPP_BIN=clang++$SELECT_CLANG_VERSION 29 | LINK_CLANGPP_BIN=0 30 | which $SELECT_CLANGPP_BIN || LINK_CLANGPP_BIN=1 31 | if [[ $LINK_CLANGPP_BIN -eq 1 ]]; then 32 | mkdir -p .local/bin 33 | ln -s "$(which "clang$SELECT_CLANG_VERSION")" "$PWD/.local/bin/clang++$SELECT_CLANG_VERSION" 34 | export PATH="$PWD/.local/bin:$PATH" 35 | fi 36 | export USE_CC=clang$SELECT_CLANG_VERSION 37 | elif [[ "x$USE_CC" == "xgcc-latest" ]]; then 38 | CURRENT_GCC_VERSION=$(gcc -x c /dev/null -dM -E | grep __GNUC__ | awk '{print $NF}') 39 | echo '#include 40 | int main() { std::cout<<"Hello"; }' >test-gcc-version.cpp 41 | let LAST_GCC_VERSION=$CURRENT_GCC_VERSION+10 42 | for ((i = $CURRENT_GCC_VERSION; $i <= $LAST_GCC_VERSION; ++i)); do 43 | TEST_GCC_VERSION=1 44 | g++-$i -x c++ test-gcc-version.cpp || TEST_GCC_VERSION=0 45 | if [[ $TEST_GCC_VERSION -eq 0 ]]; then 46 | break 47 | fi 48 | CURRENT_GCC_VERSION=$i 49 | done 50 | export USE_CC=gcc-$CURRENT_GCC_VERSION 51 | echo "Using $USE_CC" 52 | fi 53 | 54 | if [[ "$1" == "format" ]]; then 55 | python3 -m pip install --user -r ./ci/requirements.txt 56 | export PATH="$HOME/.local/bin:$PATH" 57 | bash ./ci/format.sh 58 | CHANGED="$(git -c core.autocrlf=true ls-files --modified)" 59 | if [[ ! -z "$CHANGED" ]]; then 60 | echo "The following files have changes:" 61 | echo "$CHANGED" 62 | git diff 63 | # exit 1 ; # Just warning, some versions of clang-format have different default style for unsupport syntax 64 | fi 65 | exit 0 66 | elif [[ "$1" == "coverage" ]]; then 67 | CONFIGURATION=Debug 68 | vcpkg install --triplet=$VCPKG_TARGET_TRIPLET fmt openssl libuv 69 | CRYPTO_OPTIONS="-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_CRYPTO_USE_OPENSSL=ON" 70 | bash cmake_dev.sh -lus -b $CONFIGURATION -r build_jobs_coverage_prepare -c $USE_CC -- $CRYPTO_OPTIONS -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake \ 71 | -DVCPKG_TARGET_TRIPLET=$VCPKG_TARGET_TRIPLET -DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 72 | bash cmake_dev.sh -lus -b $CONFIGURATION -r build_jobs_coverage -c $USE_CC -- $CRYPTO_OPTIONS "-DCMAKE_C_FLAGS=$GCOV_FLAGS" "-DCMAKE_CXX_FLAGS=$GCOV_FLAGS" \ 73 | "-DCMAKE_EXE_LINKER_FLAGS=$GCOV_FLAGS" -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake \ 74 | -DVCPKG_TARGET_TRIPLET=$VCPKG_TARGET_TRIPLET -DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 75 | cd build_jobs_coverage 76 | cmake --build . -j --config $CONFIGURATION || cmake --build . --config $CONFIGURATION 77 | ctest -VV . -C $CONFIGURATION -L libatbus.unit_test 78 | timeout 5s ./tools/benchmark_shm_channel_recv 12345679 1024 4194304 >recv.log 2>&1 & 79 | timeout 6s ./tools/benchmark_shm_channel_send 12345679 1024 4194304 >send.log 2>&1 || true 80 | ./tools/show_shm_channel 12345679 1 16 >/dev/null || true 81 | elif [[ "$1" == "codeql.configure" ]]; then 82 | CONFIGURATION=Debug 83 | vcpkg install --triplet=$VCPKG_TARGET_TRIPLET fmt openssl libuv 84 | CRYPTO_OPTIONS="-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_CRYPTO_USE_OPENSSL=ON" 85 | bash cmake_dev.sh -lus -b $CONFIGURATION -r build_jobs_coverage_prepare -c $USE_CC -- $CRYPTO_OPTIONS -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake \ 86 | -DVCPKG_TARGET_TRIPLET=$VCPKG_TARGET_TRIPLET -DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 87 | bash cmake_dev.sh -lus -b $CONFIGURATION -r build_jobs_coverage -c $USE_CC -- $CRYPTO_OPTIONS "-DCMAKE_C_FLAGS=$GCOV_FLAGS" "-DCMAKE_CXX_FLAGS=$GCOV_FLAGS" \ 88 | "-DCMAKE_EXE_LINKER_FLAGS=$GCOV_FLAGS" -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake \ 89 | -DVCPKG_TARGET_TRIPLET=$VCPKG_TARGET_TRIPLET -DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 90 | elif [[ "$1" == "codeql.build" ]]; then 91 | CONFIGURATION=Debug 92 | cd build_jobs_coverage 93 | cmake --build . -j --config $CONFIGURATION || cmake --build . --config $CONFIGURATION 94 | ctest -VV . -C $CONFIGURATION -L libatbus.unit_test 95 | timeout 5s ./tools/benchmark_shm_channel_recv 12345679 1024 4194304 >recv.log 2>&1 & 96 | timeout 6s ./tools/benchmark_shm_channel_send 12345679 1024 4194304 >send.log 2>&1 || true 97 | ./tools/show_shm_channel 12345679 1 16 >/dev/null || true 98 | elif [[ "$1" == "ssl.openssl" ]]; then 99 | CRYPTO_OPTIONS="-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_CRYPTO_USE_OPENSSL=ON" 100 | vcpkg install --triplet=$VCPKG_TARGET_TRIPLET fmt openssl libuv 101 | bash cmake_dev.sh -lus -b $CONFIGURATION -r build_jobs_ci -c $USE_CC -- $CRYPTO_OPTIONS -DVCPKG_TARGET_TRIPLET=$VCPKG_TARGET_TRIPLET \ 102 | -DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake -DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON \ 103 | "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 104 | cd build_jobs_ci 105 | cmake --build . -j --config $CONFIGURATION || cmake --build . --config $CONFIGURATION 106 | ctest -VV . -C $CONFIGURATION -L libatbus.unit_test 107 | elif [[ "$1" == "gcc.legacy.test" ]]; then 108 | bash cmake_dev.sh -lus -b $CONFIGURATION -r build_jobs_ci -c $USE_CC -- "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 109 | cd build_jobs_ci 110 | cmake --build . -j --config $CONFIGURATION || cmake --build . --config $CONFIGURATION 111 | ctest -VV . -C $CONFIGURATION -L libatbus.unit_test 112 | elif [[ "$1" == "clang.test" ]]; then 113 | CRYPTO_OPTIONS="-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_CRYPTO_USE_OPENSSL=ON" 114 | bash cmake_dev.sh -lus -b $CONFIGURATION -r build_jobs_ci -c $USE_CC -- $CRYPTO_OPTIONS -DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON \ 115 | "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 116 | cd build_jobs_ci 117 | cmake --build . -j --config $CONFIGURATION || cmake --build . --config $CONFIGURATION 118 | ctest -VV . -C $CONFIGURATION -L libatbus.unit_test 119 | elif [[ "$1" == "msys2.mingw.test" ]]; then 120 | pacman -S --needed --noconfirm mingw-w64-x86_64-cmake mingw-w64-x86_64-make \ 121 | mingw-w64-x86_64-curl mingw-w64-x86_64-wget mingw-w64-x86_64-perl \ 122 | mingw-w64-x86_64-git-lfs mingw-w64-x86_64-toolchain mingw-w64-x86_64-libtool \ 123 | mingw-w64-x86_64-python mingw-w64-x86_64-python-pip mingw-w64-x86_64-python-setuptools || true 124 | echo "PATH=$PATH" 125 | # git config --global http.sslBackend openssl || true 126 | # pacman -S --needed --noconfirm mingw-w64-x86_64-protobuf 127 | mkdir -p build_jobs_ci 128 | cd build_jobs_ci 129 | cmake .. -G 'MinGW Makefiles' "-DBUILD_SHARED_LIBS=$BUILD_SHARED_LIBS" -DCMAKE_BUILD_TYPE=$CONFIGURATION -DPROJECT_ENABLE_UNITTEST=ON \ 130 | -DPROJECT_ENABLE_SAMPLE=ON -DPROJECT_ENABLE_TOOLS=ON -DATBUS_MACRO_ABORT_ON_PROTECTED_ERROR=ON \ 131 | -DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_ALLOW_SHARED_LIBS=OFF \ 132 | "-DATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_LOW_MEMORY_MODE=ON" 133 | cmake --build . -j --config $CONFIGURATION || cmake --build . --config $CONFIGURATION 134 | # for EXT_PATH in $(find ../third_party/install/ -name "*.dll" | xargs dirname | sort -u); do 135 | # export PATH="$PWD/$EXT_PATH:$PATH" 136 | # done 137 | # ctest -VV . -C $CONFIGURATION -L libatbus.unit_test 138 | fi 139 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | echowithcolor(COLOR GREEN "-- Configure ${CMAKE_CURRENT_LIST_DIR}") 2 | 3 | file(RELATIVE_PATH PROJECT_ROOT_RELINC_DIR ${CMAKE_CURRENT_LIST_DIR} ${PROJECT_LIBATBUS_ROOT_INC_DIR}) 4 | 5 | set(PROJECT_LIB_SRC_LIST 6 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/detail/buffer.h" 7 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/detail/libatbus_adapter_libuv.h" 8 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/detail/libatbus_channel_export.h" 9 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/detail/libatbus_channel_types.h" 10 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/detail/libatbus_error.h" 11 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/atbus_connection.h" 12 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/atbus_endpoint.h" 13 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/atbus_message_handler.h" 14 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/atbus_connection_context.h" 15 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/atbus_node.h" 16 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/libatbus.h" 17 | "${PROJECT_LIBATBUS_ROOT_INC_DIR}/libatbus_protocol.h" 18 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/detail/buffer.cpp" 19 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/detail/libatbus_error.cpp" 20 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/atbus_connection.cpp" 21 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/atbus_endpoint.cpp" 22 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/atbus_message_handler.cpp" 23 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/atbus_connection_context.cpp" 24 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/atbus_node.cpp" 25 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/channel_io_stream.cpp" 26 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/channel_mem.cpp" 27 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/channel_shm.cpp" 28 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/channel_utility.cpp" 29 | "${PROJECT_LIBATBUS_ROOT_SRC_DIR}/libatbus_protocol.cpp") 30 | 31 | source_group(TREE "${PROJECT_SOURCE_DIR}" FILES ${PROJECT_LIB_SRC_LIST}) 32 | 33 | list(APPEND PROJECT_LIB_SRC_LIST "${PROJECT_LIBATBUS_GENERATED_DIR}/include/detail/libatbus_config.h") 34 | source_group(TREE "${PROJECT_LIBATBUS_GENERATED_DIR}" 35 | FILES "${PROJECT_LIBATBUS_GENERATED_DIR}/include/detail/libatbus_config.h") 36 | 37 | set(PROJECT_LIB_PROTOCOL_SRC_LIST "${PROJECT_LIBATBUS_GENERATED_DIR}/include/libatbus_protocol.pb.h" 38 | "${PROJECT_LIBATBUS_GENERATED_DIR}/src/libatbus_protocol.pb.cc") 39 | 40 | set_source_files_properties("${PROJECT_LIBATBUS_GENERATED_DIR}/include/libatbus_protocol.pb.h" 41 | "${PROJECT_LIBATBUS_GENERATED_DIR}/src/libatbus_protocol.pb.cc" PROPERTIES GENERATED TRUE) 42 | 43 | # ============ libatbus - src ============ 44 | if(NOT DEFINED ATBUS_MACRO_PROTOCOL_USE_DYNAMIC_LIBRARY) 45 | if(BUILD_SHARED_LIBS OR ATFRAMEWORK_USE_DYNAMIC_LIBRARY) 46 | if(DEFINED ATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_ALLOW_SHARED_LIBS) 47 | set(ATBUS_MACRO_PROTOCOL_USE_DYNAMIC_LIBRARY ${ATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_ALLOW_SHARED_LIBS}) 48 | else() 49 | set(ATBUS_MACRO_PROTOCOL_USE_DYNAMIC_LIBRARY TRUE) 50 | endif() 51 | else() 52 | set(ATBUS_MACRO_PROTOCOL_USE_DYNAMIC_LIBRARY FALSE) 53 | endif() 54 | endif() 55 | 56 | if(ATBUS_MACRO_PROTOCOL_USE_DYNAMIC_LIBRARY) 57 | add_library("${PROJECT_LIBATBUS_LIB_LINK}-protocol" SHARED ${PROJECT_LIB_PROTOCOL_SRC_LIST}) 58 | project_build_tools_set_shared_library_declaration(ATBUS_MACRO_PROTOCOL_API "${PROJECT_LIBATBUS_LIB_LINK}-protocol") 59 | else() 60 | add_library("${PROJECT_LIBATBUS_LIB_LINK}-protocol" STATIC ${PROJECT_LIB_PROTOCOL_SRC_LIST}) 61 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang|Intel|XL|XLClang") 62 | target_compile_definitions("${PROJECT_LIBATBUS_LIB_LINK}-protocol" 63 | PUBLIC "ATBUS_MACRO_PROTOCOL_API=__attribute__((visibility(\"default\")))") 64 | else() 65 | target_compile_definitions("${PROJECT_LIBATBUS_LIB_LINK}-protocol" PUBLIC "ATBUS_MACRO_PROTOCOL_API=") 66 | endif() 67 | endif() 68 | if(NOT APPLE) 69 | set_target_properties("${PROJECT_LIBATBUS_LIB_LINK}-protocol" PROPERTIES C_VISIBILITY_PRESET "hidden" 70 | CXX_VISIBILITY_PRESET "hidden") 71 | endif() 72 | set_target_properties( 73 | "${PROJECT_LIBATBUS_LIB_LINK}-protocol" 74 | PROPERTIES VERSION ${LIBATBUS_VERSION} 75 | CXX_INCLUDE_WHAT_YOU_USE "" 76 | CXX_CLANG_TIDY "") 77 | target_link_libraries("${PROJECT_LIBATBUS_LIB_LINK}-protocol" 78 | PUBLIC ${ATFRAMEWORK_CMAKE_TOOLSET_THIRD_PARTY_PROTOBUF_LINK_NAME}) 79 | target_include_directories( 80 | "${PROJECT_LIBATBUS_LIB_LINK}-protocol" 81 | PRIVATE "$" 82 | PUBLIC "$" "$") 83 | 84 | set(PROJECT_LIBATBUS_PROTOCOL_SOURCE_COMPILE_OPTIONS ${PROJECT_COMMON_PRIVATE_COMPILE_OPTIONS} 85 | ${PROJECT_BUILD_TOOLS_PATCH_PROTOBUF_SOURCES_OPTIONS}) 86 | if(PROJECT_BUILD_TOOLS_PATCH_PROTOBUF_SOURCES_REMOVE_OPTIONS) 87 | list(REMOVE_ITEM PROJECT_LIBATBUS_PROTOCOL_SOURCE_COMPILE_OPTIONS 88 | ${PROJECT_BUILD_TOOLS_PATCH_PROTOBUF_SOURCES_REMOVE_OPTIONS}) 89 | endif() 90 | 91 | if(PROJECT_LIBATBUS_PROTOCOL_SOURCE_COMPILE_OPTIONS) 92 | target_compile_options("${PROJECT_LIBATBUS_LIB_LINK}-protocol" 93 | PRIVATE ${PROJECT_LIBATBUS_PROTOCOL_SOURCE_COMPILE_OPTIONS}) 94 | endif() 95 | 96 | list(PREPEND PROJECT_LIBATBUS_PUBLIC_LINK_NAMES "${PROJECT_LIBATBUS_LIB_LINK}-protocol") 97 | add_dependencies("${PROJECT_LIBATBUS_LIB_LINK}-protocol" "atbus-generate-protocol") 98 | 99 | if(BUILD_SHARED_LIBS OR ATFRAMEWORK_USE_DYNAMIC_LIBRARY) 100 | add_library(${PROJECT_LIBATBUS_LIB_LINK} SHARED ${PROJECT_LIB_SRC_LIST}) 101 | set_target_properties( 102 | ${PROJECT_LIBATBUS_LIB_LINK} 103 | PROPERTIES C_VISIBILITY_PRESET "hidden" 104 | CXX_VISIBILITY_PRESET "hidden" 105 | VERSION ${LIBATBUS_VERSION} 106 | SOVERSION ${LIBATBUS_VERSION} 107 | INTERFACE_COMPILE_DEFINITIONS ATBUS_MACRO_API_DLL=1) 108 | target_compile_definitions(${PROJECT_LIBATBUS_LIB_LINK} PRIVATE ATBUS_MACRO_API_NATIVE=1 ATBUS_MACRO_API_DLL=1) 109 | else() 110 | add_library(${PROJECT_LIBATBUS_LIB_LINK} STATIC ${PROJECT_LIB_SRC_LIST}) 111 | set_target_properties( 112 | ${PROJECT_LIBATBUS_LIB_LINK} 113 | PROPERTIES C_VISIBILITY_PRESET "hidden" 114 | CXX_VISIBILITY_PRESET "hidden" 115 | VERSION ${LIBATBUS_VERSION}) 116 | target_compile_definitions(${PROJECT_LIBATBUS_LIB_LINK} PRIVATE ATBUS_MACRO_API_NATIVE=1) 117 | endif() 118 | 119 | add_dependencies(${PROJECT_LIBATBUS_LIB_LINK} "atbus-generate-protocol") 120 | 121 | target_include_directories( 122 | ${PROJECT_LIBATBUS_LIB_LINK} 123 | PUBLIC "$" 124 | "$" "$" 125 | PRIVATE "$") 126 | 127 | target_compile_definitions(${PROJECT_LIBATBUS_LIB_LINK} PRIVATE ATBUS_MACRO_BUILD_API=1) 128 | target_compile_options(${PROJECT_LIBATBUS_LIB_LINK} PRIVATE ${PROJECT_LIBATBUS_PRIVATE_COMPILE_OPTIONS}) 129 | target_link_libraries(${PROJECT_LIBATBUS_LIB_LINK} PUBLIC ${PROJECT_LIBATBUS_PUBLIC_LINK_NAMES}) 130 | 131 | set_property(TARGET ${PROJECT_LIBATBUS_LIB_LINK} PROPERTY FOLDER "atframework/atbus") 132 | set_property(TARGET "${PROJECT_LIBATBUS_LIB_LINK}-protocol" PROPERTY FOLDER "atframework/atbus") 133 | 134 | set(PROJECT_LIBATBUS_EXPORT_TARGETS "${PROJECT_LIBATBUS_LIB_LINK}" "${PROJECT_LIBATBUS_LIB_LINK}-protocol") 135 | add_library("atframework::${PROJECT_LIBATBUS_LIB_LINK}-protocol" ALIAS "${PROJECT_LIBATBUS_LIB_LINK}-protocol") 136 | add_library("atframework::${PROJECT_LIBATBUS_LIB_LINK}" ALIAS "${PROJECT_LIBATBUS_LIB_LINK}") 137 | 138 | install( 139 | TARGETS ${PROJECT_LIBATBUS_EXPORT_TARGETS} 140 | EXPORT ${PROJECT_LIBATBUS_EXPORT_NAME} 141 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 142 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 143 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) 144 | 145 | install( 146 | DIRECTORY ${PROJECT_LIBATBUS_ROOT_INC_DIR} 147 | DESTINATION . 148 | FILES_MATCHING 149 | REGEX ".+\\.h(pp)?$" 150 | PATTERN ".svn" EXCLUDE 151 | PATTERN ".git" EXCLUDE) 152 | 153 | install(FILES "${PROJECT_LIBATBUS_GENERATED_DIR}/include/detail/libatbus_config.h" 154 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/detail/") 155 | 156 | install(FILES "${PROJECT_LIBATBUS_GENERATED_DIR}/include/libatbus_protocol.pb.h" 157 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 158 | --------------------------------------------------------------------------------