├── .gitignore ├── CMakeLists.txt ├── README.md ├── samples ├── CMakeLists.txt ├── chat │ ├── CMakeLists.txt │ ├── chat_client.cpp │ ├── chat_message.h │ └── chat_server.cpp ├── echo │ ├── CMakeLists.txt │ ├── echo_client.cpp │ └── echo_server.cpp ├── filetransfer │ ├── CMakeLists.txt │ ├── ft_client.cpp │ ├── ft_common.h │ └── ft_server.cpp ├── relay │ ├── CMakeLists.txt │ ├── relay_local.cpp │ ├── relay_pipe.cpp │ ├── relay_protocol.h │ └── relay_server.cpp ├── socks5 │ ├── CMakeLists.txt │ ├── s5_main.cpp │ ├── s5_protocol.cpp │ └── s5_protocol.h └── timer │ ├── CMakeLists.txt │ └── timer.cpp ├── source ├── CMakeLists.txt ├── cyCore │ ├── core │ │ ├── cyc_atomic.h │ │ ├── cyc_lf_queue.h │ │ ├── cyc_logger.cpp │ │ ├── cyc_logger.h │ │ ├── cyc_ring_buf.cpp │ │ ├── cyc_ring_buf.h │ │ ├── cyc_socket_api.cpp │ │ ├── cyc_socket_api.h │ │ ├── cyc_system_api.cpp │ │ └── cyc_system_api.h │ └── cy_core.h ├── cyCrypt │ ├── crypt │ │ ├── cswrap │ │ │ ├── Rijndael.cs │ │ │ └── Test.cs │ │ ├── cyr_adler32.cpp │ │ ├── cyr_adler32.h │ │ ├── cyr_dhexchange.cpp │ │ ├── cyr_dhexchange.h │ │ ├── cyr_rijndael.cpp │ │ ├── cyr_rijndael.h │ │ ├── cyr_xorshift128.cpp │ │ └── cyr_xorshift128.h │ └── cy_crypt.h ├── cyEvent │ ├── cy_event.h │ └── event │ │ ├── cye_looper.cpp │ │ ├── cye_looper.h │ │ ├── cye_packet.cpp │ │ ├── cye_packet.h │ │ ├── cye_pipe.cpp │ │ ├── cye_pipe.h │ │ ├── cye_work_thread.cpp │ │ ├── cye_work_thread.h │ │ └── internal │ │ ├── cye_create_looper.cpp │ │ ├── cye_looper_epoll.cpp │ │ ├── cye_looper_epoll.h │ │ ├── cye_looper_kqueue.cpp │ │ ├── cye_looper_kqueue.h │ │ ├── cye_looper_select.cpp │ │ └── cye_looper_select.h ├── cyNetwork │ ├── cy_network.h │ └── network │ │ ├── cyn_address.cpp │ │ ├── cyn_address.h │ │ ├── cyn_tcp_client.cpp │ │ ├── cyn_tcp_client.h │ │ ├── cyn_tcp_connection.cpp │ │ ├── cyn_tcp_connection.h │ │ ├── cyn_tcp_server.cpp │ │ ├── cyn_tcp_server.h │ │ └── internal │ │ ├── cyn_tcp_server_master_thread.cpp │ │ ├── cyn_tcp_server_master_thread.h │ │ ├── cyn_tcp_server_work_thread.cpp │ │ └── cyn_tcp_server_work_thread.h ├── cyUtility │ └── utility │ │ ├── cyu_ring_queue.h │ │ ├── cyu_simple_opt.h │ │ ├── cyu_statistics.h │ │ ├── cyu_string_util.cpp │ │ └── cyu_string_util.h └── cyclone_config.h.in └── test ├── CMakeLists.txt └── unit ├── CMakeLists.txt ├── cyt_event_fortest.h ├── cyt_uint_system.cpp ├── cyt_unit_crypt.cpp ├── cyt_unit_event_basic.cpp ├── cyt_unit_event_socket.cpp ├── cyt_unit_event_timer.cpp ├── cyt_unit_lfqueue.cpp ├── cyt_unit_main.cpp ├── cyt_unit_packet.cpp ├── cyt_unit_pipe.cpp ├── cyt_unit_ring_buf.cpp ├── cyt_unit_ring_queue.cpp ├── cyt_unit_signal.cpp └── cyt_unit_statistics.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | cmake_minimum_required (VERSION 3.3) 6 | 7 | project(cyclone) 8 | 9 | ######## 10 | #system 11 | ######## 12 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 13 | set(CY_SYS_LINUX TRUE) 14 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") 15 | set(CY_SYS_WINDOWS TRUE) 16 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 17 | set(CY_SYS_MACOS TRUE) 18 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Android") 19 | set(CY_SYS_ANDROID TRUE) 20 | else() 21 | message(FATAL_ERROR "Unknown target system \"${CMAKE_SYSTEM_NAME}\".") 22 | endif() 23 | 24 | ######## 25 | #set cyclone files path 26 | ######## 27 | set(CY_SOURCE_CORE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/source/cyCore) 28 | set(CY_SOURCE_EVENT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/source/cyEvent) 29 | set(CY_SOURCE_NETWORK_PATH ${CMAKE_CURRENT_SOURCE_DIR}/source/cyNetwork) 30 | set(CY_SOURCE_CRYPT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/source/cyCrypt) 31 | set(CY_SOURCE_UTILITY_PATH ${CMAKE_CURRENT_SOURCE_DIR}/source/cyUtility) 32 | 33 | ######## 34 | #check include file 35 | ######## 36 | if(POLICY CMP0075) 37 | cmake_policy(SET CMP0075 NEW) 38 | endif() 39 | set(CMAKE_REQUIRED_LIBRARIES pthread) 40 | 41 | include(CheckIncludeFile) 42 | include(CheckIncludeFileCXX) 43 | check_include_file(unistd.h CY_HAVE_UNISTD_H) 44 | check_include_file(inttypes.h CY_HAVE_INTTYPES_H) 45 | check_include_file(limits.h CY_HAVE_LIMITS_H) 46 | check_include_file(sys/types.h CY_HAVE_SYS_TYPES_H) 47 | check_include_file(sys/param.h CY_HAVE_SYS_PARAM_H) 48 | check_include_file(sys/mount.h CY_HAVE_SYS_MOUNT_H) 49 | check_include_file(sys/statvfs.h CY_HAVE_SYS_STATVFS_H) 50 | check_include_file(crypt.h CY_HAVE_CRYPT_H) 51 | check_include_file(sys/prctl.h CY_HAVE_SYS_PRCTL_H) 52 | check_include_file(sys/vfs.h CY_HAVE_SYS_VFS_H) 53 | check_include_file(sys/uio.h CY_HAVE_SYS_UIO_H) 54 | check_include_file(sys/eventfd.h CY_HAVE_SYS_EVENTFD_H) 55 | 56 | ######## 57 | #check functions 58 | ######## 59 | include(CheckFunctionExists) 60 | check_function_exists(epoll_ctl CY_HAVE_EPOLL) 61 | check_function_exists(readv CY_HAVE_READWRITE_V) 62 | check_function_exists(pipe2 CY_HAVE_PIPE2) 63 | check_function_exists(kqueue CY_HAVE_KQUEUE) 64 | check_function_exists(timerfd_create CY_HAVE_TIMERFD) 65 | check_function_exists(pthread_mutex_timedlock CY_HAVE_TIMED_LOCK) 66 | 67 | ######## 68 | #compiler flag 69 | ######## 70 | if(MSVC) 71 | add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) 72 | else() 73 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++11 -Wall -Wextra -Werror -Wconversion -Wno-unused-parameter -Woverloaded-virtual -Wpointer-arith -Wshadow -Wwrite-strings -Wno-sign-conversion") 74 | endif() 75 | 76 | ######## 77 | #find third library 78 | ######## 79 | 80 | #defined in $ANDROID_SDK_ROOT/build/cmake/android.toolchain.cmake 81 | if(CY_SYS_ANDROID) 82 | unset(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM) 83 | unset(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) 84 | unset(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE) 85 | unset(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE) 86 | endif() 87 | 88 | ######## 89 | #find jemalloc library 90 | ######## 91 | find_path(JEMALLOC_SDK_ROOT 92 | NAMES include/jemalloc/jemalloc.h 93 | HINTS $ENV{JEMALLOC_SDK_ROOT} 94 | ) 95 | find_library(JEMALLOC_LIBRARIES 96 | NAMES jemalloc_pic 97 | HINTS ${JEMALLOC_SDK_ROOT}/lib 98 | ) 99 | find_path(JEMALLOC_INCLUDE_DIR 100 | NAMES jemalloc/jemalloc.h 101 | HINTS ${JEMALLOC_SDK_ROOT}/include 102 | ) 103 | 104 | if(JEMALLOC_SDK_ROOT AND JEMALLOC_LIBRARIES) 105 | set(CY_HAVE_JEMALLOC_LIBRARIES TRUE) 106 | else() 107 | set(CY_HAVE_JEMALLOC_LIBRARIES FALSE) 108 | set(JEMALLOC_LIBRARIES "") 109 | endif() 110 | 111 | mark_as_advanced( 112 | JEMALLOC_LIBRARIES 113 | JEMALLOC_INCLUDE_DIR 114 | ) 115 | 116 | if(CY_HAVE_JEMALLOC_LIBRARIES) 117 | include_directories( 118 | ${JEMALLOC_INCLUDE_DIR} 119 | ) 120 | endif() 121 | 122 | ######## 123 | #find gtest and gmock library 124 | ######## 125 | if(MSVC AND CY_MSVC_MT) 126 | set(GTEST_MSVC_SEARCH MT) 127 | endif() 128 | find_package(GTest) 129 | 130 | ######## 131 | #pthread library 132 | ######## 133 | if(CY_SYS_ANDROID) 134 | set(PTHREAD_LIBRARIES "") 135 | else() 136 | set(PTHREAD_LIBRARIES "pthread") 137 | endif() 138 | 139 | ######## 140 | #is log enable 141 | ######## 142 | set(CY_ENABLE_LOG TRUE) 143 | 144 | ######## 145 | #make configure files 146 | ######## 147 | set(CY_AUTO_INCLUDE_PATH ${CMAKE_CURRENT_BINARY_DIR}/source) 148 | set(CY_AUTO_CONFIG_FILE ${CY_AUTO_INCLUDE_PATH}/cyclone_config.h) 149 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/source/cyclone_config.h.in ${CY_AUTO_CONFIG_FILE}) 150 | 151 | ######## 152 | #sub dictionary 153 | ######## 154 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 155 | add_subdirectory(source) 156 | add_subdirectory(samples) 157 | 158 | ######## 159 | #test dictionary 160 | ######## 161 | if(GTEST_FOUND) 162 | enable_testing() 163 | add_subdirectory(test) 164 | endif() 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cyclone 2 | Cyclone is a lightweight network library, It is cross-platform and high-performance 3 | - Cross platform (Windows, Mac OS X, Linux, Android) 4 | - Non-blocking IO + IO multiplexing, support `epoll`, `kqueue`, `select` 5 | - One loop per thread + reactor model 6 | - Mostly wait-free multi-threaded design 7 | - Support vectored I/O and timerfd api(Linux) 8 | - Usefull utility support(DH key exchange, AES, adler32, and more) 9 | - Unit test and samples 10 |   11 | # Dependencies 12 | Cyclone depends on following libraries 13 | - [CMake](http://cmake.org/), at least version 2.8 14 | - [jemalloc](http://jemalloc.net/) , Optional 15 | - To run unit test: 16 | - [Google Test](https://github.com/google/googletest) 17 | 18 | Cyclone has been tested with Clang (under Mac OS X), GCC 4.8+ (under Linux), Android Build Tools 25.0 and Microsoft Visual Studio 2017(under Windows 10). 19 | 20 | # Build & Test 21 | ## On Linux or Mac OS X: 22 | ``` 23 | git clone https://github.com/thejinchao/cyclone 24 | mkdir _build && cd _build 25 | cmake -G "Unix Makefiles" ../cyclone 26 | make 27 | make test 28 | ``` 29 | ## On Windows 30 | 1. Open CMake-GUI, enter the correct directory for source code and build. Then click *Configure*, choose your installed version of the Microsoft Visual Studio. 31 | 2. Click generate after fixing all missing variables to generate your Visual Studio solution. 32 | 3. Open the solution and compile the code. 33 | 4. Right click the project *RUN TESTS* and slect *Build* to run unit test 34 | 35 | ## On Android 36 | Build on the windows host machine, make sure the flowing envionment variables have been set correctly. `ANDROID_SDK_ROOT`, `ANDROID_NDK_ROOT` 37 | ``` 38 | git clone https://github.com/thejinchao/cyclone 39 | mkdir _build && cd _build 40 | %ANDROID_SDK_ROOT%/cmake/3.10.2.4988404/bin/cmake.exe -G "Ninja" -DANDROID_ABI=armeabi-v7a ^ 41 | -DANDROID_NDK=%ANDROID_NDK_ROOT% -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_ROOT%/build/cmake/android.toolchain.cmake ^ 42 | -DANDROID_NATIVE_API_LEVEL=28 -DCMAKE_MAKE_PROGRAM=%ANDROID_SDK_ROOT%/cmake/3.10.2.4988404/bin/ninja.exe ^ 43 | ../cyclone 44 | 45 | %ANDROID_SDK_ROOT%/cmake/3.10.2.4988404/bin/ninja.exe 46 | ``` 47 | To run unit test, an android device with root authority is required. 48 | ``` 49 | adb push test/unit/cyt_unit /data/local/tmp/ 50 | adb shell chmod +x /data/local/tmp/cyt_unit 51 | adb shell /data/local/tmp/cyt_unit 52 | ``` 53 | # Samples 54 | - **echo** a typical client/server program 55 | - **timer** just show how to use timer function 56 | - **chat** a simple chat room program 57 | - **socks5** a [Socks5](http://www.ietf.org/rfc/rfc1928.txt) proxy server(only support tcp protocol) 58 | - **relay** a interesting socket tunnel utility, support n:1, 1:n, key-exchange and aes encrypt. 59 | - **filetransfer** Transfer a file to another machine as soon as possible 60 | -------------------------------------------------------------------------------- /samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | ######## 6 | #sub dictionary 7 | ######## 8 | add_subdirectory(echo) 9 | add_subdirectory(timer) 10 | add_subdirectory(chat) 11 | add_subdirectory(socks5) 12 | add_subdirectory(relay) 13 | add_subdirectory(filetransfer) 14 | -------------------------------------------------------------------------------- /samples/chat/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | include_directories( 6 | ${CY_AUTO_INCLUDE_PATH} 7 | ${CY_SOURCE_CORE_PATH} 8 | ${CY_SOURCE_EVENT_PATH} 9 | ${CY_SOURCE_NETWORK_PATH} 10 | ${CY_SOURCE_UTILITY_PATH} 11 | ${CY_UTILITY_PATH}/SimpleOpt 12 | ) 13 | 14 | add_executable(chat-server chat_server.cpp) 15 | add_executable(chat-client chat_client.cpp) 16 | 17 | set_property(TARGET chat-server PROPERTY FOLDER "samples/chat") 18 | set_property(TARGET chat-client PROPERTY FOLDER "samples/chat") 19 | 20 | if(CY_SYS_WINDOWS) 21 | target_link_libraries(chat-server 22 | cyclone 23 | ws2_32.lib 24 | shlwapi.lib 25 | winmm.lib 26 | ${JEMALLOC_LIBRARIES} 27 | ) 28 | 29 | target_link_libraries(chat-client 30 | cyclone 31 | ws2_32.lib 32 | shlwapi.lib 33 | winmm.lib 34 | ${JEMALLOC_LIBRARIES} 35 | ) 36 | 37 | else() 38 | 39 | target_link_libraries(chat-server 40 | cyclone 41 | ${JEMALLOC_LIBRARIES} 42 | ${PTHREAD_LIBRARIES} 43 | ) 44 | 45 | target_link_libraries(chat-client 46 | cyclone 47 | ${JEMALLOC_LIBRARIES} 48 | ${PTHREAD_LIBRARIES} 49 | ) 50 | 51 | endif() 52 | -------------------------------------------------------------------------------- /samples/chat/chat_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "chat_message.h" 7 | 8 | #include 9 | 10 | using namespace cyclone; 11 | using namespace std::placeholders; 12 | 13 | enum { OPT_HOST, OPT_PORT, OPT_HELP }; 14 | 15 | CSimpleOptA::SOption g_rgOptions[] = { 16 | { OPT_HOST, "-h", SO_REQ_SEP }, // "-h HOST_IP" 17 | { OPT_PORT, "-p", SO_REQ_SEP }, // "-p LISTEN_PORT" 18 | { OPT_HELP, "-?", SO_NONE }, // "-?" 19 | { OPT_HELP, "--help", SO_NONE }, // "--help" 20 | SO_END_OF_OPTIONS // END 21 | }; 22 | 23 | //------------------------------------------------------------------------------------- 24 | class ChatClient 25 | { 26 | public: 27 | void startClientThread(const std::string& server_ip, uint16_t server_port) 28 | { 29 | m_server_ip = server_ip; 30 | m_server_port = server_port; 31 | 32 | //start client thread 33 | sys_api::thread_create_detached(std::bind(&ChatClient::clientThread, this), 0, "client"); 34 | CY_LOG(L_DEBUG, "connect to %s:%d...", server_ip.c_str(), server_port); 35 | 36 | //wait connect completed 37 | sys_api::signal_wait(m_connected_signal); 38 | } 39 | 40 | public: 41 | //------------------------------------------------------------------------------------- 42 | void clientThread(void) 43 | { 44 | Looper* looper = Looper::create_looper(); 45 | 46 | m_client = std::make_shared(looper, nullptr); 47 | m_client->m_listener.on_connected = std::bind(&ChatClient::onConnected, this, _1, _3); 48 | m_client->m_listener.on_message = std::bind(&ChatClient::onMessage, this, _2); 49 | m_client->m_listener.on_close = std::bind(&ChatClient::onClose, this); 50 | 51 | m_client->connect(Address(m_server_ip.c_str(), m_server_port)); 52 | looper->loop(); 53 | 54 | m_client = nullptr; 55 | Looper::destroy_looper(looper); 56 | } 57 | 58 | //------------------------------------------------------------------------------------- 59 | virtual uint32_t onConnected(TcpClientPtr client, bool success) 60 | { 61 | CY_LOG(L_DEBUG, "connect to %s:%d %s.", 62 | client->get_server_address().get_ip(), 63 | client->get_server_address().get_port(), 64 | success ? "OK" : "FAILED"); 65 | 66 | if (success){ 67 | m_client = client; 68 | sys_api::signal_notify(m_connected_signal); 69 | return 0; 70 | } 71 | else 72 | { 73 | uint32_t retry_time = 1000 * 5; 74 | CY_LOG(L_INFO, "connect failed!, retry after %d milliseconds...\n", retry_time); 75 | return 1000 * 5; 76 | } 77 | } 78 | 79 | //------------------------------------------------------------------------------------- 80 | virtual void onMessage(TcpConnectionPtr conn) 81 | { 82 | RingBuf& buf = conn->get_input_buf(); 83 | 84 | Packet packet; 85 | packet.build_from_ringbuf(PACKET_HEAD_SIZE, buf); 86 | 87 | char temp[1024] = { 0 }; 88 | memcpy(temp, packet.get_packet_content(), packet.get_packet_size()); 89 | 90 | CY_LOG(L_INFO, "%s", temp); 91 | } 92 | 93 | //------------------------------------------------------------------------------------- 94 | virtual void onClose(void) 95 | { 96 | CY_LOG(L_INFO, "socket close"); 97 | exit(0); 98 | } 99 | 100 | public: 101 | TcpClientPtr get_client(void) { return m_client; } 102 | 103 | private: 104 | TcpClientPtr m_client; 105 | std::string m_server_ip; 106 | uint16_t m_server_port; 107 | sys_api::signal_t m_connected_signal; 108 | 109 | public: 110 | ChatClient() 111 | : m_client(nullptr) 112 | { 113 | m_connected_signal = sys_api::signal_create(); 114 | } 115 | 116 | ~ChatClient() 117 | { 118 | sys_api::signal_destroy(m_connected_signal); 119 | } 120 | }; 121 | 122 | //------------------------------------------------------------------------------------- 123 | static void printUsage(const char* moduleName) 124 | { 125 | printf("===== Chat Client(Powerd by Cyclone) =====\n"); 126 | printf("Usage: %s [-h HOST_IP][-p HOST_PORT] [-?] [--help]\n", moduleName); 127 | } 128 | 129 | //------------------------------------------------------------------------------------- 130 | int main(int argc, char* argv[]) 131 | { 132 | CSimpleOptA args(argc, argv, g_rgOptions); 133 | 134 | std::string server_ip = "127.0.0.1"; 135 | uint16_t server_port = 1978; 136 | 137 | while (args.Next()) { 138 | if (args.LastError() == SO_SUCCESS) { 139 | if (args.OptionId() == OPT_HELP) { 140 | printUsage(argv[0]); 141 | return 0; 142 | } 143 | else if (args.OptionId() == OPT_HOST) { 144 | server_ip = args.OptionArg(); 145 | } 146 | else if (args.OptionId() == OPT_PORT) { 147 | server_port = (uint16_t)atoi(args.OptionArg()); 148 | } 149 | } 150 | else { 151 | printf("Invalid argument: %s\n", args.OptionText()); 152 | return 1; 153 | } 154 | } 155 | 156 | ChatClient chatClient; 157 | chatClient.startClientThread(server_ip, server_port); 158 | 159 | //input 160 | char line[MAX_CHAT_LENGTH + 1]; 161 | while (std::cin.getline(line, MAX_CHAT_LENGTH + 1)) 162 | { 163 | if (line[0] == 0) continue; 164 | 165 | Packet packet; 166 | packet.build_from_memory(PACKET_HEAD_SIZE, 0, (uint16_t)strlen(line), line); 167 | 168 | chatClient.get_client()->send(packet.get_memory_buf(), packet.get_memory_size()); 169 | } 170 | 171 | return 0; 172 | } 173 | 174 | -------------------------------------------------------------------------------- /samples/chat/chat_message.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PACKET_HEAD_SIZE (4) 4 | #define MAX_CHAT_LENGTH (255) 5 | -------------------------------------------------------------------------------- /samples/chat/chat_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "chat_message.h" 7 | 8 | #include 9 | 10 | using namespace cyclone; 11 | using namespace std::placeholders; 12 | 13 | enum { OPT_PORT, OPT_HELP }; 14 | 15 | CSimpleOptA::SOption g_rgOptions[] = { 16 | { OPT_PORT, "-p", SO_REQ_SEP }, // "-p LISTEN_PORT" 17 | { OPT_HELP, "-?", SO_NONE }, // "-?" 18 | { OPT_HELP, "--help", SO_NONE }, // "--help" 19 | SO_END_OF_OPTIONS // END 20 | }; 21 | 22 | //------------------------------------------------------------------------------------- 23 | class ChatServer 24 | { 25 | public: 26 | void startAndJoin(uint16_t server_port) 27 | { 28 | m_clients_lock = sys_api::mutex_create(); 29 | 30 | TcpServer server; 31 | server.m_listener.on_connected = std::bind(&ChatServer::onClientConnected, this, _3); 32 | server.m_listener.on_message = std::bind(&ChatServer::onClientMessage, this, _3); 33 | server.m_listener.on_close = std::bind(&ChatServer::onClientClose, this, _3); 34 | 35 | if (!server.bind(Address(server_port, false), false)) return; 36 | 37 | if (!(server.start(sys_api::get_cpu_counts()))) return; 38 | 39 | server.join(); 40 | 41 | sys_api::mutex_destroy(m_clients_lock); 42 | } 43 | private: 44 | //------------------------------------------------------------------------------------- 45 | void onClientConnected(TcpConnectionPtr conn) 46 | { 47 | //new connection 48 | sys_api::auto_mutex lock(m_clients_lock); 49 | m_clients.insert({ conn->get_id(),{ conn->get_id(), conn } }); 50 | 51 | CY_LOG(L_DEBUG, "new connection accept, from %s:%d to %s:%d", 52 | conn->get_peer_addr().get_ip(), 53 | conn->get_peer_addr().get_port(), 54 | conn->get_local_addr().get_ip(), 55 | conn->get_local_addr().get_port()); 56 | } 57 | 58 | //------------------------------------------------------------------------------------- 59 | void onClientMessage(TcpConnectionPtr conn) 60 | { 61 | RingBuf& buf = conn->get_input_buf(); 62 | 63 | for (;;) 64 | { 65 | Packet packet; 66 | if (!packet.build_from_ringbuf(PACKET_HEAD_SIZE, buf)) return; 67 | 68 | { 69 | sys_api::auto_mutex lock(m_clients_lock); 70 | 71 | for (auto client : m_clients) { 72 | client.second.connection->send(packet.get_memory_buf(), packet.get_memory_size()); 73 | } 74 | } 75 | } 76 | } 77 | 78 | //------------------------------------------------------------------------------------- 79 | void onClientClose(TcpConnectionPtr conn) 80 | { 81 | sys_api::auto_mutex lock(m_clients_lock); 82 | 83 | m_clients.erase(conn->get_id()); 84 | CY_LOG(L_DEBUG, "connection %s:%d closed", 85 | conn->get_peer_addr().get_ip(), 86 | conn->get_peer_addr().get_port()); 87 | } 88 | 89 | private: 90 | struct Client 91 | { 92 | int32_t agent_id; 93 | TcpConnectionPtr connection; 94 | }; 95 | std::map< int32_t, Client > m_clients; 96 | sys_api::mutex_t m_clients_lock; 97 | }; 98 | 99 | //------------------------------------------------------------------------------------- 100 | static void printUsage(const char* moduleName) 101 | { 102 | printf("===== Chat Server(Powerd by Cyclone) =====\n"); 103 | printf("Usage: %s [-p LISTEN_PORT] [-?] [--help]\n", moduleName); 104 | } 105 | 106 | //------------------------------------------------------------------------------------- 107 | int main(int argc, char* argv[]) 108 | { 109 | CSimpleOptA args(argc, argv, g_rgOptions); 110 | uint16_t server_port = 1978; 111 | 112 | while (args.Next()) { 113 | if (args.LastError() == SO_SUCCESS) { 114 | if (args.OptionId() == OPT_HELP) { 115 | printUsage(argv[0]); 116 | return 0; 117 | } 118 | else if (args.OptionId() == OPT_PORT) { 119 | server_port = (uint16_t)atoi(args.OptionArg()); 120 | } 121 | 122 | } 123 | else { 124 | printf("Invalid argument: %s\n", args.OptionText()); 125 | return 1; 126 | } 127 | } 128 | 129 | CY_LOG(L_DEBUG, "listen port %d", server_port); 130 | 131 | ChatServer server; 132 | server.startAndJoin(server_port); 133 | return 0; 134 | } 135 | -------------------------------------------------------------------------------- /samples/echo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | include_directories( 6 | ${CY_AUTO_INCLUDE_PATH} 7 | ${CY_SOURCE_CORE_PATH} 8 | ${CY_SOURCE_EVENT_PATH} 9 | ${CY_SOURCE_NETWORK_PATH} 10 | ${CY_SOURCE_UTILITY_PATH} 11 | ${CY_UTILITY_PATH}/SimpleOpt 12 | ) 13 | 14 | add_executable(echo-server echo_server.cpp) 15 | add_executable(echo-client echo_client.cpp) 16 | 17 | set_property(TARGET echo-server PROPERTY FOLDER "samples/echo") 18 | set_property(TARGET echo-client PROPERTY FOLDER "samples/echo") 19 | 20 | if(CY_SYS_WINDOWS) 21 | target_link_libraries(echo-server 22 | cyclone 23 | ws2_32.lib 24 | shlwapi.lib 25 | winmm.lib 26 | ${JEMALLOC_LIBRARIES} 27 | ) 28 | 29 | target_link_libraries(echo-client 30 | cyclone 31 | ws2_32.lib 32 | shlwapi.lib 33 | winmm.lib 34 | ${JEMALLOC_LIBRARIES} 35 | ) 36 | 37 | else() 38 | 39 | target_link_libraries(echo-server 40 | cyclone 41 | ${JEMALLOC_LIBRARIES} 42 | ${PTHREAD_LIBRARIES} 43 | ) 44 | 45 | target_link_libraries(echo-client 46 | cyclone 47 | ${JEMALLOC_LIBRARIES} 48 | ${PTHREAD_LIBRARIES} 49 | ) 50 | 51 | endif() 52 | -------------------------------------------------------------------------------- /samples/echo/echo_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace cyclone; 9 | using namespace std::placeholders; 10 | 11 | #define MAX_ECHO_LENGTH (255) 12 | 13 | enum { OPT_HOST, OPT_PORT, OPT_HELP }; 14 | 15 | CSimpleOptA::SOption g_rgOptions[] = { 16 | { OPT_HOST, "-h", SO_REQ_SEP }, // "-h HOST_IP" 17 | { OPT_PORT, "-p", SO_REQ_SEP }, // "-p LISTEN_PORT" 18 | { OPT_HELP, "-?", SO_NONE }, // "-?" 19 | { OPT_HELP, "--help", SO_NONE }, // "--help" 20 | SO_END_OF_OPTIONS // END 21 | }; 22 | 23 | //------------------------------------------------------------------------------------- 24 | class EchoClient 25 | { 26 | public: 27 | void startClientThread(const std::string& server_ip, uint16_t server_port) 28 | { 29 | m_server_ip = server_ip; 30 | m_server_port = server_port; 31 | 32 | //start client thread 33 | sys_api::thread_create_detached(std::bind(&EchoClient::clientThread, this), 0, "client"); 34 | CY_LOG(L_DEBUG, "connect to %s:%d...", server_ip.c_str(), server_port); 35 | 36 | //wait connect completed 37 | sys_api::signal_wait(m_connected_signal); 38 | } 39 | 40 | private: 41 | //------------------------------------------------------------------------------------- 42 | void clientThread(void) 43 | { 44 | Looper* looper = Looper::create_looper(); 45 | 46 | m_client = std::make_shared(looper, nullptr); 47 | m_client->m_listener.on_connected = std::bind(&EchoClient::onConnected, this, _1, _3); 48 | m_client->m_listener.on_message = std::bind(&EchoClient::onMessage, this, _2); 49 | m_client->m_listener.on_close = std::bind(&EchoClient::onClose, this); 50 | 51 | m_client->connect(Address(m_server_ip.c_str(), m_server_port)); 52 | looper->loop(); 53 | 54 | m_client = nullptr; 55 | Looper::destroy_looper(looper); 56 | } 57 | 58 | //------------------------------------------------------------------------------------- 59 | uint32_t onConnected(TcpClientPtr client, bool success) 60 | { 61 | CY_LOG(L_DEBUG, "connect to %s:%d %s.", 62 | client->get_server_address().get_ip(), 63 | client->get_server_address().get_port(), 64 | success ? "OK" : "FAILED"); 65 | 66 | if (success) { 67 | sys_api::signal_notify(m_connected_signal); 68 | return 0; 69 | } 70 | else 71 | { 72 | uint32_t retry_time = 1000 * 5; 73 | CY_LOG(L_INFO, "connect failed!, retry after %d milliseconds...", retry_time); 74 | return 1000 * 5; 75 | } 76 | } 77 | 78 | //------------------------------------------------------------------------------------- 79 | void onMessage(TcpConnectionPtr conn) 80 | { 81 | RingBuf& buf = conn->get_input_buf(); 82 | 83 | char temp[MAX_ECHO_LENGTH +1] = { 0 }; 84 | buf.memcpy_out(temp, MAX_ECHO_LENGTH); 85 | 86 | CY_LOG(L_INFO, "%s", temp); 87 | } 88 | 89 | //------------------------------------------------------------------------------------- 90 | void onClose(void) 91 | { 92 | CY_LOG(L_INFO, "socket close"); 93 | exit(0); 94 | } 95 | 96 | public: 97 | TcpClientPtr get_client(void) { return m_client; } 98 | 99 | private: 100 | TcpClientPtr m_client; 101 | std::string m_server_ip; 102 | uint16_t m_server_port; 103 | sys_api::signal_t m_connected_signal; 104 | 105 | public: 106 | EchoClient() 107 | : m_client(nullptr) 108 | { 109 | m_connected_signal = sys_api::signal_create(); 110 | } 111 | 112 | ~EchoClient() 113 | { 114 | sys_api::signal_destroy(m_connected_signal); 115 | } 116 | }; 117 | 118 | //------------------------------------------------------------------------------------- 119 | static void printUsage(const char* moduleName) 120 | { 121 | printf("===== Echo Client(Powerd by Cyclone) =====\n"); 122 | printf("Usage: %s [-h HOST_IP][-p HOST_PORT] [-?] [--help]\n", moduleName); 123 | } 124 | 125 | //------------------------------------------------------------------------------------- 126 | int main(int argc, char* argv[]) 127 | { 128 | CSimpleOptA args(argc, argv, g_rgOptions); 129 | 130 | std::string server_ip = "127.0.0.1"; 131 | uint16_t server_port = 1978; 132 | 133 | while (args.Next()) { 134 | if (args.LastError() == SO_SUCCESS) { 135 | if (args.OptionId() == OPT_HELP) { 136 | printUsage(argv[0]); 137 | return 0; 138 | } 139 | else if (args.OptionId() == OPT_HOST) { 140 | server_ip =args.OptionArg(); 141 | } 142 | else if (args.OptionId() == OPT_PORT) { 143 | server_port = (uint16_t)atoi(args.OptionArg()); 144 | } 145 | } 146 | else { 147 | printf("Invalid argument: %s\n", args.OptionText()); 148 | return 1; 149 | } 150 | } 151 | 152 | EchoClient echoClient; 153 | echoClient.startClientThread(server_ip, server_port); 154 | 155 | //input 156 | char line[MAX_ECHO_LENGTH + 1] = { 0 }; 157 | while (std::cin.getline(line, MAX_ECHO_LENGTH + 1)) 158 | { 159 | if (line[0] == 0) continue; 160 | echoClient.get_client()->send(line, strlen(line)); 161 | } 162 | 163 | return 0; 164 | } 165 | 166 | -------------------------------------------------------------------------------- /samples/echo/echo_server.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace cyclone; 9 | 10 | #define MAX_ECHO_LENGTH (255) 11 | 12 | enum { OPT_PORT, OPT_HELP }; 13 | 14 | CSimpleOptA::SOption g_rgOptions[] = { 15 | { OPT_PORT, "-p", SO_REQ_SEP }, // "-p LISTEN_PORT" 16 | { OPT_HELP, "-?", SO_NONE }, // "-?" 17 | { OPT_HELP, "--help", SO_NONE }, // "--help" 18 | SO_END_OF_OPTIONS // END 19 | }; 20 | 21 | //------------------------------------------------------------------------------------- 22 | void onPeerConnected(TcpServer* server, int32_t thread_index, TcpConnectionPtr conn) 23 | { 24 | (void)server; 25 | 26 | CY_LOG(L_INFO, "[T=%d]new connection accept, from %s:%d to %s:%d", 27 | thread_index, 28 | conn->get_peer_addr().get_ip(), 29 | conn->get_peer_addr().get_port(), 30 | conn->get_local_addr().get_ip(), 31 | conn->get_local_addr().get_port()); 32 | } 33 | 34 | //------------------------------------------------------------------------------------- 35 | void onPeerMessage(TcpServer* server, int32_t thread_index, TcpConnectionPtr conn) 36 | { 37 | RingBuf& buf = conn->get_input_buf(); 38 | 39 | char temp[MAX_ECHO_LENGTH + 1] = { 0 }; 40 | buf.memcpy_out(temp, MAX_ECHO_LENGTH); 41 | 42 | CY_LOG(L_INFO, "[T=%d]receive:%s", thread_index, temp); 43 | 44 | if (strcmp(temp, "exit") == 0) { 45 | server->shutdown_connection(conn); 46 | return; 47 | } 48 | 49 | if (strcmp(temp, "shutdown") == 0) { 50 | sys_api::thread_create_detached([server](void*) { 51 | server->stop(); 52 | }, 0, nullptr); 53 | return; 54 | } 55 | 56 | size_t len = strlen(temp); 57 | for (size_t i = 0; i < len; i++) temp[i] = (char)toupper(temp[i]); 58 | 59 | conn->send(temp, strlen(temp)); 60 | } 61 | 62 | //------------------------------------------------------------------------------------- 63 | void onPeerClose(TcpServer* server, int32_t thread_index, TcpConnectionPtr conn) 64 | { 65 | (void)server; 66 | 67 | CY_LOG(L_INFO, "[T=%d]connection %s:%d closed", 68 | thread_index, 69 | conn->get_peer_addr().get_ip(), 70 | conn->get_peer_addr().get_port()); 71 | } 72 | 73 | //------------------------------------------------------------------------------------- 74 | static void printUsage(const char* moduleName) 75 | { 76 | printf("===== Echo Server(Powerd by Cyclone) =====\n"); 77 | printf("Usage: %s [-p LISTEN_PORT] [-?] [--help]\n", moduleName); 78 | } 79 | 80 | //------------------------------------------------------------------------------------- 81 | int main(int argc, char* argv[]) 82 | { 83 | CSimpleOptA args(argc, argv, g_rgOptions); 84 | uint16_t server_port = 1978; 85 | 86 | while (args.Next()) { 87 | if (args.LastError() == SO_SUCCESS) { 88 | if (args.OptionId() == OPT_HELP) { 89 | printUsage(argv[0]); 90 | return 0; 91 | } 92 | else if (args.OptionId() == OPT_PORT) { 93 | server_port = (uint16_t)atoi(args.OptionArg()); 94 | } 95 | 96 | } 97 | else { 98 | printf("Invalid argument: %s\n", args.OptionText()); 99 | return 1; 100 | } 101 | } 102 | 103 | CY_LOG(L_DEBUG, "listen port %d", server_port); 104 | 105 | TcpServer server; 106 | 107 | server.m_listener.on_connected = onPeerConnected; 108 | server.m_listener.on_close = onPeerClose; 109 | server.m_listener.on_message = onPeerMessage; 110 | 111 | if (!server.bind(Address(server_port, false), true)) 112 | return 1; 113 | 114 | if (!server.start(sys_api::get_cpu_counts())) 115 | return 1; 116 | 117 | server.join(); 118 | 119 | return 0; 120 | } -------------------------------------------------------------------------------- /samples/filetransfer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | include_directories( 6 | ${CY_AUTO_INCLUDE_PATH} 7 | ${CY_SOURCE_CORE_PATH} 8 | ${CY_SOURCE_CRYPT_PATH} 9 | ${CY_SOURCE_EVENT_PATH} 10 | ${CY_SOURCE_NETWORK_PATH} 11 | ${CY_SOURCE_UTILITY_PATH} 12 | ${CY_UTILITY_PATH}/SimpleOpt 13 | ) 14 | 15 | add_executable(ft-server 16 | ft_server.cpp 17 | ft_common.h) 18 | 19 | add_executable(ft-client 20 | ft_client.cpp 21 | ft_common.h) 22 | 23 | set_property(TARGET ft-server PROPERTY FOLDER "samples/filetransfer") 24 | set_property(TARGET ft-client PROPERTY FOLDER "samples/filetransfer") 25 | 26 | if(CY_SYS_WINDOWS) 27 | target_link_libraries(ft-server 28 | cyclone 29 | ws2_32.lib 30 | shlwapi.lib 31 | winmm.lib 32 | ${JEMALLOC_LIBRARIES} 33 | ) 34 | 35 | target_link_libraries(ft-client 36 | cyclone 37 | ws2_32.lib 38 | shlwapi.lib 39 | winmm.lib 40 | ${JEMALLOC_LIBRARIES} 41 | ) 42 | 43 | else() 44 | 45 | target_link_libraries(ft-server 46 | cyclone 47 | ${JEMALLOC_LIBRARIES} 48 | ${PTHREAD_LIBRARIES} 49 | ) 50 | 51 | target_link_libraries(ft-client 52 | cyclone 53 | ${JEMALLOC_LIBRARIES} 54 | ${PTHREAD_LIBRARIES} 55 | ) 56 | 57 | endif() 58 | -------------------------------------------------------------------------------- /samples/filetransfer/ft_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct FT_Head 6 | { 7 | int32_t size; 8 | int32_t id; 9 | }; 10 | 11 | struct FT_RequireFileInfo : public FT_Head 12 | { 13 | enum { ID = 1 }; 14 | }; 15 | 16 | struct FT_ReplyFileInfo : public FT_Head 17 | { 18 | enum { ID = 2 }; 19 | size_t fileSize; 20 | int32_t threadCounts; 21 | int32_t nameLength; //file name length(with \0 end) 22 | }; 23 | 24 | struct FT_RequireFileFragment : public FT_Head 25 | { 26 | enum { ID=3 }; 27 | size_t fileOffset; 28 | int32_t fragmentSize; 29 | }; 30 | 31 | struct FT_ReplyFileFragment_Begin : public FT_Head 32 | { 33 | enum { ID = 4 }; 34 | size_t fileOffset; 35 | int32_t fragmentSize; 36 | }; 37 | 38 | struct FT_ReplyFileFragment_End : public FT_Head 39 | { 40 | enum { ID = 5 }; 41 | uint32_t fragmentCRC; 42 | }; 43 | -------------------------------------------------------------------------------- /samples/relay/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | include_directories( 6 | ${CY_AUTO_INCLUDE_PATH} 7 | ${CY_SOURCE_CORE_PATH} 8 | ${CY_SOURCE_EVENT_PATH} 9 | ${CY_SOURCE_CRYPT_PATH} 10 | ${CY_SOURCE_NETWORK_PATH} 11 | ${CY_SOURCE_UTILITY_PATH} 12 | ${CY_UTILITY_PATH}/SimpleOpt 13 | ) 14 | add_executable(relay_local 15 | relay_local.cpp 16 | relay_protocol.h 17 | ) 18 | 19 | add_executable(relay_server 20 | relay_server.cpp 21 | relay_protocol.h 22 | ) 23 | 24 | add_executable(relay_pipe 25 | relay_pipe.cpp 26 | ) 27 | 28 | set_property(TARGET relay_local PROPERTY FOLDER "samples/relay") 29 | set_property(TARGET relay_server PROPERTY FOLDER "samples/relay") 30 | set_property(TARGET relay_pipe PROPERTY FOLDER "samples/relay") 31 | 32 | if(CY_SYS_WINDOWS) 33 | target_link_libraries(relay_local 34 | cyclone 35 | ws2_32.lib 36 | shlwapi.lib 37 | winmm.lib 38 | ${JEMALLOC_LIBRARIES} 39 | ) 40 | 41 | target_link_libraries(relay_server 42 | cyclone 43 | ws2_32.lib 44 | shlwapi.lib 45 | winmm.lib 46 | ${JEMALLOC_LIBRARIES} 47 | ) 48 | 49 | target_link_libraries(relay_pipe 50 | cyclone 51 | ws2_32.lib 52 | shlwapi.lib 53 | winmm.lib 54 | ${JEMALLOC_LIBRARIES} 55 | ) 56 | else() 57 | 58 | target_link_libraries(relay_local 59 | cyclone 60 | ${JEMALLOC_LIBRARIES} 61 | ${PTHREAD_LIBRARIES} 62 | ) 63 | 64 | target_link_libraries(relay_server 65 | cyclone 66 | ${JEMALLOC_LIBRARIES} 67 | ${PTHREAD_LIBRARIES} 68 | ) 69 | 70 | target_link_libraries(relay_pipe 71 | cyclone 72 | ${JEMALLOC_LIBRARIES} 73 | ${PTHREAD_LIBRARIES} 74 | ) 75 | endif() 76 | -------------------------------------------------------------------------------- /samples/relay/relay_protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum { 6 | RELAY_PACKET_HEADSIZE = 4 7 | }; 8 | 9 | enum { 10 | RELAY_HANDSHAKE_ID = 100, 11 | RELAY_NEW_SESSION, 12 | RELAY_CLOSE_SESSION, 13 | RELAY_FORWARD 14 | }; 15 | 16 | struct RelayHandshakeMsg 17 | { 18 | enum { ID = RELAY_HANDSHAKE_ID }; 19 | 20 | cyclone::dhkey_t dh_key; 21 | }; 22 | 23 | struct RelayNewSessionMsg 24 | { 25 | enum { ID = RELAY_NEW_SESSION }; 26 | int32_t id; 27 | }; 28 | 29 | struct RelayCloseSessionMsg 30 | { 31 | enum { ID = RELAY_CLOSE_SESSION }; 32 | 33 | int32_t id; 34 | }; 35 | 36 | struct RelayForwardMsg 37 | { 38 | enum { ID = RELAY_FORWARD }; 39 | 40 | int32_t id; 41 | int32_t size; 42 | }; -------------------------------------------------------------------------------- /samples/socks5/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | include_directories( 6 | ${CY_AUTO_INCLUDE_PATH} 7 | ${CY_SOURCE_CORE_PATH} 8 | ${CY_SOURCE_EVENT_PATH} 9 | ${CY_SOURCE_NETWORK_PATH} 10 | ${CY_SOURCE_UTILITY_PATH} 11 | ${CY_UTILITY_PATH}/SimpleOpt 12 | ) 13 | 14 | add_executable(socks5 15 | s5_main.cpp 16 | s5_protocol.h 17 | s5_protocol.cpp 18 | ) 19 | 20 | set_property(TARGET socks5 PROPERTY FOLDER "samples/socks5") 21 | 22 | if(CY_SYS_WINDOWS) 23 | target_link_libraries(socks5 24 | cyclone 25 | ws2_32.lib 26 | shlwapi.lib 27 | winmm.lib 28 | ${JEMALLOC_LIBRARIES} 29 | ) 30 | 31 | else() 32 | 33 | target_link_libraries(socks5 34 | cyclone 35 | ${JEMALLOC_LIBRARIES} 36 | ${PTHREAD_LIBRARIES} 37 | ) 38 | 39 | endif() 40 | -------------------------------------------------------------------------------- /samples/socks5/s5_protocol.cpp: -------------------------------------------------------------------------------- 1 | #include "s5_protocol.h" 2 | 3 | //------------------------------------------------------------------------------------- 4 | void s5_build_handshake_act(RingBuf& outputBuf) 5 | { 6 | uint8_t socks5_version_ack[2]; 7 | 8 | socks5_version_ack[0] = S5_VERSION; 9 | socks5_version_ack[1] = S5_METHOD_NO_AUTH; 10 | outputBuf.memcpy_into(socks5_version_ack, sizeof(socks5_version_ack)); 11 | } 12 | 13 | //------------------------------------------------------------------------------------- 14 | void s5_build_connect_act(RingBuf& outputBuf, uint8_t reply, const Address& address) 15 | { 16 | //head 17 | uint8_t temp[4] = { S5_VERSION, reply, 0, 0x01 }; 18 | outputBuf.memcpy_into(temp, 4); 19 | 20 | //address 21 | const sockaddr_in& addr = address.get_sockaddr_in(); 22 | outputBuf.memcpy_into(&addr.sin_addr.s_addr, sizeof(addr.sin_addr.s_addr)); 23 | outputBuf.memcpy_into(&addr.sin_port, sizeof(addr.sin_port)); 24 | } 25 | 26 | //------------------------------------------------------------------------------------- 27 | int32_t s5_get_handshake(RingBuf& inputBuf) 28 | { 29 | const size_t max_peek_size = 260; 30 | 31 | uint8_t temp[max_peek_size]; 32 | size_t peek_size = inputBuf.peek(0, temp, max_peek_size); 33 | if (peek_size < 3) return S5ERR_NEED_MORE_DATA; 34 | 35 | //check version 36 | if (temp[0] != S5_VERSION) return S5ERR_WRONG_VERSION; 37 | 38 | //check methods 39 | uint8_t nmethods = temp[1]; 40 | if (peek_size < (size_t)(nmethods + 2)) return S5ERR_NEED_MORE_DATA; 41 | if (peek_size > (size_t)(nmethods + 2)) return S5ERR_PROTOCOL_ERR; 42 | 43 | for (uint8_t i = 0; i < nmethods; i++) { 44 | if (temp[i + 2] == S5_METHOD_NO_AUTH) { 45 | //handshake ok 46 | inputBuf.discard(peek_size); 47 | return S5ERR_SUCCESS; 48 | } 49 | } 50 | return S5ERR_WRONG_METHOD; 51 | } 52 | 53 | //------------------------------------------------------------------------------------- 54 | int32_t s5_get_connect_request(RingBuf& inputBuf, Address& address, std::string& domain) 55 | { 56 | char head[4] = { 0 }; 57 | size_t peek_size = inputBuf.peek(0, head, 4); 58 | if (peek_size < 4) return S5ERR_NEED_MORE_DATA; 59 | 60 | //check version 61 | if (head[0] != S5_VERSION) return S5ERR_WRONG_VERSION; 62 | //check cmd 63 | if (head[1] != S5_CMD_CONNECT) return S5ERR_NOTSUPPORT; 64 | 65 | //switch ATYP 66 | switch (head[3]) { 67 | case 0x01: //IP V4 address: X'01' 68 | { 69 | if (inputBuf.size() < 10) return S5ERR_NEED_MORE_DATA; 70 | if (inputBuf.size() > 10) return S5ERR_PROTOCOL_ERR; 71 | 72 | inputBuf.discard(4); 73 | 74 | sockaddr_in addr; 75 | memset(&addr, 0, sizeof(addr)); 76 | 77 | addr.sin_family = AF_INET; 78 | inputBuf.memcpy_out(&(addr.sin_addr.s_addr), 4); 79 | inputBuf.memcpy_out(&(addr.sin_port), 2); 80 | 81 | address = Address(addr); 82 | domain = address.get_ip(); 83 | } 84 | break; 85 | 86 | case 0x03: //DOMAINNAME: X'03' 87 | { 88 | uint8_t domain_length; 89 | if (sizeof(uint8_t) != inputBuf.peek(4, &domain_length, sizeof(uint8_t))) return S5ERR_NEED_MORE_DATA; 90 | if (inputBuf.size() < (size_t)(7 + domain_length)) return S5ERR_NEED_MORE_DATA; 91 | if (inputBuf.size() > (size_t)(7 + domain_length)) return S5ERR_PROTOCOL_ERR; 92 | 93 | inputBuf.discard(5); 94 | 95 | char domain_name[260] = { 0 }; 96 | inputBuf.memcpy_out(domain_name, domain_length); 97 | 98 | sockaddr_in addr; 99 | memset(&addr, 0, sizeof(addr)); 100 | 101 | addr.sin_family = AF_INET; 102 | inputBuf.memcpy_out(&(addr.sin_port), 2); 103 | 104 | //resolve 105 | if (!socket_api::resolve_hostname(domain_name, addr)) return S5ERR_DNS_FAILED; 106 | address = Address(addr); 107 | domain = domain_name; 108 | } 109 | break; 110 | 111 | default: //IP V6 address: X'04'(not supported) 112 | return S5ERR_NOTSUPPORT; 113 | break; 114 | } 115 | 116 | return S5ERR_SUCCESS; 117 | } 118 | -------------------------------------------------------------------------------- /samples/socks5/s5_protocol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace cyclone; 7 | 8 | //all socks5 function return value 9 | #define S5ERR_SUCCESS (0) 10 | #define S5ERR_NEED_MORE_DATA (-1) 11 | #define S5ERR_WRONG_VERSION (-2) 12 | #define S5ERR_WRONG_METHOD (-3) 13 | #define S5ERR_PROTOCOL_ERR (-4) 14 | #define S5ERR_NOTSUPPORT (-5) 15 | #define S5ERR_DNS_FAILED (-6) 16 | 17 | //supported version 18 | #define S5_VERSION (0x05) 19 | //supproted method(NO AUTHENTICATION REQUIRED) 20 | #define S5_METHOD_NO_AUTH (0x00) 21 | //the only command we need care 22 | #define S5_CMD_CONNECT (0x01) 23 | 24 | /* Test version and method 25 | * 26 | * From RFC1928: 27 | * The client connects to the server, and sends a version 28 | * identifier/method selection message: 29 | * 30 | * +----+----------+----------+ 31 | * |VER | NMETHODS | METHODS | 32 | * +----+----------+----------+ 33 | * | 1 | 1 | 1 to 255 | 34 | * +----+----------+----------+ 35 | * 36 | * The values currently defined for METHOD are: 37 | * o X'00' NO AUTHENTICATION REQUIRED (supported) 38 | * o X'01' GSSAPI (not supported) 39 | * o X'02' USERNAME/PASSWORD (not supported) 40 | * o X'03' to X'7F' IANA ASSIGNED (not supported) 41 | * o X'80' to X'FE' RESERVED FOR PRIVATE METHODS (not supported) 42 | * o X'FF' NO ACCEPTABLE METHODS 43 | * 44 | */ 45 | int32_t s5_get_handshake(RingBuf& inputBuf); 46 | 47 | /*Build version packet ack in buf 48 | * 49 | * From RFC1928: 50 | * The server selects from one of the methods given in METHODS, and 51 | * sends a METHOD selection message: 52 | * 53 | * +----+--------+ 54 | * |VER | METHOD | 55 | * +----+--------+ 56 | * | 1 | 1 | 57 | * +----+--------+ 58 | * 59 | */ 60 | void s5_build_handshake_act(RingBuf& outputBuf); 61 | 62 | /* Test request packet in buf, and execute the request 63 | * 64 | * Return: 65 | * -EINVAL, invalid argument (typ, cmd, udp) 66 | * -EAGAIN, expects more bytes in buffer 67 | * -1, other error 68 | * 0, success 69 | * 70 | * From RFC1928: 71 | * The SOCKS request is formed as follows: 72 | * +----+-----+-------+------+----------+----------+ 73 | * |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | 74 | * +----+-----+-------+------+----------+----------+ 75 | * | 1 | 1 | X'00' | 1 | Variable | 2 | 76 | * +----+-----+-------+------+----------+----------+ 77 | * 78 | * Where: 79 | * o VER protocol version: X'05' 80 | * o CMD 81 | * o CONNECT X'01' ( define in S5_CMD_CONNECT ) 82 | * o BIND X'02' (not supported) 83 | * o UDP ASSOCIATE X'03' (not supported) 84 | * o RSV RESERVED 85 | * o ATYP address type of following address 86 | * o IP V4 address: X'01' 87 | * o DOMAINNAME: X'03' 88 | * o IP V6 address: X'04' (not supported) 89 | * o DST.ADDR desired destination address 90 | * o DST.PORT desired destination port in network octet 91 | * order 92 | * 93 | */ 94 | int32_t s5_get_connect_request(RingBuf& inputBuf, Address& address, std::string& domain); 95 | 96 | /*Build request packet ack in buf 97 | * 98 | * From RFC1928: 99 | * The server verifies the supplied UNAME and PASSWD, and sends the 100 | * following response: 101 | * +----+-----+-------+------+----------+----------+ 102 | * |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | 103 | * +----+-----+-------+------+----------+----------+ 104 | * | 1 | 1 | X'00' | 1 | Variable | 2 | 105 | * +----+-----+-------+------+----------+----------+ 106 | * 107 | * Where: 108 | * o VER protocol version: X'05' 109 | * o REP Reply field: 110 | * o X'00' succeeded 111 | * o X'01' general SOCKS server failure 112 | * o X'02' connection not allowed by ruleset 113 | * o X'03' Network unreachable 114 | * o X'04' Host unreachable 115 | * o X'05' Connection refused 116 | * o X'06' TTL expired 117 | * o X'07' Command not supported 118 | * o X'08' Address type not supported 119 | * o X'09' to X'FF' unassigned 120 | * o RSV RESERVED (must be set to 0x00) 121 | * o ATYP address type of following address 122 | * o IP V4 address: X'01' 123 | * o DOMAINNAME: X'03' 124 | * o IP V6 address: X'04' 125 | * o BND.ADDR server bound address 126 | * o BND.PORT server bound port in network octet order 127 | * 128 | */ 129 | void s5_build_connect_act(RingBuf& outputBuf, uint8_t reply, const Address& address); 130 | -------------------------------------------------------------------------------- /samples/timer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | include_directories( 6 | ${CY_AUTO_INCLUDE_PATH} 7 | ${CY_SOURCE_CORE_PATH} 8 | ${CY_SOURCE_EVENT_PATH} 9 | ${CY_SOURCE_NETWORK_PATH} 10 | ${CY_SOURCE_UTILITY_PATH} 11 | ) 12 | 13 | add_executable(timer timer.cpp) 14 | 15 | set_property(TARGET timer PROPERTY FOLDER "samples/timer") 16 | 17 | if(CY_SYS_WINDOWS) 18 | target_link_libraries(timer 19 | cyclone 20 | ws2_32.lib 21 | shlwapi.lib 22 | winmm.lib 23 | ${JEMALLOC_LIBRARIES} 24 | ) 25 | 26 | else() 27 | 28 | target_link_libraries(timer 29 | cyclone 30 | ${JEMALLOC_LIBRARIES} 31 | ${PTHREAD_LIBRARIES} 32 | ) 33 | 34 | endif() 35 | -------------------------------------------------------------------------------- /samples/timer/timer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace cyclone; 6 | 7 | //------------------------------------------------------------------------------------- 8 | void _timerFunction(uint32_t id, void* param) { 9 | Looper* looper = (Looper*)param; 10 | CY_LOG(L_INFO, "timer%d:push_stop_request", id); 11 | looper->push_stop_request(); 12 | } 13 | 14 | //------------------------------------------------------------------------------------- 15 | int main(int argc, char* argv[]) 16 | { 17 | (void)argc; 18 | (void)argv; 19 | 20 | Looper* looper = Looper::create_looper(); 21 | 22 | int32_t counts = 0; 23 | 24 | uint32_t id1 = looper->register_timer_event(1000-1, 0, [&counts] (uint32_t id, void*) { 25 | CY_LOG(L_INFO, "timer%d:counts=%d", id, ++counts); 26 | }); 27 | uint32_t id2 = looper->register_timer_event(3000, looper, _timerFunction); 28 | 29 | CY_LOG(L_INFO, "begin timer%d and timer%d", id1, id2); 30 | looper->loop(); 31 | 32 | Looper::destroy_looper(looper); 33 | 34 | return 0; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /source/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | ######## 6 | #cyCore 7 | ######## 8 | 9 | set(CY_CORE_INCLUDE_FILES 10 | cyCore/cy_core.h 11 | cyCore/core/cyc_logger.h 12 | cyCore/core/cyc_socket_api.h 13 | cyCore/core/cyc_system_api.h 14 | cyCore/core/cyc_ring_buf.h 15 | cyCore/core/cyc_atomic.h 16 | cyCore/core/cyc_lf_queue.h 17 | ) 18 | source_group("cyCore" FILES ${CY_CORE_INCLUDE_FILES}) 19 | 20 | set(CY_CORE_SOURCE_FILES 21 | cyCore/core/cyc_logger.cpp 22 | cyCore/core/cyc_socket_api.cpp 23 | cyCore/core/cyc_system_api.cpp 24 | cyCore/core/cyc_ring_buf.cpp 25 | ) 26 | source_group("cyCore" FILES ${CY_CORE_SOURCE_FILES}) 27 | 28 | ######## 29 | #cyEvent 30 | ######## 31 | 32 | set(CY_EVENT_INCLUDE_FILES 33 | cyEvent/cy_event.h 34 | cyEvent/event/cye_looper.h 35 | cyEvent/event/cye_pipe.h 36 | cyEvent/event/cye_work_thread.h 37 | cyEvent/event/cye_packet.h 38 | ) 39 | source_group("cyEvent" FILES ${CY_EVENT_INCLUDE_FILES}) 40 | 41 | set(CY_EVENT_SOURCE_FILES 42 | cyEvent/event/cye_looper.cpp 43 | cyEvent/event/cye_pipe.cpp 44 | cyEvent/event/cye_work_thread.cpp 45 | cyEvent/event/cye_packet.cpp 46 | ) 47 | source_group("cyEvent" FILES ${CY_EVENT_SOURCE_FILES}) 48 | 49 | 50 | if(CY_HAVE_EPOLL) 51 | set(CY_EVENT_INTERNAL_FILES 52 | cyEvent/event/internal/cye_looper_epoll.h 53 | cyEvent/event/internal/cye_looper_epoll.cpp 54 | cyEvent/event/internal/cye_create_looper.cpp 55 | ) 56 | elseif(CY_HAVE_KQUEUE) 57 | set(CY_EVENT_INTERNAL_FILES 58 | cyEvent/event/internal/cye_looper_kqueue.h 59 | cyEvent/event/internal/cye_looper_kqueue.cpp 60 | cyEvent/event/internal/cye_create_looper.cpp 61 | ) 62 | else() 63 | set(CY_EVENT_INTERNAL_FILES 64 | cyEvent/event/internal/cye_looper_select.h 65 | cyEvent/event/internal/cye_looper_select.cpp 66 | cyEvent/event/internal/cye_create_looper.cpp 67 | ) 68 | endif() 69 | source_group("cyEvent\\internal" FILES ${CY_EVENT_INTERNAL_FILES}) 70 | 71 | ######## 72 | #cyNetwork 73 | ######## 74 | 75 | set(CY_NETWORK_INCLUDE_FILES 76 | cyNetwork/cy_network.h 77 | cyNetwork/network/cyn_address.h 78 | cyNetwork/network/cyn_tcp_server.h 79 | cyNetwork/network/cyn_tcp_connection.h 80 | cyNetwork/network/cyn_tcp_client.h 81 | ) 82 | source_group("cyNetwork" FILES ${CY_NETWORK_INCLUDE_FILES}) 83 | 84 | set(CY_NETWORK_SOURCE_FILES 85 | cyNetwork/network/cyn_address.cpp 86 | cyNetwork/network/cyn_tcp_server.cpp 87 | cyNetwork/network/cyn_tcp_connection.cpp 88 | cyNetwork/network/cyn_tcp_client.cpp 89 | ) 90 | source_group("cyNetwork" FILES ${CY_NETWORK_SOURCE_FILES}) 91 | 92 | set(CY_NETWORK_INTERNAL_FILES 93 | cyNetwork/network/internal/cyn_tcp_server_master_thread.h 94 | cyNetwork/network/internal/cyn_tcp_server_work_thread.h 95 | cyNetwork/network/internal/cyn_tcp_server_master_thread.cpp 96 | cyNetwork/network/internal/cyn_tcp_server_work_thread.cpp 97 | ) 98 | source_group("cyNetwork\\internal" FILES ${CY_NETWORK_INTERNAL_FILES}) 99 | 100 | ######## 101 | #cyCrypt 102 | ######## 103 | set(CY_CRYPT_INCLUDE_FILES 104 | cyCrypt/cy_crypt.h 105 | cyCrypt/crypt/cyr_adler32.h 106 | cyCrypt/crypt/cyr_dhexchange.h 107 | cyCrypt/crypt/cyr_xorshift128.h 108 | cyCrypt/crypt/cyr_rijndael.h 109 | ) 110 | source_group("cyCrypt" FILES ${CY_CRYPT_INCLUDE_FILES}) 111 | 112 | set(CY_CRYPT_SOURCE_FILES 113 | cyCrypt/crypt/cyr_adler32.cpp 114 | cyCrypt/crypt/cyr_dhexchange.cpp 115 | cyCrypt/crypt/cyr_xorshift128.cpp 116 | cyCrypt/crypt/cyr_rijndael.cpp 117 | ) 118 | source_group("cyCrypt" FILES ${CY_CRYPT_SOURCE_FILES}) 119 | 120 | ######## 121 | #cyUtility 122 | ######## 123 | set(CY_UTILITY_INCLUDE_FILES 124 | cyUtility/utility/cyu_statistics.h 125 | cyUtility/utility/cyu_simple_opt.h 126 | cyUtility/utility/cyu_ring_queue.h 127 | cyUtility/utility/cyu_string_util.h 128 | ) 129 | source_group("cyUtility" FILES ${CY_UTILITY_INCLUDE_FILES}) 130 | 131 | set(CY_UTILITY_SOURCE_FILES 132 | cyUtility/utility/cyu_string_util.cpp 133 | ) 134 | source_group("cyUtility" FILES ${CY_UTILITY_SOURCE_FILES}) 135 | 136 | include_directories( 137 | ${CY_AUTO_INCLUDE_PATH} 138 | ${CY_SOURCE_CORE_PATH} 139 | ${CY_SOURCE_EVENT_PATH} 140 | ${CY_SOURCE_NETWORK_PATH} 141 | ${CY_SOURCE_CRYPT_PATH} 142 | ${CY_SOURCE_UTILITY_PATH} 143 | ) 144 | 145 | add_library(cyclone 146 | ${CY_CORE_SOURCE_FILES} 147 | ${CY_CORE_INCLUDE_FILES} 148 | ${CY_EVENT_INCLUDE_FILES} 149 | ${CY_EVENT_SOURCE_FILES} 150 | ${CY_EVENT_INTERNAL_FILES} 151 | ${CY_NETWORK_INCLUDE_FILES} 152 | ${CY_NETWORK_SOURCE_FILES} 153 | ${CY_NETWORK_INTERNAL_FILES} 154 | ${CY_CRYPT_INCLUDE_FILES} 155 | ${CY_CRYPT_SOURCE_FILES} 156 | ${CY_UTILITY_INCLUDE_FILES} 157 | ${CY_UTILITY_SOURCE_FILES} 158 | ) 159 | 160 | ######## 161 | #INSTALL 162 | ######## 163 | install(FILES ${CY_AUTO_CONFIG_FILE} 164 | DESTINATION include) 165 | 166 | install(DIRECTORY cyCore/ cyEvent/ cyNetwork/ cyCrypt/ cyUtility/ 167 | DESTINATION include 168 | FILES_MATCHING 169 | PATTERN "*.h" 170 | PATTERN "internal" EXCLUDE) 171 | 172 | if (MSVC) 173 | 174 | install(FILES $ 175 | DESTINATION lib/$<$:Debug>$<$:Release>) 176 | 177 | else() 178 | 179 | install(TARGETS cyclone 180 | DESTINATION lib) 181 | 182 | endif() 183 | -------------------------------------------------------------------------------- /source/cyCore/core/cyc_atomic.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | 11 | typedef std::atomic atomic_int32_t; 12 | typedef std::atomic atomic_int64_t; 13 | typedef std::atomic atomic_uint32_t; 14 | typedef std::atomic atomic_uint64_t; 15 | typedef std::atomic atomic_ptr_t; 16 | typedef std::atomic atomic_bool_t; 17 | 18 | template 19 | inline bool atomic_compare_exchange(std::atomic& obj, T expected, T desired) 20 | { 21 | return obj.compare_exchange_weak(expected, desired); 22 | } 23 | 24 | template 25 | inline bool atomic_greater_exchange(std::atomic& obj, T comparison, T desired) 26 | { 27 | T snapshot; 28 | bool greater; 29 | do { 30 | snapshot = obj.load(); 31 | greater = (comparison > snapshot); 32 | } while (greater && !obj.compare_exchange_weak(snapshot, desired)); 33 | return greater; 34 | } 35 | 36 | template 37 | inline bool atomic_smaller_exchange(std::atomic& obj, T comparison, T desired) 38 | { 39 | T snapshot; 40 | bool smaller; 41 | do { 42 | snapshot = obj.load(); 43 | smaller = (comparison < snapshot); 44 | } while (smaller && !obj.compare_exchange_weak(snapshot, desired)); 45 | return smaller; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /source/cyCore/core/cyc_lf_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include "cyc_atomic.h" 8 | 9 | namespace cyclone 10 | { 11 | 12 | // 13 | // Yet another implementation of a lock-free circular array queue 14 | // http://www.codeproject.com/Articles/153898/Yet-another-implementation-of-a-lock-free-circular 15 | // 16 | 17 | template //default 2^16, it must be a power of 2 value 18 | class LockFreeQueue 19 | { 20 | public: 21 | //returns the current number of items in the queue 22 | //It tries to take a snapshot of the size of the queue, but in busy environments 23 | //this function might return bogus values. 24 | uint32_t size(void) const { return m_count.load(); } 25 | 26 | //push an element at the tail of the queue, returns true if the element was inserted in the queue. False if the queue was full 27 | bool push(const ELEM_T& data); 28 | 29 | //pop the element at the head of the queue, returns true if the element was successfully extracted from the queue. False if the queue was empty 30 | bool pop(ELEM_T &data); 31 | 32 | private: 33 | //array to keep the elements 34 | ELEM_T m_queue[Q_SIZE]; 35 | //number of elements in the queue 36 | mutable atomic_uint32_t m_count; 37 | //where a new element will be inserted 38 | atomic_uint32_t m_writeIndex; 39 | //where the next element where be extracted from 40 | atomic_uint32_t m_readIndex; 41 | //maximum read index for multiple producer queues 42 | atomic_uint32_t m_maximumReadIndex; 43 | 44 | private: 45 | /// calculate the index in the circular array that corresponds to a particular "count" value 46 | inline uint32_t _countToIndex(uint32_t count) { return (count & (Q_SIZE - 1)); } 47 | 48 | public: 49 | LockFreeQueue() : m_count(0), m_writeIndex(0), m_readIndex(0), m_maximumReadIndex(0) {} 50 | virtual ~LockFreeQueue() { } 51 | }; 52 | 53 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 54 | //Impl 55 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 56 | template 57 | bool LockFreeQueue::push(const ELEM_T &data) 58 | { 59 | uint32_t currentReadIndex; 60 | uint32_t currentWriteIndex; 61 | 62 | do 63 | { 64 | currentWriteIndex = m_writeIndex; 65 | currentReadIndex = m_readIndex; 66 | if (_countToIndex(currentWriteIndex + 1) == 67 | _countToIndex(currentReadIndex)) 68 | { 69 | if (m_count > (Q_SIZE >> 1)) 70 | // the queue is full 71 | return false; 72 | else 73 | //maybe this thread was blocked between m_write_index.get() and m_read_index.get(), cause other threads write and read 74 | continue; 75 | } 76 | } while (!atomic_compare_exchange(m_writeIndex, currentWriteIndex, (currentWriteIndex + 1))); 77 | 78 | // We know now that this index is reserved for us. Use it to save the data 79 | m_queue[_countToIndex(currentWriteIndex)] = data; 80 | 81 | // update the maximum read index after saving the data. It wouldn't fail if there is only one thread 82 | // inserting in the queue. It might fail if there are more than 1 producer threads because this 83 | // operation has to be done in the same order as the previous CAS 84 | while (!atomic_compare_exchange(m_maximumReadIndex, currentWriteIndex, (currentWriteIndex + 1))) 85 | { 86 | // this is a good place to yield the thread in case there are more 87 | // software threads than hardware processors and you have more 88 | // than 1 producer thread 89 | sys_api::thread_yield(); 90 | } 91 | 92 | // The value was successfully inserted into the queue 93 | m_count++; 94 | return true; 95 | } 96 | 97 | 98 | //------------------------------------------------------------------------------------- 99 | template 100 | bool LockFreeQueue::pop(ELEM_T &a_data) 101 | { 102 | uint32_t currentMaximumReadIndex; 103 | uint32_t currentReadIndex; 104 | 105 | for (;;) // keep looping to try again! 106 | { 107 | // to ensure thread-safety when there is more than 1 producer thread 108 | // a second index is defined (m_maximumReadIndex) 109 | currentReadIndex = m_readIndex; 110 | currentMaximumReadIndex = m_maximumReadIndex; 111 | 112 | if (_countToIndex(currentReadIndex) == 113 | _countToIndex(currentMaximumReadIndex)) 114 | { 115 | // the queue is empty or 116 | // a producer thread has allocate space in the queue but is 117 | // waiting to commit the data into it 118 | return false; 119 | } 120 | 121 | // retrieve the data from the queue 122 | a_data = m_queue[_countToIndex(currentReadIndex)]; 123 | 124 | // try to perfrom now the CAS operation on the read index. If we succeed 125 | // a_data already contains what m_readIndex pointed to before we 126 | // increased it 127 | if (atomic_compare_exchange(m_readIndex, currentReadIndex, (currentReadIndex + 1))) 128 | { 129 | // got here. The value was retrieved from the queue. Note that the 130 | // data inside the m_queue array is not deleted nor reseted 131 | m_count--; 132 | return true; 133 | } 134 | 135 | // it failed retrieving the element off the queue. Someone else must 136 | // have read the element stored at countToIndex(currentReadIndex) 137 | // before we could perform the CAS operation 138 | 139 | } 140 | 141 | // Something went wrong. it shouldn't be possible to reach here 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /source/cyCore/core/cyc_logger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include "cyc_logger.h" 6 | 7 | #ifdef CY_SYS_WINDOWS 8 | #include 9 | #include 10 | #elif defined(CY_SYS_MACOS) 11 | #include 12 | #else 13 | #include 14 | #include 15 | #include 16 | #endif 17 | 18 | namespace cyclone 19 | { 20 | 21 | #define DEFAULT_LOG_PATH "./logs/" 22 | 23 | //------------------------------------------------------------------------------------- 24 | struct DiskLogFile 25 | { 26 | std::string file_path; 27 | std::string file_path_name; 28 | sys_api::mutex_t lock; 29 | const char* level_name[L_MAXIMUM_LEVEL]; 30 | LOG_LEVEL level_threshold; 31 | bool logpath_created; 32 | 33 | DiskLogFile() 34 | { 35 | #ifdef CY_SYS_WINDOWS 36 | socket_api::global_init(); 37 | #endif 38 | //log file path 39 | file_path = DEFAULT_LOG_PATH; 40 | 41 | //get process name 42 | char process_name[32] = { 0 }; 43 | sys_api::process_get_module_name(process_name, 32); 44 | 45 | //get host name 46 | char host_name[32]; 47 | ::gethostname(host_name, 32); 48 | 49 | //get process id 50 | pid_t process_id = sys_api::process_get_id(); 51 | 52 | //time 53 | char time_patten[20] = { 0 }; 54 | sys_api::local_time_now(time_patten, 20, "%Y%m%d-%H%M%S"); 55 | 56 | //filename 57 | char file_name[256] = { 0 }; 58 | std::snprintf(file_name, 256, "%s.%s.%s.%d.log", process_name, time_patten, host_name, process_id); 59 | 60 | //full file path 61 | file_path_name = file_path + file_name; 62 | 63 | //create lock 64 | lock = sys_api::mutex_create(); 65 | 66 | //default level(all level will be written) 67 | level_threshold = L_DEBUG; 68 | 69 | //log path didn't created 70 | logpath_created = false; 71 | 72 | //set level name 73 | level_name[L_TRACE] = "[T]"; 74 | level_name[L_DEBUG] = "[D]"; 75 | level_name[L_INFO] = "[I]"; 76 | level_name[L_WARN] = "[W]"; 77 | level_name[L_ERROR] = "[E]"; 78 | level_name[L_FATAL] = "[F]"; 79 | } 80 | }; 81 | 82 | //------------------------------------------------------------------------------------- 83 | static DiskLogFile& _get_disk_log(void) 84 | { 85 | static DiskLogFile thefile; 86 | return thefile; 87 | } 88 | 89 | //------------------------------------------------------------------------------------- 90 | bool set_log_filename(const char* pathName, const char* fileName) 91 | { 92 | if (pathName == nullptr || pathName[0] == 0) return false; 93 | if (fileName == nullptr || fileName[0] == 0) return false; 94 | 95 | char pathEnd = pathName[strlen(pathName) - 1]; 96 | bool withSeperator = (pathEnd == '/' || pathEnd == '\\'); 97 | 98 | DiskLogFile& thefile = _get_disk_log(); 99 | sys_api::auto_mutex guard(thefile.lock); 100 | 101 | thefile.file_path = pathName; 102 | 103 | thefile.file_path_name = pathName; 104 | if (!withSeperator) thefile.file_path_name += "/"; 105 | thefile.file_path_name += fileName; 106 | 107 | thefile.logpath_created = false; 108 | return true; 109 | } 110 | 111 | //------------------------------------------------------------------------------------- 112 | const char* get_log_filename(void) 113 | { 114 | DiskLogFile& thefile = _get_disk_log(); 115 | return thefile.file_path_name.c_str(); 116 | } 117 | 118 | //------------------------------------------------------------------------------------- 119 | void set_log_threshold(LOG_LEVEL level) 120 | { 121 | assert(level >= 0 && level <= L_MAXIMUM_LEVEL); 122 | if (level < 0 || level > L_MAXIMUM_LEVEL)return; 123 | 124 | DiskLogFile& thefile = _get_disk_log(); 125 | sys_api::auto_mutex guard(thefile.lock); 126 | 127 | thefile.level_threshold = level; 128 | } 129 | 130 | //------------------------------------------------------------------------------------- 131 | void disk_log(LOG_LEVEL level, const char* message, ...) 132 | { 133 | assert(level >= 0 && level < L_MAXIMUM_LEVEL); 134 | if (level < 0 || level >= L_MAXIMUM_LEVEL)return; 135 | 136 | DiskLogFile& thefile = _get_disk_log(); 137 | sys_api::auto_mutex guard(thefile.lock); 138 | 139 | //check the level 140 | if (level < thefile.level_threshold) return; 141 | 142 | //check dir 143 | #ifdef CY_SYS_WINDOWS 144 | if (!thefile.logpath_created && PathFileExists(thefile.file_path.c_str())!=TRUE) { 145 | if (0 == CreateDirectory(thefile.file_path.c_str(), NULL)) 146 | #else 147 | if (!thefile.logpath_created && access(thefile.file_path.c_str(), F_OK)!=0) { 148 | if (mkdir(thefile.file_path.c_str(), 0755) != 0) 149 | #endif 150 | { 151 | //create log path failed! 152 | return; 153 | } 154 | thefile.logpath_created = true; 155 | } 156 | 157 | FILE* fp = fopen(thefile.file_path_name.c_str(), "a"); 158 | if (fp == 0) { 159 | //create the log file first 160 | fp = fopen(thefile.file_path_name.c_str(), "w"); 161 | } 162 | if (fp == 0) return; 163 | 164 | char timebuf[32] = { 0 }; 165 | sys_api::local_time_now(timebuf, 32, "%Y_%m_%d-%H:%M:%S"); 166 | 167 | static const int32_t STATIC_BUF_LENGTH = 2048; 168 | 169 | char szTemp[STATIC_BUF_LENGTH] = { 0 }; 170 | char* p = szTemp; 171 | va_list ptr; va_start(ptr, message); 172 | int len = vsnprintf(p, STATIC_BUF_LENGTH, message, ptr); 173 | if (len < 0) { 174 | va_start(ptr, message); 175 | len = vsnprintf(0, 0, message, ptr); 176 | if (len > 0) { 177 | p = (char*)CY_MALLOC((size_t)(len + 1)); 178 | va_start(ptr, message); 179 | vsnprintf(p, (size_t)len + 1, message, ptr); 180 | p[len] = 0; 181 | } 182 | } 183 | else if (len >= STATIC_BUF_LENGTH) { 184 | p = (char*)CY_MALLOC((size_t)(len + 1)); 185 | va_start(ptr, message); 186 | vsnprintf(p, (size_t)len + 1, message, ptr); 187 | p[len] = 0; 188 | } 189 | va_end(ptr); 190 | 191 | fprintf(fp, "%s %s [%s] %s\n", 192 | timebuf, 193 | thefile.level_name[level], 194 | sys_api::thread_get_current_name(), 195 | p); 196 | fclose(fp); 197 | 198 | //print to stand output last 199 | fprintf(level >= L_ERROR ? stderr : stdout, "%s %s [%s] %s\n", 200 | timebuf, 201 | thefile.level_name[level], 202 | sys_api::thread_get_current_name(), 203 | p); 204 | 205 | if (p != szTemp) { 206 | CY_FREE(p); 207 | } 208 | } 209 | 210 | 211 | 212 | } 213 | -------------------------------------------------------------------------------- /source/cyCore/core/cyc_logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | 11 | enum LOG_LEVEL 12 | { 13 | L_TRACE, 14 | L_DEBUG, 15 | L_INFO, 16 | L_WARN, 17 | L_ERROR, 18 | L_FATAL, 19 | 20 | L_MAXIMUM_LEVEL, 21 | }; 22 | 23 | //---------------------- 24 | // log api 25 | //---------------------- 26 | 27 | //log to a disk file 28 | //default filename = process_name.date-time24h.hostname.pid.log 29 | // like "test.20150302-1736.server1.63581.log" 30 | // the time part is the time(LOCAL) of first log be written 31 | void disk_log(LOG_LEVEL level, const char* message, ...); 32 | 33 | //set current log filename 34 | bool set_log_filename(const char* pathName, const char* fileName); 35 | 36 | //get current log filename 37 | const char* get_log_filename(void); 38 | 39 | //set the a global log level, default is L_DEBUG, 40 | //all the log message lower than this level will be ignored 41 | void set_log_threshold(LOG_LEVEL level); 42 | 43 | } 44 | 45 | //useful macro 46 | #ifdef CY_ENABLE_LOG 47 | #define CY_LOG cyclone::disk_log 48 | #else 49 | #define CY_LOG (void) 50 | #endif 51 | -------------------------------------------------------------------------------- /source/cyCore/core/cyc_ring_buf.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | /// 9 | /// A byte-addressable ring buffer FIFO implementation. 10 | /// 11 | /// +-------------------+------------------+------------------+ 12 | /// | writable bytes | readable bytes | writable bytes | 13 | /// | | (CONTENT) | | 14 | /// +-------------------+------------------+------------------+ 15 | /// | | | | 16 | /// m_beginPoint <= m_readIndex <= m_writeIndex <= m_endIndex 17 | /// 18 | /// wrap 19 | /// +-------------------+------------------+------------------+ 20 | /// | readable bytes | writable bytes | readable bytes | 21 | /// | (CONTENT PART2) | | (CONTENT PART1) | 22 | /// +-------------------+------------------+------------------+ 23 | /// | | | | 24 | /// m_beginPoint <= m_writeIndex <= m_readIndex <= m_endIndex 25 | /// 26 | 27 | namespace cyclone 28 | { 29 | 30 | class RingBuf 31 | { 32 | public: 33 | enum { kDefaultCapacity = 1024 - 1 }; 34 | 35 | /// return the size of the internal buffer, in bytes. 36 | size_t size(void) const { 37 | return (m_write >= m_read) ? (m_write - m_read) : (m_end - m_read + m_write); 38 | } 39 | 40 | /// reset a ring buffer to its initial state (empty). 41 | void reset(void) { 42 | m_write = m_read = 0; 43 | } 44 | 45 | /// return the usable capacity of the ring buffer, in bytes. 46 | size_t capacity(void) const { 47 | return m_end-1; 48 | } 49 | 50 | //// return the number of free/available bytes in the ring buffer. 51 | size_t get_free_size(void) const { 52 | return (m_write >= m_read) ? (m_end - m_write + m_read - 1) : (m_read - m_write - 1); 53 | } 54 | 55 | //// return is empty 56 | bool empty(void) const { 57 | return (m_write == m_read); 58 | } 59 | 60 | /// return is full 61 | bool full(void) const { 62 | return get_free_size() == 0; 63 | } 64 | 65 | //// re-alloca memory, make sure capacity greater need_size 66 | void resize(size_t need_size); 67 | 68 | //// copy n bytes from a contiguous memory area into the ring buffer 69 | void memcpy_into(const void *src, size_t count); 70 | 71 | //// copy n bytes from the ring buffer into a contiguous memory area dst 72 | size_t memcpy_out(void *dst, size_t count); 73 | 74 | //// move data to another ring buf dst 75 | size_t moveto(RingBuf& dst, size_t count); 76 | 77 | //// search data(1 byte) and return the first position, return -1 means not find 78 | ssize_t search(size_t off, uint8_t data) const; 79 | 80 | //// copy n bytes from the ring buffer into a contiguous memory, but 81 | //// do not change current buf 82 | size_t peek(size_t off, void* dst, size_t count) const; 83 | 84 | //// just discard at least n bytes data, return size that abandon actually 85 | size_t discard(size_t count); 86 | 87 | //// call read on the socket descriptor(fd), using the ring buffer rb as the 88 | //// destination buffer for the read, and read as more data as impossible data. 89 | //// set extra_read to false if you don't want expand this ring buf 90 | ssize_t read_socket(socket_t fd, bool extra_read=true); 91 | 92 | //// call write on the socket descriptor(fd), using the ring buffer rb as the 93 | //// source buffer for writing, In Linux platform, it will only call writev 94 | //// once, and may return a short count. 95 | ssize_t write_socket(socket_t fd); 96 | 97 | //// calculate the checksum(adler32) of data from off to off+len 98 | //// if off greater than size() or off+count greater than size() 99 | //// or len equ 0 return initial adler value (1) 100 | uint32_t checksum(size_t off, size_t count) const; 101 | 102 | //// move all data to a flat memory block and return point 103 | uint8_t* normalize(void); 104 | 105 | public: 106 | RingBuf(size_t capacity = kDefaultCapacity); 107 | ~RingBuf(); 108 | 109 | private: 110 | uint8_t* m_buf; 111 | size_t m_end; 112 | size_t m_read; 113 | size_t m_write; 114 | }; 115 | 116 | } 117 | -------------------------------------------------------------------------------- /source/cyCore/core/cyc_socket_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | namespace socket_api 11 | { 12 | 13 | //// init global socket data( call WSAStartup at windows system) 14 | void global_init(void); 15 | 16 | /// Creates a blocking socket file descriptor, return INVALID_SOCKET if failed 17 | socket_t create_socket(bool udp=false); 18 | 19 | /// Close socket 20 | void close_socket(socket_t s); 21 | 22 | /// enable/disable socket non-block mode 23 | bool set_nonblock(socket_t s, bool enable); 24 | 25 | /// enable/disable socket close-on-exec mode 26 | bool set_close_onexec(socket_t s, bool enable); 27 | 28 | /// bind a local name to a socket 29 | bool bind(socket_t s, const struct sockaddr_in& addr); 30 | 31 | /// listen for connection 32 | bool listen(socket_t s); 33 | 34 | /// accept a connection on a socket, return INVALID_SOCKET if failed 35 | socket_t accept(socket_t s, struct sockaddr_in* addr); 36 | 37 | /// initiate a connection on a socket 38 | bool connect(socket_t s, const struct sockaddr_in& addr); 39 | 40 | /// write to socket file desc 41 | ssize_t write(socket_t s, const char* buf, size_t len); 42 | 43 | /// send data to socket file desc 44 | ssize_t sendto(socket_t s, const char* buf, size_t len, const struct sockaddr_in& peer_addr); 45 | 46 | /// read from a socket file desc 47 | ssize_t read(socket_t s, void *buf, size_t len); 48 | 49 | /// receive from socket 50 | ssize_t recvfrom(socket_t s, void* buf, size_t len, struct sockaddr_in& peer_addr); 51 | 52 | /// shutdown read and write part of a socket connection 53 | bool shutdown(socket_t s); 54 | 55 | /// convert ip address (like "123.123.123.123") to in_addr 56 | bool inet_pton(const char* ip, struct in_addr& a); 57 | 58 | /// convert in_addr to ip address 59 | bool inet_ntop(const struct in_addr& a, char *dst, socklen_t size); 60 | 61 | /// socket operation 62 | bool setsockopt(socket_t s, int level, int optname, const void *optval, size_t optlen); 63 | 64 | /// Enable/disable SO_REUSEADDR 65 | bool set_reuse_addr(socket_t s, bool on); 66 | 67 | /// Enable/disable SO_REUSEPORT 68 | bool set_reuse_port(socket_t s, bool on); 69 | 70 | /// Enable/disable SO_KEEPALIVE 71 | bool set_keep_alive(socket_t s, bool on); 72 | 73 | /// Enable/disable TCP_NODELAY 74 | bool set_nodelay(socket_t s, bool on); 75 | 76 | /// Set socket SO_LINGER 77 | bool set_linger(socket_t s, bool on, uint16_t linger_time); 78 | 79 | /// get socket error 80 | int get_socket_error(socket_t sockfd); 81 | 82 | /// get local name of socket 83 | bool getsockname(socket_t s, struct sockaddr_in& addr); 84 | 85 | /// get peer name of socket 86 | bool getpeername(socket_t s, struct sockaddr_in& addr); 87 | 88 | ///resolve hostname to IP address, not changing port or sin_family 89 | bool resolve_hostname(const char* hostname, struct sockaddr_in& addr); 90 | 91 | /// big-endian/little-endian convert 92 | uint16_t ntoh_16(uint16_t x); 93 | uint32_t ntoh_32(uint32_t x); 94 | 95 | //// get last socket error 96 | int get_lasterror(void); 97 | 98 | //// is lasterror WOULDBLOCK 99 | bool is_lasterror_WOULDBLOCK(void); 100 | 101 | //// tests if the remote address is connectable(timeout : max time for wait, milli second, -1 means block mode) 102 | bool check_connect(const struct sockaddr_in& addr, int timeout_ms); 103 | 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /source/cyCore/core/cyc_system_api.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #ifndef CY_SYS_WINDOWS 9 | #include 10 | #endif 11 | 12 | typedef void* thread_t; 13 | typedef std::thread::id thread_id_t; 14 | 15 | namespace cyclone 16 | { 17 | namespace sys_api 18 | { 19 | //---------------------- 20 | // process functions 21 | //---------------------- 22 | 23 | //// get current process id 24 | pid_t process_get_id(void); 25 | 26 | //// get current process name 27 | void process_get_module_name(char* module_name, size_t max_size); 28 | 29 | //---------------------- 30 | // thread functions 31 | //---------------------- 32 | 33 | //// thread entry function 34 | typedef std::function thread_function; 35 | 36 | //// get current thread id 37 | thread_id_t thread_get_current_id(void); 38 | 39 | //// get the system id of thread 40 | thread_id_t thread_get_id(thread_t t); 41 | 42 | //// create a new thread(use thread_join to release resources) 43 | thread_t thread_create(thread_function func, void* param, const char* name); 44 | 45 | //// create a new thread(all thread resources will be released automatic) 46 | void thread_create_detached(thread_function func, void* param, const char* name); 47 | 48 | //// sleep in current thread(milliseconds) 49 | void thread_sleep(int32_t msec); 50 | 51 | //// wait the thread to terminate 52 | bool thread_join(thread_t t, int32_t wait_time_ms = -1); 53 | 54 | //// get current thread name 55 | const char* thread_get_current_name(void); 56 | 57 | //// yield the processor 58 | void thread_yield(void); 59 | 60 | //---------------------- 61 | // mutex functions 62 | //---------------------- 63 | typedef void* mutex_t; 64 | 65 | /// create a mutex 66 | mutex_t mutex_create(void); 67 | 68 | /// destroy a mutex 69 | void mutex_destroy(mutex_t m); 70 | 71 | /// lock mutex(wait other owner unlock infinity time) 72 | void mutex_lock(mutex_t m); 73 | 74 | /// lock mutex, wait other owner unlock some time (milliseconds), return false mean timeout 75 | bool mutex_try_lock(mutex_t m, int32_t wait_time); 76 | 77 | /// unlock mutex 78 | void mutex_unlock(mutex_t m); 79 | 80 | /// auto lock 81 | struct auto_mutex 82 | { 83 | auto_mutex(mutex_t m) : _m(m) { mutex_lock(_m); } 84 | ~auto_mutex() { mutex_unlock(_m); } 85 | mutex_t _m; 86 | }; 87 | 88 | //---------------------- 89 | // signal/semaphore functions 90 | //---------------------- 91 | 92 | #ifdef CY_SYS_WINDOWS 93 | typedef HANDLE signal_t; 94 | #else 95 | typedef void* signal_t; 96 | #endif 97 | 98 | //// create a signal 99 | signal_t signal_create(void); 100 | 101 | //// destroy a signal 102 | void signal_destroy(signal_t s); 103 | 104 | //// wait a signal infinite 105 | void signal_wait(signal_t s); 106 | 107 | //// wait a signal in [t] millisecond(second*1000), return true immediately if the signal is lighted, if false if timeout or other error 108 | bool signal_timewait(signal_t s, int32_t ms); 109 | 110 | //// light the signal 111 | void signal_notify(signal_t s); 112 | 113 | //---------------------- 114 | // time functions 115 | //---------------------- 116 | 117 | //// get UTC time in microseconds(second*1000*1000) from Epoch(00:00:00 January 1, 1970) 118 | int64_t utc_time_now(void); 119 | 120 | /// get local time in format string(strftime) 121 | void local_time_now(char* time_dest, size_t max_size, const char* format); 122 | 123 | /// get high performance time, return microseconds(second*1000*1000) from the first call to this function 124 | int64_t performance_time_now(void); 125 | 126 | //---------------------- 127 | // utility functions 128 | //---------------------- 129 | int32_t get_cpu_counts(void); 130 | 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /source/cyCore/cy_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cswrap/Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Diagnostics; 7 | 8 | using cyclone; 9 | 10 | class Test 11 | { 12 | static void Main(string[] args) 13 | { 14 | byte[] key = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; 15 | Rijndael aes = new Rijndael(key); 16 | 17 | string plain_text = "And God called the light Day, and the darkness he called Night."; 18 | byte[] input = System.Text.Encoding.Default.GetBytes(plain_text); 19 | int length = input.Length; 20 | byte[] encrypted = { 21 | 0xe7, 0x05, 0x0e, 0xdf, 0x2e, 0x5d, 0x97, 0x62, 0x36, 0xe9, 0x17, 0xb1, 0xc1, 0x73, 0xde, 0xca, 22 | 0xa2, 0x4b, 0x50, 0x4c, 0x02, 0x49, 0xea, 0xbd, 0x26, 0x25, 0x76, 0x92, 0x7a, 0xcf, 0x68, 0xee, 23 | 0xa7, 0xa6, 0xc3, 0x75, 0xa7, 0x32, 0x13, 0x74, 0x31, 0x0f, 0xa9, 0xca, 0x0e, 0x5e, 0xab, 0x99, 24 | 0xc5, 0x31, 0xc0, 0xe4, 0x26, 0x9c, 0x26, 0x92, 0x1a, 0xf4, 0xd0, 0xd0, 0xef, 0xa8, 0x7b, 0x23 }; 25 | 26 | byte[] output = new byte[length]; 27 | byte[] output2 = new byte[length]; 28 | byte[] temp = new byte[Rijndael.BLOCK_SIZE]; 29 | 30 | aes.encrypt(input, length, ref output); 31 | Debug.Assert(output.SequenceEqual(encrypted)); 32 | 33 | aes.decrypt(output, length, ref output2); 34 | Debug.Assert(System.Text.Encoding.Default.GetString(output2).CompareTo(plain_text) == 0); 35 | 36 | Array.Clear(output, 0, length); 37 | Array.Clear(output2, 0, length); 38 | 39 | byte[] iv = (byte[])Rijndael.DefaultIV.Clone(); 40 | for (int i = 0; i < length; i += Rijndael.BLOCK_SIZE) 41 | { 42 | aes.encrypt(input.Skip(i).Take(Rijndael.BLOCK_SIZE).ToArray(), Rijndael.BLOCK_SIZE, ref temp, ref iv); 43 | Buffer.BlockCopy(temp, 0, output, i, Rijndael.BLOCK_SIZE); 44 | } 45 | Debug.Assert(output.SequenceEqual(encrypted)); 46 | 47 | Array.Copy(Rijndael.DefaultIV, iv, Rijndael.DefaultIV.Length); 48 | for (int i = 0; i < length; i += Rijndael.BLOCK_SIZE) 49 | { 50 | aes.decrypt(output.Skip(i).Take(Rijndael.BLOCK_SIZE).ToArray(), Rijndael.BLOCK_SIZE, ref temp, ref iv); 51 | Buffer.BlockCopy(temp, 0, output2, i, Rijndael.BLOCK_SIZE); 52 | } 53 | Debug.Assert(System.Text.Encoding.Default.GetString(output2).CompareTo(plain_text) == 0); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cyr_adler32.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include "cyr_adler32.h" 6 | 7 | /* adler32.c -- compute the Adler-32 checksum of a data stream 8 | * Copyright (C) 1995-2011 Mark Adler 9 | * For conditions of distribution and use, see copyright notice in zlib.h 10 | */ 11 | namespace cyclone 12 | { 13 | #define BASE 65521 /* largest prime smaller than 65536 */ 14 | #define NMAX 5552 15 | 16 | /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ 17 | 18 | #define DO1(buf,i) {adler += (uint32_t)(buf)[i]; sum2 += adler;} 19 | #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); 20 | #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); 21 | #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); 22 | #define DO16(buf) DO8(buf,0); DO8(buf,8); 23 | 24 | /* use NO_DIVIDE if your processor does not do division in hardware -- 25 | try it both ways to see which is faster */ 26 | #ifdef NO_DIVIDE 27 | /* note that this assumes BASE is 65521, where 65536 % 65521 == 15 28 | (thank you to John Reiser for pointing this out) */ 29 | # define CHOP(a) \ 30 | do { 31 | \ 32 | unsigned long tmp = a >> 16; \ 33 | a &= 0xffffUL; \ 34 | a += (tmp << 4) - tmp; \ 35 | } while (0) 36 | # define MOD28(a) \ 37 | do { 38 | \ 39 | CHOP(a); \ 40 | if (a >= BASE) a -= BASE; \ 41 | } while (0) 42 | # define MOD(a) \ 43 | do { 44 | \ 45 | CHOP(a); \ 46 | MOD28(a); \ 47 | } while (0) 48 | # define MOD63(a) \ 49 | do { /* this assumes a is not negative */ \ 50 | z_off64_t tmp = a >> 32; \ 51 | a &= 0xffffffffL; \ 52 | a += (tmp << 8) - (tmp << 5) + tmp; \ 53 | tmp = a >> 16; \ 54 | a &= 0xffffL; \ 55 | a += (tmp << 4) - tmp; \ 56 | tmp = a >> 16; \ 57 | a &= 0xffffL; \ 58 | a += (tmp << 4) - tmp; \ 59 | if (a >= BASE) a -= BASE; \ 60 | } while (0) 61 | #else 62 | # define MOD(a) a %= BASE 63 | # define MOD28(a) a %= BASE 64 | # define MOD63(a) a %= BASE 65 | #endif 66 | uint32_t adler32(uint32_t adler, const uint8_t* buf, size_t len) 67 | { 68 | uint32_t sum2; 69 | unsigned n; 70 | 71 | /* initial Adler-32 value */ 72 | if (buf == 0 || len == 0) 73 | return INITIAL_ADLER; 74 | 75 | /* split Adler-32 into component sums */ 76 | sum2 = (adler >> 16) & 0xffff; 77 | adler &= 0xffff; 78 | 79 | /* in case user likes doing a byte at a time, keep it fast */ 80 | if (len == 1) { 81 | adler += (uint32_t)buf[0]; 82 | if (adler >= BASE) 83 | adler -= BASE; 84 | sum2 += adler; 85 | if (sum2 >= BASE) 86 | sum2 -= BASE; 87 | return adler | (sum2 << 16); 88 | } 89 | 90 | /* in case short lengths are provided, keep it somewhat fast */ 91 | if (len < 16) { 92 | while (len--) { 93 | adler += (uint32_t)(*buf++); 94 | sum2 += adler; 95 | } 96 | if (adler >= BASE) 97 | adler -= BASE; 98 | MOD28(sum2); /* only added so many BASE's */ 99 | return adler | (sum2 << 16); 100 | } 101 | 102 | /* do length NMAX blocks -- requires just one modulo operation */ 103 | while (len >= NMAX) { 104 | len -= NMAX; 105 | n = NMAX / 16; /* NMAX is divisible by 16 */ 106 | do { 107 | DO16(buf); /* 16 sums unrolled */ 108 | buf += 16; 109 | } while (--n); 110 | MOD(adler); 111 | MOD(sum2); 112 | } 113 | 114 | /* do remaining bytes (less than NMAX, still just one modulo) */ 115 | if (len) { /* avoid modulos if none remaining */ 116 | while (len >= 16) { 117 | len -= 16; 118 | DO16(buf); 119 | buf += 16; 120 | } 121 | while (len--) { 122 | adler += (uint32_t)(*buf++); 123 | sum2 += adler; 124 | } 125 | MOD(adler); 126 | MOD(sum2); 127 | } 128 | 129 | /* return recombined sums */ 130 | return adler | (sum2 << 16); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cyr_adler32.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | 11 | //initial adler32 value 12 | #define INITIAL_ADLER (1u) 13 | 14 | /* 15 | * Update a running Adler-32 checksum with the bytes buf[0..len-1] and 16 | * return the updated checksum. If buf is NULL, this function returns the 17 | * required initial value for the checksum. 18 | * 19 | * An Adler-32 checksum is almost as reliable as a CRC32 but can be computed 20 | * much faster. 21 | * 22 | * Usage example: 23 | * 24 | * uint32_t adler = cyclone::INITIAL_ADLER; 25 | * 26 | * while (read_buffer(buffer, length) != EOF) { 27 | * adler = cyclone::adler32(adler, buffer, length); 28 | * } 29 | * 30 | */ 31 | uint32_t adler32(uint32_t adler, const uint8_t* buf, size_t len); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cyr_dhexchange.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include "cyr_dhexchange.h" 6 | 7 | namespace cyclone 8 | { 9 | 10 | /* 11 | copy from skynet @cloudwu 12 | https://github.com/cloudwu/skynet 13 | https://github.com/thejinchao/dhexchange 14 | */ 15 | 16 | /* P = 2^128-159 = 0xffffffffffffffffffffffffffffff61 (The biggest 64bit prime) */ 17 | static const dhkey_t P = { { 0xffffffffffffff61ULL, 0xffffffffffffffffULL } }; 18 | static const dhkey_t INVERT_P = { { 159, 0 } }; 19 | static const dhkey_t G = { { 5, 0 } }; 20 | 21 | /*--------------------------------------------------------------------------*/ 22 | static int INLINE 23 | _u128_is_zero(const dhkey_t& key) { 24 | return (key.dq.low == 0 && key.dq.high == 0); 25 | } 26 | 27 | /*--------------------------------------------------------------------------*/ 28 | static int INLINE 29 | _u128_is_odd(const dhkey_t& key) { 30 | return (key.dq.low & 1); 31 | } 32 | 33 | /*--------------------------------------------------------------------------*/ 34 | static void INLINE 35 | _u128_lshift(dhkey_t* key) { 36 | uint64_t t = (key->dq.low >> 63) & 1; 37 | key->dq.high = (key->dq.high << 1) | t; 38 | key->dq.low = key->dq.low << 1; 39 | } 40 | 41 | /*--------------------------------------------------------------------------*/ 42 | static void INLINE 43 | _u128_rshift(dhkey_t* key) { 44 | uint64_t t = (key->dq.high & 1) << 63; 45 | key->dq.high = key->dq.high >> 1; 46 | key->dq.low = (key->dq.low >> 1) | t; 47 | } 48 | 49 | /*--------------------------------------------------------------------------*/ 50 | static int INLINE 51 | _u128_compare(const dhkey_t& a, const dhkey_t& b) { 52 | if (a.dq.high > b.dq.high) return 1; 53 | else if (a.dq.high == b.dq.high) { 54 | if (a.dq.low > b.dq.low) return 1; 55 | else if (a.dq.low == b.dq.low) return 0; 56 | else return -1; 57 | } else 58 | return -1; 59 | } 60 | 61 | /*--------------------------------------------------------------------------*/ 62 | static void INLINE 63 | _u128_add(dhkey_t* r, const dhkey_t& a, const dhkey_t& b) { 64 | uint64_t overflow = 0; 65 | uint64_t low = a.dq.low + b.dq.low; 66 | if (low < a.dq.low || low < b.dq.low) { 67 | overflow = 1; 68 | } 69 | 70 | r->dq.low = low; 71 | r->dq.high = a.dq.high + b.dq.high + overflow; 72 | } 73 | 74 | /*--------------------------------------------------------------------------*/ 75 | static void INLINE 76 | _u128_add_i(dhkey_t* r, const dhkey_t& a, const uint64_t& b) { 77 | uint64_t overflow = 0; 78 | uint64_t low = a.dq.low + b; 79 | if (low < a.dq.low || low < b) { 80 | overflow = 1; 81 | } 82 | 83 | r->dq.low = low; 84 | r->dq.high = a.dq.high + overflow; 85 | } 86 | 87 | /*--------------------------------------------------------------------------*/ 88 | static void INLINE 89 | _u128_sub(dhkey_t* r, const dhkey_t& a, const dhkey_t& b) { 90 | dhkey_t invert_b; 91 | invert_b.dq.low = ~b.dq.low; 92 | invert_b.dq.high = ~b.dq.high; 93 | _u128_add_i(&invert_b, invert_b, 1); 94 | _u128_add(r, a, invert_b); 95 | } 96 | 97 | /*--------------------------------------------------------------------------*/ 98 | /* r = a*b mod P */ 99 | static void 100 | _mulmodp(dhkey_t* r, dhkey_t a, dhkey_t b) 101 | { 102 | dhkey_t t; 103 | dhkey_t double_a; 104 | dhkey_t P_a; 105 | 106 | r->dq.low = r->dq.high = 0; 107 | while (!_u128_is_zero(b)) { 108 | if (_u128_is_odd(b)) { 109 | _u128_sub(&t, P, a); 110 | 111 | if (_u128_compare(*r, t) >= 0) { 112 | _u128_sub(r, *r, t); 113 | } 114 | else { 115 | _u128_add(r, *r, a); 116 | } 117 | } 118 | double_a = a; 119 | _u128_lshift(&double_a); 120 | 121 | _u128_sub(&P_a, P, a); 122 | 123 | if (_u128_compare(a, P_a) >= 0) { 124 | _u128_add(&a, double_a, INVERT_P); 125 | } 126 | else { 127 | a = double_a; 128 | } 129 | _u128_rshift(&b); 130 | } 131 | } 132 | 133 | /*--------------------------------------------------------------------------*/ 134 | /* r = a^b mod P (reduce) */ 135 | static void 136 | _powmodp_r(dhkey_t* r, const dhkey_t& a, const dhkey_t& b) 137 | { 138 | dhkey_t t; 139 | dhkey_t half_b = b; 140 | 141 | if (b.dq.high == 0 && b.dq.low == 1) { 142 | *r = a; 143 | return; 144 | } 145 | 146 | _u128_rshift(&half_b); 147 | 148 | _powmodp_r(&t, a, half_b); 149 | _mulmodp(&t, t, t); 150 | 151 | if (_u128_is_odd(b)) { 152 | _mulmodp(&t, t, a); 153 | } 154 | *r = t; 155 | } 156 | 157 | /*--------------------------------------------------------------------------*/ 158 | /* r = a^b mod P */ 159 | static void 160 | _powmodp(dhkey_t* r, dhkey_t a, dhkey_t b) 161 | { 162 | if (_u128_compare(a, P)>0) 163 | _u128_sub(&a, a, P); 164 | 165 | _powmodp_r(r, a, b); 166 | } 167 | 168 | /*--------------------------------------------------------------------------*/ 169 | void DH_generate_key_pair(dhkey_t& public_key, dhkey_t& private_key) 170 | { 171 | /* generate random private key */ 172 | int i; 173 | for (i = 0; i < DH_KEY_LENGTH; i++) { 174 | private_key.bytes[i] = (uint8_t)(rand() & 0xFF); 175 | } 176 | 177 | /* pub_key = G^prv_key mod P*/ 178 | _powmodp(&public_key, G, private_key); 179 | } 180 | 181 | /*--------------------------------------------------------------------------*/ 182 | void 183 | DH_generate_key_secret(dhkey_t& secret_key, const dhkey_t& my_private, const dhkey_t& another_public) 184 | { 185 | /* secret_key = other_key^prv_key mod P*/ 186 | _powmodp(&secret_key, another_public, my_private); 187 | } 188 | 189 | } 190 | 191 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cyr_dhexchange.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | namespace cyclone 7 | { 8 | 9 | //Diffie-Hellman key exchange 10 | #define DH_KEY_LENGTH (16) 11 | 12 | typedef union _dhkey_t { 13 | struct _dq { 14 | uint64_t low; 15 | uint64_t high; 16 | } dq; 17 | uint8_t bytes[DH_KEY_LENGTH]; 18 | } dhkey_t; 19 | 20 | 21 | void DH_generate_key_pair(dhkey_t& public_key, dhkey_t& private_key); 22 | 23 | void DH_generate_key_secret(dhkey_t& secret_key, const dhkey_t& my_private, const dhkey_t& another_public); 24 | 25 | 26 | } 27 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cyr_rijndael.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | 11 | class Rijndael 12 | { 13 | public: 14 | enum { BLOCK_SIZE = 16 }; 15 | typedef uint8_t BLOCK[BLOCK_SIZE]; 16 | 17 | //Default Initial Vector 18 | static const BLOCK DefaultIV; 19 | 20 | //Construct, and expand a user-supplied key material into a session key. 21 | Rijndael(const BLOCK key); 22 | ~Rijndael(); 23 | 24 | public: 25 | //Encrypt memory, use CBC mode 26 | //@remark In CBC Mode a ciphertext block is obtained by first xoring the 27 | //plaintext block with the previous ciphertext block, and encrypting the resulting value. 28 | //@remark size should be > 0 and multiple of m_blockSize 29 | void encrypt(const uint8_t* input, uint8_t* output, size_t size, BLOCK iv=nullptr); 30 | 31 | //Decrypt memory, use CBC mode 32 | //@remark size should be > 0 and multiple of m_blockSize 33 | void decrypt(const uint8_t* input, uint8_t* output, size_t size, BLOCK iv = nullptr); 34 | 35 | private: 36 | //Convenience method to encrypt exactly one block of plaintext, assuming 37 | //Rijndael's default block size (128-bit). 38 | void _encryptBlock(const uint8_t* in, uint8_t* result); 39 | 40 | //Convenience method to decrypt exactly one block of plaintext, assuming 41 | //Rijndael's default block size (128-bit). 42 | void _decryptBlock(const uint8_t* in, uint8_t* result); 43 | 44 | //Auxiliary Function 45 | void _xor(uint8_t* buff, const uint8_t* chain); 46 | 47 | private: 48 | enum {ROUNDS=10, BC=4, KC=4}; 49 | //Encryption (m_Ke) round key 50 | uint32_t m_Ke[ROUNDS + 1][BC]; 51 | //Decryption (m_Kd) round key 52 | uint32_t m_Kd[ROUNDS + 1][BC]; 53 | }; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cyr_xorshift128.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include "cyr_xorshift128.h" 6 | 7 | namespace cyclone 8 | { 9 | 10 | //------------------------------------------------------------------------------------- 11 | void XorShift128::make(void) 12 | { 13 | for (int32_t i = 0; i < 8; i++) { 14 | uint64_t mask = ~(((uint64_t)0xFF) << (i * 8)); 15 | 16 | seed0 = (seed0 & mask) | ((uint64_t)((rand() & 0xFF)) << (i * 8)); 17 | seed1 = (seed1 & mask) | ((uint64_t)((rand() & 0xFF)) << (i * 8)); 18 | } 19 | } 20 | 21 | //------------------------------------------------------------------------------------- 22 | void xorshift128(uint8_t* buf, size_t byte_length, XorShift128& seed) 23 | { 24 | //xor per 8 bytes 25 | size_t i64_counts = (byte_length >> 3);// +((byte_length & 7) ? 1 : 0); 26 | size_t tail_bytes = byte_length & 7; 27 | 28 | uint64_t* input = (uint64_t*)buf; 29 | for (size_t i = 0; i < i64_counts; i++, input++) { 30 | (*input) ^= seed.next(); 31 | } 32 | 33 | if (tail_bytes == 0) return; 34 | uint64_t last_seed = seed.next(); 35 | 36 | for (size_t t = 0; t < tail_bytes; t++) { 37 | *(((uint8_t*)input) + t) ^= (uint8_t)((last_seed >> (t<<3)) & 0xFF); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /source/cyCrypt/crypt/cyr_xorshift128.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | namespace cyclone 7 | { 8 | 9 | struct XorShift128 { 10 | uint64_t seed0; 11 | uint64_t seed1; 12 | 13 | void make(void); 14 | 15 | uint64_t next(void) { 16 | uint64_t x = seed0; 17 | uint64_t y = seed1; 18 | 19 | seed0 = y; 20 | x ^= x << 23; // a 21 | x ^= x >> 17; // b 22 | x ^= y ^ (y >> 26); // c 23 | seed1 = x; 24 | return x + y; 25 | } 26 | }; 27 | 28 | void xorshift128(uint8_t* buf, size_t byte_length, XorShift128& seed); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /source/cyCrypt/cy_crypt.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /source/cyEvent/cy_event.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /source/cyEvent/event/cye_looper.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace cyclone 11 | { 12 | 13 | class Looper : noncopyable 14 | { 15 | public: 16 | typedef uint32_t event_id_t; 17 | typedef uint32_t event_t; 18 | static const event_id_t INVALID_EVENT_ID; 19 | 20 | //the value of event_t 21 | enum { 22 | kNone = 0, // 0000 23 | kRead = 1, // 0001 24 | kWrite = 2, // 0010 25 | }; 26 | 27 | typedef std::function event_callback; 28 | typedef std::function timer_callback; 29 | 30 | public: 31 | //---------------------- 32 | // event operation(NOT thread safe) 33 | //---------------------- 34 | 35 | //// registe event, return channel id 36 | event_id_t register_event(socket_t sockfd, 37 | event_t type, 38 | void* param, 39 | event_callback _on_read, 40 | event_callback _on_write); 41 | 42 | //// registe timer event 43 | event_id_t register_timer_event(uint32_t milliSeconds, 44 | void* param, 45 | timer_callback _on_timer); 46 | 47 | //// unregister event 48 | void delete_event(event_id_t id); 49 | 50 | //// main loop(reactor process) 51 | void loop(void); 52 | //// reactor step 53 | void step(void); 54 | //// push stop request 55 | void push_stop_request(void); 56 | //// is quit cmd active 57 | bool is_quit_pending(void) const { return m_quit_cmd.load() != 0; } 58 | 59 | //// update event 60 | void disable_read(event_id_t id); 61 | void enable_read(event_id_t id); 62 | bool is_read(event_id_t id) const; 63 | 64 | void disable_write(event_id_t id); 65 | void enable_write(event_id_t id); 66 | bool is_write(event_id_t id) const; 67 | 68 | void disable_all(event_id_t id); 69 | 70 | //---------------------- 71 | // utility functions(NOT thread safe) 72 | //---------------------- 73 | thread_id_t get_thread_id(void) const { return m_current_thread; } 74 | uint64_t get_loop_counts(void) const { return m_loop_counts; } 75 | 76 | protected: 77 | Looper(); 78 | virtual ~Looper(); 79 | 80 | public: 81 | static Looper* create_looper(void); 82 | static void destroy_looper(Looper*); 83 | 84 | //---------------------- 85 | // inner data 86 | //---------------------- 87 | protected: 88 | enum { DEFAULT_CHANNEL_BUF_COUNTS = 16 }; 89 | 90 | struct channel_s 91 | { 92 | event_id_t id; 93 | socket_t fd; 94 | event_t event; 95 | void *param; 96 | bool active; 97 | bool timer; 98 | 99 | event_callback on_read; 100 | event_callback on_write; 101 | 102 | event_id_t next; 103 | event_id_t prev; //only used in select looper 104 | }; 105 | typedef std::vector< channel_s > channel_buffer; 106 | typedef std::vector< event_id_t > channel_list; 107 | 108 | channel_buffer m_channelBuffer; //all event buf 109 | event_id_t m_free_head; //free list head in event buf 110 | int32_t m_active_channel_counts; 111 | uint64_t m_loop_counts; 112 | 113 | thread_id_t m_current_thread; 114 | 115 | sys_api::mutex_t m_lock; 116 | 117 | Pipe* m_inner_pipe; //pipe to push loop continue 118 | atomic_int32_t m_inner_pipe_touched; 119 | atomic_int32_t m_quit_cmd; 120 | 121 | /// Polls the I/O events. 122 | virtual void _poll( 123 | channel_list& readChannelList, 124 | channel_list& writeChannelList, 125 | bool block) = 0; 126 | /// Changes the interested I/O events. 127 | virtual void _update_channel_add_event(channel_s& channel, event_t type) = 0; 128 | virtual void _update_channel_remove_event(channel_s& channel, event_t type) = 0; 129 | 130 | /// for timer 131 | struct timer_s 132 | { 133 | timer_callback on_timer; 134 | void* param; 135 | uint32_t milli_seconds; 136 | 137 | #ifdef CY_SYS_WINDOWS 138 | Pipe pipe; 139 | HANDLE htimer; 140 | #else 141 | #endif 142 | }; 143 | 144 | static void _on_timer_event_callback(event_id_t id, socket_t fd, event_t event, void* param); 145 | 146 | #ifdef CY_SYS_WINDOWS 147 | static void __stdcall _on_windows_timer(PVOID param, BOOLEAN timer_or_wait_fired); 148 | #endif 149 | 150 | //inner pipe functions 151 | void _touch_inner_pipe(void); 152 | static void _on_inner_pipe_touched(event_id_t id, socket_t fd, event_t event, void* param); 153 | 154 | private: 155 | event_id_t _get_free_slot(void); 156 | }; 157 | 158 | } 159 | -------------------------------------------------------------------------------- /source/cyEvent/event/cye_packet.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | #include "cye_packet.h" 7 | 8 | namespace cyclone 9 | { 10 | 11 | //------------------------------------------------------------------------------------- 12 | void* Packet::operator new(size_t size) 13 | { 14 | void* p = CY_MALLOC(size); 15 | return p; 16 | } 17 | 18 | //------------------------------------------------------------------------------------- 19 | void Packet::operator delete(void* p) 20 | { 21 | CY_FREE(p); 22 | } 23 | 24 | //------------------------------------------------------------------------------------- 25 | Packet* Packet::alloc_packet(const Packet* other) 26 | { 27 | Packet* p = new Packet(); 28 | 29 | if (other && other->m_memory_size > 0) { 30 | p->_resize(other->m_head_size, other->get_packet_size()); 31 | memcpy(p->m_memory_buf, other->m_memory_buf, other->m_memory_size); 32 | } 33 | 34 | return p; 35 | } 36 | 37 | //------------------------------------------------------------------------------------- 38 | void Packet::free_packet(Packet* p) 39 | { 40 | delete p; 41 | } 42 | 43 | //------------------------------------------------------------------------------------- 44 | Packet::Packet() 45 | : m_head_size(0) 46 | , m_memory_buf(nullptr) 47 | , m_memory_size(0) 48 | , m_packet_size(0) 49 | , m_packet_id(0) 50 | , m_content(nullptr) 51 | { 52 | 53 | } 54 | 55 | //------------------------------------------------------------------------------------- 56 | Packet::~Packet() 57 | { 58 | clean(); 59 | } 60 | 61 | //------------------------------------------------------------------------------------- 62 | void Packet::clean(void) 63 | { 64 | m_head_size = 0; 65 | 66 | if (m_memory_buf && m_memory_buf != m_static_buf) 67 | { 68 | CY_FREE(m_memory_buf); 69 | } 70 | m_memory_buf = nullptr; 71 | m_memory_size = 0; 72 | m_packet_size = 0; 73 | m_packet_id = 0; 74 | m_content = nullptr; 75 | } 76 | 77 | //------------------------------------------------------------------------------------- 78 | uint16_t Packet::get_packet_size(void) const 79 | { 80 | return m_packet_size ? socket_api::ntoh_16(*m_packet_size) : (uint16_t)0; 81 | } 82 | 83 | //------------------------------------------------------------------------------------- 84 | uint16_t Packet::get_packet_id(void) const 85 | { 86 | return m_packet_id ? socket_api::ntoh_16(*m_packet_id) : (uint16_t)0; 87 | } 88 | 89 | //------------------------------------------------------------------------------------- 90 | void Packet::_resize(size_t head_size, size_t packet_size) 91 | { 92 | assert(head_size >= 2 * sizeof(uint16_t)); 93 | 94 | m_head_size = head_size; 95 | m_memory_size = head_size + packet_size; 96 | size_t need_memory_size = m_memory_size + MEMORY_SAFE_TAIL_SIZE; 97 | 98 | if (need_memory_size <= STATIC_MEMORY_LENGTH) 99 | m_memory_buf = m_static_buf; 100 | else 101 | m_memory_buf = (char*)CY_MALLOC(need_memory_size); 102 | memset(m_memory_buf, 0xCE, need_memory_size); //fill memory with 0xCE (CyclonE) 103 | 104 | m_packet_size = (uint16_t*)m_memory_buf; 105 | m_packet_id = (uint16_t*)(m_memory_buf+sizeof(uint16_t)); 106 | m_content = packet_size>0 ? (char*)(m_memory_buf + head_size) : nullptr; 107 | } 108 | 109 | //------------------------------------------------------------------------------------- 110 | void Packet::build_from_memory(size_t head_size, uint16_t packet_id, 111 | uint16_t packet_size_part1, const char* packet_content_part1, 112 | uint16_t packet_size_part2, const char* packet_content_part2) 113 | { 114 | clean(); 115 | 116 | int32_t packet_size = (int32_t)packet_size_part1 + (int32_t)packet_size_part2; 117 | 118 | //check size 119 | assert(packet_size <= std::numeric_limits::max()); 120 | if (packet_size > std::numeric_limits::max()) return; 121 | 122 | //prepare memory 123 | _resize(head_size, (size_t)packet_size); 124 | 125 | *m_packet_size = socket_api::ntoh_16((uint16_t)packet_size); 126 | *m_packet_id = socket_api::ntoh_16(packet_id); 127 | 128 | // no content? 129 | if (m_content==nullptr) return; 130 | 131 | if (packet_size_part1 > 0 && packet_content_part1) { 132 | memcpy(m_content, packet_content_part1, packet_size_part1); 133 | } 134 | 135 | if (packet_size_part2 > 0 && packet_content_part2) { 136 | memcpy(m_content+packet_size_part1, packet_content_part2, packet_size_part2); 137 | } 138 | } 139 | 140 | //------------------------------------------------------------------------------------- 141 | bool Packet::build_from_pipe(size_t head_size, Pipe& pipe) 142 | { 143 | clean(); 144 | 145 | //read size 146 | uint16_t packet_size; 147 | if (sizeof(packet_size) != pipe.read((char*)&packet_size, sizeof(packet_size))){ 148 | return false; 149 | } 150 | 151 | //prepare memory 152 | _resize(head_size, (size_t)socket_api::ntoh_16(packet_size)); 153 | *m_packet_size = packet_size; 154 | 155 | //read other 156 | size_t remain = head_size + get_packet_size() - sizeof(uint16_t); 157 | if ((ssize_t)remain != pipe.read((char*)m_packet_id, remain)){ 158 | clean(); 159 | return false; 160 | } 161 | 162 | return true; 163 | } 164 | 165 | //------------------------------------------------------------------------------------- 166 | bool Packet::build_from_ringbuf(size_t head_size, RingBuf& ring_buf) 167 | { 168 | clean(); 169 | 170 | size_t buf_len = ring_buf.size(); 171 | 172 | uint16_t packet_size; 173 | if (sizeof(packet_size) != ring_buf.peek(0, &packet_size, sizeof(packet_size))) return false; 174 | packet_size = socket_api::ntoh_16(packet_size); 175 | 176 | if (buf_len < head_size + (size_t)packet_size) return false; 177 | 178 | _resize(head_size, packet_size); 179 | 180 | return (m_memory_size==ring_buf.memcpy_out(m_memory_buf, m_memory_size)); 181 | } 182 | 183 | } 184 | 185 | -------------------------------------------------------------------------------- /source/cyEvent/event/cye_packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | /* 7 | Low High 8 | +-------------+------------+ <---+ get_memory_buf() 9 | / | PacketID | PacketSize | 10 | HeadSize +-------------+------------+ 11 | \ | (User Define Head) | 12 | +--------------------------+ <---+ get_packet_content() 13 | / | (Packet Content) | 14 | / | | 15 | PacketSize | | 16 | \ | | 17 | \ | | 18 | +--------------------------+ 19 | MEMORY_SAFE_TAIL_SIZE | Safe Tail | 20 | +--------------------------+ 21 | 22 | *MemorySize = HeadSize+PacketSize 23 | *PacketID and PacketSize is big endain 16bit ingeter 24 | 25 | */ 26 | namespace cyclone 27 | { 28 | 29 | class Packet : noncopyable 30 | { 31 | public: 32 | void clean(void); 33 | 34 | //build from memory 35 | // | packet_size | packet_id | user_define_head | packet_content1 | packet_content2 | 36 | // | uint16 | uint16 | ... | packet_size1 | packet_size2 | 37 | // | <--------------- head_size --------->|<-------------- packet_size -------------->| 38 | // 39 | void build_from_memory(size_t head_size, uint16_t packet_id, 40 | uint16_t packet_size_part1, const char* packet_content_part1, 41 | uint16_t packet_size_part2=0, const char* packet_content_part2=nullptr); 42 | 43 | //build from Pipe and RingBuf 44 | // | packet_size | packet_id | user_define_head | packet_content | 45 | // | uint16 | uint16 | ... | ... | 46 | // | <--------------- head_size --------->|<----- packet_size -->| 47 | // 48 | bool build_from_pipe(size_t head_size, Pipe& pipe); 49 | bool build_from_ringbuf(size_t head_size, RingBuf& ring_buf); 50 | 51 | public: 52 | char* get_memory_buf(void) { return m_memory_buf; } 53 | const char* get_memory_buf(void) const { return m_memory_buf; } 54 | size_t get_memory_size(void) const { return m_memory_size; } 55 | 56 | uint16_t get_packet_size(void) const; 57 | uint16_t get_packet_id(void) const; 58 | char* get_packet_content(void) { return m_content; } 59 | const char* get_packet_content(void) const { return m_content; } 60 | 61 | private: 62 | void _resize(size_t head_size, size_t packet_size); 63 | 64 | private: 65 | size_t m_head_size; 66 | 67 | enum { STATIC_MEMORY_LENGTH = 1024 }; 68 | enum { MEMORY_SAFE_TAIL_SIZE = 8 }; 69 | 70 | char m_static_buf[STATIC_MEMORY_LENGTH]; 71 | char* m_memory_buf; 72 | size_t m_memory_size; 73 | 74 | uint16_t *m_packet_size; 75 | uint16_t *m_packet_id; 76 | char *m_content; 77 | 78 | public: 79 | Packet(); 80 | ~Packet(); 81 | 82 | void* operator new(size_t); 83 | void operator delete(void*); 84 | 85 | static Packet* alloc_packet(const Packet* other = 0); 86 | static void free_packet(Packet*); 87 | }; 88 | 89 | } 90 | 91 | -------------------------------------------------------------------------------- /source/cyEvent/event/cye_pipe.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | #include "cye_pipe.h" 7 | 8 | #include 9 | 10 | namespace cyclone 11 | { 12 | 13 | //------------------------------------------------------------------------------------- 14 | bool Pipe::construct_socket_pipe(pipe_port_t handles[2]) 15 | { 16 | // 17 | //https://trac.transmissionbt.com/browser/trunk/libtransmission/trevent.c 18 | // 19 | handles[0] = handles[1] = INVALID_SOCKET; 20 | 21 | socket_t s = socket_api::create_socket(); 22 | if (s == INVALID_SOCKET) 23 | { 24 | return false; 25 | } 26 | 27 | struct sockaddr_in serv_addr; 28 | memset(&serv_addr, 0, sizeof(serv_addr)); 29 | serv_addr.sin_family = AF_INET; 30 | serv_addr.sin_port = htons(0); 31 | serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 32 | 33 | for(;;) { 34 | if (!socket_api::bind(s, serv_addr)) 35 | break; 36 | if (!socket_api::listen(s)) 37 | break; 38 | if (!socket_api::getsockname(s, serv_addr)) 39 | break; 40 | if ((handles[1] = socket_api::create_socket()) == INVALID_SOCKET) 41 | break; 42 | if (!socket_api::set_nonblock(handles[1], true)) 43 | break; 44 | if (!socket_api::set_close_onexec(handles[1], true)) 45 | break; 46 | if (!socket_api::connect(handles[1], serv_addr)) 47 | break; 48 | if ((handles[0] = socket_api::accept(s, 0)) == INVALID_SOCKET) 49 | break; 50 | if (!socket_api::set_nonblock(handles[0], true)) 51 | break; 52 | if (!socket_api::set_close_onexec(handles[0], true)) 53 | break; 54 | 55 | socket_api::close_socket(s); 56 | return true; 57 | } 58 | 59 | //error case 60 | if (handles[0] != INVALID_SOCKET) 61 | socket_api::close_socket(handles[0]); 62 | if (handles[1] != INVALID_SOCKET) 63 | socket_api::close_socket(handles[1]); 64 | socket_api::close_socket(s); 65 | handles[0] = handles[1] = INVALID_SOCKET; 66 | return false; 67 | } 68 | 69 | //------------------------------------------------------------------------------------- 70 | void Pipe::destroy_socket_pipe(pipe_port_t handles[2]) 71 | { 72 | if (handles[0] != INVALID_SOCKET) { 73 | socket_api::close_socket(handles[0]); 74 | handles[0] = INVALID_SOCKET; 75 | } 76 | 77 | if (handles[1] != INVALID_SOCKET) { 78 | socket_api::close_socket(handles[1]); 79 | handles[1] = INVALID_SOCKET; 80 | } 81 | } 82 | 83 | //------------------------------------------------------------------------------------- 84 | Pipe::Pipe() 85 | { 86 | #ifdef CY_SYS_WINDOWS 87 | if (!construct_socket_pipe(m_pipe_fd)) 88 | #else 89 | #ifdef CY_HAVE_PIPE2 90 | if(::pipe2(m_pipe_fd, O_NONBLOCK|O_CLOEXEC)<0) 91 | #else 92 | if(::pipe(m_pipe_fd)<0 || 93 | !socket_api::set_nonblock(m_pipe_fd[0], true) || !socket_api::set_close_onexec(m_pipe_fd[0], true) || 94 | !socket_api::set_nonblock(m_pipe_fd[1], true) || !socket_api::set_close_onexec(m_pipe_fd[1], true)) 95 | #endif 96 | #endif 97 | { 98 | CY_LOG(L_FATAL, "create pipe failed!"); 99 | } 100 | } 101 | 102 | //------------------------------------------------------------------------------------- 103 | Pipe::~Pipe() 104 | { 105 | #ifdef CY_SYS_WINDOWS 106 | destroy_socket_pipe(m_pipe_fd); 107 | #else 108 | ::close(m_pipe_fd[0]); 109 | ::close(m_pipe_fd[1]); 110 | #endif 111 | } 112 | 113 | //------------------------------------------------------------------------------------- 114 | ssize_t Pipe::write(const char* buf, size_t len) 115 | { 116 | return socket_api::write(m_pipe_fd[1], buf, len); 117 | } 118 | 119 | //------------------------------------------------------------------------------------- 120 | ssize_t Pipe::read(char* buf, size_t len) 121 | { 122 | return socket_api::read(m_pipe_fd[0], buf, len); 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /source/cyEvent/event/cye_pipe.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | 11 | typedef socket_t pipe_port_t; 12 | 13 | class Pipe : noncopyable 14 | { 15 | public: 16 | pipe_port_t get_read_port(void) { return m_pipe_fd[0]; } 17 | pipe_port_t get_write_port(void) { return m_pipe_fd[1]; } 18 | 19 | ssize_t write(const char* buf, size_t len); 20 | ssize_t read(char* buf, size_t len); 21 | 22 | static bool construct_socket_pipe(pipe_port_t handles[2]); 23 | static void destroy_socket_pipe(pipe_port_t handles[2]); 24 | 25 | private: 26 | pipe_port_t m_pipe_fd[2]; 27 | 28 | public: 29 | Pipe(); //build pipe 30 | ~Pipe(); 31 | }; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /source/cyEvent/event/cye_work_thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | 7 | #include "cye_work_thread.h" 8 | 9 | namespace cyclone 10 | { 11 | 12 | //------------------------------------------------------------------------------------- 13 | WorkThread::WorkThread() 14 | : m_thread(nullptr) 15 | , m_looper(nullptr) 16 | , m_is_queue_empty(true) 17 | , m_on_start(nullptr) 18 | , m_on_message(nullptr) 19 | { 20 | } 21 | 22 | //------------------------------------------------------------------------------------- 23 | WorkThread::~WorkThread() 24 | { 25 | //TODO: stop the thread 26 | } 27 | 28 | //------------------------------------------------------------------------------------- 29 | void WorkThread::start(const char* name) 30 | { 31 | assert(m_thread== nullptr); 32 | assert(name); 33 | 34 | work_thread_param param; 35 | param._this = this; 36 | param._ready = 0; 37 | 38 | //run the work thread 39 | m_name = name ? name : "worker"; 40 | m_thread = sys_api::thread_create( 41 | std::bind(&WorkThread::_work_thread, this, std::placeholders::_1), ¶m, m_name.c_str()); 42 | 43 | //wait work thread ready signal 44 | while (param._ready == 0) sys_api::thread_yield(); //BUSY LOOP! 45 | } 46 | 47 | //------------------------------------------------------------------------------------- 48 | void WorkThread::_work_thread(void* param) 49 | { 50 | work_thread_param* thread_param = (work_thread_param*)param; 51 | 52 | //create work event looper 53 | m_looper = Looper::create_looper(); 54 | 55 | //register pipe read event 56 | m_looper->register_event(m_pipe.get_read_port(), Looper::kRead, this, 57 | std::bind(&WorkThread::_on_message, this), 0); 58 | 59 | // set work thread ready signal 60 | thread_param->_ready = 1; 61 | thread_param = nullptr;//we don't use it again! 62 | 63 | //we start! 64 | if (m_on_start && !m_on_start()) { 65 | Looper::destroy_looper(m_looper); 66 | m_looper = nullptr; 67 | return; 68 | } 69 | 70 | //enter loop ... 71 | m_looper->loop(); 72 | 73 | //delete the looper 74 | Looper::destroy_looper(m_looper); 75 | m_looper = nullptr; 76 | } 77 | 78 | //------------------------------------------------------------------------------------- 79 | void WorkThread::_on_message(void) 80 | { 81 | assert(sys_api::thread_get_current_id() == m_looper->get_thread_id()); 82 | 83 | for (;;) { 84 | int8_t dummy; 85 | if (m_pipe.read((char*)&dummy, sizeof(dummy)) <= 0) break; 86 | 87 | for (;;) { 88 | Packet* packet = nullptr; 89 | if (!m_message_queue.pop(packet)) break; 90 | 91 | //call listener 92 | if (m_on_message) { 93 | m_on_message(packet); 94 | } 95 | 96 | Packet::free_packet(packet); 97 | } 98 | } 99 | 100 | //set empty flag 101 | m_is_queue_empty = true; 102 | } 103 | 104 | //------------------------------------------------------------------------------------- 105 | void WorkThread::send_message(uint16_t id, uint16_t size_part1, const char* msg_part1, uint16_t size_part2, const char* msg_part2) 106 | { 107 | Packet* packet = Packet::alloc_packet(); 108 | packet->build_from_memory(MESSAGE_HEAD_SIZE, id, size_part1, msg_part1, size_part2, msg_part2); 109 | 110 | m_message_queue.push(packet); 111 | 112 | _wakeup(); 113 | } 114 | 115 | //------------------------------------------------------------------------------------- 116 | void WorkThread::send_message(const Packet* message) 117 | { 118 | Packet* packet = Packet::alloc_packet(message); 119 | m_message_queue.push(packet); 120 | 121 | _wakeup(); 122 | } 123 | 124 | //------------------------------------------------------------------------------------- 125 | void WorkThread::send_message(const Packet** message, int32_t counts) 126 | { 127 | for (int32_t i = 0; i < counts; i++){ 128 | Packet* packet = Packet::alloc_packet(message[i]); 129 | m_message_queue.push(packet); 130 | } 131 | 132 | _wakeup(); 133 | } 134 | 135 | //------------------------------------------------------------------------------------- 136 | void WorkThread::join(void) 137 | { 138 | if (m_thread != nullptr) { 139 | sys_api::thread_join(m_thread); 140 | m_thread = nullptr; 141 | } 142 | } 143 | 144 | //------------------------------------------------------------------------------------- 145 | void WorkThread::_wakeup(void) 146 | { 147 | if (atomic_compare_exchange(m_is_queue_empty, true, false)) { 148 | int8_t dummy = 0; 149 | m_pipe.write((const char*)&dummy, sizeof(dummy)); 150 | } 151 | } 152 | 153 | } 154 | 155 | -------------------------------------------------------------------------------- /source/cyEvent/event/cye_work_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | namespace cyclone 7 | { 8 | //pre-define 9 | class Packet; 10 | 11 | class WorkThread : noncopyable 12 | { 13 | public: 14 | typedef std::function StartCallback; 15 | typedef std::function MessageCallback; 16 | 17 | public: 18 | enum { MESSAGE_HEAD_SIZE = 4 }; 19 | 20 | //// run thread 21 | void start(const char* name); 22 | 23 | //// is work thread running? 24 | bool is_running(void) const { return m_thread != nullptr; } 25 | 26 | //// set callback function 27 | void set_on_start(StartCallback func) { m_on_start = func; } 28 | void set_on_message(MessageCallback func) { m_on_message = func; } 29 | 30 | //// send message to this work thread (thread safe) 31 | void send_message(uint16_t id, uint16_t size_part1, const char* msg_part1, uint16_t size_part2 = 0, const char* msg_part2 = nullptr); 32 | void send_message(const Packet* message); 33 | void send_message(const Packet** message, int32_t counts); 34 | 35 | //// get work thread looper (thread safe) 36 | Looper* get_looper(void) const { return m_looper; } 37 | 38 | //// get work thread name (thread safe) 39 | const char* get_name(void) const { return m_name.c_str(); } 40 | 41 | //// join work thread(thread safe) 42 | void join(void); 43 | 44 | private: 45 | std::string m_name; 46 | thread_t m_thread; 47 | Looper* m_looper; 48 | atomic_bool_t m_is_queue_empty; 49 | Pipe m_pipe; 50 | 51 | typedef LockFreeQueue MessageQueue; 52 | MessageQueue m_message_queue; 53 | 54 | StartCallback m_on_start; 55 | MessageCallback m_on_message; 56 | 57 | private: 58 | /// work thread param 59 | struct work_thread_param 60 | { 61 | WorkThread* _this; 62 | atomic_int32_t _ready; 63 | }; 64 | /// work thread function 65 | void _work_thread(void* param); 66 | 67 | //// on work thread receive message 68 | void _on_message(void); 69 | 70 | /// wakeup the looper 71 | void _wakeup(void); 72 | public: 73 | WorkThread(); 74 | virtual ~WorkThread(); 75 | }; 76 | 77 | } 78 | -------------------------------------------------------------------------------- /source/cyEvent/event/internal/cye_create_looper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | 7 | #include "cye_looper_epoll.h" 8 | #include "cye_looper_select.h" 9 | #include "cye_looper_kqueue.h" 10 | 11 | namespace cyclone 12 | { 13 | 14 | //------------------------------------------------------------------------------------- 15 | Looper* Looper::create_looper(void) 16 | { 17 | #if (CY_POLL_TECH==CY_POLL_EPOLL) 18 | return new Looper_epoll(); 19 | #elif (CY_POLL_TECH == CY_POLL_KQUEUE) 20 | return new Looper_kqueue(); 21 | #else 22 | return new Looper_select(); 23 | #endif 24 | } 25 | 26 | //------------------------------------------------------------------------------------- 27 | void Looper::destroy_looper(Looper* looper) 28 | { 29 | delete looper; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /source/cyEvent/event/internal/cye_looper_epoll.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | #include "cye_looper_epoll.h" 7 | 8 | namespace cyclone 9 | { 10 | 11 | //------------------------------------------------------------------------------------- 12 | Looper_epoll::Looper_epoll() 13 | : Looper() 14 | , m_events(DEFAULT_CHANNEL_BUF_COUNTS) 15 | , m_eoll_fd(::epoll_create1(EPOLL_CLOEXEC)) 16 | { 17 | } 18 | 19 | //------------------------------------------------------------------------------------- 20 | Looper_epoll::~Looper_epoll() 21 | { 22 | ::close(m_eoll_fd); 23 | } 24 | 25 | //------------------------------------------------------------------------------------- 26 | void Looper_epoll::_poll( 27 | channel_list& readChannelList, 28 | channel_list& writeChannelList, 29 | bool block) 30 | { 31 | 32 | int num_events = 0; 33 | do { 34 | num_events = ::epoll_wait(m_eoll_fd, 35 | &*m_events.begin(), static_cast(m_events.size()), 36 | block ? -1 : 0); 37 | }while (num_events < 0 && socket_api::get_lasterror() == EINTR); //gdb may cause interrupted system call 38 | 39 | if (num_events < 0) 40 | { 41 | //error log something... 42 | CY_LOG(L_ERROR, "epoll_wait error, err=%d", socket_api::get_lasterror()); 43 | return; 44 | } 45 | else if (num_events == 0) 46 | { 47 | // 48 | return; 49 | } 50 | 51 | //fill active channels 52 | for (int i = 0; i < num_events; i++) 53 | { 54 | const epoll_event& event = m_events[(size_t)i]; 55 | uint32_t revents = event.events; 56 | channel_s* channel = &(m_channelBuffer[event.data.u32]); 57 | 58 | if (revents & (EPOLLERR | EPOLLHUP)) { 59 | //error fd, it's not necessary to log it 60 | //CY_LOG(L_ERROR, "got error event, err=0x%x", revents); 61 | } 62 | 63 | if ((revents & (EPOLLERR | EPOLLHUP)) 64 | && (revents & (EPOLLIN | EPOLLOUT)) == 0) 65 | { 66 | /* 67 | * if the error events were returned without EPOLLIN or EPOLLOUT, 68 | * then add these flags to handle the events at least in one 69 | * active handler @nginx 70 | */ 71 | revents |= EPOLLIN | EPOLLOUT; 72 | } 73 | 74 | if ((revents & EPOLLIN) && channel->active && channel->on_read != 0) 75 | { 76 | //read event 77 | readChannelList.push_back(channel->id); 78 | } 79 | 80 | if ((revents & EPOLLOUT) && channel->active && channel->on_write != 0) 81 | { 82 | //read event 83 | writeChannelList.push_back(channel->id); 84 | } 85 | } 86 | 87 | if ((size_t)num_events == m_events.size()) 88 | { 89 | m_events.resize(m_events.size() * 2); 90 | } 91 | } 92 | 93 | //------------------------------------------------------------------------------------- 94 | bool Looper_epoll::_set_event(channel_s& channel, int operation, uint32_t events) 95 | { 96 | //int operation = channel.active ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; 97 | 98 | struct epoll_event event; 99 | memset(&event, 0, sizeof(event)); 100 | if (operation != EPOLL_CTL_DEL) 101 | { 102 | event.events = events; 103 | event.data.u32 = channel.id; 104 | } 105 | 106 | if (::epoll_ctl(m_eoll_fd, operation, channel.fd, &event) < 0) { 107 | //log something... 108 | CY_LOG(L_ERROR, "epoll_ctl error, err=%d", socket_api::get_lasterror()); 109 | return false; 110 | } 111 | return true; 112 | } 113 | 114 | //------------------------------------------------------------------------------------- 115 | void Looper_epoll::_update_channel_add_event(channel_s& channel, event_t event) 116 | { 117 | if (channel.event == event || event == kNone) return; 118 | 119 | uint32_t event_to_set = 0; 120 | 121 | if (((event & kRead) || (channel.event & kRead)) && channel.on_read) 122 | event_to_set |= (EPOLLIN | EPOLLRDHUP); 123 | 124 | if (((event & kWrite) || (channel.event & kWrite)) && channel.on_write) 125 | event_to_set |= EPOLLOUT; 126 | 127 | int operation = channel.active ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; 128 | 129 | if (_set_event(channel, operation, event_to_set)) 130 | { 131 | if(!channel.active) m_active_channel_counts++; 132 | 133 | channel.event |= event; 134 | channel.active = true; 135 | } 136 | 137 | _touch_inner_pipe(); 138 | } 139 | 140 | //------------------------------------------------------------------------------------- 141 | void Looper_epoll::_update_channel_remove_event(channel_s& channel, event_t event) 142 | { 143 | if ((channel.event & event) == kNone || !channel.active) return; 144 | uint32_t event_to_set = 0; 145 | 146 | if ((channel.event & kRead) && !(event & kRead) && channel.on_read) 147 | event_to_set |= (EPOLLIN | EPOLLRDHUP); 148 | 149 | if ((channel.event & kWrite) && !(event & kWrite) && channel.on_write) 150 | event_to_set |= EPOLLOUT; 151 | 152 | if (event_to_set!=0) 153 | { 154 | if(_set_event(channel, EPOLL_CTL_MOD, event_to_set)) 155 | { 156 | channel.event &= ~event; 157 | } 158 | } 159 | else 160 | { 161 | if (_set_event(channel, EPOLL_CTL_DEL, 0)) 162 | { 163 | if(channel.active) m_active_channel_counts--; 164 | 165 | channel.event = kNone; 166 | channel.active = false; 167 | } 168 | } 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /source/cyEvent/event/internal/cye_looper_epoll.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #ifdef CY_HAVE_EPOLL 10 | #include 11 | 12 | namespace cyclone 13 | { 14 | 15 | class Looper_epoll : public Looper 16 | { 17 | public: 18 | /// Polls the I/O events. 19 | virtual void _poll( 20 | channel_list& readChannelList, 21 | channel_list& writeChannelList, 22 | bool block); 23 | /// Changes the interested I/O events. 24 | virtual void _update_channel_add_event(channel_s& channel, event_t event); 25 | virtual void _update_channel_remove_event(channel_s& channel, event_t event); 26 | 27 | private: 28 | typedef std::vector event_vector; 29 | 30 | event_vector m_events; 31 | int m_eoll_fd; 32 | 33 | private: 34 | bool _set_event(channel_s& channel, int operation, uint32_t events); 35 | 36 | public: 37 | Looper_epoll(); 38 | virtual ~Looper_epoll(); 39 | }; 40 | 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /source/cyEvent/event/internal/cye_looper_kqueue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | #include "cye_looper_kqueue.h" 7 | 8 | namespace cyclone 9 | { 10 | 11 | //------------------------------------------------------------------------------------- 12 | Looper_kqueue::Looper_kqueue() 13 | : Looper() 14 | , m_kqueue(::kqueue()) 15 | { 16 | m_change_evlist.resize(DEFAULT_MAX_CHANGE_COUNTS); 17 | m_trigger_evlist.resize(DEFAULT_MAX_TRIGGER_COUNTS); 18 | m_current_index = 0; 19 | } 20 | 21 | //------------------------------------------------------------------------------------- 22 | Looper_kqueue::~Looper_kqueue() 23 | { 24 | ::close(m_kqueue); 25 | m_change_evlist.clear(); 26 | m_trigger_evlist.clear(); 27 | m_current_index = 0; 28 | } 29 | 30 | //------------------------------------------------------------------------------------- 31 | void Looper_kqueue::_poll( 32 | channel_list& readChannelList, 33 | channel_list& writeChannelList, 34 | bool block) 35 | { 36 | int n = (int) m_current_index; 37 | m_current_index = 0; 38 | 39 | struct timespec ts={0,0}; 40 | 41 | int event_counts = ::kevent(m_kqueue, &(m_change_evlist[0]), n, 42 | &(m_trigger_evlist[0]), (int)m_trigger_evlist.size(), block ? nullptr : &ts); 43 | 44 | int err_info = event_counts==-1 ? errno : 0; 45 | 46 | if(err_info!=0) { 47 | CY_LOG(L_ERROR, "kevent() error, no=%d", err_info); 48 | return; 49 | } 50 | 51 | if(event_counts==0) { 52 | if(block) { 53 | CY_LOG(L_ERROR, "kevent() returned no events without timeout"); 54 | } 55 | return; 56 | } 57 | 58 | for (size_t i = 0; i < (size_t)event_counts; i++) { 59 | 60 | const struct kevent& ev = m_trigger_evlist[i]; 61 | 62 | if (ev.flags & EV_ERROR) { 63 | CY_LOG(L_ERROR, "kevent() error on %d filter:%d flags:%04Xd", 64 | (int)ev.ident, ev.filter, ev.flags); 65 | continue; 66 | } 67 | channel_s* channel = &(m_channelBuffer[(uint32_t)(uintptr_t)ev.udata]); 68 | 69 | if ((ev.filter==EVFILT_READ||ev.filter==EVFILT_TIMER) && channel->active && channel->on_read != 0) 70 | { 71 | //read event 72 | readChannelList.push_back(channel->id); 73 | } 74 | 75 | if ((ev.filter ==EVFILT_WRITE) && channel->active && channel->on_write != 0) 76 | { 77 | //write event 78 | writeChannelList.push_back(channel->id); 79 | } 80 | } 81 | } 82 | 83 | //------------------------------------------------------------------------------------- 84 | bool Looper_kqueue::_add_changes(channel_s& channel, int16_t filter, uint16_t flags) 85 | { 86 | if(m_current_index>=m_change_evlist.size()) { 87 | //error log something... 88 | CY_LOG(L_WARN, "kqueue change list is filled up"); 89 | 90 | struct timespec ts={0,0}; 91 | 92 | //upload current changes to kernel 93 | if (::kevent(m_kqueue, &(m_change_evlist[0]), (int)m_current_index, nullptr, 0, &ts)== -1) 94 | { 95 | CY_LOG(L_ERROR, "kevent() failed"); 96 | return false; 97 | } 98 | 99 | m_current_index = 0; 100 | } 101 | 102 | struct kevent* kev = &m_change_evlist[m_current_index]; 103 | 104 | kev->ident = (uintptr_t)channel.fd; 105 | kev->filter = (short) filter; 106 | kev->flags = (u_short) flags; 107 | kev->fflags = 0; 108 | kev->data = channel.timer ? ((timer_s*)(channel.param))->milli_seconds : 0; 109 | kev->udata = (void*)(uintptr_t)channel.id; 110 | 111 | m_current_index++; 112 | return true; 113 | } 114 | 115 | //------------------------------------------------------------------------------------- 116 | void Looper_kqueue::_update_channel_add_event(channel_s& channel, event_t event) 117 | { 118 | assert(event==kRead || event==kWrite); 119 | int16_t filter = 0; 120 | 121 | if(channel.timer) { 122 | //timer event 123 | filter = EVFILT_TIMER; 124 | } 125 | else if ((event == kRead) && !(channel.event & kRead) && channel.on_read) 126 | filter = EVFILT_READ; 127 | 128 | else if ((event == kWrite) && !(channel.event & kWrite) && channel.on_write) 129 | filter = EVFILT_WRITE; 130 | 131 | if (_add_changes(channel, filter, EV_ADD|EV_ENABLE)) 132 | { 133 | if(!channel.active) m_active_channel_counts++; 134 | 135 | channel.event |= event; 136 | channel.active = true; 137 | } 138 | 139 | _touch_inner_pipe(); 140 | } 141 | 142 | //------------------------------------------------------------------------------------- 143 | void Looper_kqueue::_update_channel_remove_event(channel_s& channel, event_t event) 144 | { 145 | assert(event==kRead || event==kWrite); 146 | 147 | if ((channel.event & event) == kNone || !channel.active) return; 148 | 149 | int16_t filter = 0; 150 | 151 | if(channel.timer) { 152 | filter = EVFILT_TIMER; 153 | } 154 | else if ((event == kRead) && (channel.event & kRead) && channel.on_read) 155 | filter = EVFILT_READ; 156 | 157 | else if ((event == kWrite) && (channel.event & kWrite) && channel.on_write) 158 | filter = EVFILT_WRITE; 159 | 160 | //TODO: BAD! 161 | // if the event is still not passed to a kernel we will not pass it 162 | for(size_t i=0; i 7 | #include 8 | 9 | #ifdef CY_HAVE_KQUEUE 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace cyclone 16 | { 17 | 18 | class Looper_kqueue : public Looper 19 | { 20 | public: 21 | /// Polls the I/O events. 22 | virtual void _poll( 23 | channel_list& readChannelList, 24 | channel_list& writeChannelList, 25 | bool block); 26 | /// Changes the interested I/O events. 27 | virtual void _update_channel_add_event(channel_s& channel, event_t event); 28 | virtual void _update_channel_remove_event(channel_s& channel, event_t event); 29 | 30 | private: 31 | typedef std::vector kevent_list; 32 | enum {DEFAULT_MAX_CHANGE_COUNTS=512}; 33 | enum {DEFAULT_MAX_TRIGGER_COUNTS=512}; 34 | 35 | 36 | int m_kqueue; 37 | kevent_list m_change_evlist; 38 | kevent_list m_trigger_evlist; 39 | size_t m_current_index; 40 | 41 | private: 42 | bool _add_changes(channel_s& channel, int16_t events, uint16_t flags); 43 | 44 | public: 45 | Looper_kqueue(); 46 | virtual ~Looper_kqueue(); 47 | }; 48 | 49 | } 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /source/cyEvent/event/internal/cye_looper_select.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | #include "cye_looper_select.h" 7 | 8 | namespace cyclone 9 | { 10 | 11 | //------------------------------------------------------------------------------------- 12 | Looper_select::Looper_select() 13 | : Looper() 14 | , m_max_read_counts(0) 15 | , m_max_write_counts(0) 16 | #ifndef CY_SYS_WINDOWS 17 | , m_max_fd(INVALID_SOCKET) 18 | #endif 19 | , m_active_head(INVALID_EVENT_ID) 20 | { 21 | FD_ZERO(&m_master_read_fd_set); 22 | FD_ZERO(&m_master_write_fd_set); 23 | } 24 | 25 | //------------------------------------------------------------------------------------- 26 | Looper_select::~Looper_select() 27 | { 28 | 29 | } 30 | 31 | //------------------------------------------------------------------------------------- 32 | void Looper_select::_poll( 33 | channel_list& readChannelList, 34 | channel_list& writeChannelList, 35 | bool block) 36 | { 37 | #ifndef CY_SYS_WINDOWS 38 | if (m_max_fd == INVALID_SOCKET) 39 | { 40 | for (event_id_t i = m_active_head; i != INVALID_EVENT_ID;) 41 | { 42 | channel_s& channel = m_channelBuffer[i]; 43 | if ((m_max_fd == INVALID_SOCKET) || 44 | ((channel.fd > m_max_fd) && (channel.event != kNone))) 45 | { 46 | m_max_fd = channel.fd; 47 | } 48 | 49 | i = channel.next; 50 | } 51 | } 52 | #endif 53 | 54 | m_work_read_fd_set = m_master_read_fd_set; 55 | m_work_write_fd_set = m_master_write_fd_set; 56 | #ifdef CY_SYS_WINDOWS 57 | m_work_expt_fd_set = m_master_read_fd_set; 58 | #endif 59 | 60 | int ready = 0; 61 | if (m_max_read_counts > 0 || m_max_write_counts>0) 62 | { 63 | timeval time_out = { 0, 0 }; 64 | ready = ::select( 65 | #ifdef CY_SYS_WINDOWS 66 | 0, 67 | &m_work_read_fd_set, &m_work_write_fd_set, &m_work_expt_fd_set, 68 | block ? 0 : &time_out); 69 | #else 70 | (int)(m_max_fd + 1), 71 | &m_work_read_fd_set, &m_work_write_fd_set, 0, 72 | block ? 0 : &time_out); 73 | #endif 74 | } 75 | else 76 | { 77 | //empty set, cause busy-loop, it should never happen! 78 | sys_api::thread_sleep(1); 79 | } 80 | 81 | int err = (ready < -1) ? socket_api::get_lasterror() : 0; 82 | 83 | if (err < 0) 84 | { 85 | if(err== 86 | #ifdef CY_SYS_WINDOWS 87 | WSAENOTSOCK) 88 | #else 89 | EBADF) 90 | #endif 91 | { 92 | //TODO: invalid socket fd, need recheck channel buffer and kickoff some connection 93 | } 94 | return; 95 | } 96 | 97 | if (ready == 0) 98 | { 99 | //time out 100 | return; 101 | } 102 | else 103 | { 104 | for (event_id_t i = m_active_head; i != INVALID_EVENT_ID;) 105 | { 106 | channel_s& channel = m_channelBuffer[i]; 107 | 108 | if ( FD_ISSET(channel.fd, &m_work_read_fd_set) 109 | #ifdef CY_SYS_WINDOWS 110 | || FD_ISSET(channel.fd, &m_work_expt_fd_set) 111 | #endif 112 | ) 113 | { 114 | assert(channel.event & kRead); 115 | assert(channel.on_read); 116 | 117 | readChannelList.push_back(i); 118 | } 119 | 120 | if (FD_ISSET(channel.fd, &m_work_write_fd_set)) 121 | { 122 | assert(channel.event & kWrite); 123 | assert(channel.on_write); 124 | 125 | writeChannelList.push_back(i); 126 | } 127 | 128 | i = channel.next; 129 | } 130 | } 131 | } 132 | 133 | //------------------------------------------------------------------------------------- 134 | void Looper_select::_insert_to_active_list(channel_s& channel) 135 | { 136 | if (channel.active) return; 137 | 138 | if (m_active_head != INVALID_EVENT_ID) { 139 | channel_s& head = m_channelBuffer[m_active_head]; 140 | head.prev = channel.id; 141 | } 142 | 143 | channel.next = m_active_head; 144 | channel.prev = INVALID_EVENT_ID; 145 | 146 | m_active_head = channel.id; 147 | channel.active = true; 148 | 149 | m_active_channel_counts++; 150 | } 151 | 152 | //------------------------------------------------------------------------------------- 153 | void Looper_select::_remove_from_active_list(channel_s& channel) 154 | { 155 | if (!channel.active) return; 156 | 157 | if (channel.next != INVALID_EVENT_ID) { 158 | channel_s& next = m_channelBuffer[channel.next]; 159 | next.prev = channel.prev; 160 | } 161 | 162 | if (channel.prev != INVALID_EVENT_ID) { 163 | channel_s& prev = m_channelBuffer[channel.prev]; 164 | prev.next = channel.next; 165 | } 166 | if (channel.id == m_active_head) 167 | m_active_head = channel.next; 168 | 169 | channel.active = false; 170 | 171 | m_active_channel_counts--; 172 | } 173 | 174 | //------------------------------------------------------------------------------------- 175 | void Looper_select::_update_channel_add_event(channel_s& channel, event_t event) 176 | { 177 | if (channel.event == event || event == kNone) return; 178 | socket_t fd = channel.fd; 179 | 180 | if (((event & kRead) && m_max_read_counts >= FD_SETSIZE) 181 | || ((event & kWrite) && m_max_write_counts >= FD_SETSIZE)) 182 | { 183 | //log error ,maximum number of descriptors supported by select() is FD_SETSIZE 184 | CY_LOG(L_ERROR, "maximum number of descriptors supported by select() is FD_SETSIZE(%d)", FD_SETSIZE); 185 | return; 186 | } 187 | 188 | if ((event & kRead) && !(channel.event & kRead) && channel.on_read) 189 | { 190 | FD_SET(fd, &m_master_read_fd_set); 191 | m_max_read_counts++; 192 | channel.event |= kRead; 193 | _insert_to_active_list(channel); 194 | #ifndef CY_SYS_WINDOWS 195 | if (m_max_fd == INVALID_SOCKET || m_max_fd < fd) m_max_fd = fd; 196 | #endif 197 | } 198 | 199 | if ((event & kWrite) && !(channel.event & kWrite) && channel.on_write) 200 | { 201 | FD_SET(fd, &m_master_write_fd_set); 202 | m_max_write_counts++; 203 | channel.event |= kWrite; 204 | _insert_to_active_list(channel); 205 | #ifndef CY_SYS_WINDOWS 206 | if (m_max_fd == INVALID_SOCKET || m_max_fd < fd) m_max_fd = fd; 207 | #endif 208 | } 209 | 210 | _touch_inner_pipe(); 211 | } 212 | 213 | //------------------------------------------------------------------------------------- 214 | void Looper_select::_update_channel_remove_event(channel_s& channel, event_t event) 215 | { 216 | if ((channel.event & event) == kNone || !channel.active) return; 217 | socket_t fd = channel.fd; 218 | 219 | if ((event & kRead) && (channel.event & kRead)) 220 | { 221 | FD_CLR(fd, &m_master_read_fd_set); 222 | m_max_read_counts--; 223 | channel.event &= ~((event_t)kRead); 224 | #ifndef CY_SYS_WINDOWS 225 | if (m_max_fd == fd) { m_max_fd = INVALID_SOCKET; } 226 | #endif 227 | } 228 | 229 | if ((event & kWrite) && (channel.event & kWrite)) 230 | { 231 | FD_CLR(fd, &m_master_write_fd_set); 232 | m_max_write_counts--; 233 | channel.event &= ~((event_t)kWrite); 234 | #ifndef CY_SYS_WINDOWS 235 | if (m_max_fd == fd) { m_max_fd = INVALID_SOCKET; } 236 | #endif 237 | } 238 | 239 | if (channel.event == kNone) 240 | { 241 | _remove_from_active_list(channel); 242 | } 243 | } 244 | 245 | } 246 | 247 | -------------------------------------------------------------------------------- /source/cyEvent/event/internal/cye_looper_select.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace cyclone 10 | { 11 | 12 | class Looper_select : public Looper 13 | { 14 | protected: 15 | virtual void _poll( 16 | channel_list& readChannelList, 17 | channel_list& writeChannelList, 18 | bool block); 19 | /// Changes the interested I/O events. 20 | virtual void _update_channel_add_event(channel_s& channel, event_t event); 21 | virtual void _update_channel_remove_event(channel_s& channel, event_t event); 22 | 23 | private: 24 | fd_set m_master_read_fd_set; 25 | fd_set m_master_write_fd_set; 26 | 27 | fd_set m_work_read_fd_set; 28 | fd_set m_work_write_fd_set; 29 | 30 | #ifdef CY_SYS_WINDOWS 31 | fd_set m_work_expt_fd_set; 32 | #endif 33 | 34 | int32_t m_max_read_counts; 35 | int32_t m_max_write_counts; 36 | 37 | #ifndef CY_SYS_WINDOWS //disable max_fd at windows platform 38 | socket_t m_max_fd; 39 | #endif 40 | protected: 41 | event_id_t m_active_head; //active list in fd_set; 42 | 43 | private: 44 | void _insert_to_active_list(channel_s& channel); 45 | void _remove_from_active_list(channel_s& channel); 46 | 47 | public: 48 | Looper_select(); 49 | virtual ~Looper_select(); 50 | }; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /source/cyNetwork/cy_network.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /source/cyNetwork/network/cyn_address.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | #include "cyn_address.h" 7 | 8 | namespace cyclone 9 | { 10 | 11 | //------------------------------------------------------------------------------------- 12 | Address::Address(uint16_t port, bool loopbackOnly) 13 | { 14 | memset(&m_address, 0, sizeof m_address); 15 | m_address.sin_family = AF_INET; 16 | m_address.sin_addr.s_addr = loopbackOnly ? htonl(INADDR_LOOPBACK) : INADDR_ANY; 17 | m_address.sin_port = htons(port); 18 | 19 | socket_api::inet_ntop(m_address.sin_addr, m_ip_string, IP_ADDRESS_LEN); 20 | } 21 | 22 | //------------------------------------------------------------------------------------- 23 | Address::Address(const char* ip, uint16_t port) 24 | { 25 | memset(&m_address, 0, sizeof m_address); 26 | m_address.sin_family = AF_INET; 27 | m_address.sin_port = htons(port); 28 | socket_api::inet_pton(ip, m_address.sin_addr); 29 | 30 | socket_api::inet_ntop(m_address.sin_addr, m_ip_string, IP_ADDRESS_LEN); 31 | } 32 | 33 | //------------------------------------------------------------------------------------- 34 | Address::Address(const struct sockaddr_in& addr) 35 | : m_address(addr) 36 | { 37 | socket_api::inet_ntop(m_address.sin_addr, m_ip_string, IP_ADDRESS_LEN); 38 | } 39 | 40 | //------------------------------------------------------------------------------------- 41 | Address::Address(const Address& other) 42 | { 43 | memcpy(&m_address, &(other.m_address), sizeof(m_address)); 44 | memcpy(&m_ip_string, other.m_ip_string, IP_ADDRESS_LEN); 45 | } 46 | 47 | //------------------------------------------------------------------------------------- 48 | Address& Address::operator=(const Address& other) 49 | { 50 | memcpy(&m_address, &(other.m_address), sizeof(m_address)); 51 | memcpy(&m_ip_string, other.m_ip_string, IP_ADDRESS_LEN); 52 | return *this; 53 | } 54 | 55 | //------------------------------------------------------------------------------------- 56 | Address::Address(bool peer, socket_t sfd) 57 | { 58 | if (peer) 59 | socket_api::getpeername(sfd, m_address); 60 | else 61 | socket_api::getsockname(sfd, m_address); 62 | socket_api::inet_ntop(m_address.sin_addr, m_ip_string, IP_ADDRESS_LEN); 63 | } 64 | 65 | //------------------------------------------------------------------------------------- 66 | Address::Address() 67 | { 68 | memset(&m_address, 0, sizeof m_address); 69 | memset(m_ip_string, 0, IP_ADDRESS_LEN); 70 | } 71 | 72 | //------------------------------------------------------------------------------------- 73 | const char* Address::get_ip(void) const 74 | { 75 | return m_ip_string; 76 | } 77 | 78 | //------------------------------------------------------------------------------------- 79 | uint16_t Address::get_port(void) const 80 | { 81 | return socket_api::ntoh_16(m_address.sin_port); 82 | } 83 | 84 | //------------------------------------------------------------------------------------- 85 | uint32_t Address::hash_value(const sockaddr_in& addr) 86 | { 87 | const uint32_t FNV_offset_basis = 2166136261; 88 | const uint32_t FNV_prime = 16777619; 89 | 90 | uint32_t hash = FNV_offset_basis; 91 | 92 | //hash address 93 | const uint8_t* v = (const uint8_t*)&(addr.sin_addr); 94 | for (size_t i = 0; i < 4; i++) { 95 | hash = hash ^ v[0]; 96 | hash = hash * FNV_prime; 97 | } 98 | //hash port 99 | v = (const uint8_t*)&(addr.sin_port); 100 | for (size_t i = 0; i < 2; i++) { 101 | hash = hash ^ v[0]; 102 | hash = hash * FNV_prime; 103 | } 104 | return hash; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /source/cyNetwork/network/cyn_address.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | 11 | class Address 12 | { 13 | public: 14 | //// get native address 15 | const struct sockaddr_in& get_sockaddr_in() const { return m_address; } 16 | 17 | //// get ip address like "123.123.123.123" 18 | const char* get_ip(void) const; 19 | uint16_t get_port(void) const; 20 | 21 | public: 22 | /// Constructs an endpoint with given port number. 23 | /// Mostly used in TcpServer listening. 24 | /// loopbackOnly bind to "127.0.0.1" as local port or "0.0.0.0" as internet port 25 | explicit Address(uint16_t port, bool loopbackOnly); 26 | 27 | /// Constructs an endpoint with given ip and port. 28 | /// @c ip should be "123.123.123.123" 29 | Address(const char* ip, uint16_t port); 30 | 31 | /// Constructs an endpoint with given struct @c sockaddr_in 32 | /// Mostly used when accepting new connections 33 | Address(const struct sockaddr_in& addr); 34 | 35 | /// Constructs current address to which the socket sfd is bound 36 | /// @param peer constructs the peer address of active socket 37 | Address(bool peer, socket_t sfd); 38 | 39 | Address(const Address& other); 40 | Address(); 41 | 42 | public: 43 | bool operator==(Address const & other) const 44 | { 45 | return (m_address.sin_addr.s_addr == other.m_address.sin_addr.s_addr) && (m_address.sin_port == other.m_address.sin_port); 46 | } 47 | 48 | bool operator<(Address const & other) const 49 | { 50 | return (m_address.sin_addr.s_addr < other.m_address.sin_addr.s_addr) || (m_address.sin_addr.s_addr == other.m_address.sin_addr.s_addr && m_address.sin_port < other.m_address.sin_port); 51 | } 52 | 53 | Address& operator=(const Address& other); 54 | 55 | public: 56 | static uint32_t hash_value(const sockaddr_in& addr); 57 | 58 | private: 59 | struct sockaddr_in m_address; 60 | 61 | enum { IP_ADDRESS_LEN=32 }; 62 | char m_ip_string[IP_ADDRESS_LEN]; 63 | }; 64 | 65 | } 66 | 67 | namespace std { 68 | template <> 69 | struct hash { 70 | std::size_t operator()(const cyclone::Address& addr) const { 71 | return std::hash()(cyclone::Address::hash_value(addr.get_sockaddr_in())); 72 | } 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /source/cyNetwork/network/cyn_tcp_client.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #include 5 | #include 6 | 7 | #include "cyn_tcp_client.h" 8 | 9 | namespace cyclone 10 | { 11 | 12 | #define RELEASE_EVENT(looper, id) \ 13 | if (id != Looper::INVALID_EVENT_ID) { \ 14 | looper->delete_event(id); \ 15 | id = Looper::INVALID_EVENT_ID; \ 16 | } 17 | //------------------------------------------------------------------------------------- 18 | TcpClient::TcpClient(Looper* looper, void* param, int id) 19 | : m_id(id) 20 | , m_socket(INVALID_SOCKET) 21 | , m_socket_event_id(Looper::INVALID_EVENT_ID) 22 | , m_retry_timer_id(Looper::INVALID_EVENT_ID) 23 | , m_looper(looper) 24 | , m_param(param) 25 | , m_connection(nullptr) 26 | { 27 | //looper must be set 28 | assert(looper); 29 | 30 | m_connection_lock = sys_api::mutex_create(); 31 | 32 | m_listener.on_connected = nullptr; 33 | m_listener.on_message = nullptr; 34 | m_listener.on_close = nullptr; 35 | } 36 | 37 | //------------------------------------------------------------------------------------- 38 | TcpClient::~TcpClient() 39 | { 40 | assert(sys_api::thread_get_current_id() == m_looper->get_thread_id()); 41 | 42 | sys_api::mutex_destroy(m_connection_lock); 43 | m_connection_lock = nullptr; 44 | 45 | RELEASE_EVENT(m_looper, m_socket_event_id); 46 | RELEASE_EVENT(m_looper, m_retry_timer_id); 47 | 48 | if (m_connection) { 49 | assert(m_connection->get_state() == TcpConnection::kDisconnected); 50 | } 51 | } 52 | 53 | //------------------------------------------------------------------------------------- 54 | bool TcpClient::connect(const Address& addr) 55 | { 56 | assert(sys_api::thread_get_current_id() == m_looper->get_thread_id()); 57 | assert(m_socket == INVALID_SOCKET); 58 | 59 | sys_api::auto_mutex lock(m_connection_lock); 60 | 61 | //create socket 62 | m_socket = socket_api::create_socket(); 63 | //set socket to non-block and close-onexec 64 | socket_api::set_nonblock(m_socket, true); 65 | socket_api::set_close_onexec(m_socket, true); 66 | //set other socket option 67 | socket_api::set_keep_alive(m_socket, true); 68 | socket_api::set_linger(m_socket, false, 0); 69 | 70 | m_serverAddr = addr; 71 | 72 | //set event callback 73 | m_socket_event_id = m_looper->register_event(m_socket, Looper::kRead | Looper::kWrite, this, 74 | std::bind(&TcpClient::_on_socket_read_write, this), 75 | std::bind(&TcpClient::_on_socket_read_write, this) 76 | ); 77 | 78 | //start connect to server 79 | if (!socket_api::connect(m_socket, addr.get_sockaddr_in())) 80 | { 81 | CY_LOG(L_ERROR, "connect to server error, errno=%d", socket_api::get_lasterror()); 82 | return false; 83 | } 84 | 85 | CY_LOG(L_DEBUG, "begin connect to %s:%d", addr.get_ip(), addr.get_port()); 86 | return true; 87 | } 88 | 89 | //------------------------------------------------------------------------------------- 90 | TcpConnection::State TcpClient::get_connection_state(void) const 91 | { 92 | sys_api::auto_mutex lock(m_connection_lock); 93 | 94 | if (m_connection) return m_connection->get_state(); 95 | else return (m_socket==INVALID_SOCKET) ? TcpConnection::kDisconnected : TcpConnection::kConnecting; 96 | } 97 | 98 | //------------------------------------------------------------------------------------- 99 | void TcpClient::_on_connect_status_changed(bool timeout) 100 | { 101 | assert(m_connection == nullptr); 102 | 103 | if (timeout || socket_api::get_socket_error(m_socket) != 0) { 104 | //logic callback 105 | uint32_t retry_sleep_ms = 0; 106 | if (m_listener.on_connected) { 107 | retry_sleep_ms = m_listener.on_connected(shared_from_this(), nullptr, false); 108 | } 109 | CY_LOG(L_DEBUG, "connect to %s:%d failed! ", m_serverAddr.get_ip(), m_serverAddr.get_port()); 110 | _abort_connect(retry_sleep_ms); 111 | } 112 | else { 113 | //connect success! 114 | 115 | //remove from event system, taked by TcpConnection 116 | RELEASE_EVENT(m_looper, m_socket_event_id); 117 | 118 | //established the connection 119 | m_connection = std::make_shared(m_id, m_socket, m_looper, this); 120 | CY_LOG(L_DEBUG, "connect to %s:%d success", m_serverAddr.get_ip(), m_serverAddr.get_port()); 121 | 122 | //bind callback functions 123 | if (m_listener.on_message) { 124 | m_connection->set_on_message([this](TcpConnectionPtr conn) { 125 | m_listener.on_message(shared_from_this(), conn); 126 | }); 127 | } 128 | 129 | if(m_listener.on_close) { 130 | m_connection->set_on_close([this](TcpConnectionPtr conn) { 131 | CY_LOG(L_DEBUG, "disconnect from %s:%d", m_serverAddr.get_ip(), m_serverAddr.get_port()); 132 | m_listener.on_close(shared_from_this(), conn); 133 | }); 134 | } 135 | 136 | //send cached message 137 | if (!m_sendCache.empty()) { 138 | m_connection->send((const char*)m_sendCache.normalize(), m_sendCache.size()); 139 | m_sendCache.reset(); 140 | } 141 | //logic callback 142 | if (m_listener.on_connected) { 143 | m_listener.on_connected(shared_from_this(), m_connection, true); 144 | } 145 | } 146 | } 147 | 148 | //------------------------------------------------------------------------------------- 149 | void TcpClient::_abort_connect(uint32_t retry_sleep_ms) 150 | { 151 | assert(sys_api::thread_get_current_id() == m_looper->get_thread_id()); 152 | assert(get_connection_state() == TcpConnection::kConnecting); 153 | 154 | RELEASE_EVENT(m_looper, m_socket_event_id); 155 | RELEASE_EVENT(m_looper, m_retry_timer_id); 156 | 157 | //close current socket 158 | socket_api::close_socket(m_socket); 159 | m_socket = INVALID_SOCKET; 160 | 161 | if (retry_sleep_ms>0) { 162 | //retry connection? create retry the timer 163 | m_retry_timer_id = m_looper->register_timer_event(retry_sleep_ms, this, 164 | std::bind(&TcpClient::_on_retry_connect_timer, this, std::placeholders::_1)); 165 | CY_LOG(L_DEBUG, "try connect to %s:%d after %d mill seconds", m_serverAddr.get_ip(), m_serverAddr.get_port(), retry_sleep_ms); 166 | } 167 | } 168 | 169 | //------------------------------------------------------------------------------------- 170 | void TcpClient::disconnect(void) 171 | { 172 | assert(sys_api::thread_get_current_id() == m_looper->get_thread_id()); 173 | m_sendCache.reset(); 174 | switch (get_connection_state()) 175 | { 176 | case TcpConnection::kDisconnected: 177 | break; 178 | 179 | case TcpConnection::kConnecting: 180 | _abort_connect(0u); 181 | break; 182 | 183 | default: 184 | assert(m_connection != nullptr); 185 | m_connection->shutdown(); 186 | break; 187 | } 188 | } 189 | 190 | //------------------------------------------------------------------------------------- 191 | void TcpClient::_on_retry_connect_timer(Looper::event_id_t id) 192 | { 193 | assert(id == m_retry_timer_id); 194 | assert(m_connection == nullptr); 195 | 196 | //remove the timer 197 | RELEASE_EVENT(m_looper, m_retry_timer_id); 198 | 199 | //connect again 200 | if (!connect(m_serverAddr)) { 201 | //failed at once!, logic callback 202 | if (m_listener.on_connected) { 203 | uint32_t retry_sleep_ms = m_listener.on_connected(shared_from_this(), nullptr, false); 204 | 205 | //retry connection? 206 | if (retry_sleep_ms>0) { 207 | m_retry_timer_id = m_looper->register_timer_event(retry_sleep_ms, this, 208 | std::bind(&TcpClient::_on_retry_connect_timer, this, std::placeholders::_1)); 209 | } 210 | } 211 | } 212 | } 213 | 214 | //------------------------------------------------------------------------------------- 215 | void TcpClient::_on_socket_read_write(void) 216 | { 217 | if (get_connection_state() == TcpConnection::kConnecting) { 218 | _on_connect_status_changed(false); 219 | } 220 | } 221 | 222 | //------------------------------------------------------------------------------------- 223 | void TcpClient::send(const char* buf, size_t len) 224 | { 225 | switch (get_connection_state()) 226 | { 227 | case TcpConnection::kConnecting: 228 | { 229 | assert(sys_api::thread_get_current_id() == m_looper->get_thread_id()); 230 | m_sendCache.memcpy_into(buf, len); 231 | } 232 | break; 233 | case TcpConnection::kConnected: 234 | { 235 | m_connection->send(buf, len); 236 | } 237 | break; 238 | default: 239 | break; 240 | } 241 | 242 | } 243 | 244 | } 245 | 246 | -------------------------------------------------------------------------------- /source/cyNetwork/network/cyn_tcp_client.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include "cyn_tcp_connection.h" 8 | 9 | namespace cyclone 10 | { 11 | 12 | //pre-define 13 | class TcpClient; 14 | typedef std::shared_ptr TcpClientPtr; 15 | 16 | class TcpClient : public std::enable_shared_from_this, noncopyable, public TcpConnection::Owner 17 | { 18 | public: 19 | typedef std::function ConnectedCallback; 20 | typedef std::function MessageCallback; 21 | typedef std::function CloseCallback; 22 | 23 | struct Listener { 24 | ConnectedCallback on_connected; 25 | MessageCallback on_message; 26 | CloseCallback on_close; 27 | }; 28 | Listener m_listener; 29 | 30 | public: 31 | //// connect to remote server(NOT thread safe) 32 | bool connect(const Address& addr); 33 | //// disconnect(NOT thread safe) 34 | void disconnect(void); 35 | //// get server address 36 | Address get_server_address(void) const { return m_serverAddr; } 37 | /// send message(thread safe after connected, NOT thread safe when connecting) 38 | void send(const char* buf, size_t len); 39 | /// get callback param(thread safe) 40 | const void* get_param(void) const { return m_param; } 41 | /// get current connection state(thread safe); 42 | TcpConnection::State get_connection_state(void) const; 43 | /// Connection Owner type 44 | virtual OWNER_TYPE get_connection_owner_type(void) const { return kClient; } 45 | 46 | private: 47 | int m_id; 48 | socket_t m_socket; 49 | Looper::event_id_t m_socket_event_id; 50 | Looper::event_id_t m_retry_timer_id; 51 | 52 | Address m_serverAddr; 53 | Looper* m_looper; 54 | void* m_param; 55 | TcpConnectionPtr m_connection; 56 | sys_api::mutex_t m_connection_lock; 57 | RingBuf m_sendCache; 58 | 59 | private: 60 | /// on read/write callback function 61 | void _on_socket_read_write(void); 62 | void _on_retry_connect_timer(Looper::event_id_t id); 63 | 64 | private: 65 | void _on_connect_status_changed(bool timeout); 66 | void _abort_connect(uint32_t retry_sleep_ms); 67 | 68 | public: 69 | TcpClient(Looper* looper, void* param, int id=0); 70 | virtual ~TcpClient(); 71 | }; 72 | 73 | } 74 | -------------------------------------------------------------------------------- /source/cyNetwork/network/cyn_tcp_connection.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace cyclone 11 | { 12 | 13 | class TcpConnection; 14 | typedef std::shared_ptr TcpConnectionPtr; 15 | 16 | class TcpConnection : public std::enable_shared_from_this, noncopyable 17 | { 18 | public: 19 | typedef std::function EventCallback; 20 | class Owner { 21 | public: 22 | enum OWNER_TYPE { kServer=0, kClient }; 23 | virtual OWNER_TYPE get_connection_owner_type(void) const = 0; 24 | }; 25 | public: 26 | //connection state(kConnecting should not appear in Connection) 27 | // shutdown() 28 | // O -------------------> kConnected ---------------------------------------------------------------> kDisconnecting 29 | // | | 30 | // | _on_socket_close() _on_socket_close() | 31 | // ---------------------------> kDisconnected <---------------------------------- 32 | // 33 | enum State { kConnecting, kConnected, kDisconnecting, kDisconnected }; 34 | 35 | /// get id(thread safe) 36 | int32_t get_id(void) const { return m_id; } 37 | 38 | /// get current state(thread safe) 39 | State get_state(void) const; 40 | 41 | /// get peer address (thread safe) 42 | const Address& get_peer_addr(void) const { return m_peer_addr; } 43 | 44 | /// get local address (thread safe) 45 | const Address& get_local_addr(void) const { return m_local_addr; } 46 | 47 | /// get input stream buf (NOT thread safe, call it in work thread) 48 | RingBuf& get_input_buf(void) { return m_read_buf; } 49 | 50 | /// send message(thread safe) 51 | void send(const char* buf, size_t len); 52 | 53 | /// get native socket 54 | socket_t get_socket(void) { return m_socket; } 55 | 56 | /// set/get connection debug name(NOT thread safe) 57 | void set_name(const char* name); 58 | const char* get_name(void) const { return m_name.c_str(); } 59 | 60 | /// get owner 61 | Owner* get_owner(void) { return m_owner; } 62 | 63 | /// set/get param(NOT thread safe) 64 | void set_param(void* param); 65 | void* get_param(void) { return m_param; } 66 | 67 | /// get looper 68 | Looper* get_looper(void) const { return m_looper; } 69 | 70 | ///set callback function 71 | void set_on_message(EventCallback callback) { m_on_message = callback; } 72 | void set_on_send_complete(EventCallback callback) { m_on_send_complete = callback; } 73 | void set_on_close(EventCallback callback) { m_on_close = callback; } 74 | 75 | /// shutdown the connection 76 | void shutdown(void); 77 | 78 | private: 79 | int32_t m_id; 80 | socket_t m_socket; 81 | std::atomic m_state; 82 | Address m_local_addr; 83 | Address m_peer_addr; 84 | Looper* m_looper; 85 | Looper::event_id_t m_event_id; 86 | Owner* m_owner; 87 | void* m_param; 88 | 89 | enum { kDefaultReadBufSize=1024, kDefaultWriteBufSize=1024 }; 90 | 91 | RingBuf m_read_buf; 92 | 93 | RingBuf m_write_buf; 94 | sys_api::mutex_t m_write_buf_lock; //for multi thread lock 95 | 96 | EventCallback m_on_message; 97 | EventCallback m_on_send_complete; 98 | EventCallback m_on_close; 99 | 100 | std::string m_name; 101 | 102 | private: 103 | //// on socket read event 104 | void _on_socket_read(void); 105 | 106 | //// on socket read event 107 | void _on_socket_write(void); 108 | 109 | //// on socket close 110 | void _on_socket_close(void); 111 | 112 | //// on socket error 113 | void _on_socket_error(void); 114 | 115 | /// send message (not thread safe, must int work thread) 116 | void _send(const char* buf, size_t len); 117 | 118 | //// is write buf empty(thread safe) 119 | bool _is_writeBuf_empty(void) const; 120 | 121 | public: 122 | // record the max size of read buf and write buf 123 | size_t get_readebuf_max_size(void) const { return m_readbuf_minmax_size.max(); } 124 | size_t get_writebuf_max_size(void) const { return m_writebuf_minmax_size.max(); } 125 | 126 | // record the size and counts of packet received and sent with given times(millisecond) 127 | // not thread safe, must call in work thread, and can only be called once 128 | void start_read_statistics(int32_t period_time); 129 | void start_write_statistics(int32_t period_time); 130 | 131 | // get read and write statistics data(total size, and packet counts in given time) 132 | std::pair get_read_statistics(void) const; 133 | std::pair get_write_statistics(void) const; 134 | 135 | private: 136 | MinMaxValue m_readbuf_minmax_size; 137 | MinMaxValue m_writebuf_minmax_size; 138 | PeriodValue * m_read_statistics; 139 | PeriodValue * m_write_statistics; 140 | 141 | public: 142 | TcpConnection(int32_t id, socket_t sfd, Looper* looper, Owner* owner); 143 | ~TcpConnection(); 144 | }; 145 | 146 | } 147 | -------------------------------------------------------------------------------- /source/cyNetwork/network/cyn_tcp_server.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include "cyn_tcp_connection.h" 9 | 10 | namespace cyclone 11 | { 12 | 13 | //pre-define 14 | class TcpServerMasterThread; 15 | class TcpServerWorkThread; 16 | 17 | class TcpServer : noncopyable 18 | { 19 | public: 20 | typedef std::function MasterThreadStartCallback; 21 | typedef std::function MasterThreadCommandCallback; 22 | 23 | typedef std::function WorkThreadStartCallback; 24 | typedef std::function WorkThreadCommandCallback; 25 | 26 | typedef std::function EventCallback; 27 | 28 | enum { kCustomMasterThreadCmdID_Begin=10 }; 29 | 30 | struct Listener { 31 | MasterThreadStartCallback on_master_thread_start; 32 | MasterThreadCommandCallback on_master_thread_command; 33 | 34 | WorkThreadStartCallback on_work_thread_start; 35 | WorkThreadCommandCallback on_work_thread_command; 36 | 37 | EventCallback on_connected; 38 | EventCallback on_message; 39 | EventCallback on_close; 40 | }; 41 | Listener m_listener; 42 | 43 | public: 44 | /// add a bind port, return false means too much port has been binded or bind failed 45 | // NOT thread safe, and this function must be called before start the server 46 | bool bind(const Address& bind_addr, bool enable_reuse_port=true); 47 | 48 | /// start the server(start one accept thread and n work threads) 49 | /// (thread safe, but you wouldn't want call it again...) 50 | bool start(int32_t work_thread_counts); 51 | 52 | /// wait server to terminate(thread safe) 53 | void join(void); 54 | 55 | /// stop the server gracefully 56 | //(NOT thread safe, you can't call this function in any work thread) 57 | void stop(void); 58 | 59 | /// shutdown one of connection(thread safe) 60 | void shutdown_connection(TcpConnectionPtr conn); 61 | 62 | /// get bind address, if index is invalid return default Address value 63 | Address get_bind_address(size_t index); 64 | 65 | /// stop listen binded port(thread safe, after start the server) 66 | void stop_listen(size_t index); 67 | 68 | /// send message to master thread(thread safe) 69 | void send_master_message(uint16_t id, uint16_t size, const char* message); 70 | void send_master_message(const Packet* message); 71 | 72 | /// send work message to one of work thread(thread safe) 73 | void send_work_message(int32_t work_thread_index, const Packet* message); 74 | void send_work_message(int32_t work_thread_index, const Packet** message, int32_t counts); 75 | 76 | /// get work thread counts 77 | int32_t get_work_thread_counts(void) const { return m_workthread_counts; } 78 | 79 | int32_t get_next_connection_id(void) { 80 | return m_next_connection_id++; 81 | } 82 | 83 | private: 84 | enum { MAX_WORK_THREAD_COUNTS = 32 }; 85 | 86 | /// master thread 87 | TcpServerMasterThread* m_master_thread; 88 | 89 | /// work thread pool 90 | typedef std::vector< TcpServerWorkThread* > ServerWorkThreadArray; 91 | ServerWorkThreadArray m_work_thread_pool; 92 | 93 | int32_t m_workthread_counts; 94 | atomic_int32_t m_next_workthread_id; 95 | 96 | atomic_int32_t m_running; 97 | atomic_int32_t m_shutdown_ing; 98 | 99 | enum { kStartConnectionID = 1 }; 100 | atomic_int32_t m_next_connection_id; 101 | 102 | private: 103 | //called by master thread 104 | void _on_accept_socket(socket_t fd); 105 | 106 | friend class TcpServerMasterThread; 107 | private: 108 | // called by server work thread only 109 | void _on_socket_connected(int32_t work_thread_index, TcpConnectionPtr conn); 110 | void _on_socket_message(int32_t work_thread_index, TcpConnectionPtr conn); 111 | void _on_socket_close(int32_t work_thread_index, TcpConnectionPtr conn); 112 | 113 | friend class TcpServerWorkThread; 114 | public: 115 | TcpServer(); 116 | ~TcpServer(); 117 | }; 118 | 119 | } 120 | -------------------------------------------------------------------------------- /source/cyNetwork/network/internal/cyn_tcp_server_master_thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | 5 | #include 6 | #include "cyn_tcp_server_master_thread.h" 7 | 8 | namespace cyclone 9 | { 10 | //------------------------------------------------------------------------------------- 11 | TcpServerMasterThread::TcpServerMasterThread(TcpServer* server) 12 | : m_server(server) 13 | { 14 | assert(m_server); 15 | } 16 | 17 | //------------------------------------------------------------------------------------- 18 | TcpServerMasterThread::~TcpServerMasterThread() 19 | { 20 | assert(m_acceptor_sockets.empty()); 21 | } 22 | 23 | //------------------------------------------------------------------------------------- 24 | void TcpServerMasterThread::send_thread_message(uint16_t id, uint16_t size, const char* message) 25 | { 26 | assert(m_master_thread.is_running()); 27 | 28 | m_master_thread.send_message(id, size, message); 29 | } 30 | 31 | //------------------------------------------------------------------------------------- 32 | void TcpServerMasterThread::send_thread_message(const Packet* message) 33 | { 34 | assert(m_master_thread.is_running()); 35 | m_master_thread.send_message(message); 36 | } 37 | 38 | //------------------------------------------------------------------------------------- 39 | void TcpServerMasterThread::send_thread_message(const Packet** message, int32_t counts) 40 | { 41 | assert(m_master_thread.is_running()); 42 | m_master_thread.send_message(message, counts); 43 | } 44 | 45 | //------------------------------------------------------------------------------------- 46 | bool TcpServerMasterThread::bind_socket(const Address& bind_addr, bool enable_reuse_port) 47 | { 48 | //must bind socket before run master thread 49 | assert(!m_master_thread.is_running()); 50 | if (m_master_thread.is_running()) return false; 51 | 52 | //create a non blocking socket 53 | socket_t sfd = socket_api::create_socket(); 54 | if (sfd == INVALID_SOCKET) { 55 | CY_LOG(L_ERROR, "create socket error"); 56 | return false; 57 | } 58 | 59 | //set socket to non-block mode 60 | if (!socket_api::set_nonblock(sfd, true)) { 61 | //the process should be stop 62 | CY_LOG(L_ERROR, "set socket to non block mode error"); 63 | return false; 64 | } 65 | 66 | //set socket close on exe flag, the file descriptor will be closed open across an execve. 67 | socket_api::set_close_onexec(sfd, true); 68 | 69 | //set accept socket option 70 | #ifdef CY_SYS_WINDOWS 71 | (void)enable_reuse_port; 72 | #else 73 | if (enable_reuse_port) { 74 | //http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t 75 | socket_api::set_reuse_port(sfd, true); 76 | socket_api::set_reuse_addr(sfd, true); 77 | } 78 | #endif 79 | 80 | //bind address 81 | if (!(socket_api::bind(sfd, bind_addr.get_sockaddr_in()))) { 82 | CY_LOG(L_ERROR, "bind to address %s:%d failed", bind_addr.get_ip(), bind_addr.get_port()); 83 | socket_api::close_socket(sfd); 84 | return false; 85 | } 86 | 87 | CY_LOG(L_DEBUG, "bind to address %s:%d ok", bind_addr.get_ip(), bind_addr.get_port()); 88 | m_acceptor_sockets.push_back(std::make_tuple(sfd, Looper::INVALID_EVENT_ID)); 89 | 90 | return true; 91 | } 92 | 93 | //------------------------------------------------------------------------------------- 94 | bool TcpServerMasterThread::start(void) 95 | { 96 | //already running? 97 | assert(!m_master_thread.is_running()); 98 | if (m_master_thread.is_running()) return false; 99 | 100 | m_master_thread.set_on_start(std::bind(&TcpServerMasterThread::_on_thread_start, this)); 101 | m_master_thread.set_on_message(std::bind(&TcpServerMasterThread::_on_thread_message, this, std::placeholders::_1)); 102 | m_master_thread.start("tcp_master"); 103 | return true; 104 | } 105 | 106 | //------------------------------------------------------------------------------------- 107 | Address TcpServerMasterThread::get_bind_address(size_t index) 108 | { 109 | Address address; 110 | if (index >= m_acceptor_sockets.size()) return address; 111 | 112 | sockaddr_in addr; 113 | socket_api::getsockname(std::get<0>(m_acceptor_sockets[index]), addr); 114 | 115 | return Address(addr); 116 | } 117 | 118 | //------------------------------------------------------------------------------------- 119 | bool TcpServerMasterThread::_on_thread_start(void) 120 | { 121 | int32_t counts = 0; 122 | for (auto& listen_socket : m_acceptor_sockets) 123 | { 124 | socket_t sfd = std::get<0>(listen_socket); 125 | auto& event_id = std::get<1>(listen_socket); 126 | 127 | //register accept event 128 | event_id = m_master_thread.get_looper()->register_event(sfd, 129 | Looper::kRead, 130 | this, 131 | std::bind(&TcpServerMasterThread::_on_accept_event, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), 132 | 0); 133 | 134 | //begin listen 135 | socket_api::listen(sfd); 136 | counts++; 137 | } 138 | 139 | CY_LOG(L_DEBUG, "tcp master thread run, listen %d port(s)", counts); 140 | 141 | if (m_server->m_listener.on_master_thread_start) 142 | { 143 | m_server->m_listener.on_master_thread_start(m_server, m_master_thread.get_looper()); 144 | } 145 | return true; 146 | } 147 | 148 | //------------------------------------------------------------------------------------- 149 | void TcpServerMasterThread::_on_thread_message(Packet* message) 150 | { 151 | //accept thread command 152 | assert(message); 153 | 154 | uint16_t msg_id = message->get_packet_id(); 155 | if (msg_id == ShutdownCmd::ID) { 156 | Looper* looper = m_master_thread.get_looper(); 157 | 158 | //close all listen socket(s) 159 | for (auto listen_socket : m_acceptor_sockets) { 160 | auto& sfd = std::get<0>(listen_socket); 161 | auto& event_id = std::get<1>(listen_socket); 162 | 163 | if (event_id != Looper::INVALID_EVENT_ID) { 164 | looper->delete_event(event_id); 165 | event_id = Looper::INVALID_EVENT_ID; 166 | } 167 | if (sfd != INVALID_SOCKET) { 168 | socket_api::close_socket(sfd); 169 | sfd = INVALID_SOCKET; 170 | } 171 | } 172 | m_acceptor_sockets.clear(); 173 | 174 | //stop looper 175 | looper->push_stop_request(); 176 | } 177 | else if (msg_id == StopListenCmd::ID) { 178 | assert(message->get_packet_size() == sizeof(StopListenCmd)); 179 | StopListenCmd stopListenCmd; 180 | memcpy(&stopListenCmd, message->get_packet_content(), sizeof(stopListenCmd)); 181 | 182 | Looper* looper = m_master_thread.get_looper(); 183 | auto& listen_socket = m_acceptor_sockets[stopListenCmd.index]; 184 | auto& sfd = std::get<0>(listen_socket); 185 | auto& event_id = std::get<1>(listen_socket); 186 | 187 | //disable event 188 | if (event_id != Looper::INVALID_EVENT_ID) { 189 | looper->delete_event(event_id); 190 | event_id = Looper::INVALID_EVENT_ID; 191 | } 192 | //close socket 193 | if (sfd != INVALID_SOCKET) { 194 | socket_api::close_socket(sfd); 195 | sfd = INVALID_SOCKET; 196 | } 197 | } 198 | else if (msg_id >= kCustomCmdID_Begin) { 199 | //extra message 200 | if (m_server->m_listener.on_master_thread_command) { 201 | m_server->m_listener.on_master_thread_command(m_server, message); 202 | } 203 | } 204 | } 205 | 206 | //------------------------------------------------------------------------------------- 207 | void TcpServerMasterThread::_on_accept_event(Looper::event_id_t id, socket_t fd, Looper::event_t event) 208 | { 209 | (void)id; 210 | (void)event; 211 | 212 | //is shutdown in processing? 213 | if (m_server->m_shutdown_ing.load() > 0) return; 214 | 215 | //call accept and create peer socket 216 | socket_t connfd = socket_api::accept(fd, 0); 217 | if (connfd == INVALID_SOCKET) 218 | { 219 | //log error 220 | CY_LOG(L_ERROR, "accept socket error"); 221 | return; 222 | } 223 | 224 | m_server->_on_accept_socket(connfd); 225 | } 226 | 227 | //------------------------------------------------------------------------------------- 228 | void TcpServerMasterThread::join(void) 229 | { 230 | m_master_thread.join(); 231 | } 232 | 233 | } 234 | 235 | -------------------------------------------------------------------------------- /source/cyNetwork/network/internal/cyn_tcp_server_master_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace cyclone 10 | { 11 | 12 | class TcpServerMasterThread : noncopyable 13 | { 14 | public: 15 | //accept thread command 16 | enum { 17 | kShutdownCmdID = 1, kStopListenCmdID, 18 | 19 | kCustomCmdID_Begin = TcpServer::kCustomMasterThreadCmdID_Begin, 20 | }; 21 | 22 | struct ShutdownCmd 23 | { 24 | enum { ID = kShutdownCmdID }; 25 | }; 26 | 27 | struct StopListenCmd 28 | { 29 | enum { ID = kStopListenCmdID }; 30 | size_t index; 31 | }; 32 | 33 | public: //call by TcpServer Only 34 | //// send message to this work thread (thread safe) 35 | void send_thread_message(uint16_t id, uint16_t size, const char* message); 36 | void send_thread_message(const Packet* message); 37 | void send_thread_message(const Packet** message, int32_t counts); 38 | 39 | // add a binded socket(called by TcpServer only!) 40 | bool bind_socket(const Address& bind_addr, bool enable_reuse_port); 41 | // start master thread 42 | bool start(void); 43 | /// get bind address(called by TcpServer only!) 44 | Address get_bind_address(size_t index); 45 | /// get bind socket size 46 | size_t get_bind_socket_size(void) const { 47 | return m_acceptor_sockets.size(); 48 | } 49 | 50 | //// join work thread(thread safe) 51 | void join(void); 52 | 53 | private: 54 | TcpServer* m_server; 55 | WorkThread m_master_thread; 56 | 57 | typedef std::vector< std::tuple > SocketVector; 58 | SocketVector m_acceptor_sockets; 59 | 60 | private: 61 | /// master thread function start 62 | bool _on_thread_start(void); 63 | 64 | /// master thread message 65 | void _on_thread_message(Packet*); 66 | 67 | /// on accept callback function 68 | void _on_accept_event(Looper::event_id_t id, socket_t fd, Looper::event_t event); 69 | 70 | public: 71 | TcpServerMasterThread(TcpServer* server); 72 | ~TcpServerMasterThread(); 73 | }; 74 | 75 | } 76 | -------------------------------------------------------------------------------- /source/cyNetwork/network/internal/cyn_tcp_server_work_thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | 5 | #include 6 | #include "cyn_tcp_server_work_thread.h" 7 | 8 | namespace cyclone 9 | { 10 | 11 | //------------------------------------------------------------------------------------- 12 | TcpServerWorkThread::TcpServerWorkThread(TcpServer* server, int32_t index) 13 | : m_server(server) 14 | , m_index(index) 15 | { 16 | //run work thread 17 | m_work_thread = new WorkThread(); 18 | m_work_thread->set_on_start(std::bind(&TcpServerWorkThread::_on_workthread_start, this)); 19 | m_work_thread->set_on_message(std::bind(&TcpServerWorkThread::_on_workthread_message, this, std::placeholders::_1)); 20 | 21 | char temp[MAX_PATH] = { 0 }; 22 | std::snprintf(temp, MAX_PATH, "tcp_work_%d", m_index); 23 | m_work_thread->start(temp); 24 | } 25 | 26 | //------------------------------------------------------------------------------------- 27 | TcpServerWorkThread::~TcpServerWorkThread() 28 | { 29 | delete m_work_thread; 30 | } 31 | 32 | //------------------------------------------------------------------------------------- 33 | void TcpServerWorkThread::send_thread_message(uint16_t id, uint16_t size, const char* message) 34 | { 35 | assert(m_work_thread); 36 | 37 | m_work_thread->send_message(id, size, message); 38 | } 39 | 40 | //------------------------------------------------------------------------------------- 41 | void TcpServerWorkThread::send_thread_message(const Packet* message) 42 | { 43 | assert(m_work_thread); 44 | m_work_thread->send_message(message); 45 | } 46 | 47 | //------------------------------------------------------------------------------------- 48 | void TcpServerWorkThread::send_thread_message(const Packet** message, int32_t counts) 49 | { 50 | assert(m_work_thread); 51 | m_work_thread->send_message(message, counts); 52 | } 53 | 54 | //------------------------------------------------------------------------------------- 55 | bool TcpServerWorkThread::is_in_workthread(void) const 56 | { 57 | return sys_api::thread_get_current_id() == m_work_thread->get_looper()->get_thread_id(); 58 | } 59 | 60 | //------------------------------------------------------------------------------------- 61 | TcpConnectionPtr TcpServerWorkThread::get_connection(int32_t connection_id) 62 | { 63 | assert(is_in_workthread()); 64 | 65 | ConnectionMap::iterator it = m_connections.find(connection_id); 66 | if (it == m_connections.end()) return 0; 67 | return it->second; 68 | } 69 | 70 | //------------------------------------------------------------------------------------- 71 | bool TcpServerWorkThread::_on_workthread_start(void) 72 | { 73 | CY_LOG(L_INFO, "Tcp work thread %d start...", m_index); 74 | 75 | if (m_server->m_listener.on_work_thread_start) { 76 | m_server->m_listener.on_work_thread_start(m_server, get_index(), m_work_thread->get_looper()); 77 | } 78 | return true; 79 | } 80 | 81 | //------------------------------------------------------------------------------------- 82 | void TcpServerWorkThread::_on_workthread_message(Packet* message) 83 | { 84 | assert(is_in_workthread()); 85 | assert(message); 86 | assert(m_server); 87 | 88 | uint16_t msg_id = message->get_packet_id(); 89 | if (msg_id == NewConnectionCmd::ID) 90 | { 91 | assert(message->get_packet_size() == sizeof(NewConnectionCmd)); 92 | NewConnectionCmd newConnectionCmd; 93 | memcpy(&newConnectionCmd, message->get_packet_content(), sizeof(NewConnectionCmd)); 94 | 95 | //create tcp connection 96 | TcpConnectionPtr conn = std::make_shared(m_server->get_next_connection_id(), newConnectionCmd.sfd, m_work_thread->get_looper(), this); 97 | CY_LOG(L_DEBUG, "receive new connection, id=%d, peer_addr=%s:%d", conn->get_id(), conn->get_peer_addr().get_ip(), conn->get_peer_addr().get_port()); 98 | 99 | //bind onMessage function 100 | conn->set_on_message([this](TcpConnectionPtr connection) { 101 | m_server->_on_socket_message(this->get_index(), connection); 102 | }); 103 | 104 | //bind onClose function 105 | conn->set_on_close([this](TcpConnectionPtr connection) { 106 | m_server->_on_socket_close(this->get_index(), connection); 107 | }); 108 | 109 | //notify server listener 110 | m_server->_on_socket_connected(get_index(), conn); 111 | 112 | m_connections.insert(std::make_pair(conn->get_id(), conn)); 113 | } 114 | else if (msg_id == CloseConnectionCmd::ID) 115 | { 116 | assert(message->get_packet_size() == sizeof(CloseConnectionCmd)); 117 | CloseConnectionCmd closeConnectionCmd; 118 | memcpy(&closeConnectionCmd, message->get_packet_content(), sizeof(CloseConnectionCmd)); 119 | 120 | ConnectionMap::iterator it = m_connections.find(closeConnectionCmd.conn_id); 121 | if (it == m_connections.end()) return; 122 | 123 | TcpConnectionPtr conn = it->second; 124 | TcpConnection::State curr_state = conn->get_state(); 125 | 126 | CY_LOG(L_DEBUG, "receive close connection cmd, id=%d, state=%d", conn->get_id(), conn->get_state()); 127 | if (curr_state == TcpConnection::kConnected) 128 | { 129 | //shutdown,and wait 130 | conn->shutdown(); 131 | } 132 | else if (curr_state == TcpConnection::kDisconnected) 133 | { 134 | //delete the connection object 135 | m_connections.erase(conn->get_id()); 136 | } 137 | else 138 | { 139 | //kDisconnecting... 140 | //shutdown is in process, do nothing... 141 | } 142 | 143 | //if all connection is shutdown, and server is in shutdown process, quit the loop 144 | if (m_connections.empty() && closeConnectionCmd.shutdown_ing > 0) { 145 | //push loop quit command 146 | m_work_thread->get_looper()->push_stop_request(); 147 | return; 148 | } 149 | } 150 | else if (msg_id == ShutdownCmd::ID) 151 | { 152 | CY_LOG(L_DEBUG, "receive shutdown cmd"); 153 | //all connection is disconnect, just quit the loop 154 | if (m_connections.empty()) { 155 | //push loop request command 156 | m_work_thread->get_looper()->push_stop_request(); 157 | return; 158 | } 159 | 160 | //send shutdown command to all connection 161 | ConnectionMap::iterator it, end = m_connections.end(); 162 | for (it = m_connections.begin(); it != end; ++it) 163 | { 164 | TcpConnectionPtr conn = it->second; 165 | if (conn->get_state() == TcpConnection::kConnected) 166 | { 167 | conn->shutdown(); 168 | } 169 | } 170 | //just wait... 171 | } 172 | else 173 | { 174 | //extra message 175 | if (m_server->m_listener.on_work_thread_command) { 176 | m_server->m_listener.on_work_thread_command(m_server, get_index(), message); 177 | } 178 | } 179 | } 180 | 181 | //------------------------------------------------------------------------------------- 182 | void TcpServerWorkThread::join(void) 183 | { 184 | m_work_thread->join(); 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /source/cyNetwork/network/internal/cyn_tcp_server_work_thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include "../cyn_tcp_connection.h" 7 | 8 | namespace cyclone 9 | { 10 | 11 | class TcpServerWorkThread : noncopyable, public TcpConnection::Owner 12 | { 13 | public: 14 | enum { kNewConnectionCmdID = 1, kCloseConnectionCmdID, kShutdownCmdID }; 15 | struct NewConnectionCmd 16 | { 17 | enum { ID = kNewConnectionCmdID }; 18 | socket_t sfd; 19 | }; 20 | 21 | struct CloseConnectionCmd 22 | { 23 | enum { ID = kCloseConnectionCmdID }; 24 | int32_t conn_id; 25 | int32_t shutdown_ing; 26 | }; 27 | 28 | struct ShutdownCmd 29 | { 30 | enum { ID = kShutdownCmdID }; 31 | }; 32 | 33 | public: //call by TcpServer Only 34 | //// send message to this work thread (thread safe) 35 | void send_thread_message(uint16_t id, uint16_t size, const char* message); 36 | void send_thread_message(const Packet* message); 37 | void send_thread_message(const Packet** message, int32_t counts); 38 | 39 | //// get work thread index in work thread pool (thread safe) 40 | int32_t get_index(void) const { return m_index; } 41 | //// is current thread in work thread (thread safe) 42 | bool is_in_workthread(void) const; 43 | //// join work thread(thread safe) 44 | void join(void); 45 | //// get connection(NOT thread safe, MUST call in work thread) 46 | TcpConnectionPtr get_connection(int32_t connection_id); 47 | /// Connection Owner type 48 | virtual OWNER_TYPE get_connection_owner_type(void) const { return kServer; } 49 | 50 | private: 51 | TcpServer* m_server; 52 | const int32_t m_index; 53 | WorkThread* m_work_thread; 54 | 55 | typedef std::unordered_map< int32_t, TcpConnectionPtr > ConnectionMap; 56 | ConnectionMap m_connections; 57 | 58 | private: 59 | //// called by work thread 60 | bool _on_workthread_start(void); 61 | void _on_workthread_message(Packet*); 62 | 63 | public: 64 | TcpServerWorkThread(TcpServer* server, int32_t index); 65 | virtual ~TcpServerWorkThread(); 66 | }; 67 | 68 | } 69 | -------------------------------------------------------------------------------- /source/cyUtility/utility/cyu_ring_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | 11 | template 12 | class RingQueue 13 | { 14 | public: 15 | enum { kDefaultCapacity = 32 - 1 }; 16 | typedef std::function WalkFunc; 17 | 18 | size_t size(void) const { 19 | return (m_write >= m_read) ? (m_write - m_read) : (m_end - m_read + m_write); 20 | } 21 | 22 | void reset(void) { 23 | m_write = m_read = 0; 24 | } 25 | 26 | size_t capacity(void) const { 27 | return m_end - 1; 28 | } 29 | 30 | size_t get_free_size(void) const { 31 | return (m_write >= m_read) ? (m_end - m_write + m_read - 1) : (m_read - m_write - 1); 32 | } 33 | 34 | bool empty(void) const { 35 | return (m_write == m_read); 36 | } 37 | 38 | void push(const T& value) { 39 | if (get_free_size() == 0) { 40 | _auto_size((size_t)1); 41 | } 42 | m_vector[m_write] = value; 43 | m_write = _next(m_write); 44 | } 45 | 46 | const T& front(void) const { 47 | assert(!empty()); 48 | 49 | return m_vector[m_read]; 50 | } 51 | 52 | const T& back(void) const { 53 | assert(!empty()); 54 | 55 | return m_vector[_prev(m_write)]; 56 | } 57 | 58 | const T& get(size_t index) const { 59 | assert(index>=0 && index= size()) { 66 | reset(); 67 | return; 68 | } 69 | m_read = _next(m_read, counts); 70 | } 71 | 72 | void walk(WalkFunc walk_func) 73 | { 74 | if (walk_func == nullptr || empty()) return; 75 | 76 | size_t index = 0; 77 | size_t pos = m_read; 78 | while (pos != m_write) { 79 | if (!walk_func(index++, m_vector[pos])) return; 80 | pos = _next(pos); 81 | } 82 | } 83 | 84 | void walk_reserve(WalkFunc walk_func) 85 | { 86 | if (walk_func == nullptr || empty()) return; 87 | size_t index = size() - 1; 88 | size_t pos = m_write; 89 | while (pos != m_read) { 90 | pos = _prev(pos); 91 | if (!walk_func(index--, m_vector[pos])) return; 92 | }; 93 | } 94 | 95 | private: 96 | size_t _next(size_t pos, size_t step=1) const { 97 | return (pos + step) % m_end; 98 | } 99 | 100 | size_t _prev(size_t pos, size_t step=1) const { 101 | return (pos >= step) ? (pos - step) : (pos + m_end - step); 102 | } 103 | 104 | void _auto_size(size_t more_size) 105 | { 106 | size_t free_size = get_free_size(); 107 | if (free_size >= more_size) return; 108 | 109 | if (m_fixed) { 110 | pop(more_size - free_size); 111 | } 112 | else { 113 | size_t need_size = more_size + size() + 1; 114 | 115 | //auto inc size 116 | size_t new_size = 2; 117 | while (new_size < need_size) new_size *= 2; 118 | m_vector.resize(new_size); 119 | 120 | //move data if wrap condition 121 | if (m_read > m_write) { 122 | std::move(m_vector.begin(), m_vector.begin()+ (ValueVectorDifferenceType)m_write, m_vector.begin()+ (ValueVectorDifferenceType)m_end); 123 | m_write += m_end; 124 | } 125 | m_end = new_size; 126 | } 127 | } 128 | public: 129 | RingQueue(size_t fixed_capacity = 0) : m_fixed(fixed_capacity>0) 130 | { 131 | size_t cap = kDefaultCapacity + 1; 132 | m_end = cap; 133 | 134 | if (m_fixed) { 135 | cap = ((size_t)(fixed_capacity /8) + 1) * 8; //align 8 136 | m_end = fixed_capacity + 1; 137 | } 138 | 139 | m_vector.resize(cap); 140 | reset(); 141 | } 142 | private: 143 | typedef std::vector ValueVector; 144 | using ValueVectorDifferenceType = typename ValueVector::difference_type; 145 | 146 | bool m_fixed; 147 | ValueVector m_vector; 148 | size_t m_read; 149 | size_t m_write; 150 | size_t m_end; 151 | }; 152 | 153 | } 154 | -------------------------------------------------------------------------------- /source/cyUtility/utility/cyu_statistics.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | namespace cyclone 11 | { 12 | // MinMaxValue: 13 | // Used to count the maximum and minimum values of a variable in the whole life cycle 14 | // such as the size of a sending cache 15 | template 16 | struct MinMaxValue 17 | { 18 | void update(const T& value) 19 | { 20 | atomic_greater_exchange(m_max, value, value); 21 | atomic_smaller_exchange(m_min, value, value); 22 | } 23 | 24 | T min(void) const 25 | { 26 | return m_min.load(); 27 | } 28 | 29 | T max(void) const 30 | { 31 | return m_max.load(); 32 | } 33 | 34 | public: 35 | MinMaxValue() 36 | { 37 | m_min = std::numeric_limits::max(); 38 | m_max = std::numeric_limits::min(); 39 | } 40 | 41 | MinMaxValue(const T& init) 42 | { 43 | m_min = init; 44 | m_max = init; 45 | } 46 | 47 | private: 48 | typedef std::atomic AtomicValue; 49 | 50 | AtomicValue m_min; 51 | AtomicValue m_max; 52 | }; 53 | 54 | // PeriodValue: 55 | // More concerned about the performance of a variable in a certain period of time(in millisecond) 56 | template 57 | struct PeriodValue 58 | { 59 | public: 60 | struct AutoLock { 61 | AutoLock(sys_api::mutex_t l) : _lock(l){ 62 | if CONSTEXPR(WithLock) { 63 | sys_api::mutex_lock(_lock); 64 | } 65 | } 66 | ~AutoLock() { 67 | if CONSTEXPR(WithLock) { 68 | sys_api::mutex_unlock(_lock); 69 | } 70 | } 71 | sys_api::mutex_t _lock; 72 | }; 73 | public: 74 | void push(const T& value, int64_t cur_performance_time=0) 75 | { 76 | AutoLock lock(m_lock); 77 | 78 | if (cur_performance_time == 0) { 79 | cur_performance_time = sys_api::performance_time_now() / 1000ll; 80 | } 81 | 82 | m_valueQueue.push(std::make_pair(cur_performance_time, value)); 83 | 84 | //is value queue full? 85 | if (m_valueQueue.get_free_size() == 0) { 86 | //pop the front value if expired 87 | if (cur_performance_time - m_valueQueue.front().first > m_time_period) { 88 | m_valueQueue.pop(); 89 | } 90 | } 91 | } 92 | 93 | size_t total_counts(void) const 94 | { 95 | return m_valueQueue.size(); 96 | } 97 | 98 | std::pair sum_and_counts(int64_t cur_performance_time = 0) const 99 | { 100 | AutoLock lock(m_lock); 101 | 102 | if (cur_performance_time == 0) { 103 | cur_performance_time = sys_api::performance_time_now() / 1000ll; 104 | } 105 | 106 | _update(cur_performance_time); 107 | if (m_valueQueue.empty()) return std::make_pair(ZeroValue, 0); 108 | 109 | T sum = ZeroValue; 110 | int32_t counts=0; 111 | m_valueQueue.walk([&sum, &counts](size_t index, const ValuePair& v) -> bool { 112 | sum += v.second; 113 | counts++; 114 | return true; 115 | }); 116 | 117 | return std::make_pair(sum, counts); 118 | } 119 | 120 | int32_t get_time_period(void) const { 121 | return m_time_period; 122 | } 123 | 124 | public: 125 | PeriodValue(int32_t time_period_ms=1000) 126 | : m_time_period(time_period_ms) 127 | , m_valueQueue(0) //not fixed size 128 | , m_lock(nullptr) 129 | { 130 | if CONSTEXPR(WithLock) { 131 | m_lock = sys_api::mutex_create(); 132 | } 133 | } 134 | 135 | ~PeriodValue() 136 | { 137 | if CONSTEXPR(WithLock) { 138 | sys_api::mutex_destroy(m_lock); 139 | } 140 | } 141 | 142 | private: 143 | void _update(int64_t cur_time) const 144 | { 145 | if (m_valueQueue.empty()) return; 146 | int64_t expire = cur_time - m_time_period; 147 | 148 | if (m_valueQueue.front().first >= expire) return; // not necessary 149 | size_t left_bounder = 0; 150 | 151 | if (m_valueQueue.back().first < expire) { //all element expired 152 | m_valueQueue.reset(); 153 | return; 154 | } 155 | size_t right_bounder = m_valueQueue.size()-1; 156 | 157 | while (right_bounder - left_bounder > 1) { 158 | size_t mid = (left_bounder + right_bounder) / 2; //binary search 159 | 160 | if (m_valueQueue.get(mid).first >= expire) { 161 | right_bounder = mid; 162 | } 163 | else { 164 | left_bounder = mid; 165 | } 166 | } 167 | //discard all expired element 168 | m_valueQueue.pop(left_bounder + 1); 169 | } 170 | 171 | public: 172 | typedef std::pair ValuePair; 173 | typedef RingQueue ValueQueue; 174 | 175 | private: 176 | int32_t m_time_period; //millisecond 177 | mutable ValueQueue m_valueQueue; 178 | sys_api::mutex_t m_lock; 179 | }; 180 | 181 | } 182 | -------------------------------------------------------------------------------- /source/cyUtility/utility/cyu_string_util.cpp: -------------------------------------------------------------------------------- 1 | #include "cyu_string_util.h" 2 | 3 | namespace cyclone 4 | { 5 | namespace string_util 6 | { 7 | 8 | //------------------------------------------------------------------------------------- 9 | std::string size_to_string(float s) 10 | { 11 | char temp[64] = { 0 }; 12 | 13 | static const size_t KB = 1024; 14 | static const size_t MB = 1024 * 1024; 15 | static const size_t GB = 1024 * 1024 * 1024; 16 | 17 | if (s < KB) { 18 | std::snprintf(temp, 64, "%.2f ", s); 19 | } 20 | else if (s < MB) { 21 | std::snprintf(temp, 64, "%.2f KB", (float)s / (float)(KB)); 22 | } 23 | else if (s < GB) { 24 | std::snprintf(temp, 64, "%.2f MB", (float)s / (float)(MB)); 25 | } 26 | else { 27 | std::snprintf(temp, 64, "%.2f GB", (float)s / (float)(GB)); 28 | } 29 | 30 | return std::string(temp); 31 | } 32 | 33 | //------------------------------------------------------------------------------------- 34 | std::string size_to_string(size_t s) 35 | { 36 | return size_to_string((float)s); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /source/cyUtility/utility/cyu_string_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace cyclone 9 | { 10 | namespace string_util 11 | { 12 | 13 | // convert size to string with 'KB', 'MB', 'GB' tail 14 | std::string size_to_string(float s); 15 | std::string size_to_string(size_t s); 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /source/cyclone_config.h.in: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright(C) thecodeway.com 3 | */ 4 | #ifndef _CYCLONE_CONFIG_H_ 5 | #define _CYCLONE_CONFIG_H_ 6 | 7 | #cmakedefine CY_HAVE_UNISTD_H 1 8 | #cmakedefine CY_HAVE_INTTYPES_H 1 9 | #cmakedefine CY_HAVE_SYS_UIO_H 1 10 | #cmakedefine CY_HAVE_SYS_EVENTFD_H 1 11 | #cmakedefine CY_HAVE_JEMALLOC_LIBRARIES 1 12 | 13 | #cmakedefine CY_SYS_LINUX 1 14 | #cmakedefine CY_SYS_WINDOWS 1 15 | #cmakedefine CY_SYS_MACOS 1 16 | #cmakedefine CY_SYS_ANDROID 1 17 | 18 | #cmakedefine CY_HAVE_EPOLL 1 19 | #cmakedefine CY_HAVE_KQUEUE 1 20 | #cmakedefine CY_HAVE_READWRITE_V 1 21 | #cmakedefine CY_HAVE_PIPE2 1 22 | #cmakedefine CY_HAVE_TIMERFD 1 23 | #cmakedefine CY_HAVE_TIMED_LOCK 1 24 | 25 | #cmakedefine CY_ENABLE_LOG 1 26 | #cmakedefine CY_ENABLE_DEBUG 1 27 | 28 | #define CY_POLL_EPOLL 1 29 | #define CY_POLL_KQUEUE 2 30 | #define CY_POLL_SELECT 3 31 | 32 | #ifdef CY_HAVE_EPOLL 33 | #define CY_POLL_TECH CY_POLL_EPOLL 34 | #elif defined CY_HAVE_KQUEUE 35 | #define CY_POLL_TECH CY_POLL_KQUEUE 36 | #else 37 | #define CY_POLL_TECH CY_POLL_SELECT 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #ifdef CY_HAVE_UNISTD_H 47 | #include 48 | #endif 49 | 50 | #ifdef CY_HAVE_INTTYPES_H 51 | #include 52 | #endif 53 | 54 | #ifdef CY_HAVE_SYS_TYPES 55 | #include 56 | #endif 57 | 58 | #ifdef CY_HAVE_JEMALLOC_LIBRARIES 59 | #include 60 | #define CY_MALLOC je_malloc 61 | #define CY_FREE je_free 62 | #else 63 | #define CY_MALLOC malloc 64 | #define CY_FREE free 65 | #endif 66 | 67 | #include 68 | #include 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | #include 77 | 78 | #ifdef CY_SYS_WINDOWS 79 | #define FD_SETSIZE (1024) 80 | #include 81 | #include 82 | #include 83 | #else 84 | #include 85 | #include 86 | #include 87 | #endif 88 | 89 | #if defined (__LP64__) || defined (__64BIT__) || defined (_LP64) || (__WORDSIZE == 64) || defined(_WIN64) 90 | #define CY_64BIT 1 91 | #endif 92 | 93 | #ifdef CY_SYS_WINDOWS 94 | #undef min 95 | #undef max 96 | typedef SOCKET socket_t; 97 | typedef int32_t pid_t; 98 | 99 | #ifdef _WIN64 100 | typedef signed __int64 ssize_t; 101 | #else 102 | typedef _W64 signed int ssize_t; 103 | #endif 104 | #else 105 | typedef int socket_t; 106 | #ifndef INVALID_SOCKET 107 | #define INVALID_SOCKET (-1) 108 | #endif 109 | #ifndef SOCKET_ERROR 110 | #define SOCKET_ERROR (-1) 111 | #endif 112 | #endif 113 | 114 | #ifndef MAX_PATH 115 | #define MAX_PATH (260) 116 | #endif 117 | 118 | #ifdef CY_SYS_WINDOWS 119 | #define INLINE inline 120 | #else 121 | #define INLINE __inline 122 | #endif 123 | 124 | //wait c++17 support 125 | #define CONSTEXPR 126 | 127 | class noncopyable 128 | { 129 | protected: 130 | noncopyable() {} 131 | ~noncopyable() {} 132 | private: 133 | noncopyable( const noncopyable& ) = delete; 134 | const noncopyable& operator=(const noncopyable&) = delete; 135 | }; 136 | 137 | #endif 138 | 139 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | ######## 6 | #sub dictionary 7 | ######## 8 | add_subdirectory(unit) 9 | -------------------------------------------------------------------------------- /test/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | #Copyright(C) thecodeway.com 3 | # 4 | 5 | include_directories( 6 | ${CY_AUTO_INCLUDE_PATH} 7 | ${CY_SOURCE_CORE_PATH} 8 | ${CY_SOURCE_CRYPT_PATH} 9 | ${CY_SOURCE_EVENT_PATH} 10 | ${CY_SOURCE_NETWORK_PATH} 11 | ${CY_SOURCE_UTILITY_PATH} 12 | ${GTEST_INCLUDE_DIRS} 13 | ) 14 | 15 | link_directories( 16 | ${GTEST_LIBRARIES_PATH} 17 | ) 18 | 19 | set(cyt_unit_sources 20 | cyt_unit_main.cpp 21 | cyt_unit_lfqueue.cpp 22 | cyt_unit_crypt.cpp 23 | cyt_unit_ring_buf.cpp 24 | cyt_unit_pipe.cpp 25 | cyt_unit_signal.cpp 26 | cyt_event_fortest.h 27 | cyt_unit_event_basic.cpp 28 | cyt_unit_event_timer.cpp 29 | cyt_unit_event_socket.cpp 30 | cyt_uint_system.cpp 31 | cyt_unit_packet.cpp 32 | cyt_unit_statistics.cpp 33 | cyt_unit_ring_queue.cpp 34 | ) 35 | 36 | add_executable(cyt_unit 37 | ${cyt_unit_sources} 38 | ) 39 | 40 | set_property(TARGET cyt_unit PROPERTY FOLDER "test/unit") 41 | 42 | if(CY_SYS_WINDOWS) 43 | target_link_libraries(cyt_unit 44 | cyclone 45 | ws2_32.lib 46 | shlwapi.lib 47 | winmm.lib 48 | ${GTEST_BOTH_LIBRARIES} 49 | ) 50 | 51 | else() 52 | 53 | target_link_libraries(cyt_unit 54 | cyclone 55 | ${JEMALLOC_LIBRARIES} 56 | ${PTHREAD_LIBRARIES} 57 | ${GTEST_BOTH_LIBRARIES} 58 | ) 59 | 60 | endif() 61 | 62 | gtest_add_tests(cyt_unit SOURCES ${cyt_unit_sources}) 63 | -------------------------------------------------------------------------------- /test/unit/cyt_event_fortest.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | //------------------------------------------------------------------------------------- 8 | class EventLooper_ForTest : public 9 | #if (CY_POLL_TECH==CY_POLL_EPOLL) 10 | cyclone::Looper_epoll 11 | #elif (CY_POLL_TECH == CY_POLL_KQUEUE) 12 | cyclone::Looper_kqueue 13 | #else 14 | cyclone::Looper_select 15 | #endif 16 | { 17 | public: 18 | typedef std::vector< channel_s > channel_buffer; 19 | 20 | public: 21 | //functions for test 22 | const channel_buffer& get_channel_buf(void) const { return m_channelBuffer; } 23 | event_id_t get_free_head(void) const { return m_free_head; } 24 | int32_t get_active_channel_counts(void) const { return m_active_channel_counts; } 25 | int32_t get_active_channel_list_counts(void) const { 26 | #if (CY_POLL_TECH==CY_POLL_SELECT) 27 | int32_t counts = 0; 28 | event_id_t current = m_active_head; 29 | while (current != INVALID_EVENT_ID) { 30 | counts++; 31 | current = m_channelBuffer[current].next; 32 | } 33 | return counts; 34 | #else 35 | return m_active_channel_counts; 36 | #endif 37 | } 38 | int32_t get_free_channel_counts(void) const { 39 | int32_t counts = 0; 40 | event_id_t current = m_free_head; 41 | while (current != INVALID_EVENT_ID) { 42 | counts++; 43 | current = m_channelBuffer[current].next; 44 | } 45 | return counts; 46 | } 47 | static size_t get_DEFAULT_CHANNEL_BUF_COUNTS(void) { return DEFAULT_CHANNEL_BUF_COUNTS; } 48 | 49 | void reset_loop_counts(void) { m_loop_counts = 0; } 50 | }; 51 | 52 | //------------------------------------------------------------------------------------- 53 | #define CHECK_CHANNEL_SIZE(c, a, f) \ 54 | EXPECT_EQ((size_t)(c), channels.size()); \ 55 | EXPECT_EQ((int32_t)(a), looper.get_active_channel_counts()); \ 56 | EXPECT_EQ((int32_t)(a), looper.get_active_channel_list_counts()); \ 57 | EXPECT_EQ((int32_t)(f), looper.get_free_channel_counts()); 58 | 59 | -------------------------------------------------------------------------------- /test/unit/cyt_unit_event_basic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "cyt_event_fortest.h" 3 | 4 | #include 5 | 6 | using namespace cyclone; 7 | 8 | namespace { 9 | 10 | //------------------------------------------------------------------------------------- 11 | TEST(EventLooper, Basic) 12 | { 13 | EventLooper_ForTest looper; 14 | const size_t default_channel_counts = EventLooper_ForTest::get_DEFAULT_CHANNEL_BUF_COUNTS(); 15 | 16 | const auto& channels = looper.get_channel_buf(); 17 | CHECK_CHANNEL_SIZE(0, 0, 0); 18 | 19 | Looper::event_id_t id = looper.register_timer_event(1, 0, 0); 20 | CHECK_CHANNEL_SIZE(default_channel_counts, 1, default_channel_counts - 1); 21 | 22 | looper.disable_all(id); 23 | CHECK_CHANNEL_SIZE(default_channel_counts, 0, default_channel_counts - 1); 24 | 25 | looper.enable_read(id); 26 | CHECK_CHANNEL_SIZE(default_channel_counts, 1, default_channel_counts - 1); 27 | 28 | looper.disable_all(id); 29 | CHECK_CHANNEL_SIZE(default_channel_counts, 0, default_channel_counts - 1); 30 | 31 | looper.delete_event(id); 32 | CHECK_CHANNEL_SIZE(default_channel_counts, 0, default_channel_counts); 33 | 34 | std::vector id_buffer; 35 | for (size_t i = 0; i < default_channel_counts; i++) { 36 | id_buffer.push_back(looper.register_timer_event(1, 0, 0)); 37 | CHECK_CHANNEL_SIZE(default_channel_counts, i+1, default_channel_counts - i - 1); 38 | } 39 | 40 | id_buffer.push_back(looper.register_timer_event(1, 0, 0)); 41 | CHECK_CHANNEL_SIZE(default_channel_counts * 2, default_channel_counts + 1, default_channel_counts - 1); 42 | 43 | looper.disable_all(id_buffer[id_buffer.size()-1]); 44 | CHECK_CHANNEL_SIZE(default_channel_counts * 2, default_channel_counts, default_channel_counts - 1); 45 | 46 | looper.delete_event(id_buffer[id_buffer.size() - 1]); 47 | CHECK_CHANNEL_SIZE(default_channel_counts * 2, default_channel_counts, default_channel_counts); 48 | 49 | for (size_t i = 0; i < default_channel_counts; i++) { 50 | looper.disable_all(id_buffer[i]); 51 | CHECK_CHANNEL_SIZE(default_channel_counts * 2, default_channel_counts-i-1, default_channel_counts+i); 52 | 53 | looper.delete_event(id_buffer[i]); 54 | CHECK_CHANNEL_SIZE(default_channel_counts * 2, default_channel_counts-i-1, default_channel_counts+i+1); 55 | } 56 | 57 | CHECK_CHANNEL_SIZE(default_channel_counts * 2, 0, default_channel_counts*2); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /test/unit/cyt_unit_main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //------------------------------------------------------------------------------------- 5 | int main(int argc, char* argv[]) 6 | { 7 | testing::InitGoogleTest(&argc, argv); 8 | srand((uint32_t)::time(0)); 9 | 10 | return RUN_ALL_TESTS(); 11 | } 12 | -------------------------------------------------------------------------------- /test/unit/cyt_unit_packet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace cyclone; 7 | 8 | namespace { 9 | 10 | //------------------------------------------------------------------------------------- 11 | uint32_t* _makeHead(uint16_t size, uint16_t id, uint32_t& head_for_check) 12 | { 13 | head_for_check = ((uint32_t)(socket_api::ntoh_16(id)) << 16) | ((uint32_t)(socket_api::ntoh_16(size))); 14 | return &head_for_check; 15 | } 16 | 17 | //------------------------------------------------------------------------------------- 18 | #define PACKET_CHECK(packet_size, packet_id, head_size, check_buf) \ 19 | EXPECT_EQ(0, memcmp(packet.get_memory_buf(), _makeHead((packet_size), (packet_id), head), sizeof(uint32_t))); \ 20 | EXPECT_EQ(((head_size) + (packet_size)), packet.get_memory_size()); \ 21 | EXPECT_EQ((packet_id), packet.get_packet_id()); \ 22 | EXPECT_EQ((packet_size), packet.get_packet_size()); \ 23 | EXPECT_EQ(0, memcmp(packet.get_packet_content(), (check_buf), (packet_size))); 24 | 25 | //------------------------------------------------------------------------------------- 26 | #define PACKET_CHECK_WITH_RESERVED(packet_size, packet_id, head_size, check_buf, reserved, resize) \ 27 | PACKET_CHECK(packet_size, packet_id, head_size, check_buf) \ 28 | EXPECT_EQ(0, memcmp(packet.get_memory_buf()+sizeof(uint32_t), (reserved), (resize))); 29 | 30 | //------------------------------------------------------------------------------------- 31 | #define PACKET_CHECK_ZERO() \ 32 | EXPECT_EQ(nullptr, packet.get_memory_buf()); \ 33 | EXPECT_EQ(0ull, packet.get_memory_size()); \ 34 | EXPECT_EQ(0u, packet.get_packet_id()); \ 35 | EXPECT_EQ(0u, packet.get_packet_size()); \ 36 | EXPECT_EQ(nullptr, packet.get_packet_content()); 37 | 38 | //------------------------------------------------------------------------------------- 39 | TEST(Packet, Basic) 40 | { 41 | const size_t HEAD_SIZE = 8; 42 | const uint16_t PACKET_ID = 0x1234; 43 | const uint32_t RESERVED = 0xFACEC00Du; 44 | 45 | uint32_t head = 0; 46 | 47 | Packet packet; 48 | PACKET_CHECK_ZERO(); 49 | 50 | packet.build_from_memory(HEAD_SIZE, PACKET_ID, 0, nullptr, 0, nullptr); 51 | EXPECT_EQ(0, memcmp(packet.get_memory_buf(), _makeHead(0, PACKET_ID, head), sizeof(uint32_t))); 52 | EXPECT_EQ(HEAD_SIZE, packet.get_memory_size()); 53 | EXPECT_EQ(PACKET_ID, packet.get_packet_id()); 54 | EXPECT_EQ(0, packet.get_packet_size()); 55 | EXPECT_EQ(nullptr, packet.get_packet_content()); 56 | 57 | packet.clean(); 58 | PACKET_CHECK_ZERO(); 59 | 60 | const size_t buf_size = 1024; 61 | const size_t half_size = buf_size / 2; 62 | char temp_buf[buf_size] = { 0 }; 63 | for (size_t i = 0; i < buf_size; i++) { 64 | ((uint8_t*)temp_buf)[i] = (uint8_t)(rand() & 0xFF); 65 | } 66 | 67 | //build from small memory 68 | packet.build_from_memory(HEAD_SIZE, PACKET_ID, (uint16_t)half_size, temp_buf); 69 | PACKET_CHECK(half_size, PACKET_ID, HEAD_SIZE, temp_buf); 70 | 71 | packet.clean(); 72 | PACKET_CHECK_ZERO(); 73 | 74 | //build 1k memory buf 75 | packet.build_from_memory(HEAD_SIZE, PACKET_ID, (uint16_t)buf_size, temp_buf); 76 | PACKET_CHECK(buf_size, PACKET_ID, HEAD_SIZE, temp_buf); 77 | 78 | packet.clean(); 79 | PACKET_CHECK_ZERO(); 80 | 81 | //build as 2 parts 82 | packet.build_from_memory(HEAD_SIZE, PACKET_ID, (uint16_t)half_size, temp_buf, (uint16_t)half_size, temp_buf+ half_size); 83 | PACKET_CHECK(buf_size, PACKET_ID, HEAD_SIZE, temp_buf); 84 | 85 | packet.clean(); 86 | PACKET_CHECK_ZERO(); 87 | 88 | //build from ringbuf 89 | RingBuf rb; 90 | EXPECT_FALSE(packet.build_from_ringbuf(HEAD_SIZE, rb)); 91 | 92 | rb.memcpy_into(_makeHead(buf_size, PACKET_ID, head), sizeof(uint32_t)); 93 | rb.memcpy_into(&RESERVED, sizeof(RESERVED)); 94 | EXPECT_FALSE(packet.build_from_ringbuf(HEAD_SIZE, rb)); 95 | 96 | rb.memcpy_into(temp_buf, half_size); 97 | EXPECT_FALSE(packet.build_from_ringbuf(HEAD_SIZE, rb)); 98 | 99 | rb.memcpy_into(temp_buf+ half_size, half_size); 100 | EXPECT_TRUE(packet.build_from_ringbuf(HEAD_SIZE, rb)); 101 | PACKET_CHECK_WITH_RESERVED(buf_size, PACKET_ID, HEAD_SIZE, temp_buf, &RESERVED, sizeof(RESERVED)); 102 | 103 | packet.clean(); 104 | PACKET_CHECK_ZERO(); 105 | 106 | //build from pipe 107 | Pipe pipe; 108 | EXPECT_FALSE(packet.build_from_pipe(HEAD_SIZE, pipe)); 109 | 110 | pipe.write((const char*)_makeHead(buf_size, PACKET_ID, head), sizeof(uint32_t)); 111 | pipe.write((const char*)&RESERVED, sizeof(RESERVED)); 112 | EXPECT_FALSE(packet.build_from_pipe(HEAD_SIZE, pipe)); 113 | PACKET_CHECK_ZERO(); 114 | 115 | pipe.write((const char*)_makeHead(buf_size, PACKET_ID, head), sizeof(uint32_t)); 116 | pipe.write((const char*)&RESERVED, sizeof(RESERVED)); 117 | pipe.write(temp_buf, buf_size); 118 | EXPECT_TRUE(packet.build_from_pipe(HEAD_SIZE, pipe)); 119 | PACKET_CHECK_WITH_RESERVED(buf_size, PACKET_ID, HEAD_SIZE, temp_buf, &RESERVED, sizeof(RESERVED)); 120 | 121 | //large memory 122 | const size_t max_size = 0xFFFF; 123 | const size_t half_max_size = max_size / 2; 124 | char* large_buf = (char*)CY_MALLOC(max_size); 125 | for (size_t i = 0; i < max_size; i++) { 126 | ((uint8_t*)large_buf)[i] = (uint8_t)(rand() & 0xFF); 127 | } 128 | packet.build_from_memory(HEAD_SIZE, PACKET_ID, (uint16_t)max_size, large_buf); 129 | PACKET_CHECK(max_size, PACKET_ID, HEAD_SIZE, large_buf); 130 | 131 | //build as 2 parts 132 | packet.build_from_memory(HEAD_SIZE, PACKET_ID, (uint16_t)half_max_size, large_buf, (uint16_t)(max_size-half_max_size), large_buf + half_max_size); 133 | PACKET_CHECK(max_size, PACKET_ID, HEAD_SIZE, large_buf); 134 | 135 | CY_FREE(large_buf); 136 | large_buf = nullptr; 137 | 138 | packet.clean(); 139 | PACKET_CHECK_ZERO(); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /test/unit/cyt_unit_pipe.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace cyclone; 7 | 8 | namespace { 9 | //------------------------------------------------------------------------------------- 10 | TEST(Pipe, Basic) 11 | { 12 | Pipe pipe; 13 | 14 | const char* plain_text = "Hello,World!"; 15 | const size_t text_len = strlen(plain_text); 16 | 17 | const size_t readbuf_len = 1024; 18 | char read_buf[readbuf_len] = { 0 }; 19 | 20 | ssize_t read_size = pipe.read(read_buf, readbuf_len); 21 | EXPECT_TRUE(socket_api::is_lasterror_WOULDBLOCK()); 22 | EXPECT_EQ(SOCKET_ERROR, read_size); 23 | 24 | ssize_t write_size = pipe.write(plain_text, text_len); 25 | EXPECT_EQ(text_len, (size_t)write_size); 26 | 27 | read_size = pipe.read(read_buf, readbuf_len); 28 | EXPECT_EQ(text_len, (size_t)read_size); 29 | EXPECT_STREQ(plain_text, read_buf); 30 | 31 | read_size = pipe.read(read_buf, readbuf_len); 32 | EXPECT_TRUE(socket_api::is_lasterror_WOULDBLOCK()); 33 | EXPECT_EQ(SOCKET_ERROR, read_size); 34 | } 35 | 36 | //------------------------------------------------------------------------------------- 37 | TEST(Pipe, Overflow) 38 | { 39 | Pipe pipe; 40 | 41 | XorShift128 rndPush, rndPop; 42 | rndPush.make(); 43 | rndPop.seed0 = rndPush.seed0; 44 | rndPop.seed1 = rndPush.seed1; 45 | 46 | const size_t snd_block_size = 1024; 47 | assert(snd_block_size % sizeof(uint64_t) == 0); 48 | 49 | char snd_block[snd_block_size] = { 0 }; 50 | size_t rnd_counts = snd_block_size / sizeof(uint64_t); 51 | 52 | size_t total_snd_size = 0; 53 | for (;;) { 54 | for (size_t i = 0; i < rnd_counts; i++) { 55 | uint64_t rnd = rndPush.next(); 56 | ((uint64_t*)snd_block)[i] = rnd; 57 | } 58 | 59 | ssize_t write_size = pipe.write(snd_block, snd_block_size); 60 | if (write_size <= 0) { 61 | EXPECT_TRUE(socket_api::is_lasterror_WOULDBLOCK()); 62 | break; 63 | } 64 | total_snd_size += (size_t)write_size; 65 | } 66 | 67 | size_t total_rcv_size = 0; 68 | for (;;) { 69 | uint64_t read_data; 70 | ssize_t read_size = pipe.read((char*)&read_data, sizeof(read_data)); 71 | if (read_size <= 0) { 72 | EXPECT_TRUE(socket_api::is_lasterror_WOULDBLOCK()); 73 | break; 74 | } 75 | total_rcv_size += (size_t)read_size; 76 | EXPECT_EQ(read_data, rndPop.next()); 77 | } 78 | 79 | EXPECT_EQ(total_snd_size, total_rcv_size); 80 | } 81 | 82 | //------------------------------------------------------------------------------------- 83 | struct ThreadData 84 | { 85 | Pipe pipe; 86 | XorShift128 rnd; 87 | size_t total_size; 88 | }; 89 | 90 | //------------------------------------------------------------------------------------- 91 | void _push_function(void* param) 92 | { 93 | ThreadData* data = (ThreadData*)param; 94 | XorShift128 rnd = data->rnd; 95 | RingBuf sndBuf; 96 | 97 | size_t total_snd_size = 0; 98 | 99 | for (;;) { 100 | while (sndBuf.get_free_size() >= sizeof(uint64_t) && total_snd_size<(data->total_size)) { 101 | uint64_t next_snd = rnd.next(); 102 | sndBuf.memcpy_into(&next_snd, sizeof(next_snd)); 103 | total_snd_size += sizeof(next_snd); 104 | } 105 | 106 | if (sndBuf.empty()) break; 107 | 108 | ssize_t write_size = sndBuf.write_socket(data->pipe.get_write_port()); 109 | if (write_size <= 0) { 110 | sys_api::thread_yield(); 111 | continue; 112 | } 113 | } 114 | EXPECT_EQ(total_snd_size, data->total_size); 115 | } 116 | 117 | //------------------------------------------------------------------------------------- 118 | void _pop_function(void* param) 119 | { 120 | ThreadData* data = (ThreadData*)param; 121 | XorShift128 rnd = data->rnd; 122 | RingBuf rcvBuf; 123 | 124 | size_t total_rcv_size = 0; 125 | uint64_t read_data; 126 | 127 | while (total_rcv_size<(data->total_size)) { 128 | ssize_t read_size = rcvBuf.read_socket(data->pipe.get_read_port(), false); 129 | while (rcvBuf.size() >= sizeof(uint64_t)) { 130 | EXPECT_EQ(sizeof(uint64_t), rcvBuf.memcpy_out(&read_data, sizeof(uint64_t))); 131 | EXPECT_EQ(rnd.next(), read_data); 132 | total_rcv_size += sizeof(uint64_t); 133 | } 134 | 135 | if (read_size <= 0) { 136 | sys_api::thread_yield(); 137 | } 138 | } 139 | EXPECT_TRUE(rcvBuf.empty()); 140 | EXPECT_EQ(total_rcv_size, data->total_size); 141 | EXPECT_EQ(SOCKET_ERROR, (data->pipe).read((char*)&read_data, sizeof(read_data))); 142 | EXPECT_TRUE(socket_api::is_lasterror_WOULDBLOCK()); 143 | EXPECT_EQ(RingBuf::kDefaultCapacity, rcvBuf.capacity()); 144 | } 145 | 146 | //------------------------------------------------------------------------------------- 147 | TEST(Pipe, MultiThread) 148 | { 149 | ThreadData data; 150 | data.rnd.make(); 151 | data.total_size = 1024 * 1024*10; //1MB 152 | 153 | thread_t pop_thread = sys_api::thread_create(_pop_function, &data, "pop"); 154 | thread_t push_thread = sys_api::thread_create(_push_function, &data, "push"); 155 | 156 | //join 157 | sys_api::thread_join(pop_thread); 158 | sys_api::thread_join(push_thread); 159 | } 160 | 161 | } -------------------------------------------------------------------------------- /test/unit/cyt_unit_signal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace cyclone; 5 | 6 | namespace { 7 | //------------------------------------------------------------------------------------- 8 | TEST(Signal, Basic) 9 | { 10 | sys_api::signal_t signal = sys_api::signal_create(); 11 | 12 | //wait 13 | EXPECT_FALSE(sys_api::signal_timewait(signal, 0)); 14 | EXPECT_FALSE(sys_api::signal_timewait(signal, 1)); 15 | 16 | //notify, wait and timewait 17 | sys_api::signal_notify(signal); 18 | sys_api::signal_wait(signal); 19 | EXPECT_FALSE(sys_api::signal_timewait(signal, 1)); 20 | 21 | //notify and time_wait twice 22 | sys_api::signal_notify(signal); 23 | EXPECT_TRUE(sys_api::signal_timewait(signal, 1)); 24 | EXPECT_FALSE(sys_api::signal_timewait(signal, 0)); 25 | 26 | //notify twice and time_wait twice 27 | sys_api::signal_notify(signal); 28 | sys_api::signal_notify(signal); 29 | EXPECT_TRUE(sys_api::signal_timewait(signal, 0)); 30 | EXPECT_FALSE(sys_api::signal_timewait(signal, 1)); 31 | 32 | //time_wait 33 | int64_t begin_time = sys_api::performance_time_now(); 34 | EXPECT_FALSE(sys_api::signal_timewait(signal, 100)); 35 | int64_t end_time = sys_api::performance_time_now(); 36 | EXPECT_GE(end_time - begin_time, 100*1000); 37 | EXPECT_LE(end_time - begin_time, 110*1000); 38 | 39 | 40 | sys_api::signal_destroy(signal); 41 | } 42 | 43 | //------------------------------------------------------------------------------------- 44 | struct ThreadData 45 | { 46 | sys_api::signal_t signal_ping; 47 | sys_api::signal_t signal_pong; 48 | atomic_int32_t* live_counts; 49 | }; 50 | 51 | //------------------------------------------------------------------------------------- 52 | static void _threadFunction(void* param) 53 | { 54 | ThreadData* data = (ThreadData*)param; 55 | 56 | sys_api::signal_wait(data->signal_ping); 57 | data->live_counts->fetch_sub(1); 58 | sys_api::signal_notify(data->signal_pong); 59 | 60 | delete data; 61 | } 62 | 63 | //------------------------------------------------------------------------------------- 64 | TEST(Signal, Multithread) 65 | { 66 | const int32_t thread_counts = 10; 67 | 68 | sys_api::signal_t signal_ping = sys_api::signal_create(); 69 | sys_api::signal_t signal_pong = sys_api::signal_create(); 70 | atomic_int32_t live_counts(0); 71 | 72 | for (int32_t i = 0; i < thread_counts; i++) { 73 | ThreadData* data = new ThreadData(); 74 | data->signal_ping = signal_ping; 75 | data->signal_pong = signal_pong; 76 | data->live_counts = &live_counts; 77 | 78 | sys_api::thread_create_detached(_threadFunction, data, ""); 79 | 80 | live_counts++; 81 | } 82 | 83 | EXPECT_EQ(thread_counts, live_counts); 84 | 85 | while (live_counts>0) { 86 | int32_t current_live_counts = live_counts; 87 | sys_api::signal_notify(signal_ping); 88 | sys_api::signal_wait(signal_pong); 89 | EXPECT_EQ(current_live_counts - 1, live_counts.load()); 90 | } 91 | 92 | EXPECT_EQ(0, live_counts); 93 | EXPECT_FALSE(sys_api::signal_timewait(signal_ping, 0)); 94 | EXPECT_FALSE(sys_api::signal_timewait(signal_pong, 0)); 95 | 96 | sys_api::signal_destroy(signal_ping); 97 | sys_api::signal_destroy(signal_pong); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /test/unit/cyt_unit_statistics.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace cyclone; 9 | 10 | //------------------------------------------------------------------------------------- 11 | TEST(Statistics, MinMaxValue) 12 | { 13 | typedef MinMaxValue IntMinMaxValue; 14 | 15 | { 16 | IntMinMaxValue v; 17 | EXPECT_EQ(v.min(), std::numeric_limits::max()); 18 | EXPECT_EQ(v.max(), std::numeric_limits::min()); 19 | } 20 | 21 | { 22 | IntMinMaxValue v(123); 23 | EXPECT_EQ(v.min(), 123); 24 | EXPECT_EQ(v.max(), 123); 25 | } 26 | 27 | { 28 | IntMinMaxValue v; 29 | v.update(123); 30 | EXPECT_EQ(v.min(), 123); 31 | EXPECT_EQ(v.max(), 123); 32 | } 33 | 34 | { 35 | IntMinMaxValue v; 36 | v.update(1); 37 | v.update(2); 38 | EXPECT_EQ(v.min(), 1); 39 | EXPECT_EQ(v.max(), 2); 40 | } 41 | 42 | { 43 | IntMinMaxValue v; 44 | int32_t firstValue = rand()-RAND_MAX/2; 45 | int32_t _min = firstValue; 46 | int32_t _max = firstValue; 47 | v.update(firstValue); 48 | 49 | for (int32_t i = 0; i < 256; i++) { 50 | int32_t randomValue = rand() - RAND_MAX / 2; 51 | if (randomValue > _max) _max = randomValue; 52 | if (randomValue < _min) _min = randomValue; 53 | v.update(randomValue); 54 | } 55 | 56 | EXPECT_EQ(v.max(), _max); 57 | EXPECT_EQ(v.min(), _min); 58 | } 59 | 60 | //multi thread 61 | { 62 | int32_t firstValue = rand() - RAND_MAX / 2; 63 | int32_t _min = firstValue; 64 | int32_t _max = firstValue; 65 | 66 | IntMinMaxValue v(firstValue); 67 | 68 | struct ThreadContext 69 | { 70 | int32_t _min; 71 | int32_t _max; 72 | thread_t _thread; 73 | }; 74 | 75 | int32_t threadCounts = sys_api::get_cpu_counts(); 76 | if (threadCounts < 4) threadCounts = 4; 77 | 78 | ThreadContext** test_threads = new ThreadContext*[threadCounts]; 79 | 80 | for (int32_t i = 0; i < threadCounts; i++) { 81 | 82 | ThreadContext* ctx = new ThreadContext; 83 | ctx->_min = ctx->_max = firstValue; 84 | ctx->_thread = sys_api::thread_create([&v](void* param) { 85 | 86 | ThreadContext* _ctx = (ThreadContext*)param; 87 | 88 | for (int32_t j = 0; j < 0xFFFF; j++) { 89 | int32_t value = rand() - RAND_MAX / 2; 90 | 91 | if (value > _ctx->_max) _ctx->_max = value; 92 | if (value < _ctx->_min) _ctx->_min = value; 93 | v.update(value); 94 | } 95 | }, ctx, ""); 96 | 97 | test_threads[i] = ctx; 98 | } 99 | 100 | for (int32_t i = 0; i < threadCounts; i++) { 101 | sys_api::thread_join(test_threads[i]->_thread); 102 | 103 | if (test_threads[i]->_max > _max) _max = test_threads[i]->_max; 104 | if (test_threads[i]->_min < _min) _min = test_threads[i]->_min; 105 | } 106 | 107 | EXPECT_EQ(v.max(), _max); 108 | EXPECT_EQ(v.min(), _min); 109 | } 110 | } 111 | 112 | //------------------------------------------------------------------------------------- 113 | TEST(Statistics, PeriodValue) 114 | { 115 | typedef PeriodValue IntPeriodValue; 116 | 117 | { 118 | IntPeriodValue v; 119 | 120 | EXPECT_EQ(v.total_counts(), 0); 121 | EXPECT_EQ(v.sum_and_counts(), std::make_pair(0, 0)); 122 | } 123 | 124 | { 125 | IntPeriodValue v(4000); 126 | 127 | // 0-100, 1-200, 2-300, 3-400, ... ,31-3200 128 | for (int32_t i = 0; i < 32; i++) { 129 | v.push(i, (int64_t)(i + 1) * 100ll); 130 | } 131 | 132 | //all 133 | EXPECT_EQ(v.total_counts(), 32); 134 | EXPECT_EQ(v.sum_and_counts(3300), std::make_pair(496, 32)); // 496 = (0+31)*32/2 135 | } 136 | 137 | { 138 | IntPeriodValue v(1000); 139 | 140 | // 0-100, 1-200, 2-300, 3-400, ... ,39-4000 141 | for (int32_t i = 0; i < 40; i++) { 142 | v.push(i, (int64_t)(i + 1) * 100ll); 143 | } 144 | 145 | EXPECT_EQ(v.total_counts(), IntPeriodValue::ValueQueue::kDefaultCapacity-1); 146 | 147 | //30-3100, 31-3200, ..., 39-4000 148 | EXPECT_EQ(v.sum_and_counts(4100), std::make_pair(345, 10)); //345 = (30 + 39) * 10 / 2 149 | EXPECT_EQ(v.total_counts(), 10); 150 | 151 | //39-4000 152 | EXPECT_EQ(v.sum_and_counts(5000), std::make_pair(39, 1)); 153 | EXPECT_EQ(v.total_counts(), 1); 154 | 155 | //all expired 156 | EXPECT_EQ(v.sum_and_counts(5001), std::make_pair(0, 0)); 157 | EXPECT_EQ(v.total_counts(), 0); 158 | } 159 | } 160 | --------------------------------------------------------------------------------