├── .clang-format ├── .clang-tidy ├── .github └── workflows │ ├── build-and-test.yml │ ├── clang-format-lint.yml │ └── clang-tidy-review.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── docs ├── connector.zh-cn.md ├── eventloop.zh-cn.md ├── listen_thread.zh-cn.md ├── main.zh-cn.md ├── merge_send.zh-cn.md ├── promise_receive.zh-cn.md ├── socket.zh-cn.md ├── tcp_connection.zh-cn.md └── tcp_service.zh-cn.md ├── examples ├── BenchWebsocket.cpp ├── BroadCastClient.cpp ├── BroadCastServer.cpp ├── CMakeLists.txt ├── HttpClient.cpp ├── HttpServer.cpp ├── PingPongClient.cpp ├── PingPongServer.cpp ├── PromiseReceive.cpp ├── SingleThreadPingPongServer.cpp └── WebBinaryProxy.cpp ├── image ├── broadcast.png └── pingpong.png ├── include └── brynet │ ├── Version.hpp │ ├── base │ ├── AppStatus.hpp │ ├── Array.hpp │ ├── Buffer.hpp │ ├── NonCopyable.hpp │ ├── Packet.hpp │ ├── Platform.hpp │ ├── Stack.hpp │ ├── Timer.hpp │ ├── WaitGroup.hpp │ ├── crypto │ │ ├── Base64.hpp │ │ └── SHA1.hpp │ └── endian │ │ └── Endian.hpp │ └── net │ ├── AsyncConnector.hpp │ ├── Channel.hpp │ ├── CurrentThread.hpp │ ├── EventLoop.hpp │ ├── Exception.hpp │ ├── ListenThread.hpp │ ├── Poller.hpp │ ├── PromiseReceive.hpp │ ├── SSLHelper.hpp │ ├── SendableMsg.hpp │ ├── Socket.hpp │ ├── SocketLibFunction.hpp │ ├── SocketLibTypes.hpp │ ├── TcpConnection.hpp │ ├── TcpService.hpp │ ├── detail │ ├── ConnectionOption.hpp │ ├── ConnectorDetail.hpp │ ├── ConnectorWorkInfo.hpp │ ├── IOLoopData.hpp │ ├── ListenThreadDetail.hpp │ ├── TCPServiceDetail.hpp │ └── WakeupChannel.hpp │ ├── http │ ├── HttpFormat.hpp │ ├── HttpParser.hpp │ ├── HttpService.hpp │ ├── WebSocketFormat.hpp │ └── http_parser.h │ ├── port │ └── Win.hpp │ └── wrapper │ ├── ConnectionBuilder.hpp │ ├── HttpConnectionBuilder.hpp │ ├── HttpServiceBuilder.hpp │ └── ServiceBuilder.hpp └── tests ├── CMakeLists.txt ├── catch.hpp ├── test_array.cpp ├── test_buffer.cpp ├── test_endian.cpp ├── test_http.cpp ├── test_packet.cpp ├── test_stack.cpp ├── test_sync_connect.cpp ├── test_timer.cpp └── test_wait_group.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | # Generated from CLion C/C++ Code Style settings 2 | BasedOnStyle: Google 3 | Language: Cpp 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignOperands: true 8 | AllowAllArgumentsOnNextLine: false 9 | AllowAllConstructorInitializersOnNextLine: false 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: false 14 | AllowShortIfStatementsOnASingleLine: false 15 | AllowShortLambdasOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakTemplateDeclarations: Yes 19 | BreakBeforeBraces: Custom 20 | BraceWrapping: 21 | AfterCaseLabel: true 22 | AfterClass: true 23 | AfterStruct: true 24 | AfterControlStatement: Always 25 | AfterEnum: true 26 | AfterFunction: true 27 | AfterNamespace: false 28 | AfterUnion: false 29 | BeforeCatch: true 30 | BeforeElse: true 31 | BeforeLambdaBody: false 32 | IndentBraces: false 33 | SplitEmptyFunction: false 34 | SplitEmptyRecord: true 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeTernaryOperators: true 37 | BreakConstructorInitializers: BeforeColon 38 | BreakInheritanceList: BeforeColon 39 | ColumnLimit: 0 40 | CompactNamespaces: true 41 | ContinuationIndentWidth: 8 42 | IndentCaseLabels: true 43 | IndentPPDirectives: None 44 | IndentWidth: 4 45 | KeepEmptyLinesAtTheStartOfBlocks: true 46 | MaxEmptyLinesToKeep: 2 47 | NamespaceIndentation: None 48 | ObjCSpaceAfterProperty: false 49 | ObjCSpaceBeforeProtocolList: true 50 | PointerAlignment: Right 51 | ReflowComments: false 52 | SpaceAfterCStyleCast: true 53 | SpaceAfterLogicalNot: false 54 | SpaceAfterTemplateKeyword: false 55 | SpaceBeforeAssignmentOperators: true 56 | SpaceBeforeCpp11BracedList: false 57 | SpaceBeforeCtorInitializerColon: true 58 | SpaceBeforeInheritanceColon: true 59 | SpaceBeforeParens: ControlStatements 60 | SpaceBeforeRangeBasedForLoopColon: true 61 | SpaceInEmptyParentheses: false 62 | SpacesBeforeTrailingComments: 0 63 | SpacesInAngles: false 64 | SpacesInCStyleCastParentheses: false 65 | SpacesInContainerLiterals: false 66 | SpacesInParentheses: false 67 | SpacesInSquareBrackets: false 68 | TabWidth: 4 69 | UseTab: Never 70 | -------------------------------------------------------------------------------- /.github/workflows/clang-format-lint.yml: -------------------------------------------------------------------------------- 1 | name: clang-format-lint 2 | on: 3 | push: {} 4 | pull_request: {} 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: DoozyX/clang-format-lint-action@v0.13 13 | with: 14 | ource: '.' 15 | exclude: './tests' 16 | extensions: 'h,c,hpp,cc,cpp' 17 | clangFormatVersion: 11 -------------------------------------------------------------------------------- /.github/workflows/clang-tidy-review.yml: -------------------------------------------------------------------------------- 1 | name: clang-tidy-review 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '**.cpp' 7 | - '**.hpp' 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | review: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | submodules: true 20 | 21 | - name: Run clang-tidy 22 | uses: ZedThree/clang-tidy-review@v0.8.3 23 | id: review 24 | with: 25 | build_dir: build 26 | config_file: ".clang-tidy" 27 | # Googletest triggers a _lot_ of clang-tidy warnings, so ignore all 28 | # the unit tests until they're fixed or ignored upstream 29 | exclude: "tests/unit/*cxx" 30 | cmake_command: | 31 | cmake . -B build -Dbrynet_BUILD_TESTS=ON \ 32 | -Dbrynet_BUILD_EXAMPLES=ON \ 33 | -DCMAKE_EXPORT_COMPILE_COMMANDS=On -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.5.1) 2 | 3 | project(brynet) 4 | 5 | if(APPLE) 6 | set(CMAKE_MACOSX_RPATH 1) 7 | endif() 8 | 9 | 10 | if(NOT APPLE) 11 | find_library(LIB_OPENSSL NAMES crypto ssl) 12 | if(LIB_OPENSSL) 13 | add_definitions(-DBRYNET_USE_OPENSSL) 14 | SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-as-needed -ldl -lrt -lssl -lcrypto") 15 | elseif() 16 | message("not found openssl") 17 | endif() 18 | endif() 19 | 20 | SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") 21 | install(DIRECTORY include/brynet 22 | DESTINATION "${INCLUDE_INSTALL_DIR}") 23 | 24 | option(brynet_BUILD_EXAMPLES "Build examples" ON) 25 | option(brynet_BUILD_TESTS "Build test" ON) 26 | 27 | if (brynet_BUILD_EXAMPLES) 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 29 | if(UNIX) 30 | if(NOT CMAKE_BUILD_TYPE) 31 | set(CMAKE_BUILD_TYPE "Release") 32 | endif() 33 | SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -ggdb -Wall -Wextra -D_DEBUG") 34 | SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -g -ggdb -Wall -Wextra -DNDEBUG") 35 | endif() 36 | 37 | add_subdirectory(examples) 38 | endif(brynet_BUILD_EXAMPLES) 39 | 40 | if (brynet_BUILD_TESTS) 41 | if(WIN32) 42 | if (MSVC_VERSION VERSION_GREATER 1900) 43 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++latest") 44 | add_subdirectory(tests) 45 | endif() 46 | elseif(UNIX) 47 | if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0) 48 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 49 | else() 50 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 51 | endif() 52 | add_subdirectory(tests) 53 | endif() 54 | endif(brynet_BUILD_TESTS) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 IronsDu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Brynet 2 | ======= 3 | Header Only Cross platform high performance TCP network library using C++ 11. 4 | 5 | [![996.icu](https://img.shields.io/badge/link-996.icu-red.svg)](https://996.icu) 6 | [![LICENSE](https://img.shields.io/badge/license-NPL%20(The%20996%20Prohibited%20License)-blue.svg)](https://github.com/996icu/996.ICU/blob/master/LICENSE) 7 | [![Platform](https://img.shields.io/badge/Platform-Linux,%20Windows,%20MacOS-green.svg?style=flat-square)](https://github.com/IronsDu/brynet) 8 | 9 | ## Build status 10 | [![build-and-test](https://github.com/IronsDu/brynet/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/IronsDu/brynet/actions/workflows/build-and-test.yml) 11 | 12 | ## Features 13 | * Header only 14 | * Cross platform (Linux | Windows | MacOS) 15 | * High performance and safety use. 16 | * None depend 17 | * Multi-threaded 18 | * SSL support 19 | * Support HTTP、HTTPS、WebSocket 20 | * IPv6 support 21 | 22 | ## Documentation 23 | - [简体中文](https://github.com/IronsDu/brynet/blob/master/docs/main.zh-cn.md) 24 | 25 | ## Compatibility 26 | * Visual C++ 2013+ (32/64-bit) 27 | * GCC 4.8+ (32/64-bit) 28 | * Clang (Supported C++ 11) 29 | 30 | ## Macro 31 | * BRYNET_VERSION 32 | * BRYNET_USE_OPENSSL 33 | 34 | 35 | ## Build Example 36 | 1. `cmake . -Dbrynet_BUILD_EXAMPLES=ON -Dbrynet_BUILD_TESTS=ON` 37 | 2. If you use Windows, please open brynet.sln then build. If on Linux or MacOS, only enter `make`. 38 | 39 | ## Only Install 40 | 1. `cmake .` 41 | 2. `sudo make install` 42 | 43 | ## Usages 44 | * [Examples](#examples) 45 | * [Users](#users) 46 | 47 | ## Benchmark 48 | Under localhost, use CentOS 6.5 virtual mahcine(host machine is Win10 i5) 49 | * PingPong 50 | 51 | Benchamrk's server and client both only use one thread, and packet size is 4k 52 | 53 | ![PingPong](image/pingpong.png "PingPong") 54 | 55 | * Broadcast 56 | 57 | Server use two network threads and one logic thread, client use one network(also process logic) thread. every packet size is 46 bytes. 58 | every packet contain client's id. 59 | server broadcast packet to all client when recv one packet from any client. 60 | client send one packet when recv packet from server and packet's id equal self. 61 | 62 | ![Broadcast](image/broadcast.png "Broadcast") 63 | 64 | * Ab HTTP(1 network thread) 65 | ``` 66 | Server Hostname: 127.0.0.1 67 | Server Port: 9999 68 | 69 | Document Path: /abc/de?a=1 70 | Document Length: 25 bytes 71 | 72 | Concurrency Level: 100 73 | Time taken for tests: 17.734 seconds 74 | Complete requests: 500000 75 | Failed requests: 0 76 | Total transferred: 41000000 bytes 77 | HTML transferred: 12500000 bytes 78 | Requests per second: 28194.36 [#/sec] (mean) 79 | Time per request: 3.547 [ms] (mean) 80 | Time per request: 0.035 [ms] (mean, across all concurrent requests) 81 | Transfer rate: 2257.75 [Kbytes/sec] received 82 | 83 | Connection Times (ms) 84 | min mean[+/-sd] median max 85 | Connect: 0 2 0.2 2 3 86 | Processing: 1 2 0.3 2 7 87 | Waiting: 0 1 0.4 1 6 88 | Total: 2 4 0.2 4 7 89 | 90 | Percentage of the requests served within a certain time (ms) 91 | 50% 4 92 | 66% 4 93 | 75% 4 94 | 80% 4 95 | 90% 4 96 | 95% 4 97 | 98% 4 98 | 99% 4 99 | 100% 7 (longest request) 100 | ``` 101 | 102 | Examples 103 | ---------------------------- 104 | * [PingPongServer](https://github.com/IronsDu/dodo/blob/master/examples/PingPongServer.cpp) 105 | * [PingPongClient](https://github.com/IronsDu/dodo/blob/master/examples/PingPongClient.cpp) 106 | * [BroadCastServer](https://github.com/IronsDu/dodo/blob/master/examples/BroadCastServer.cpp) 107 | * [BroadCastClient](https://github.com/IronsDu/dodo/blob/master/examples/BroadCastClient.cpp) 108 | * [Http&Websocket Server](https://github.com/IronsDu/dodo/blob/master/examples/HttpServer.cpp) 109 | * [HttpServerClient](https://github.com/IronsDu/dodo/blob/master/examples/HttpClient.cpp) 110 | * [BenchWebsocket](https://github.com/IronsDu/dodo/blob/master/examples/BenchWebsocket.cpp) benchmark websocket client 111 | * [PromiseReceive](https://github.com/IronsDu/brynet/blob/master/examples/PromiseReceive.cpp) use the promise style receive http response 112 | * [WebSocketProxy](https://github.com/IronsDu/dodo/blob/master/examples/WebBinaryProxy.cpp) one proxy server between websocket client and binary protocol server 113 | * more examples please see [examples](https://github.com/IronsDu/dodo/tree/master/examples); 114 | 115 | Users 116 | ---------------------------- 117 | * [Redis proxy](https://github.com/IronsDu/DBProxy) 118 | * [gayrpc](https://github.com/IronsDu/gayrpc) 119 | * [gaylord - distributed virtual actor framework](https://github.com/IronsDu/gaylord) 120 | * [Joynet - Lua network library](https://github.com/IronsDu/Joynet) 121 | * [grpc-gateway](https://github.com/IronsDu/grpc-gateway) 122 | -------------------------------------------------------------------------------- /docs/connector.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `AsyncConnector`是一个异步创建外部连接的类.源代码见:[AsyncConnector.hpp](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/AsyncConnector.hpp). 3 | 4 | # 接口 5 | 6 | - `AsyncConnector::Create` 7 | 8 | 9 | 必须(只能)使用此静态方法创建`AsyncConnector::Ptr`智能指针对象,以用于后续使用. 10 | 11 | - `AsyncConnector::startWorkerThread(void)` 12 | 13 | (线程安全)使用此成员函数即可开启工作线程(在开启的工作线程里负责异步链接) 14 | 15 | - `AsyncConnector::stopWorkerThread(void)` 16 | 17 | (线程安全)停止工作线程(当工作线程结束时此函数才返回) 18 | 19 | - `AsyncConnector::asyncConnect(const std::vector& options)` 20 | 21 | (线程安全)请求创建外部链接,options为异步连接的选项,比如`ConnectOption::WithAddr`为指定服务器地址,`ConnectOption::WithCompletedCallback`则指定完成回调
22 | 如果没有开启工作线程,那么此函数会产生异常! 23 | 24 | ## 示例 25 | ```C++ 26 | auto connector = AsyncConnector::Create(); 27 | connector->startWorkerThread(); 28 | 29 | // set timeout is 1s 30 | connector->asyncConnect({ 31 | ConnectOption::WithAddr("127.0.0.1", 9999), 32 | ConnectOption::WithTimeout(std::chrono::seconds(1)), 33 | ConnectOption::WithCompletedCallback([](TcpSocket::Ptr socket) { 34 | std::cout << "connect success" << std::endl; 35 | // 在此我们就可以将 socket 用于网络库的其他部分,比如用于`TCPService::addTcpConnection` 36 | }), 37 | ConnectOption::WithFailedCallback([]() { 38 | std::cout << "connect failed" << std::endl; 39 | }) 40 | }); 41 | 42 | // wait 2s for completed 43 | std::this_thread::sleep_for(2s); 44 | 45 | connector->stopWorkerThread(); 46 | ``` 47 | 48 | # 注意事项 49 | - 如果没有开启工作线程或者关闭了工作线程,那么 `asyncConnect` 接口中传入的两个回调均不会被执行,并且会产生异常. 50 | 因此确保不要随意在服务工作时调用`stopWorkerThread` 51 | - 如果给`asyncConnect` 接口传入的选项不同时包含`WithCompletedCallback`和`WithFailedCallback`,会产生异常,因为我们不可能使用asyncConnect时 52 | 既不关心成功也不关心失败。 53 | -------------------------------------------------------------------------------- /docs/eventloop.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `EventLoop`用于socket的读写检测,以及线程间通信. 3 | 4 | 源码见:[EventLoop.hpp](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/EventLoop.hpp), 5 | `EventLoop`不是必须使用智能指针,可以使用值对象,当然它也是禁止拷贝的。 6 | 7 | # 接口 8 | - `EventLoop::loop(int64_t milliseconds)` 9 | 10 | 进行一次轮询,milliseconds为超时时间(毫秒).
11 | 也即当没有任何外部事件产生时,此函数等待milliseconds毫秒后返回.
12 | 当有事件产生时,做完工作后即可返回,所需时间依负荷而定.
13 | 通常,我们会开启一个线程,在其中间断性的调用`loop`接口。 14 | 15 | - `EventLoop::bindCurrentThread` 16 | 17 | 初始化调度EventLoop的thread id,用于使用EventLoop接口时时判断当前线程是否处于EventLoop::loop所在线程。在使用EventLoop的一些函数,比如runAsyncFunctor时,都需要执行bindCurrentThread(当然,你也可以直接调用loop来初始化) 18 | 19 | - `EventLoop::wakeup(void)` 20 | 21 | (线程安全)唤醒可能阻塞在`EventLoop::loop`中的等待。
22 | (当然,当EventLoop没有处于等待时,wakeup不会做任何事情,即没有额外开销)
23 | 另外! 此函数永远成功。其返回值仅仅表示函数内部是否真正触发了唤醒所需的函数。 24 | 25 | - `EventLoop::runAsyncFunctor(std::function)` 26 | 27 | (线程安全)投递一个异步函数给`EventLoop`,此函数会在`EventLoop::loop`调用中被执行。如果此时EventLoop还没有初始化thread id(通过bindCurrentThread或loop),那么此函数会抛出异常,避免用户忘记调度IO工作线程时就投递了任务而导致逻辑错误。 28 | 29 | - `EventLoop::runFunctorAfterLoop(std::function)` 30 | 31 | 在`EventLoop::loop`所在线程中投递一个延迟函数,此函数会在`loop`接口中的末尾(也即函数返回之前)时被调用。
32 | 此函数只能在io线程(也就是调用loop函数的所在线程)使用,如果在其他线程中调用此函数会产生异常。 33 | 34 | - `EventLoop::isInLoopThread(void)` 35 | 36 | (线程安全)检测当前线程是否和 `EventLoop::loop`所在线程(也就是最先调用`loop`接口的线程)一样。 37 | 38 | - `brynet::base::Timer::WeakPtr EventLoop::runAfter(std::chrono::nanoseconds timeout, std::function&& callback) 39 | 40 | 开启一个延迟执行的函数,当到期时会在EventLoop工作线程(即loop函数所在线程)里执行callback。可通过返回的brynet::base::Timer::WeakPtr的cancel函数来取消定时器。 41 | 42 | - `RepeatTimer::Ptr EventLoop::brynet::base::RepeatTimer::Ptr runIntervalTimer(std::chrono::nanoseconds timeout, std::function&& callback)` 43 | 44 | 开启一个重复执行的延迟执行的函数,当每次到期时会在EventLoop工作线程(即loop函数所在线程)里执行callback。可通过返回的RepeatTimer::Ptr的cancel函数来取消定时器。 45 | 46 | 47 | # 注意事项 48 | - 当我们第一次在某个线程中调用`loop`之后,就不应该在其他线程中调用`loop`(当然如果你调用了,也没有任何效果/影响) 49 | - 如果没有任何线程调用`loop`,那么使用`pushAsyncProc`投递的异步函数将不会被执行,直到有线程调用了`loop`接口,这些异步函数才会被执行。 50 | 51 | # 示例 52 | ```C++ 53 | EventLoop ev; 54 | ev.runAsyncFunctor([] { 55 | std::cout << "hello world" << std::endl; 56 | }); 57 | ev.loop(1); 58 | 59 | // output hello world 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/listen_thread.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `ListenThread `是一个用于接受外部连接的类.源代码见:[ListenThread.hpp](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/ListenThread.hpp). 3 | 4 | # 接口 5 | 6 | - `ListenThread::Create(bool isIPV6, const std::string& ip, int port, const AccepCallback& callback, const std::vector& process = {})` 7 | 8 | 创建`ListenThread::Ptr`智能指针对象,此对象用于后续工作. `callback`是连接进入的回调,在其中使用传进的`TcpSocket::Ptr`,当`callback`为nullptr时会产生异常。
9 | `process `也是连接进入时的回调,但它的参数类型是`TcpSocket&`,主要用于一些socket选项设置。 10 | 11 | - `ListenThread::startThread()` 12 | 13 | (线程安全)此成员函数用于开启工作/监听线程,如果`startThread`失败(比如监听失败)会产生异常。 14 | 15 | - `ListenThread::stopListen(void)` 16 | 17 | (线程安全)此成员函数用于停止工作线程,这个函数需要等待监听线程完全结束才会返回. 18 | 19 | ## 示例 20 | ```C++ 21 | auto listenThread = ListenThread::Create(false, 22 | "0.0.0.0", 23 | 9999, 24 | [](TcpSocket::Ptr socket) { 25 | std::cout << "accepted connection" << std::endl; 26 | // 在此我们就可以将 socket 用于网络库的其他部分,比如用于`TCPService` 27 | }, 28 | { 29 | [](TcpSocket& socket) { 30 | socket.setNodelay(); // and do some setting. 31 | } 32 | }); 33 | listenThread->startThread(); 34 | 35 | // wait 2s 36 | std::this_thread::sleep_for(2s); 37 | 38 | listenThread->stopListen(); 39 | ``` 40 | 41 | # 注意事项 42 | - 请小心`ListenThread::startThread`产生异常 43 | -------------------------------------------------------------------------------- /docs/main.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `brynet` 是一个多线程的异步网络库,能够运行在Linux和Windows环境下。 3 | 仅仅需要C++ 11编译器,且没有其他任何第三方依赖。 4 | 5 | # 架构 6 | `brynet` 核心分为以下几个部分 7 | ## 事件层 8 | 由`EventLoop`提供,用于检测socket的可读可写,并包含一个事件通知,可用于线程间通信(消息队列). 9 | 详情见[eventloop](https://github.com/IronsDu/brynet/blob/master/docs/eventloop.zh-cn.md) 10 | 11 | ## 网络连接 12 | 由`ListenThread`和`AsyncConnector`提供,前者用于接收外部的链接请求,后者用于向外部进行网络连接. 13 | 详情见[listen_thread](https://github.com/IronsDu/brynet/blob/master/docs/listen_thread.zh-cn.md)和[connector](https://github.com/IronsDu/brynet/blob/master/docs/connector.zh-cn.md),使用这两个组件可以让我们得到`TcpSocket::Ptr`和`TcpConnection::Ptr`,`TcpConnection::Ptr`是我们最会频繁使用的类型。 14 | 15 | ## 安全的Socket对象 16 | `brynet`不对用户暴露原始的socket fd,而是提供`TcpSocket::Ptr`,详见[Socket](https://github.com/IronsDu/brynet/blob/master/docs/socket.zh-cn.md) 17 | 18 | ## 安全的TcpConnection连接对象 19 | 当然用户也一般不直接使用`TcpSocket::Ptr`,而是使用`TcpConnection::Ptr`,详见[TcpConnection](https://github.com/IronsDu/brynet/blob/master/docs/tcp_connection.zh-cn.md) 20 | 21 | ## 高级特性 22 | - 提供 `promise receive` 方便解析网络消息,详见:[promise_receive](https://github.com/IronsDu/brynet/blob/master/docs/promise_receive.zh-cn.md) 23 | 24 | # 效率优化 25 | 详情见[merge_send](https://github.com/IronsDu/brynet/blob/master/docs/merge_send.zh-cn.md) 26 | 27 | # 使用SSL 28 | 请确保你已经安装了openssl,然后在编译`brynet`时定义`USE_OPENSSL`宏即可。 29 | 30 | # 完整示例 31 | 请查看[example](https://github.com/IronsDu/brynet/tree/master/examples) 32 | 33 | # 建议 34 | - 建议使用[wrapper](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/wrapper)目录下的包装代码来使用brynet,它会让您使用得更便捷,提高您的开发效率。在[examples](https://github.com/IronsDu/brynet/tree/master/examples)目录里有一些示例就使用了它,您可以参考一下。 35 | - 你完全可以使用(链接)官方的http_parser,将其路径添加到工程的包含路径即可(则会自动替代brynet自带的http_parser.h) 36 | 37 | # 应用项目 38 | * [ARK - distributed plugin framework](https://github.com/ArkNX/ARK) 39 | * [Redis proxy](https://github.com/IronsDu/DBProxy) 40 | * [gayrpc](https://github.com/IronsDu/gayrpc) 41 | * [gaylord - distributed virtual actor framework](https://github.com/IronsDu/gaylord) 42 | * [Joynet - Lua network library](https://github.com/IronsDu/Joynet) 43 | * [grpc-gateway](https://github.com/IronsDu/grpc-gateway) -------------------------------------------------------------------------------- /docs/merge_send.zh-cn.md: -------------------------------------------------------------------------------- 1 | `brynet`不会对每一个消息都进行`send`系统调用. 2 | 3 | 当上层需要发送消息时,是投递一个`std::function`投递到`EventLoop`中。 4 | 请打开[TcpConnection.h](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/TcpConnection.hpp) 5 | 6 | 在`std::function`执行时会将消息放入`TcpConnection`的待发送队列中,见`TcpConnection::send`函数。 7 | 8 | 在此异步回调函数中又会继续投递(有且仅有)一个延迟函数`TcpConnection::runAfterFlush`,延迟函数均在`EventLoop::loop`调用结束之前被调用。 9 | 10 | 这个延迟函数就负责将待发送队列里的数据尝试一次性批量发送(Linux下使用writev,Windwos下回进行合并),见`TcpConnection::quickFlush`和`TcpConnection::normalFlush`。 -------------------------------------------------------------------------------- /docs/promise_receive.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `PromiseReceive`可以提供更高级更方便的消息解析.源代码见:[PromiseReceive.h](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/PromiseReceive.hpp).
3 | 使用此辅助类,可以更方便的解析`HTTP`、`Redis`等文本协议 4 | 5 | # 接口 6 | 7 | - `brynet::net::setupPromiseReceive(const TCPSession::Ptr& session)` 8 | 9 | 首先使用此静态方法创建`PromiseReceive::Ptr`智能指针对象,接管 `session`的数据解析.
10 | 因此使用者一定不要再为`TCPSession::Ptr`调用`setDataCallback`接口设置数据回调! 11 | 12 | - `PromiseReceive::receive(size_t len, Handle handle)` 13 | 14 | (非线程安全)投递一个异步读取,当满足长度>=len时,调用`handle`
15 | `Handle`类型为:`std::function`,当`handle`返回值为`true`表示重复投递此`receive`请求(待下次满足条件时会继续调用`handle`) 16 | 17 | - `PromiseReceive::receive(std::shared_ptr len, Handle handle)` 18 | 19 | (非线程安全)重载版本,提供智能指针作为参数,此值delay决议,用于调用`receive`时不确定长度的情况。 20 | 21 | - `PromiseReceive::receiveUntil(std::string str, Handle handle)` 22 | 23 | (非线程安全)当收到的数据中包含`str`时,回调`handle`。 24 | 25 | ## 示例 26 | ```C++ 27 | auto enterCallback = [](const TcpConnection::Ptr& session) { 28 | auto promiseReceive = setupPromiseReceive(session); 29 | auto contentLength = std::make_shared(); 30 | 31 | promiseReceive 32 | ->receiveUntil("\r\n", [](const char* buffer, size_t len) { 33 | auto headline = std::string(buffer, len); 34 | std::cout << headline << std::endl; 35 | return false; 36 | }) 37 | ->receiveUntil("\r\n", [promiseReceive, contentLength](const char* buffer, size_t len) { 38 | auto headerValue = std::string(buffer, len); 39 | std::cout << headerValue << std::endl; 40 | if (len > 2) 41 | { 42 | const static std::string ContentLenghtFlag = "Content-Length: "; 43 | auto pos = headerValue.find(ContentLenghtFlag); 44 | if (pos != std::string::npos) 45 | { 46 | auto lenStr = headerValue.substr(pos + ContentLenghtFlag.size(), headerValue.size()); 47 | *contentLength = std::stoi(lenStr); 48 | } 49 | return true; 50 | } 51 | return false; 52 | })->receive(contentLength, [session](const char* buffer, size_t len) { 53 | HttpResponse response; 54 | response.setStatus(HttpResponse::HTTP_RESPONSE_STATUS::OK); 55 | response.setContentType("text/html; charset=utf-8"); 56 | response.setBody("hello world "); 57 | 58 | auto result = response.getResult(); 59 | session->send(result.c_str(), result.size()); 60 | session->postShutdown(); 61 | 62 | return false; 63 | }); 64 | }; 65 | ``` 66 | 67 | # 注意事项 68 | - `PromiseReceive`的接口都不是线程安全的,请务必小心,确保只在一个线程中调用相关接口。 69 | - 务必注意`receive`和`receiveUntil`中的回调参数的返回值(false/true)。 -------------------------------------------------------------------------------- /docs/socket.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `TcpSocket::Ptr`和`ListenSocket::Ptr`是对socket fd的封装,结合`std::unique_ptr`使得fd资源更安全,避免泄露.源代码见:[Socket.h](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/Socket.hpp). 3 | 4 | # 接口 5 | 6 | - `TcpSocket::Create(sock fd, bool serverSide)` 7 | 8 | 必须(只能)使用此静态方法创建`TcpSocket::Ptr`智能指针对象,用于后续工作.`serverSide`表示是否服务端socket,如果是accept返回的fd,那么`serverSide`则为true, 9 | 如果是`connect`返回的fd,则为false。 10 | 11 | - `TcpSocket::isServerSide(void)` 12 | 13 | 检测`socket`是否为服务端server(即为accept返回的),如果为false,则表示是客户端socket(即为connect返回的fd)。 14 | 15 | - `TcpSocket::setNodelay(void)` 16 | 17 | 设置`TCP_NODELAY` 18 | 19 | - `TcpSocket::setNonblock(void)` 20 | 21 | 设置非阻塞 22 | 23 | - `TcpSocket::setSendSize(int size)` 24 | 25 | 设置`SO_SNDBUF`发送缓冲区大小(通常不建议修改) 26 | 27 | - `TcpSocket::setRecvSize(int size)` 28 | 29 | 设置`SO_RCVBUF`发送缓冲区大小(通常不建议修改) 30 | 31 | - `TcpSocket::getRemoteIP(void)` 32 | 33 | 获取socket的远端IP地址 34 | 35 | 36 | ## 示例 37 | 无。因为通常`brynet`的使用者不需要自己通过fd来创建`TcpSocket`对象,而是直接使用`Connector`和`ListenThread`所产生的`TcpSocket::Ptr`即可. 38 | -------------------------------------------------------------------------------- /docs/tcp_connection.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `TcpConnection::Ptr`是对TCP连接的封装.源代码见:[TcpConnection.h](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/TcpConnection.hpp). 3 | 4 | # 接口 5 | 6 | - `TcpConnection::Create(TcpSocket::Ptr, size_t maxRecvBufferSize, EnterCallback&&, EventLoop::Ptr)` 7 | 8 | 9 | 必须(只能)使用此静态方法创建`TcpConnection::Ptr`智能指针对象,用于后续工作.`maxRecvBufferSize`表示最大接收缓冲区(它一定要不小于逻辑层单个最大包的长度!)。 10 | 11 | - `TcpConnection::send(const char* buffer, size_t len, PacketSendedCallback&& callback = nullptr)` 12 | 13 | 发送消息.如果`callback`不为nullptr,那么当io线程将此消息全部"发送完成"(投递到内核TCP缓冲区)时会调用callback。 14 | 15 | - `TcpConnection::send(const SendableMsg::Ptr& msg, PacketSendedCallback&& callback)` 16 | 17 | 发送消息,用户可以继承SendableMsg,重写`data`和`size`函数,且最重要的是:能实现自己的消息内存管理器。 18 | 19 | - `Connection::setDataCallback(std::function callback)` 20 | 21 | 设置处理接受消息的回调函数。等下次接收到对端发送的数据时,会再传递给此回调函数。 22 | -------------------------------------------------------------------------------- /docs/tcp_service.zh-cn.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | `ITCPService`是`brynet`中对`TcpConnection`和`EventLoop`的封装抽象类型。它的派生类分为两个:`IOThreadTcpService`和`EventLoopTcpService`。 3 | 4 | 5 | `IOThreadTcpService`开启额外的IO工作线程,并使用其独立的EventLoop处理网络。 6 | 7 | `EventLoopTcpService`则不会开启额外的线程,而是直接使用用户设定的EventLoop来处理网络,其线程就是调用`loop`函数来调度此EventLoop的线程, 8 | 它通常可以用于不需要极致性能的场景,比如直接在主线程处理IO。 9 | 10 | 源代码见:[TCPService.h](https://github.com/IronsDu/brynet/blob/master/include/brynet/net/TCPService.hpp).
11 | 12 | # 接口 13 | 14 | - `ITcpService::addTcpConnection(TcpSocket::Ptr socket, Options...)` 15 | 16 | 这是ITcpService抽象类的接口,其用途是将一个TcpConnection交给具体的ITcpService管理,其中Options请查阅`AddSocketOption的WithXXX系列函数`。 17 | 18 | - `IOThreadTcpService::Create(void)` 19 | 20 | 21 | 此静态函数用于创建网络服务对象,且只能使用此接口创建`ITcpService`对象。
22 | 用户通过服务对象操作网络会话。 23 | 24 | - `IOThreadTcpService::startWorkerThread(size_t threadNum, FRAME_CALLBACK callback = nullptr)` 25 | 26 | (线程安全)开启IO工作线程,每一个工作线程有一个`EventLoop`负责事件检测。 27 | 28 | 29 | - `IOThreadTcpService::stopWorkerThread()` 30 | 31 | (线程安全)关闭IO工作线程。 32 | 33 | 34 | - `EventLoopTcpService::Create(EventLoop::Ptr eventLoop)` 35 | 36 | 根据用户指定的EventLoop构造一个单线程TcpService管理器。 37 | 38 | 39 | 40 | ## 示例 41 | ```C++ 42 | auto service = IOThreadTcpService::Create(); 43 | // use blocking connect for test 44 | auto fd = ox_socket_connect(false, ip, port); 45 | auto socket = TcpSocket::Create(fd, false); 46 | 47 | service->addTcpConnection(socket, AddSocketOption::WithMaxRecvBufferSize(1024*1024)); 48 | 49 | std::this_thread::sleep_for(2s); 50 | ``` 51 | -------------------------------------------------------------------------------- /examples/BenchWebsocket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace brynet; 15 | using namespace brynet::net; 16 | using namespace brynet::net::http; 17 | using namespace brynet::base; 18 | 19 | std::atomic count; 20 | 21 | static void sendPacket(const HttpSession::Ptr& session, const char* data, size_t len) 22 | { 23 | char buff[1024]; 24 | BasePacketWriter bw(buff, sizeof(buff), true, true); 25 | bw.writeINT8('{'); 26 | bw.writeBuffer("\"data\":\"", 8); 27 | bw.writeBuffer(data, len); 28 | bw.writeINT8('"'); 29 | bw.writeINT8('}'); 30 | 31 | std::string frame; 32 | WebSocketFormat::wsFrameBuild(bw.getData(), 33 | bw.getPos(), 34 | frame, 35 | WebSocketFormat::WebSocketFrameType::TEXT_FRAME, 36 | true, 37 | true); 38 | session->send(std::move(frame)); 39 | } 40 | 41 | int main(int argc, char** argv) 42 | { 43 | if (argc < 3) 44 | { 45 | std::cout << "./benchwebsocket host port [ connections workers ]"; 46 | return 1; 47 | } 48 | 49 | const char* host = argv[1]; 50 | int port = std::atoi(argv[2]); 51 | int connections = argc > 3 ? std::atoi(argv[3]) : 200; 52 | size_t workers = argc > 4 ? std::atoi(argv[4]) : std::thread::hardware_concurrency(); 53 | 54 | std::cout << "host: " << host << ':' << port << " | connections: " << connections << " | workers: " << workers << std::endl; 55 | 56 | auto enterCallback = [host](const HttpSession::Ptr& httpSession, HttpSessionHandlers& handlers) { 57 | HttpRequest request; 58 | request.setMethod(HttpRequest::HTTP_METHOD::HTTP_METHOD_GET); 59 | request.setUrl("/ws"); 60 | request.addHeadValue("Host", host); 61 | request.addHeadValue("Upgrade", "websocket"); 62 | request.addHeadValue("Connection", "Upgrade"); 63 | request.addHeadValue("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ=="); 64 | request.addHeadValue("Sec-WebSocket-Version", "13"); 65 | 66 | std::string requestStr = request.getResult(); 67 | httpSession->send(requestStr.c_str(), requestStr.size()); 68 | 69 | handlers.setWSConnected([](const HttpSession::Ptr& session, const HTTPParser&) { 70 | for (int i = 0; i < 200; i++) 71 | { 72 | sendPacket(session, "hello, world!", 13); 73 | } 74 | }); 75 | 76 | handlers.setWSCallback([](const HttpSession::Ptr& session, 77 | WebSocketFormat::WebSocketFrameType, const std::string& payload) { 78 | std::cout << payload << std::endl; 79 | sendPacket(session, "hello, world!", 13); 80 | count += 1; 81 | }); 82 | }; 83 | 84 | auto service = IOThreadTcpService::Create(); 85 | service->startWorkerThread(workers); 86 | 87 | auto connector = AsyncConnector::Create(); 88 | connector->startWorkerThread(); 89 | 90 | wrapper::HttpConnectionBuilder connectionBuilder; 91 | connectionBuilder.WithService(service) 92 | .WithConnector(connector) 93 | .WithMaxRecvBufferSize(1024 * 1024); 94 | 95 | for (int i = 0; i < connections; i++) 96 | { 97 | connectionBuilder 98 | .WithAddr(host, port) 99 | .WithTimeout(std::chrono::seconds(10)) 100 | .AddSocketProcessCallback([](TcpSocket& socket) { 101 | socket.setNodelay(); 102 | }) 103 | .WithEnterCallback(enterCallback) 104 | .asyncConnect(); 105 | } 106 | 107 | brynet::net::EventLoop mainLoop; 108 | while (true) 109 | { 110 | mainLoop.loop(5000); 111 | std::cout << (count / 5) << std::endl; 112 | count = 0; 113 | if (app_kbhit()) 114 | { 115 | break; 116 | } 117 | } 118 | 119 | return 0; 120 | } 121 | -------------------------------------------------------------------------------- /examples/BroadCastClient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | using namespace brynet; 15 | using namespace brynet::net; 16 | using namespace brynet::base; 17 | 18 | atomic_llong TotalRecvPacketNum = ATOMIC_VAR_INIT(0); 19 | atomic_llong TotalRecvSize = ATOMIC_VAR_INIT(0); 20 | 21 | int main(int argc, char** argv) 22 | { 23 | if (argc != 5) 24 | { 25 | fprintf(stderr, "Usage: \n"); 26 | exit(-1); 27 | } 28 | 29 | std::string ip = argv[1]; 30 | int port = atoi(argv[2]); 31 | int clientNum = atoi(argv[3]); 32 | int packetLen = atoi(argv[4]); 33 | 34 | brynet::net::base::InitSocket(); 35 | 36 | auto clientEventLoop = std::make_shared(); 37 | clientEventLoop->bindCurrentThread(); 38 | 39 | for (int i = 0; i < clientNum; i++) 40 | { 41 | auto fd = brynet::net::base::Connect(false, ip, port); 42 | brynet::net::base::SocketSetSendSize(fd, 32 * 1024); 43 | brynet::net::base::SocketSetRecvSize(fd, 32 * 1024); 44 | brynet::net::base::SocketNodelay(fd); 45 | 46 | auto enterCallback = [packetLen](const TcpConnection::Ptr& dataSocket) { 47 | static_assert(sizeof(dataSocket.get()) <= sizeof(int64_t), "ud's size must less int64"); 48 | 49 | auto HEAD_LEN = sizeof(uint32_t) + sizeof(uint16_t); 50 | 51 | std::shared_ptr sp = std::make_shared(false); 52 | sp->writeUINT32(HEAD_LEN + sizeof(int64_t) + packetLen); 53 | sp->writeUINT16(1); 54 | sp->writeINT64((int64_t) dataSocket.get()); 55 | sp->writeBinary(std::string(packetLen, '_')); 56 | 57 | for (int i = 0; i < 1; ++i) 58 | { 59 | dataSocket->send(sp->getData(), sp->getPos()); 60 | } 61 | 62 | dataSocket->setDataCallback([dataSocket](brynet::base::BasePacketReader& reader) { 63 | while (true) 64 | { 65 | if (!reader.enough(sizeof(uint32_t))) 66 | { 67 | break; 68 | } 69 | 70 | auto buffer = reader.currentBuffer(); 71 | auto packetLen = reader.readUINT32(); 72 | if (!reader.enough(packetLen - sizeof(uint32_t))) 73 | { 74 | break; 75 | } 76 | 77 | TotalRecvSize += packetLen; 78 | TotalRecvPacketNum++; 79 | 80 | reader.readUINT16(); 81 | int64_t addr = reader.readINT64(); 82 | 83 | if (addr == reinterpret_cast(dataSocket.get())) 84 | { 85 | dataSocket->send(buffer, packetLen); 86 | } 87 | 88 | reader.addPos(packetLen - sizeof(uint32_t) - sizeof(uint16_t) - sizeof(int64_t)); 89 | reader.savePos(); 90 | } 91 | }); 92 | 93 | dataSocket->setDisConnectCallback([](const TcpConnection::Ptr& dataSocket) { 94 | (void) dataSocket; 95 | }); 96 | }; 97 | auto tcpConnection = TcpConnection::Create(TcpSocket::Create(fd, false), 98 | 1024 * 1024, 99 | enterCallback, 100 | clientEventLoop, 101 | nullptr, 102 | nullptr); 103 | } 104 | 105 | auto now = std::chrono::steady_clock::now(); 106 | while (true) 107 | { 108 | clientEventLoop->loop(10); 109 | if ((std::chrono::steady_clock::now() - now) >= std::chrono::seconds(1)) 110 | { 111 | if (TotalRecvSize / 1024 == 0) 112 | { 113 | std::cout << "total recv : " << TotalRecvSize << " bytes/s" 114 | << ", num " << TotalRecvPacketNum << endl; 115 | } 116 | else if ((TotalRecvSize / 1024) / 1024 == 0) 117 | { 118 | std::cout << "total recv : " << TotalRecvSize / 1024 << " K/s" 119 | << ", num " << TotalRecvPacketNum << endl; 120 | } 121 | else 122 | { 123 | std::cout << "total recv : " << (TotalRecvSize / 1024) / 1024 << " M/s" 124 | << ", num " << TotalRecvPacketNum << endl; 125 | } 126 | 127 | now = std::chrono::steady_clock::now(); 128 | TotalRecvSize = 0; 129 | TotalRecvPacketNum = 0; 130 | } 131 | if (app_kbhit()) 132 | { 133 | break; 134 | } 135 | } 136 | 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /examples/BroadCastServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace brynet; 16 | using namespace brynet::net; 17 | using namespace brynet::base; 18 | 19 | std::atomic_llong TotalSendLen = ATOMIC_VAR_INIT(0); 20 | std::atomic_llong TotalRecvLen = ATOMIC_VAR_INIT(0); 21 | 22 | std::atomic_llong SendPacketNum = ATOMIC_VAR_INIT(0); 23 | std::atomic_llong RecvPacketNum = ATOMIC_VAR_INIT(0); 24 | 25 | std::vector clients; 26 | IOThreadTcpService::Ptr service; 27 | 28 | static void addClientID(const TcpConnection::Ptr& session) 29 | { 30 | clients.push_back(session); 31 | } 32 | 33 | static void removeClientID(const TcpConnection::Ptr& session) 34 | { 35 | for (auto it = clients.begin(); it != clients.end(); ++it) 36 | { 37 | if (*it == session) 38 | { 39 | clients.erase(it); 40 | break; 41 | } 42 | } 43 | } 44 | 45 | static size_t getClientNum() 46 | { 47 | return clients.size(); 48 | } 49 | 50 | static void broadCastPacket(const brynet::net::SendableMsg::Ptr& packet) 51 | { 52 | auto packetLen = packet->size(); 53 | RecvPacketNum++; 54 | TotalRecvLen += packetLen; 55 | 56 | for (const auto& session : clients) 57 | { 58 | session->send(packet); 59 | } 60 | 61 | SendPacketNum += clients.size(); 62 | TotalSendLen += (clients.size() * packetLen); 63 | } 64 | 65 | int main(int argc, char** argv) 66 | { 67 | if (argc != 3) 68 | { 69 | fprintf(stderr, "Usage : \n"); 70 | exit(-1); 71 | } 72 | 73 | int port = atoi(argv[1]); 74 | int threadNum = atoi(argv[2]); 75 | brynet::net::base::InitSocket(); 76 | 77 | service = IOThreadTcpService::Create(); 78 | auto mainLoop = std::make_shared(); 79 | mainLoop->bindCurrentThread(); 80 | auto listenThread = ListenThread::Create(false, "0.0.0.0", port, [mainLoop](TcpSocket::Ptr socket) { 81 | socket->setNodelay(); 82 | socket->setSendSize(32 * 1024); 83 | socket->setRecvSize(32 * 1024); 84 | 85 | auto enterCallback = [mainLoop](const TcpConnection::Ptr& session) { 86 | mainLoop->runAsyncFunctor([session]() { 87 | addClientID(session); 88 | }); 89 | 90 | session->setDisConnectCallback([mainLoop](const TcpConnection::Ptr& session) { 91 | mainLoop->runAsyncFunctor([session]() { 92 | removeClientID(session); 93 | }); 94 | }); 95 | 96 | session->setHighWaterCallback([]() { 97 | std::cout << "high water" << std::endl; 98 | }, 99 | 1024 * 1024 * 100); 100 | 101 | session->setDataCallback([mainLoop](brynet::base::BasePacketReader& reader) { 102 | while (true) 103 | { 104 | auto buffer = reader.currentBuffer(); 105 | if (!reader.enough(sizeof(uint32_t))) 106 | { 107 | break; 108 | } 109 | 110 | auto packetLen = reader.readUINT32(); 111 | if (!reader.enough(packetLen - sizeof(uint32_t))) 112 | { 113 | break; 114 | } 115 | 116 | auto packet = brynet::net::MakeStringMsg(buffer, packetLen); 117 | mainLoop->runAsyncFunctor([packet]() { 118 | broadCastPacket(packet); 119 | }); 120 | 121 | reader.addPos(packetLen - sizeof(uint32_t)); 122 | reader.savePos(); 123 | } 124 | }); 125 | }; 126 | ConnectionOption option; 127 | option.enterCallback.emplace_back(enterCallback); 128 | option.maxRecvBufferSize = 1024 * 1024; 129 | service->addTcpConnection(std::move(socket), option); 130 | }); 131 | 132 | listenThread->startListen(); 133 | service->startWorkerThread(threadNum); 134 | 135 | auto now = std::chrono::steady_clock::now(); 136 | while (true) 137 | { 138 | mainLoop->loop(10); 139 | auto diff = std::chrono::steady_clock::now() - now; 140 | if (diff >= std::chrono::seconds(1)) 141 | { 142 | auto msDiff = std::chrono::duration_cast(diff).count(); 143 | std::cout << "cost " << msDiff << " ms, clientnum:" << getClientNum() << ", recv " << (TotalRecvLen / 1024) * 1000 / msDiff << " K/s, " 144 | << "num : " << RecvPacketNum * 1000 / msDiff << ", send " << (TotalSendLen / 1024) / 1024 * 1000 / msDiff << " M/s, " 145 | << " num: " << SendPacketNum * 1000 / msDiff << std::endl; 146 | TotalRecvLen = 0; 147 | TotalSendLen = 0; 148 | RecvPacketNum = 0; 149 | SendPacketNum = 0; 150 | now = std::chrono::steady_clock::now(); 151 | } 152 | 153 | if (app_kbhit()) 154 | { 155 | break; 156 | } 157 | } 158 | 159 | service->stopWorkerThread(); 160 | 161 | return 0; 162 | } 163 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories("${PROJECT_SOURCE_DIR}/include/") 2 | 3 | FILE(GLOB_RECURSE HEADER_LIST 4 | *.h 5 | *.hpp 6 | ../include/brynet/base/*.h 7 | ../include/brynet/base/*.hpp 8 | ../include/brynet/base/endian/*.h 9 | ../include/brynet/base/endian/*.hpp 10 | ../include/brynet/base/crypto/*.h 11 | ../include/brynet/base/crypto/*.hpp 12 | ../include/brynet/net/*.h 13 | ../include/brynet/net/*.hpp 14 | ../include/brynet/net/detail/*.h 15 | ../include/brynet/net/detail/*.hpp 16 | ../include/brynet/net/http/*.h 17 | ../include/brynet/net/http/*.hpp 18 | ../include/brynet/net/port/*.h 19 | ../include/brynet/net/port/*.hpp 20 | ../include/brynet/net/wrapper/*.h 21 | ../include/brynet/net/wrapper/*.hpp ) 22 | 23 | add_executable(pingpongserver PingPongServer.cpp ${HEADER_LIST}) 24 | if(WIN32) 25 | target_link_libraries(pingpongserver ws2_32) 26 | elseif(UNIX) 27 | find_package(Threads REQUIRED) 28 | target_link_libraries(pingpongserver pthread) 29 | endif() 30 | 31 | add_executable(singlethread-pingpongserver SingleThreadPingPongServer.cpp ${HEADER_LIST}) 32 | if(WIN32) 33 | target_link_libraries(singlethread-pingpongserver ws2_32) 34 | elseif(UNIX) 35 | find_package(Threads REQUIRED) 36 | target_link_libraries(singlethread-pingpongserver pthread) 37 | endif() 38 | 39 | add_executable(pingpongclient PingPongClient.cpp) 40 | if(WIN32) 41 | target_link_libraries(pingpongclient ws2_32) 42 | elseif(UNIX) 43 | find_package(Threads REQUIRED) 44 | target_link_libraries(pingpongclient pthread) 45 | endif() 46 | 47 | add_executable(broadcastserver BroadCastServer.cpp) 48 | if(WIN32) 49 | target_link_libraries(broadcastserver ws2_32) 50 | elseif(UNIX) 51 | find_package(Threads REQUIRED) 52 | target_link_libraries(broadcastserver pthread) 53 | endif() 54 | 55 | add_executable(broadcastclient BroadCastClient.cpp) 56 | if(WIN32) 57 | target_link_libraries(broadcastclient ws2_32) 58 | elseif(UNIX) 59 | find_package(Threads REQUIRED) 60 | target_link_libraries(broadcastclient pthread) 61 | endif() 62 | 63 | add_executable(httpserver HttpServer.cpp) 64 | if(WIN32) 65 | target_link_libraries(httpserver ws2_32) 66 | elseif(UNIX) 67 | find_package(Threads REQUIRED) 68 | target_link_libraries(httpserver pthread) 69 | if(LIB_OPENSSL) 70 | target_link_libraries(httpserver ssl crypto) 71 | endif() 72 | endif() 73 | 74 | add_executable(httpclient HttpClient.cpp) 75 | if(WIN32) 76 | target_link_libraries(httpclient ws2_32) 77 | elseif(UNIX) 78 | find_package(Threads REQUIRED) 79 | target_link_libraries(httpclient pthread) 80 | if(LIB_OPENSSL) 81 | target_link_libraries(httpclient ssl crypto) 82 | endif() 83 | endif() 84 | 85 | add_executable(benchwebsocket BenchWebsocket.cpp) 86 | if(WIN32) 87 | target_link_libraries(benchwebsocket ws2_32) 88 | elseif(UNIX) 89 | find_package(Threads REQUIRED) 90 | target_link_libraries(benchwebsocket pthread) 91 | endif() 92 | 93 | add_executable(promisereceive PromiseReceive.cpp) 94 | if(WIN32) 95 | target_link_libraries(promisereceive ws2_32) 96 | elseif(UNIX) 97 | find_package(Threads REQUIRED) 98 | target_link_libraries(promisereceive pthread) 99 | endif() 100 | 101 | add_executable(webbinaryproxy WebBinaryProxy.cpp) 102 | if(WIN32) 103 | target_link_libraries(webbinaryproxy ws2_32) 104 | elseif(UNIX) 105 | find_package(Threads REQUIRED) 106 | target_link_libraries(webbinaryproxy pthread) 107 | endif() -------------------------------------------------------------------------------- /examples/HttpClient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace brynet; 12 | using namespace brynet::net; 13 | using namespace brynet::net::http; 14 | 15 | static std::atomic_llong counter = ATOMIC_VAR_INIT(0); 16 | 17 | int main(int argc, char** argv) 18 | { 19 | (void) argc; 20 | (void) argv; 21 | 22 | auto service = IOThreadTcpService::Create(); 23 | service->startWorkerThread(2); 24 | 25 | auto connector = brynet::net::AsyncConnector::Create(); 26 | connector->startWorkerThread(); 27 | 28 | HttpRequest request; 29 | request.setMethod(HttpRequest::HTTP_METHOD::HTTP_METHOD_GET); 30 | request.setUrl("/"); 31 | request.addHeadValue("Host", "www.baidu.com"); 32 | 33 | HttpQueryParameter p; 34 | p.add("key", "DCD9C36F1F54A96F707DFBE833600167"); 35 | p.add("appid", "929390"); 36 | p.add("ticket", 37 | "140000006FC57764C95D45085373F104" 38 | "01001001359F745C1800000001000000020000009" 39 | "DACD3DE1202A8C0431E100003000000B200000032" 40 | "000000040000005373F104010010016E2E0E009D" 41 | "ACD3DE1202A8C000000000AAA16F5C2A518B5C" 42 | "0100FC96040000000000061129B849B0397DD" 43 | "62E0B1B0373451EC08E1BAB70FC18E21094F" 44 | "C5F4674EDD50226ABB33D71C601B8E65542F" 45 | "B9A9F48BFF87AC30904D272FAD5F15CD2D5428" 46 | "D44827BA58A45886119D6244D672A0C1909C5D" 47 | "7BD9096D96EB8BAC30E006BE6D405E5B25659" 48 | "CF3D343C9627078C5FD4CE0120D80DDB2FA09E76111143F132CA0B"); 49 | request.setQuery(p.getResult()); 50 | 51 | std::string requestStr = request.getResult(); 52 | 53 | for (size_t i = 0; i < 1; i++) 54 | { 55 | // ConnectOption::WithAddr("23.73.140.64", 80), 56 | wrapper::HttpConnectionBuilder() 57 | .WithConnector(connector) 58 | .WithService(service) 59 | .WithAddr("14.215.177.39", 80) 60 | .WithTimeout(std::chrono::seconds(10)) 61 | .WithFailedCallback([]() { 62 | std::cout << "connect failed" << std::endl; 63 | }) 64 | .WithMaxRecvBufferSize(1000) 65 | .WithEnterCallback([requestStr](const HttpSession::Ptr& session, HttpSessionHandlers& handlers) { 66 | (void) session; 67 | std::cout << "connect success" << std::endl; 68 | session->send(requestStr); 69 | handlers.setHttpBodyCallback([](const HTTPParser& httpParser, 70 | const HttpSession::Ptr& session, 71 | const char* body, 72 | size_t length) { 73 | (void) httpParser; 74 | (void) session; 75 | (void) body; 76 | (void) length; 77 | }); 78 | handlers.setHeaderCallback([](const HTTPParser& httpParser, 79 | const HttpSession::Ptr& session) { 80 | (void) httpParser; 81 | (void) session; 82 | }); 83 | handlers.setHttpEndCallback([](const HTTPParser& httpParser, 84 | const HttpSession::Ptr& session) { 85 | (void) session; 86 | // because has call setHttpBodyCallback, so the body from http parser is empty. 87 | std::cout << httpParser.getBody() << std::endl; 88 | counter.fetch_add(1); 89 | std::cout << "counter:" << counter.load() << std::endl; 90 | session->postClose(); 91 | }); 92 | }) 93 | .asyncConnect(); 94 | } 95 | 96 | while (true) 97 | { 98 | std::this_thread::sleep_for(std::chrono::seconds(1)); 99 | if (brynet::base::app_kbhit()) 100 | { 101 | break; 102 | } 103 | } 104 | 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /examples/HttpServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace brynet; 12 | using namespace brynet::net; 13 | using namespace brynet::net::http; 14 | 15 | int main(int argc, char** argv) 16 | { 17 | if (argc != 3) 18 | { 19 | fprintf(stderr, "Usage: \n"); 20 | exit(-1); 21 | } 22 | 23 | const auto port = std::atoi(argv[1]); 24 | auto service = IOThreadTcpService::Create(); 25 | service->startWorkerThread(atoi(argv[2])); 26 | 27 | auto httpEnterCallback = [](const HTTPParser& httpParser, 28 | const HttpSession::Ptr& session) { 29 | (void) httpParser; 30 | HttpResponse response; 31 | //std::cout << "method:" << http_method_str(static_cast(httpParser.method())) << std::endl; 32 | response.setBody(std::string("hello world ")); 33 | if (httpParser.isKeepAlive()) 34 | { 35 | response.addHeadValue("Connection", "Keep-Alive"); 36 | session->send(response.getResult()); 37 | } 38 | else 39 | { 40 | response.addHeadValue("Connection", "Close"); 41 | session->send(response.getResult(), [session]() { 42 | session->postShutdown(); 43 | }); 44 | } 45 | }; 46 | 47 | auto wsEnterCallback = [](const HttpSession::Ptr& httpSession, 48 | WebSocketFormat::WebSocketFrameType opcode, 49 | const std::string& payload) { 50 | std::cout << "frame enter of type:" << int(opcode) << std::endl; 51 | std::cout << "payload is:" << payload << std::endl; 52 | // echo frame 53 | httpSession->send(WebSocketFormat::wsFrameBuild(payload)); 54 | }; 55 | 56 | wrapper::HttpListenerBuilder listenBuilder; 57 | listenBuilder 58 | .WithService(service) 59 | .AddSocketProcess([](TcpSocket& socket) { 60 | socket.setNodelay(); 61 | }) 62 | .WithMaxRecvBufferSize(1024) 63 | .WithAddr(false, "0.0.0.0", port) 64 | .WithReusePort() 65 | .WithEnterCallback([httpEnterCallback, wsEnterCallback](const HttpSession::Ptr& httpSession, HttpSessionHandlers& handlers) { 66 | handlers.setHttpEndCallback(httpEnterCallback); 67 | handlers.setWSCallback(wsEnterCallback); 68 | }) 69 | .asyncRun(); 70 | 71 | while (true) 72 | { 73 | std::this_thread::sleep_for(std::chrono::seconds(1)); 74 | if (brynet::base::app_kbhit()) 75 | { 76 | break; 77 | } 78 | } 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /examples/PingPongClient.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace brynet; 9 | using namespace brynet::net; 10 | 11 | int main(int argc, char** argv) 12 | { 13 | if (argc != 6) 14 | { 15 | fprintf(stderr, "Usage: \n"); 16 | exit(-1); 17 | } 18 | 19 | std::string tmp(atoi(argv[5]), 'a'); 20 | 21 | auto service = IOThreadTcpService::Create(); 22 | service->startWorkerThread(atoi(argv[3])); 23 | 24 | auto connector = AsyncConnector::Create(); 25 | connector->startWorkerThread(); 26 | 27 | auto enterCallback = [tmp](const TcpConnection::Ptr& session) { 28 | session->setDataCallback([session](brynet::base::BasePacketReader& reader) { 29 | session->send(reader.begin(), reader.size()); 30 | reader.consumeAll(); 31 | }); 32 | session->send(tmp); 33 | }; 34 | 35 | auto failedCallback = []() { 36 | std::cout << "connect failed" << std::endl; 37 | }; 38 | 39 | wrapper::ConnectionBuilder connectionBuilder; 40 | connectionBuilder 41 | .WithService(service) 42 | .WithConnector(connector) 43 | .WithMaxRecvBufferSize(1024 * 1024) 44 | .AddEnterCallback(enterCallback); 45 | 46 | const auto num = std::atoi(argv[4]); 47 | const auto ip = argv[1]; 48 | const auto port = std::atoi(argv[2]); 49 | for (auto i = 0; i < num; i++) 50 | { 51 | try 52 | { 53 | connectionBuilder 54 | .WithAddr(ip, port) 55 | .WithTimeout(std::chrono::seconds(10)) 56 | .WithFailedCallback(failedCallback) 57 | .AddSocketProcessCallback([](TcpSocket& socket) { 58 | socket.setNodelay(); 59 | }) 60 | .asyncConnect(); 61 | } 62 | catch (std::runtime_error& e) 63 | { 64 | std::cout << "error:" << e.what() << std::endl; 65 | } 66 | } 67 | 68 | while (true) 69 | { 70 | std::this_thread::sleep_for(std::chrono::seconds(1)); 71 | if (brynet::base::app_kbhit()) 72 | { 73 | break; 74 | } 75 | } 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /examples/PingPongServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace brynet; 10 | using namespace brynet::net; 11 | 12 | std::atomic_llong TotalRecvSize = ATOMIC_VAR_INIT(0); 13 | std::atomic_llong total_client_num = ATOMIC_VAR_INIT(0); 14 | std::atomic_llong total_packet_num = ATOMIC_VAR_INIT(0); 15 | 16 | int main(int argc, char** argv) 17 | { 18 | if (argc != 3) 19 | { 20 | fprintf(stderr, "Usage: \n"); 21 | exit(-1); 22 | } 23 | 24 | auto service = IOThreadTcpService::Create(); 25 | service->startWorkerThread(atoi(argv[2])); 26 | 27 | auto enterCallback = [](const TcpConnection::Ptr& session) { 28 | total_client_num++; 29 | 30 | session->setDataCallback([session](brynet::base::BasePacketReader& reader) { 31 | session->send(reader.begin(), reader.size()); 32 | TotalRecvSize += reader.size(); 33 | total_packet_num++; 34 | reader.consumeAll(); 35 | }); 36 | 37 | session->setDisConnectCallback([](const TcpConnection::Ptr& session) { 38 | (void) session; 39 | total_client_num--; 40 | }); 41 | }; 42 | 43 | wrapper::ListenerBuilder listener; 44 | listener.WithService(service) 45 | .AddSocketProcess({[](TcpSocket& socket) { 46 | socket.setNodelay(); 47 | }}) 48 | .WithMaxRecvBufferSize(1024) 49 | .AddEnterCallback(enterCallback) 50 | .WithAddr(false, "0.0.0.0", atoi(argv[1])) 51 | .asyncRun(); 52 | 53 | EventLoop mainLoop; 54 | while (true) 55 | { 56 | mainLoop.loop(1000); 57 | if (TotalRecvSize / 1024 == 0) 58 | { 59 | std::cout << "total recv : " << TotalRecvSize << " bytes/s, of client num:" << total_client_num << std::endl; 60 | } 61 | else if ((TotalRecvSize / 1024) / 1024 == 0) 62 | { 63 | std::cout << "total recv : " << TotalRecvSize / 1024 << " K/s, of client num:" << total_client_num << std::endl; 64 | } 65 | else 66 | { 67 | std::cout << "total recv : " << (TotalRecvSize / 1024) / 1024 << " M/s, of client num:" << total_client_num << std::endl; 68 | } 69 | 70 | std::cout << "packet num:" << total_packet_num << std::endl; 71 | total_packet_num = 0; 72 | TotalRecvSize = 0; 73 | 74 | if (brynet::base::app_kbhit()) 75 | { 76 | break; 77 | } 78 | } 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /examples/PromiseReceive.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace brynet; 11 | using namespace brynet::net; 12 | using namespace brynet::net::http; 13 | 14 | int main(int argc, char** argv) 15 | { 16 | if (argc != 3) 17 | { 18 | fprintf(stderr, "Usage: \n"); 19 | exit(-1); 20 | } 21 | 22 | auto service = IOThreadTcpService::Create(); 23 | service->startWorkerThread(atoi(argv[2])); 24 | 25 | auto enterCallback = [](const TcpConnection::Ptr& session) { 26 | auto promiseReceive = setupPromiseReceive(session); 27 | auto contentLength = std::make_shared(); 28 | 29 | promiseReceive 30 | ->receiveUntil("\r\n", [](const char* buffer, size_t len) { 31 | auto headline = std::string(buffer, len); 32 | std::cout << headline << std::endl; 33 | return false; 34 | }) 35 | ->receiveUntil("\r\n", [promiseReceive, contentLength](const char* buffer, size_t len) { 36 | auto headerValue = std::string(buffer, len); 37 | std::cout << headerValue << std::endl; 38 | if (len > 2) 39 | { 40 | const static std::string ContentLenghtFlag = "Content-Length: "; 41 | auto pos = headerValue.find(ContentLenghtFlag); 42 | if (pos != std::string::npos) 43 | { 44 | auto lenStr = headerValue.substr(pos + ContentLenghtFlag.size(), headerValue.size()); 45 | *contentLength = std::stoi(lenStr); 46 | } 47 | return true; 48 | } 49 | return false; 50 | }) 51 | ->receive(contentLength, [session](const char* buffer, size_t len) { 52 | (void) buffer; 53 | (void) len; 54 | HttpResponse response; 55 | response.setStatus(HttpResponse::HTTP_RESPONSE_STATUS::OK); 56 | response.setContentType("text/html; charset=utf-8"); 57 | response.setBody("hello world "); 58 | 59 | session->send(response.getResult()); 60 | session->postShutdown(); 61 | 62 | return false; 63 | }); 64 | }; 65 | 66 | wrapper::ListenerBuilder listener; 67 | listener.WithService(service) 68 | .AddSocketProcess({[](TcpSocket& socket) { 69 | socket.setNodelay(); 70 | }}) 71 | .WithMaxRecvBufferSize(1024 * 1024) 72 | .AddEnterCallback(enterCallback) 73 | .WithAddr(false, "0.0.0.0", atoi(argv[1])) 74 | .asyncRun(); 75 | 76 | while (true) 77 | { 78 | std::this_thread::sleep_for(std::chrono::seconds(1)); 79 | if (brynet::base::app_kbhit()) 80 | { 81 | break; 82 | } 83 | } 84 | 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /examples/SingleThreadPingPongServer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace brynet; 10 | using namespace brynet::net; 11 | 12 | std::atomic_llong TotalRecvSize = ATOMIC_VAR_INIT(0); 13 | std::atomic_llong total_client_num = ATOMIC_VAR_INIT(0); 14 | std::atomic_llong total_packet_num = ATOMIC_VAR_INIT(0); 15 | 16 | int main(int argc, char** argv) 17 | { 18 | if (argc != 3) 19 | { 20 | fprintf(stderr, "Usage: \n"); 21 | exit(-1); 22 | } 23 | 24 | auto enterCallback = [](const TcpConnection::Ptr& session) { 25 | total_client_num++; 26 | 27 | session->setDataCallback([session](brynet::base::BasePacketReader& reader) { 28 | session->send(reader.begin(), reader.size()); 29 | TotalRecvSize += reader.size(); 30 | total_packet_num++; 31 | reader.consumeAll(); 32 | }); 33 | 34 | session->setDisConnectCallback([](const TcpConnection::Ptr& session) { 35 | (void) session; 36 | total_client_num--; 37 | }); 38 | }; 39 | 40 | EventLoop::Ptr mainLoop = std::make_shared(); 41 | mainLoop->bindCurrentThread(); 42 | 43 | auto service = EventLoopTcpService::Create(mainLoop); 44 | 45 | wrapper::ListenerBuilder listener; 46 | listener.WithService(service) 47 | .AddSocketProcess({[](TcpSocket& socket) { 48 | socket.setNodelay(); 49 | }}) 50 | .WithMaxRecvBufferSize(1024) 51 | .AddEnterCallback(enterCallback) 52 | .WithAddr(false, "0.0.0.0", atoi(argv[1])) 53 | .asyncRun(); 54 | 55 | mainLoop->runIntervalTimer(std::chrono::seconds(1), [&]() { 56 | if (TotalRecvSize / 1024 == 0) 57 | { 58 | std::cout << "total recv : " << TotalRecvSize << " bytes/s, of client num:" << total_client_num << std::endl; 59 | } 60 | else if ((TotalRecvSize / 1024) / 1024 == 0) 61 | { 62 | std::cout << "total recv : " << TotalRecvSize / 1024 << " K/s, of client num:" << total_client_num << std::endl; 63 | } 64 | else 65 | { 66 | std::cout << "total recv : " << (TotalRecvSize / 1024) / 1024 << " M/s, of client num:" << total_client_num << std::endl; 67 | } 68 | 69 | std::cout << "packet num:" << total_packet_num << std::endl; 70 | total_packet_num = 0; 71 | TotalRecvSize = 0; 72 | }); 73 | 74 | while (true) 75 | { 76 | mainLoop->loop(1000); 77 | 78 | if (brynet::base::app_kbhit()) 79 | { 80 | break; 81 | } 82 | } 83 | 84 | return 0; 85 | } 86 | -------------------------------------------------------------------------------- /examples/WebBinaryProxy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | using namespace brynet; 12 | using namespace brynet::net; 13 | 14 | int main(int argc, char** argv) 15 | { 16 | if (argc != 4) 17 | { 18 | fprintf(stderr, "Usage: "); 19 | exit(-1); 20 | } 21 | 22 | int bindPort = atoi(argv[1]); 23 | string backendIP = argv[2]; 24 | int backendPort = atoi(argv[3]); 25 | 26 | auto tcpService = IOThreadTcpService::Create(); 27 | tcpService->startWorkerThread(std::thread::hardware_concurrency()); 28 | 29 | auto asyncConnector = AsyncConnector::Create(); 30 | asyncConnector->startWorkerThread(); 31 | 32 | auto listenThread = ListenThread::Create(false, 33 | "0.0.0.0", 34 | bindPort, 35 | [tcpService, asyncConnector, backendIP, backendPort](TcpSocket::Ptr socket) { 36 | auto status = std::make_shared(true); 37 | auto enterCallback = [tcpService, asyncConnector, backendIP, backendPort, status](const TcpConnection::Ptr& session) { 38 | std::shared_ptr shareBackendSession = std::make_shared(nullptr); 39 | std::shared_ptr> cachePacket = std::make_shared>(); 40 | 41 | auto enterCallback = [tcpService, session, shareBackendSession, cachePacket, status](TcpSocket::Ptr socket) { 42 | auto enterCallback = [=](const TcpConnection::Ptr& backendSession) { 43 | if (!*status) /*if http client already close*/ 44 | { 45 | backendSession->postDisConnect(); 46 | return; 47 | } 48 | 49 | *shareBackendSession = backendSession; 50 | 51 | for (auto& p : *cachePacket) 52 | { 53 | backendSession->send(p); 54 | } 55 | cachePacket->clear(); 56 | 57 | backendSession->setDisConnectCallback([=](const TcpConnection::Ptr& backendSession) { 58 | (void) backendSession; 59 | *shareBackendSession = nullptr; 60 | if (*status) 61 | { 62 | session->postDisConnect(); 63 | } 64 | }); 65 | 66 | backendSession->setDataCallback([=](brynet::base::BasePacketReader& reader) { 67 | /* receive data from backend server, then send to http client */ 68 | session->send(reader.begin(), reader.size()); 69 | reader.consumeAll(); 70 | }); 71 | }; 72 | 73 | ConnectionOption option; 74 | option.enterCallback.emplace_back(enterCallback); 75 | option.maxRecvBufferSize = 32 * 1024; 76 | tcpService->addTcpConnection(std::move(socket), option); 77 | }; 78 | 79 | /* new connect to backend server */ 80 | ConnectOption option; 81 | option.ip = backendIP; 82 | option.port = backendPort; 83 | option.timeout = std::chrono::seconds(10); 84 | option.completedCallback = enterCallback; 85 | asyncConnector->asyncConnect(option); 86 | 87 | session->setDataCallback([=](brynet::base::BasePacketReader& reader) { 88 | TcpConnection::Ptr backendSession = *shareBackendSession; 89 | if (backendSession == nullptr) 90 | { 91 | /*cache it*/ 92 | cachePacket->push_back(std::string(reader.begin(), reader.size())); 93 | } 94 | else 95 | { 96 | /* receive data from http client, then send to backend server */ 97 | backendSession->send(reader.begin(), reader.size()); 98 | } 99 | 100 | reader.consumeAll(); 101 | }); 102 | 103 | session->setDisConnectCallback([=](const TcpConnection::Ptr& session) { 104 | /*if http client close, then close it's backend server */ 105 | TcpConnection::Ptr backendSession = *shareBackendSession; 106 | if (backendSession != nullptr) 107 | { 108 | backendSession->postDisConnect(); 109 | } 110 | *status = false; 111 | }); 112 | }; 113 | 114 | ConnectionOption option; 115 | option.enterCallback.emplace_back(enterCallback); 116 | option.maxRecvBufferSize = 32 * 1024; 117 | tcpService->addTcpConnection(std::move(socket), option); 118 | }); 119 | 120 | // listen for front http client 121 | listenThread->startListen(); 122 | while (true) 123 | { 124 | std::this_thread::sleep_for(std::chrono::seconds(1)); 125 | if (brynet::base::app_kbhit()) 126 | { 127 | break; 128 | } 129 | } 130 | 131 | return 0; 132 | } 133 | -------------------------------------------------------------------------------- /image/broadcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IronsDu/brynet/22c761b7872b5bae600201fc9d94d9e8a9b97591/image/broadcast.png -------------------------------------------------------------------------------- /image/pingpong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IronsDu/brynet/22c761b7872b5bae600201fc9d94d9e8a9b97591/image/pingpong.png -------------------------------------------------------------------------------- /include/brynet/Version.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define BRYNET_VERSION 1012003 4 | -------------------------------------------------------------------------------- /include/brynet/base/AppStatus.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef BRYNET_PLATFORM_WINDOWS 10 | #include 11 | #else 12 | #include 13 | #include 14 | #include 15 | #endif 16 | 17 | namespace brynet { namespace base { 18 | 19 | static bool app_kbhit() 20 | { 21 | #ifdef BRYNET_PLATFORM_WINDOWS 22 | return _kbhit(); 23 | #else 24 | struct termios oldt; 25 | tcgetattr(STDIN_FILENO, &oldt); 26 | auto newt = oldt; 27 | newt.c_lflag &= ~(ICANON | ECHO); 28 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 29 | const auto oldf = fcntl(STDIN_FILENO, F_GETFL, 0); 30 | fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK); 31 | 32 | const auto ch = getchar(); 33 | 34 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 35 | fcntl(STDIN_FILENO, F_SETFL, oldf); 36 | 37 | if (ch != EOF) 38 | { 39 | ungetc(ch, stdin); 40 | return true; 41 | } 42 | 43 | return false; 44 | #endif 45 | } 46 | 47 | }}// namespace brynet::base 48 | -------------------------------------------------------------------------------- /include/brynet/base/Array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace brynet { namespace base { 8 | 9 | struct array_s 10 | { 11 | void* buffer; 12 | size_t buffer_size; 13 | size_t element_size; 14 | size_t element_num; 15 | }; 16 | 17 | static void array_delete(struct array_s* self) 18 | { 19 | if (self == nullptr) 20 | { 21 | return; 22 | } 23 | 24 | if (self->buffer != nullptr) 25 | { 26 | free(self->buffer); 27 | self->buffer = nullptr; 28 | } 29 | 30 | self->element_num = 0; 31 | free(self); 32 | self = nullptr; 33 | } 34 | 35 | static struct array_s* array_new(size_t num, size_t element_size) 36 | { 37 | auto ret = (struct array_s*) malloc(sizeof(struct array_s)); 38 | if (ret == nullptr) 39 | { 40 | return nullptr; 41 | } 42 | 43 | const auto buffer_size = num * element_size; 44 | 45 | ret->buffer_size = 0; 46 | ret->element_size = 0; 47 | ret->element_num = 0; 48 | ret->buffer = malloc(buffer_size); 49 | 50 | if (ret->buffer != nullptr) 51 | { 52 | ret->element_size = element_size; 53 | ret->element_num = num; 54 | ret->buffer_size = buffer_size; 55 | } 56 | else 57 | { 58 | array_delete(ret); 59 | ret = nullptr; 60 | } 61 | 62 | return ret; 63 | } 64 | 65 | static void* array_at(struct array_s* self, size_t index) 66 | { 67 | void* ret = nullptr; 68 | 69 | if (index < self->element_num) 70 | { 71 | ret = (char*) (self->buffer) + (index * self->element_size); 72 | } 73 | else 74 | { 75 | assert(false); 76 | } 77 | 78 | return ret; 79 | } 80 | 81 | static bool array_set(struct array_s* self, size_t index, const void* data) 82 | { 83 | void* old_data = array_at(self, index); 84 | 85 | if (old_data != nullptr) 86 | { 87 | memcpy(old_data, data, self->element_size); 88 | return true; 89 | } 90 | else 91 | { 92 | return false; 93 | } 94 | } 95 | 96 | static bool array_increase(struct array_s* self, size_t increase_num) 97 | { 98 | if (increase_num == 0) 99 | { 100 | return false; 101 | } 102 | 103 | const auto new_buffer_size = self->buffer_size + increase_num * self->element_size; 104 | auto new_buffer = malloc(new_buffer_size); 105 | 106 | if (new_buffer != nullptr) 107 | { 108 | memcpy(new_buffer, self->buffer, self->buffer_size); 109 | free(self->buffer); 110 | self->buffer = new_buffer; 111 | self->element_num += increase_num; 112 | self->buffer_size = new_buffer_size; 113 | 114 | return true; 115 | } 116 | else 117 | { 118 | assert(false); 119 | return false; 120 | } 121 | } 122 | 123 | static size_t array_num(const struct array_s* self) 124 | { 125 | return self->element_num; 126 | } 127 | 128 | }}// namespace brynet::base 129 | -------------------------------------------------------------------------------- /include/brynet/base/Buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace brynet { namespace base { 7 | 8 | struct buffer_s 9 | { 10 | char* data; 11 | size_t data_len; 12 | 13 | size_t write_pos; 14 | size_t read_pos; 15 | }; 16 | 17 | static void buffer_delete(struct buffer_s* self) 18 | { 19 | if (self == nullptr) 20 | { 21 | return; 22 | } 23 | 24 | if (self->data != nullptr) 25 | { 26 | free(self->data); 27 | self->data = nullptr; 28 | } 29 | 30 | free(self); 31 | self = nullptr; 32 | } 33 | 34 | static struct buffer_s* buffer_new(size_t buffer_size) 35 | { 36 | struct buffer_s* ret = (struct buffer_s*) malloc(sizeof(struct buffer_s)); 37 | if (ret == nullptr) 38 | { 39 | return nullptr; 40 | } 41 | 42 | ret->data_len = 0; 43 | ret->read_pos = 0; 44 | ret->write_pos = 0; 45 | ret->data = (char*) malloc(sizeof(char) * buffer_size); 46 | 47 | if (ret->data != nullptr) 48 | { 49 | ret->data_len = buffer_size; 50 | } 51 | else 52 | { 53 | buffer_delete(ret); 54 | ret = nullptr; 55 | } 56 | 57 | return ret; 58 | } 59 | 60 | static size_t buffer_getreadvalidcount(struct buffer_s* self) 61 | { 62 | return self->write_pos - self->read_pos; 63 | } 64 | 65 | static void buffer_adjustto_head(struct buffer_s* self) 66 | { 67 | if (self->read_pos == 0) 68 | { 69 | return; 70 | } 71 | 72 | const auto len = buffer_getreadvalidcount(self); 73 | if (len > 0) 74 | { 75 | memmove(self->data, self->data + self->read_pos, len); 76 | } 77 | 78 | self->read_pos = 0; 79 | self->write_pos = len; 80 | } 81 | 82 | static void buffer_init(struct buffer_s* self) 83 | { 84 | self->read_pos = 0; 85 | self->write_pos = 0; 86 | } 87 | 88 | static size_t buffer_getwritepos(struct buffer_s* self) 89 | { 90 | return self->write_pos; 91 | } 92 | 93 | static size_t buffer_getreadpos(struct buffer_s* self) 94 | { 95 | return self->read_pos; 96 | } 97 | 98 | static bool buffer_addwritepos(struct buffer_s* self, size_t value) 99 | { 100 | const size_t temp = self->write_pos + value; 101 | if (temp <= self->data_len) 102 | { 103 | self->write_pos = temp; 104 | return true; 105 | } 106 | else 107 | { 108 | return false; 109 | } 110 | } 111 | 112 | static bool buffer_addreadpos(struct buffer_s* self, size_t value) 113 | { 114 | const size_t temp = self->read_pos + value; 115 | if (temp <= self->data_len) 116 | { 117 | self->read_pos = temp; 118 | return true; 119 | } 120 | else 121 | { 122 | return false; 123 | } 124 | } 125 | 126 | static size_t buffer_getwritevalidcount(struct buffer_s* self) 127 | { 128 | return self->data_len - self->write_pos; 129 | } 130 | 131 | static size_t buffer_getsize(struct buffer_s* self) 132 | { 133 | return self->data_len; 134 | } 135 | 136 | static char* buffer_getwriteptr(struct buffer_s* self) 137 | { 138 | if (self->write_pos < self->data_len) 139 | { 140 | return self->data + self->write_pos; 141 | } 142 | else 143 | { 144 | return nullptr; 145 | } 146 | } 147 | 148 | static char* buffer_getreadptr(struct buffer_s* self) 149 | { 150 | if (self->read_pos < self->data_len) 151 | { 152 | return self->data + self->read_pos; 153 | } 154 | else 155 | { 156 | return nullptr; 157 | } 158 | } 159 | 160 | static bool buffer_write(struct buffer_s* self, const char* data, size_t len) 161 | { 162 | bool write_ret = true; 163 | 164 | if (buffer_getwritevalidcount(self) >= len) 165 | { 166 | memcpy(buffer_getwriteptr(self), data, len); 167 | buffer_addwritepos(self, len); 168 | } 169 | else 170 | { 171 | size_t left_len = self->data_len - buffer_getreadvalidcount(self); 172 | if (left_len >= len) 173 | { 174 | buffer_adjustto_head(self); 175 | buffer_write(self, data, len); 176 | } 177 | else 178 | { 179 | write_ret = false; 180 | } 181 | } 182 | 183 | return write_ret; 184 | } 185 | 186 | }}// namespace brynet::base 187 | -------------------------------------------------------------------------------- /include/brynet/base/NonCopyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace brynet { namespace base { 4 | 5 | class NonCopyable 6 | { 7 | public: 8 | NonCopyable(const NonCopyable&) = delete; 9 | const NonCopyable& operator=(const NonCopyable&) = delete; 10 | 11 | protected: 12 | NonCopyable() = default; 13 | ~NonCopyable() = default; 14 | }; 15 | 16 | }}// namespace brynet::base 17 | -------------------------------------------------------------------------------- /include/brynet/base/Platform.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined _MSC_VER || defined __MINGW32__ 4 | #define BRYNET_PLATFORM_WINDOWS 5 | #pragma comment(lib, "ws2_32") 6 | #elif defined __APPLE_CC__ || defined __APPLE__ 7 | #define BRYNET_PLATFORM_DARWIN 8 | #elif defined __FreeBSD__ 9 | #define BRYNET_PLATFORM_FREEBSD 10 | #else 11 | #define BRYNET_PLATFORM_LINUX 12 | #endif 13 | -------------------------------------------------------------------------------- /include/brynet/base/Stack.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace brynet { namespace base { 7 | 8 | struct stack_s 9 | { 10 | struct array_s* array; 11 | size_t element_size; 12 | 13 | size_t element_num; 14 | size_t front; 15 | size_t num; 16 | }; 17 | 18 | static void stack_delete(struct stack_s* self) 19 | { 20 | if (self == nullptr) 21 | { 22 | return; 23 | } 24 | 25 | if (self->array != nullptr) 26 | { 27 | array_delete(self->array); 28 | self->array = nullptr; 29 | } 30 | 31 | self->element_num = 0; 32 | self->front = 0; 33 | self->num = 0; 34 | free(self); 35 | self = nullptr; 36 | } 37 | 38 | static struct stack_s* stack_new(size_t num, size_t element_size) 39 | { 40 | struct stack_s* ret = (struct stack_s*) malloc(sizeof(struct stack_s)); 41 | if (ret == nullptr) 42 | { 43 | return nullptr; 44 | } 45 | 46 | ret->element_size = 0; 47 | ret->element_num = 0; 48 | ret->front = 0; 49 | ret->num = 0; 50 | ret->array = array_new(num, element_size); 51 | 52 | if (ret->array != nullptr) 53 | { 54 | ret->element_size = element_size; 55 | ret->element_num = num; 56 | } 57 | else 58 | { 59 | stack_delete(ret); 60 | ret = nullptr; 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | static void stack_init(struct stack_s* self) 67 | { 68 | self->front = 0; 69 | self->num = 0; 70 | } 71 | 72 | static size_t stack_num(struct stack_s* self) 73 | { 74 | return self->num; 75 | } 76 | 77 | static bool stack_increase(struct stack_s* self, size_t increase_num) 78 | { 79 | struct array_s* tmp = array_new(self->element_num + increase_num, 80 | self->element_size); 81 | if (tmp == nullptr) 82 | { 83 | return false; 84 | } 85 | 86 | { 87 | size_t current_num = self->element_num; 88 | size_t current_stack_num = stack_num(self); 89 | for (size_t i = 0; i < current_stack_num; ++i) 90 | { 91 | array_set(tmp, i, array_at(self->array, (self->front + i) % current_num)); 92 | } 93 | 94 | self->front = 0; 95 | array_delete(self->array); 96 | self->array = tmp; 97 | self->element_num = array_num(self->array); 98 | } 99 | 100 | return true; 101 | } 102 | 103 | static size_t stack_size(struct stack_s* self) 104 | { 105 | return self->element_num; 106 | } 107 | 108 | static bool stack_isfull(struct stack_s* self) 109 | { 110 | return (self->num == self->element_num); 111 | } 112 | 113 | static bool stack_push(struct stack_s* self, const void* data) 114 | { 115 | if (stack_isfull(self)) 116 | { 117 | stack_increase(self, stack_size(self)); 118 | } 119 | 120 | if (stack_isfull(self)) 121 | { 122 | return false; 123 | } 124 | 125 | array_set(self->array, (self->front + self->num) % self->element_num, data); 126 | self->num++; 127 | return true; 128 | } 129 | 130 | static void* stack_front(struct stack_s* self) 131 | { 132 | void* ret = nullptr; 133 | 134 | if (stack_num(self) > 0) 135 | { 136 | ret = array_at(self->array, self->front); 137 | } 138 | 139 | return ret; 140 | } 141 | 142 | static void* stack_popfront(struct stack_s* self) 143 | { 144 | void* ret = stack_front(self); 145 | 146 | if (ret != nullptr) 147 | { 148 | self->num--; 149 | self->front++; 150 | self->front %= self->element_num; 151 | } 152 | 153 | return ret; 154 | } 155 | 156 | static void* stack_popback(struct stack_s* self) 157 | { 158 | void* ret = nullptr; 159 | 160 | if (stack_num(self) > 0) 161 | { 162 | self->num--; 163 | ret = array_at(self->array, (self->front + self->num) % self->element_num); 164 | } 165 | 166 | return ret; 167 | } 168 | 169 | }}// namespace brynet::base 170 | -------------------------------------------------------------------------------- /include/brynet/base/Timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace brynet { namespace base { 12 | 13 | class TimerMgr; 14 | 15 | class Timer final 16 | { 17 | public: 18 | using Ptr = std::shared_ptr; 19 | using WeakPtr = std::weak_ptr; 20 | using Callback = std::function; 21 | 22 | Timer(std::chrono::steady_clock::time_point startTime, 23 | std::chrono::nanoseconds lastTime, 24 | Callback&& callback) 25 | : mCallback(std::move(callback)), 26 | mStartTime(startTime), 27 | mLastTime(lastTime) 28 | { 29 | } 30 | 31 | const std::chrono::steady_clock::time_point& getStartTime() const 32 | { 33 | return mStartTime; 34 | } 35 | 36 | const std::chrono::nanoseconds& getLastTime() const 37 | { 38 | return mLastTime; 39 | } 40 | 41 | std::chrono::nanoseconds getLeftTime() const 42 | { 43 | const auto now = std::chrono::steady_clock::now(); 44 | return getLastTime() - (now - getStartTime()); 45 | } 46 | 47 | void cancel() 48 | { 49 | std::call_once(mExecuteOnceFlag, [this]() { 50 | mCallback = nullptr; 51 | }); 52 | } 53 | 54 | private: 55 | void operator()() 56 | { 57 | Callback callback; 58 | std::call_once(mExecuteOnceFlag, [&callback, this]() { 59 | callback = std::move(mCallback); 60 | mCallback = nullptr; 61 | }); 62 | 63 | if (callback != nullptr) 64 | { 65 | callback(); 66 | } 67 | } 68 | 69 | private: 70 | std::once_flag mExecuteOnceFlag; 71 | Callback mCallback; 72 | const std::chrono::steady_clock::time_point mStartTime; 73 | const std::chrono::nanoseconds mLastTime; 74 | 75 | friend class TimerMgr; 76 | }; 77 | 78 | class RepeatTimer final 79 | { 80 | public: 81 | using Ptr = std::shared_ptr; 82 | 83 | RepeatTimer() 84 | { 85 | mCancel.store(false); 86 | } 87 | 88 | void cancel() 89 | { 90 | mCancel.store(true); 91 | } 92 | 93 | bool isCancel() const 94 | { 95 | return mCancel.load(); 96 | } 97 | 98 | private: 99 | std::atomic_bool mCancel; 100 | }; 101 | 102 | class TimerMgr final : public std::enable_shared_from_this 103 | { 104 | public: 105 | using Ptr = std::shared_ptr; 106 | 107 | template 108 | Timer::WeakPtr addTimer( 109 | std::chrono::nanoseconds timeout, 110 | F&& callback, 111 | TArgs&&... args) 112 | { 113 | auto timer = std::make_shared( 114 | std::chrono::steady_clock::now(), 115 | std::chrono::nanoseconds(timeout), 116 | std::bind(std::forward(callback), std::forward(args)...)); 117 | mTimers.push(timer); 118 | 119 | return timer; 120 | } 121 | 122 | template 123 | RepeatTimer::Ptr addIntervalTimer( 124 | std::chrono::nanoseconds interval, 125 | F&& callback, 126 | TArgs&&... args) 127 | { 128 | auto sharedThis = shared_from_this(); 129 | auto repeatTimer = std::make_shared(); 130 | auto wrapperCallback = std::bind(std::forward(callback), std::forward(args)...); 131 | addTimer(interval, [sharedThis, interval, wrapperCallback, repeatTimer]() { 132 | stubRepeatTimerCallback(sharedThis, interval, wrapperCallback, repeatTimer); 133 | }); 134 | 135 | return repeatTimer; 136 | } 137 | 138 | void addTimer(const Timer::Ptr& timer) 139 | { 140 | mTimers.push(timer); 141 | } 142 | 143 | void schedule() 144 | { 145 | while (!mTimers.empty()) 146 | { 147 | auto tmp = mTimers.top(); 148 | if (tmp->getLeftTime() > std::chrono::nanoseconds::zero()) 149 | { 150 | break; 151 | } 152 | 153 | mTimers.pop(); 154 | (*tmp)(); 155 | } 156 | } 157 | 158 | bool isEmpty() const 159 | { 160 | return mTimers.empty(); 161 | } 162 | 163 | // if timer empty, return zero 164 | std::chrono::nanoseconds nearLeftTime() const 165 | { 166 | if (mTimers.empty()) 167 | { 168 | return std::chrono::nanoseconds::zero(); 169 | } 170 | 171 | auto result = mTimers.top()->getLeftTime(); 172 | if (result < std::chrono::nanoseconds::zero()) 173 | { 174 | return std::chrono::nanoseconds::zero(); 175 | } 176 | 177 | return result; 178 | } 179 | 180 | void clear() 181 | { 182 | while (!mTimers.empty()) 183 | { 184 | mTimers.pop(); 185 | } 186 | } 187 | 188 | template 189 | void helperAddIntervalTimer( 190 | const RepeatTimer::Ptr& repeatTimer, 191 | std::chrono::nanoseconds interval, 192 | F&& callback, 193 | TArgs&&... args) 194 | { 195 | auto sharedThis = shared_from_this(); 196 | auto wrapperCallback = std::bind(std::forward(callback), std::forward(args)...); 197 | addTimer(interval, [sharedThis, interval, wrapperCallback, repeatTimer]() { 198 | stubRepeatTimerCallback(sharedThis, interval, wrapperCallback, repeatTimer); 199 | }); 200 | } 201 | 202 | private: 203 | static void stubRepeatTimerCallback(const TimerMgr::Ptr& timerMgr, 204 | std::chrono::nanoseconds interval, 205 | const std::function& callback, 206 | const RepeatTimer::Ptr& repeatTimer) 207 | { 208 | if (repeatTimer->isCancel()) 209 | { 210 | return; 211 | } 212 | callback(); 213 | timerMgr->addTimer(interval, [timerMgr, interval, callback, repeatTimer]() { 214 | stubRepeatTimerCallback(timerMgr, interval, callback, repeatTimer); 215 | }); 216 | } 217 | 218 | private: 219 | class CompareTimer 220 | { 221 | public: 222 | bool operator()(const Timer::Ptr& left, 223 | const Timer::Ptr& right) const 224 | { 225 | const auto startDiff = left->getStartTime() - right->getStartTime(); 226 | const auto lastDiff = left->getLastTime() - right->getLastTime(); 227 | const auto diff = startDiff.count() + lastDiff.count(); 228 | return diff > 0; 229 | } 230 | }; 231 | 232 | std::priority_queue, CompareTimer> mTimers; 233 | }; 234 | 235 | }}// namespace brynet::base 236 | -------------------------------------------------------------------------------- /include/brynet/base/WaitGroup.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace brynet { namespace base { 11 | 12 | class WaitGroup : public NonCopyable 13 | { 14 | public: 15 | typedef std::shared_ptr Ptr; 16 | 17 | static Ptr Create() 18 | { 19 | struct make_shared_enabler : public WaitGroup 20 | { 21 | }; 22 | return std::make_shared(); 23 | } 24 | 25 | public: 26 | void add(int i = 1) 27 | { 28 | mCounter += i; 29 | } 30 | 31 | void done() 32 | { 33 | --mCounter; 34 | mCond.notify_all(); 35 | } 36 | 37 | void wait() 38 | { 39 | std::unique_lock l(mMutex); 40 | mCond.wait(l, [&] { 41 | return mCounter <= 0; 42 | }); 43 | } 44 | 45 | template 46 | void wait(const std::chrono::duration& timeout) 47 | { 48 | std::unique_lock l(mMutex); 49 | mCond.wait_for(l, timeout, [&] { 50 | return mCounter <= 0; 51 | }); 52 | } 53 | 54 | private: 55 | WaitGroup() 56 | : mCounter(0) 57 | { 58 | } 59 | 60 | virtual ~WaitGroup() = default; 61 | 62 | private: 63 | std::mutex mMutex; 64 | std::atomic mCounter; 65 | std::condition_variable mCond; 66 | }; 67 | }}// namespace brynet::base 68 | -------------------------------------------------------------------------------- /include/brynet/base/crypto/Base64.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _BRYNET_BASE_BASE64_H 2 | #define _BRYNET_BASE_BASE64_H 3 | 4 | #include 5 | 6 | namespace brynet { namespace base { namespace crypto { 7 | 8 | static const std::string base64_chars = 9 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 10 | "abcdefghijklmnopqrstuvwxyz" 11 | "0123456789+/"; 12 | 13 | static bool is_base64(unsigned char c) 14 | { 15 | return (isalnum(c) || (c == '+') || (c == '/')); 16 | } 17 | 18 | static std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) 19 | { 20 | std::string ret; 21 | int i = 0; 22 | int j = 0; 23 | unsigned char char_array_3[3]; 24 | unsigned char char_array_4[4]; 25 | 26 | while (in_len--) 27 | { 28 | char_array_3[i++] = *(bytes_to_encode++); 29 | if (i == 3) 30 | { 31 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 32 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 33 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 34 | char_array_4[3] = char_array_3[2] & 0x3f; 35 | 36 | for (i = 0; (i < 4); i++) 37 | ret += base64_chars[char_array_4[i]]; 38 | i = 0; 39 | } 40 | } 41 | 42 | if (i) 43 | { 44 | for (j = i; j < 3; j++) 45 | char_array_3[j] = '\0'; 46 | 47 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 48 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 49 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 50 | char_array_4[3] = char_array_3[2] & 0x3f; 51 | 52 | for (j = 0; (j < i + 1); j++) 53 | ret += base64_chars[char_array_4[j]]; 54 | 55 | while ((i++ < 3)) 56 | ret += '='; 57 | } 58 | 59 | return ret; 60 | } 61 | 62 | static std::string base64_decode(std::string const& encoded_string) 63 | { 64 | int in_len = encoded_string.size(); 65 | int i = 0; 66 | int j = 0; 67 | int in_ = 0; 68 | unsigned char char_array_4[4], char_array_3[3]; 69 | std::string ret; 70 | 71 | while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) 72 | { 73 | char_array_4[i++] = encoded_string[in_]; 74 | in_++; 75 | if (i == 4) 76 | { 77 | for (i = 0; i < 4; i++) 78 | char_array_4[i] = base64_chars.find(char_array_4[i]); 79 | 80 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 81 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 82 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 83 | 84 | for (i = 0; (i < 3); i++) 85 | ret += char_array_3[i]; 86 | i = 0; 87 | } 88 | } 89 | 90 | if (i) 91 | { 92 | for (j = i; j < 4; j++) 93 | char_array_4[j] = 0; 94 | 95 | for (j = 0; j < 4; j++) 96 | char_array_4[j] = base64_chars.find(char_array_4[j]); 97 | 98 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 99 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 100 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 101 | 102 | for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | }}}// namespace brynet::base::crypto 109 | 110 | #endif -------------------------------------------------------------------------------- /include/brynet/base/endian/Endian.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef BRYNET_PLATFORM_LINUX 10 | #include 11 | #elif defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 12 | #include 13 | #endif 14 | 15 | namespace brynet { namespace base { namespace endian { 16 | 17 | inline uint64_t hl64ton(uint64_t hostValue) 18 | { 19 | uint64_t ret = 0; 20 | uint32_t high, low; 21 | 22 | low = hostValue & 0xFFFFFFFF; 23 | high = (hostValue >> 32) & 0xFFFFFFFF; 24 | low = htonl(low); 25 | high = htonl(high); 26 | ret = low; 27 | ret <<= 32; 28 | ret |= high; 29 | 30 | return ret; 31 | } 32 | 33 | inline uint64_t ntohl64(uint64_t netValue) 34 | { 35 | uint64_t ret = 0; 36 | uint32_t high, low; 37 | 38 | low = netValue & 0xFFFFFFFF; 39 | high = (netValue >> 32) & 0xFFFFFFFF; 40 | low = ntohl(low); 41 | high = ntohl(high); 42 | ret = low; 43 | ret <<= 32; 44 | ret |= high; 45 | 46 | return ret; 47 | } 48 | 49 | #ifdef BRYNET_PLATFORM_WINDOWS 50 | inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true) 51 | { 52 | return convert ? hl64ton(host64) : host64; 53 | } 54 | inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true) 55 | { 56 | return convert ? htonl(host32) : host32; 57 | } 58 | 59 | inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true) 60 | { 61 | return convert ? htons(host16) : host16; 62 | } 63 | 64 | inline uint64_t networkToHost64(uint64_t net64, bool convert = true) 65 | { 66 | return convert ? ntohl64(net64) : net64; 67 | } 68 | 69 | inline uint32_t networkToHost32(uint32_t net32, bool convert = true) 70 | { 71 | return convert ? ntohl(net32) : net32; 72 | } 73 | 74 | inline uint16_t networkToHost16(uint16_t net16, bool convert = true) 75 | { 76 | return convert ? ntohs(net16) : net16; 77 | } 78 | #elif defined BRYNET_PLATFORM_LINUX 79 | inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true) 80 | { 81 | return convert ? htobe64(host64) : host64; 82 | } 83 | inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true) 84 | { 85 | return convert ? htobe32(host32) : host32; 86 | } 87 | 88 | inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true) 89 | { 90 | return convert ? htobe16(host16) : host16; 91 | } 92 | 93 | inline uint64_t networkToHost64(uint64_t net64, bool convert = true) 94 | { 95 | return convert ? be64toh(net64) : net64; 96 | } 97 | 98 | inline uint32_t networkToHost32(uint32_t net32, bool convert = true) 99 | { 100 | return convert ? be32toh(net32) : net32; 101 | } 102 | 103 | inline uint16_t networkToHost16(uint16_t net16, bool convert = true) 104 | { 105 | return convert ? be16toh(net16) : net16; 106 | } 107 | #elif defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 108 | inline uint64_t hostToNetwork64(uint64_t host64, bool convert = true) 109 | { 110 | return convert ? hl64ton(host64) : host64; 111 | } 112 | inline uint32_t hostToNetwork32(uint32_t host32, bool convert = true) 113 | { 114 | return convert ? htonl(host32) : host32; 115 | } 116 | 117 | inline uint16_t hostToNetwork16(uint16_t host16, bool convert = true) 118 | { 119 | return convert ? htons(host16) : host16; 120 | } 121 | 122 | inline uint64_t networkToHost64(uint64_t net64, bool convert = true) 123 | { 124 | return convert ? ntohl64(net64) : net64; 125 | } 126 | 127 | inline uint32_t networkToHost32(uint32_t net32, bool convert = true) 128 | { 129 | return convert ? ntohl(net32) : net32; 130 | } 131 | 132 | inline uint16_t networkToHost16(uint16_t net16, bool convert = true) 133 | { 134 | return convert ? ntohs(net16) : net16; 135 | } 136 | #endif 137 | 138 | }}}// namespace brynet::base::endian 139 | -------------------------------------------------------------------------------- /include/brynet/net/AsyncConnector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace brynet { namespace net { 6 | 7 | using ConnectOption = detail::ConnectOption; 8 | class AsyncConnector : public detail::AsyncConnectorDetail, 9 | public std::enable_shared_from_this 10 | { 11 | public: 12 | using Ptr = std::shared_ptr; 13 | 14 | void startWorkerThread() 15 | { 16 | detail::AsyncConnectorDetail::startWorkerThread(); 17 | } 18 | 19 | void stopWorkerThread() 20 | { 21 | detail::AsyncConnectorDetail::stopWorkerThread(); 22 | } 23 | 24 | void asyncConnect(const ConnectOption& option) 25 | { 26 | detail::AsyncConnectorDetail::asyncConnect(option); 27 | } 28 | 29 | static Ptr Create() 30 | { 31 | class make_shared_enabler : public AsyncConnector 32 | { 33 | }; 34 | return std::make_shared(); 35 | } 36 | 37 | private: 38 | AsyncConnector() = default; 39 | }; 40 | 41 | }}// namespace brynet::net 42 | -------------------------------------------------------------------------------- /include/brynet/net/Channel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace brynet { namespace net { 4 | 5 | class EventLoop; 6 | 7 | class Channel 8 | { 9 | public: 10 | virtual ~Channel() = default; 11 | 12 | private: 13 | virtual void canSend() = 0; 14 | virtual void canRecv(bool willClose) = 0; 15 | virtual void onClose() = 0; 16 | 17 | friend class EventLoop; 18 | }; 19 | 20 | }}// namespace brynet::net 21 | -------------------------------------------------------------------------------- /include/brynet/net/CurrentThread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef BRYNET_PLATFORM_WINDOWS 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #include 9 | #elif defined BRYNET_PLATFORM_LINUX 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #elif defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 16 | #include 17 | #include 18 | #include 19 | #endif 20 | 21 | namespace brynet { namespace net { namespace current_thread { 22 | 23 | #ifdef BRYNET_PLATFORM_WINDOWS 24 | using THREAD_ID_TYPE = DWORD; 25 | #elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 26 | #if defined BRYNET_PLATFORM_FREEBSD 27 | using THREAD_ID_TYPE = pthread_t; 28 | #else 29 | using THREAD_ID_TYPE = int; 30 | #endif 31 | #endif 32 | 33 | static THREAD_ID_TYPE& tid() 34 | { 35 | #ifdef BRYNET_PLATFORM_WINDOWS 36 | static __declspec(thread) THREAD_ID_TYPE cachedTid = 0; 37 | #elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 38 | static __thread THREAD_ID_TYPE cachedTid = 0; 39 | #endif 40 | 41 | if (cachedTid == 0) 42 | { 43 | #ifdef BRYNET_PLATFORM_WINDOWS 44 | cachedTid = GetCurrentThreadId(); 45 | #elif defined BRYNET_PLATFORM_LINUX 46 | cachedTid = static_cast(::syscall(SYS_gettid)); 47 | #elif defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 48 | #if defined BRYNET_PLATFORM_FREEBSD 49 | cachedTid = pthread_self(); 50 | #else 51 | // warning: 'syscall' is deprecated: 52 | // first deprecated in macOS 10.12 - syscall(2) is unsupported; 53 | // please switch to a supported interface. 54 | uint64_t tid64; 55 | pthread_threadid_np(NULL, &tid64); 56 | cachedTid = (pid_t) tid64; 57 | #endif 58 | #endif 59 | } 60 | 61 | return cachedTid; 62 | } 63 | 64 | }}}// namespace brynet::net::current_thread 65 | -------------------------------------------------------------------------------- /include/brynet/net/Exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace brynet { namespace net { 7 | 8 | class ConnectException : public std::runtime_error 9 | { 10 | public: 11 | explicit ConnectException(const std::string& message) 12 | : std::runtime_error(message) 13 | { 14 | } 15 | 16 | explicit ConnectException(const char* message) 17 | : std::runtime_error(message) 18 | { 19 | } 20 | }; 21 | 22 | class BrynetCommonException : public std::runtime_error 23 | { 24 | public: 25 | explicit BrynetCommonException(const std::string& message) 26 | : std::runtime_error(message) 27 | { 28 | } 29 | 30 | explicit BrynetCommonException(const char* message) 31 | : std::runtime_error(message) 32 | { 33 | } 34 | }; 35 | 36 | }}// namespace brynet::net 37 | -------------------------------------------------------------------------------- /include/brynet/net/ListenThread.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace brynet { namespace net { 6 | 7 | class ListenThread : public detail::ListenThreadDetail, 8 | public std::enable_shared_from_this 9 | { 10 | public: 11 | using Ptr = std::shared_ptr; 12 | using AccepCallback = std::function; 13 | using TcpSocketProcessCallback = std::function; 14 | 15 | void startListen() 16 | { 17 | detail::ListenThreadDetail::startListen(); 18 | } 19 | 20 | void stopListen() 21 | { 22 | detail::ListenThreadDetail::stopListen(); 23 | } 24 | 25 | public: 26 | static Ptr Create(bool isIPV6, 27 | const std::string& ip, 28 | int port, 29 | const AccepCallback& callback, 30 | const std::vector& processCallbacks = {}, 31 | bool enabledReusePort = false) 32 | { 33 | class make_shared_enabler : public ListenThread 34 | { 35 | public: 36 | make_shared_enabler(bool isIPV6, 37 | const std::string& ip, 38 | int port, 39 | const AccepCallback& callback, 40 | const std::vector& processCallbacks, 41 | bool enabledReusePort) 42 | : ListenThread(isIPV6, ip, port, callback, processCallbacks, enabledReusePort) 43 | {} 44 | }; 45 | return std::make_shared(isIPV6, ip, port, callback, processCallbacks, enabledReusePort); 46 | } 47 | 48 | protected: 49 | ListenThread(bool isIPV6, 50 | const std::string& ip, 51 | int port, 52 | const AccepCallback& callback, 53 | const std::vector& processCallbacks, 54 | bool enabledReusePort) 55 | : detail::ListenThreadDetail(isIPV6, ip, port, callback, processCallbacks, enabledReusePort) 56 | {} 57 | }; 58 | 59 | }}// namespace brynet::net 60 | -------------------------------------------------------------------------------- /include/brynet/net/Poller.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 9 | #include 10 | #endif 11 | 12 | namespace brynet { namespace base { 13 | 14 | #ifdef BRYNET_PLATFORM_WINDOWS 15 | const static int CHECK_READ_FLAG = (POLLIN | POLLRDNORM | POLLRDBAND); 16 | const static int CHECK_WRITE_FLAG = (POLLOUT | POLLWRNORM); 17 | const static int CHECK_ERROR_FLAG = (POLLERR | POLLHUP); 18 | #elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 19 | const static int CHECK_READ_FLAG = (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI); 20 | const static int CHECK_WRITE_FLAG = (POLLOUT | POLLWRNORM | POLLWRBAND); 21 | const static int CHECK_ERROR_FLAG = (POLLERR | POLLHUP); 22 | #endif 23 | 24 | enum CheckType 25 | { 26 | ReadCheck = 0x1, 27 | WriteCheck = 0x2, 28 | ErrorCheck = 0x4, 29 | }; 30 | 31 | struct poller_s 32 | { 33 | struct pollfd* pollFds; 34 | int nfds; 35 | int limitSize; 36 | }; 37 | 38 | static void upstep_pollfd(struct poller_s* self, int upSize) 39 | { 40 | if (upSize <= 0) 41 | { 42 | return; 43 | } 44 | 45 | struct pollfd* newPollfds = (struct pollfd*) malloc( 46 | sizeof(struct pollfd) * (self->limitSize + upSize)); 47 | if (newPollfds == nullptr) 48 | { 49 | return; 50 | } 51 | 52 | if (self->pollFds != nullptr) 53 | { 54 | memcpy(newPollfds, self->pollFds, sizeof(struct pollfd) * self->nfds); 55 | free(self->pollFds); 56 | self->pollFds = nullptr; 57 | } 58 | self->pollFds = newPollfds; 59 | self->limitSize += upSize; 60 | } 61 | 62 | static struct pollfd* find_pollfd(struct poller_s* self, BrynetSocketFD fd) 63 | { 64 | for (int i = 0; i < self->nfds; i++) 65 | { 66 | if (self->pollFds[i].fd == fd) 67 | { 68 | return self->pollFds + i; 69 | } 70 | } 71 | 72 | return nullptr; 73 | } 74 | 75 | static void try_remove_pollfd(struct poller_s* self, BrynetSocketFD fd) 76 | { 77 | int pos = -1; 78 | for (int i = 0; i < self->nfds; i++) 79 | { 80 | if (self->pollFds[i].fd == fd) 81 | { 82 | pos = i; 83 | break; 84 | } 85 | } 86 | 87 | if (pos != -1) 88 | { 89 | memmove(self->pollFds + pos, 90 | self->pollFds + pos + 1, 91 | sizeof(struct pollfd) * (self->nfds - pos - 1)); 92 | self->nfds--; 93 | assert(self->nfds >= 0); 94 | } 95 | } 96 | 97 | static struct poller_s* poller_new(void) 98 | { 99 | struct poller_s* ret = (struct poller_s*) malloc(sizeof(struct poller_s)); 100 | if (ret != nullptr) 101 | { 102 | ret->pollFds = NULL; 103 | ret->limitSize = 0; 104 | ret->nfds = 0; 105 | upstep_pollfd(ret, 1024); 106 | } 107 | 108 | return ret; 109 | } 110 | 111 | static void poller_delete(struct poller_s* self) 112 | { 113 | free(self->pollFds); 114 | self->pollFds = nullptr; 115 | self->nfds = 0; 116 | self->limitSize = 0; 117 | 118 | free(self); 119 | self = nullptr; 120 | } 121 | 122 | static void poller_add(struct poller_s* self, BrynetSocketFD fd, int type) 123 | { 124 | if (self->limitSize == self->nfds) 125 | { 126 | upstep_pollfd(self, 128); 127 | } 128 | 129 | if (self->limitSize <= self->nfds) 130 | { 131 | return; 132 | } 133 | 134 | struct pollfd* pf = find_pollfd(self, fd); 135 | if (pf == nullptr) 136 | { 137 | /*real add*/ 138 | pf = self->pollFds + self->nfds; 139 | pf->events = 0; 140 | pf->fd = fd; 141 | 142 | self->nfds++; 143 | } 144 | 145 | if (type & ReadCheck) 146 | { 147 | pf->events |= CHECK_READ_FLAG; 148 | } 149 | 150 | if (type & WriteCheck) 151 | { 152 | pf->events |= CHECK_WRITE_FLAG; 153 | } 154 | 155 | if (type & ErrorCheck) 156 | { 157 | //pf->events |= CHECK_ERROR_FLAG; TODO::on windows, not supports 158 | } 159 | } 160 | 161 | static void poller_del(struct poller_s* self, BrynetSocketFD fd, int type) 162 | { 163 | struct pollfd* pf = find_pollfd(self, fd); 164 | if (pf == nullptr) 165 | { 166 | return; 167 | } 168 | 169 | if (type & ReadCheck) 170 | { 171 | pf->events &= ~CHECK_READ_FLAG; 172 | } 173 | 174 | if (type & WriteCheck) 175 | { 176 | pf->events &= ~CHECK_WRITE_FLAG; 177 | } 178 | 179 | if (type & ErrorCheck) 180 | { 181 | pf->events &= ~CHECK_ERROR_FLAG; 182 | } 183 | 184 | if (pf->events == 0) 185 | { 186 | try_remove_pollfd(self, fd); 187 | } 188 | } 189 | 190 | static void poller_remove(struct poller_s* self, BrynetSocketFD fd) 191 | { 192 | try_remove_pollfd(self, fd); 193 | } 194 | 195 | static bool check_event(const struct pollfd* pf, enum CheckType type) 196 | { 197 | if (pf == nullptr) 198 | { 199 | return false; 200 | } 201 | 202 | if ((type & ReadCheck) && 203 | (pf->revents & CHECK_READ_FLAG)) 204 | { 205 | return true; 206 | } 207 | else if ((type & WriteCheck) && 208 | (pf->revents & CHECK_WRITE_FLAG)) 209 | { 210 | return true; 211 | } 212 | else if ((type & ErrorCheck) && 213 | (pf->revents & CHECK_ERROR_FLAG)) 214 | { 215 | return true; 216 | } 217 | else 218 | { 219 | return false; 220 | } 221 | } 222 | 223 | static void poller_visitor(struct poller_s* self, 224 | enum CheckType type, 225 | struct stack_s* result) 226 | { 227 | for (int i = 0; i < self->nfds; i++) 228 | { 229 | if (check_event(self->pollFds + i, type)) 230 | { 231 | stack_push(result, &self->pollFds[i].fd); 232 | } 233 | } 234 | } 235 | 236 | static int poller_poll(struct poller_s* self, long overtime) 237 | { 238 | #ifdef BRYNET_PLATFORM_WINDOWS 239 | int ret = WSAPoll(&self->pollFds[0], self->nfds, overtime); 240 | #elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 241 | int ret = poll(self->pollFds, self->nfds, overtime); 242 | #endif 243 | 244 | if (ret == BRYNET_SOCKET_ERROR) 245 | { 246 | ret = (BRYNET_ERRNO != BRYNET_EINTR) ? -1 : 0; 247 | } 248 | 249 | return ret; 250 | } 251 | 252 | static bool poller_check(struct poller_s* self, BrynetSocketFD fd, enum CheckType type) 253 | { 254 | const struct pollfd* pf = find_pollfd(self, fd); 255 | if (pf == NULL) 256 | { 257 | return false; 258 | } 259 | return check_event(pf, type); 260 | } 261 | 262 | }}// namespace brynet::base 263 | -------------------------------------------------------------------------------- /include/brynet/net/PromiseReceive.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace brynet { namespace net { 6 | 7 | /* binary search in memory */ 8 | void memsearch(const char* hay, size_t haysize, const char* needle, size_t needlesize, size_t& result, bool& isOK) 9 | { 10 | size_t haypos, needlepos; 11 | haysize -= needlesize; 12 | 13 | for (haypos = 0; haypos <= haysize; haypos++) 14 | { 15 | for (needlepos = 0; needlepos < needlesize; needlepos++) 16 | { 17 | if (hay[haypos + needlepos] != needle[needlepos]) 18 | { 19 | // Next character in haystack. 20 | break; 21 | } 22 | } 23 | if (needlepos == needlesize) 24 | { 25 | result = haypos; 26 | isOK = true; 27 | return; 28 | } 29 | } 30 | 31 | isOK = false; 32 | } 33 | 34 | class PromiseReceive; 35 | 36 | std::shared_ptr setupPromiseReceive(const TcpConnection::Ptr& session); 37 | 38 | class PromiseReceive : public std::enable_shared_from_this 39 | { 40 | public: 41 | using Ptr = std::shared_ptr; 42 | using Handle = std::function; 43 | 44 | PromiseReceive::Ptr receive(size_t len, Handle handle) 45 | { 46 | return receive(std::make_shared(len), std::move(handle)); 47 | } 48 | 49 | PromiseReceive::Ptr receive(std::shared_ptr len, Handle handle) 50 | { 51 | return helpReceive(std::move(len), "", std::move(handle)); 52 | } 53 | 54 | PromiseReceive::Ptr receiveUntil(std::string str, Handle handle) 55 | { 56 | if (str.empty()) 57 | { 58 | throw std::runtime_error("str is empty"); 59 | } 60 | 61 | return helpReceive(nullptr, std::move(str), std::move(handle)); 62 | } 63 | 64 | private: 65 | PromiseReceive::Ptr helpReceive(std::shared_ptr len, std::string str, Handle handle) 66 | { 67 | auto pr = std::make_shared(); 68 | pr->len = std::move(len); 69 | pr->str = std::move(str); 70 | pr->handle = std::move(handle); 71 | mPendingReceives.push_back(std::move(pr)); 72 | 73 | return shared_from_this(); 74 | } 75 | 76 | size_t process(const char* buffer, const size_t len) 77 | { 78 | size_t procLen = 0; 79 | 80 | while (!mPendingReceives.empty() && len >= procLen) 81 | { 82 | auto pendingReceive = mPendingReceives.front(); 83 | if (pendingReceive->len != nullptr) 84 | { 85 | const auto tryReceiveLen = *pendingReceive->len; 86 | if ((len - procLen) < tryReceiveLen) 87 | { 88 | break; 89 | } 90 | 91 | mPendingReceives.pop_front(); 92 | procLen += tryReceiveLen; 93 | if (pendingReceive->handle(buffer + procLen - tryReceiveLen, tryReceiveLen) && tryReceiveLen > 0) 94 | { 95 | mPendingReceives.push_front(pendingReceive); 96 | } 97 | } 98 | else if (!pendingReceive->str.empty()) 99 | { 100 | size_t pos = 0; 101 | bool isOK = false; 102 | auto data = buffer + procLen; 103 | memsearch(buffer + procLen, 104 | len - procLen, 105 | pendingReceive->str.c_str(), 106 | pendingReceive->str.size(), 107 | pos, 108 | isOK); 109 | 110 | if (!isOK) 111 | { 112 | break; 113 | } 114 | 115 | mPendingReceives.pop_front(); 116 | procLen += (pos + pendingReceive->str.size()); 117 | if (pendingReceive->handle(data, pos)) 118 | { 119 | mPendingReceives.push_front(pendingReceive); 120 | } 121 | } 122 | else 123 | { 124 | break; 125 | } 126 | } 127 | 128 | return procLen; 129 | } 130 | 131 | private: 132 | struct PendingReceive 133 | { 134 | std::shared_ptr len; 135 | std::string str; 136 | Handle handle; 137 | }; 138 | 139 | std::deque> mPendingReceives; 140 | 141 | friend std::shared_ptr setupPromiseReceive(const TcpConnection::Ptr& session); 142 | }; 143 | 144 | std::shared_ptr setupPromiseReceive(const TcpConnection::Ptr& session) 145 | { 146 | auto promiseReceive = std::make_shared(); 147 | session->setDataCallback([promiseReceive](brynet::base::BasePacketReader& reader) { 148 | auto procLen = promiseReceive->process(reader.begin(), reader.size()); 149 | reader.addPos(procLen); 150 | reader.savePos(); 151 | }); 152 | 153 | return promiseReceive; 154 | } 155 | 156 | }}// namespace brynet::net 157 | -------------------------------------------------------------------------------- /include/brynet/net/SSLHelper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef BRYNET_USE_OPENSSL 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | #include 17 | #include 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | 22 | #endif 23 | 24 | namespace brynet { namespace net { 25 | 26 | #ifdef BRYNET_USE_OPENSSL 27 | 28 | #ifndef CRYPTO_THREADID_set_callback 29 | static void cryptoSetThreadIDCallback(CRYPTO_THREADID* id) 30 | { 31 | #ifdef BRYNET_PLATFORM_WINDOWS 32 | CRYPTO_THREADID_set_numeric(id, 33 | static_cast(GetCurrentThreadId())); 34 | #elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 35 | CRYPTO_THREADID_set_numeric(id, 36 | static_cast(pthread_self())); 37 | #endif 38 | } 39 | #endif 40 | 41 | #ifndef CRYPTO_set_locking_callback 42 | static std::unordered_map> cryptoLocks; 43 | static void cryptoLockingCallback(int mode, 44 | int type, 45 | const char* file, int line) 46 | { 47 | (void) file; 48 | (void) line; 49 | if (mode & CRYPTO_LOCK) 50 | { 51 | cryptoLocks[type]->lock(); 52 | } 53 | else if (mode & CRYPTO_UNLOCK) 54 | { 55 | cryptoLocks[type]->unlock(); 56 | } 57 | } 58 | #endif 59 | 60 | static std::once_flag initCryptoThreadSafeSupportOnceFlag; 61 | static void InitCryptoThreadSafeSupport() 62 | { 63 | #ifndef CRYPTO_THREADID_set_callback 64 | CRYPTO_THREADID_set_callback(cryptoSetThreadIDCallback); 65 | #endif 66 | 67 | #ifndef CRYPTO_set_locking_callback 68 | for (int i = 0; i < CRYPTO_num_locks(); i++) 69 | { 70 | cryptoLocks[i] = std::make_shared(); 71 | } 72 | CRYPTO_set_locking_callback(cryptoLockingCallback); 73 | #endif 74 | } 75 | #endif 76 | 77 | class SSLHelper : public brynet::base::NonCopyable, 78 | public std::enable_shared_from_this 79 | { 80 | public: 81 | using Ptr = std::shared_ptr; 82 | 83 | #ifdef BRYNET_USE_OPENSSL 84 | static void InitThreadSafeSupport() 85 | { 86 | std::call_once(initCryptoThreadSafeSupportOnceFlag, 87 | InitCryptoThreadSafeSupport); 88 | } 89 | 90 | bool initSSL(const std::string& certificate, 91 | const std::string& privatekey) 92 | { 93 | SSLHelper::InitThreadSafeSupport(); 94 | 95 | if (mOpenSSLCTX != nullptr) 96 | { 97 | return false; 98 | } 99 | if (certificate.empty() || privatekey.empty()) 100 | { 101 | return false; 102 | } 103 | 104 | mOpenSSLCTX = SSL_CTX_new(SSLv23_method()); 105 | SSL_CTX_set_client_CA_list(mOpenSSLCTX, 106 | SSL_load_client_CA_file(certificate.c_str())); 107 | SSL_CTX_set_verify_depth(mOpenSSLCTX, 10); 108 | 109 | if (SSL_CTX_use_certificate_chain_file(mOpenSSLCTX, 110 | certificate.c_str()) <= 0) 111 | { 112 | SSL_CTX_free(mOpenSSLCTX); 113 | mOpenSSLCTX = nullptr; 114 | return false; 115 | } 116 | 117 | if (SSL_CTX_use_PrivateKey_file(mOpenSSLCTX, 118 | privatekey.c_str(), 119 | SSL_FILETYPE_PEM) <= 0) 120 | { 121 | SSL_CTX_free(mOpenSSLCTX); 122 | mOpenSSLCTX = nullptr; 123 | return false; 124 | } 125 | 126 | if (!SSL_CTX_check_private_key(mOpenSSLCTX)) 127 | { 128 | SSL_CTX_free(mOpenSSLCTX); 129 | mOpenSSLCTX = nullptr; 130 | return false; 131 | } 132 | 133 | return true; 134 | } 135 | 136 | void destroySSL() 137 | { 138 | if (mOpenSSLCTX != nullptr) 139 | { 140 | SSL_CTX_free(mOpenSSLCTX); 141 | mOpenSSLCTX = nullptr; 142 | } 143 | } 144 | 145 | SSL_CTX* getOpenSSLCTX() 146 | { 147 | return mOpenSSLCTX; 148 | } 149 | #endif 150 | static Ptr Create() 151 | { 152 | class make_shared_enabler : public SSLHelper 153 | { 154 | }; 155 | return std::make_shared(); 156 | } 157 | 158 | protected: 159 | SSLHelper() 160 | { 161 | #ifdef BRYNET_USE_OPENSSL 162 | mOpenSSLCTX = nullptr; 163 | #endif 164 | } 165 | 166 | virtual ~SSLHelper() 167 | { 168 | #ifdef BRYNET_USE_OPENSSL 169 | destroySSL(); 170 | #endif 171 | } 172 | 173 | private: 174 | #ifdef BRYNET_USE_OPENSSL 175 | SSL_CTX* mOpenSSLCTX; 176 | #endif 177 | }; 178 | 179 | }}// namespace brynet::net 180 | -------------------------------------------------------------------------------- /include/brynet/net/SendableMsg.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace brynet { namespace net { 7 | 8 | class SendableMsg 9 | { 10 | public: 11 | using Ptr = std::shared_ptr; 12 | 13 | virtual ~SendableMsg() = default; 14 | 15 | virtual const void* data() = 0; 16 | virtual size_t size() = 0; 17 | }; 18 | 19 | class StringSendMsg : public SendableMsg 20 | { 21 | public: 22 | explicit StringSendMsg(const char* buffer, size_t len) 23 | : mMsg(buffer, len) 24 | {} 25 | 26 | explicit StringSendMsg(const std::string& buffer) 27 | : mMsg(buffer) 28 | {} 29 | 30 | explicit StringSendMsg(std::string&& buffer) 31 | : mMsg(std::move(buffer)) 32 | {} 33 | 34 | const void* data() override 35 | { 36 | return static_cast(mMsg.data()); 37 | } 38 | size_t size() override 39 | { 40 | return mMsg.size(); 41 | } 42 | 43 | private: 44 | std::string mMsg; 45 | }; 46 | 47 | static SendableMsg::Ptr MakeStringMsg(const char* buffer, size_t len) 48 | { 49 | return std::make_shared(buffer, len); 50 | } 51 | 52 | static SendableMsg::Ptr MakeStringMsg(const std::string& buffer) 53 | { 54 | return std::make_shared(buffer); 55 | } 56 | 57 | static SendableMsg::Ptr MakeStringMsg(std::string&& buffer) 58 | { 59 | return std::make_shared(std::move(buffer)); 60 | } 61 | 62 | }}// namespace brynet::net -------------------------------------------------------------------------------- /include/brynet/net/Socket.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace brynet { namespace net { 11 | 12 | class TcpConnection; 13 | 14 | class UniqueFd final : public brynet::base::NonCopyable 15 | { 16 | public: 17 | explicit UniqueFd(BrynetSocketFD fd) 18 | : mFD(fd) 19 | {} 20 | 21 | ~UniqueFd() 22 | { 23 | brynet::net::base::SocketClose(mFD); 24 | } 25 | 26 | UniqueFd(const UniqueFd& other) = delete; 27 | UniqueFd& operator=(const UniqueFd& other) = delete; 28 | 29 | BrynetSocketFD getFD() const 30 | { 31 | return mFD; 32 | } 33 | 34 | private: 35 | BrynetSocketFD mFD; 36 | }; 37 | 38 | class TcpSocket : public brynet::base::NonCopyable 39 | { 40 | private: 41 | class TcpSocketDeleter 42 | { 43 | public: 44 | void operator()(TcpSocket* ptr) const 45 | { 46 | delete ptr; 47 | } 48 | }; 49 | 50 | public: 51 | using Ptr = std::unique_ptr; 52 | 53 | public: 54 | static Ptr Create(BrynetSocketFD fd, bool serverSide) 55 | { 56 | class make_unique_enabler : public TcpSocket 57 | { 58 | public: 59 | make_unique_enabler(BrynetSocketFD fd, bool serverSide) 60 | : TcpSocket(fd, serverSide) 61 | {} 62 | }; 63 | 64 | return Ptr(new make_unique_enabler(fd, serverSide)); 65 | } 66 | 67 | public: 68 | void setNodelay() const 69 | { 70 | brynet::net::base::SocketNodelay(mFD); 71 | } 72 | 73 | bool setNonblock() const 74 | { 75 | return brynet::net::base::SocketNonblock(mFD); 76 | } 77 | 78 | void setSendSize(int sdSize) const 79 | { 80 | brynet::net::base::SocketSetSendSize(mFD, sdSize); 81 | } 82 | 83 | void setRecvSize(int rdSize) const 84 | { 85 | brynet::net::base::SocketSetRecvSize(mFD, rdSize); 86 | } 87 | 88 | std::string getRemoteIP() const 89 | { 90 | return brynet::net::base::GetIPOfSocket(mFD); 91 | } 92 | 93 | bool isServerSide() const 94 | { 95 | return mServerSide; 96 | } 97 | 98 | protected: 99 | TcpSocket(BrynetSocketFD fd, bool serverSide) 100 | : mFD(fd), 101 | mServerSide(serverSide) 102 | { 103 | } 104 | 105 | virtual ~TcpSocket() 106 | { 107 | brynet::net::base::SocketClose(mFD); 108 | } 109 | 110 | BrynetSocketFD getFD() const 111 | { 112 | return mFD; 113 | } 114 | 115 | private: 116 | const BrynetSocketFD mFD; 117 | const bool mServerSide; 118 | 119 | friend class TcpConnection; 120 | }; 121 | 122 | class EintrError : public std::exception 123 | { 124 | }; 125 | 126 | class AcceptError : public std::runtime_error 127 | { 128 | public: 129 | explicit AcceptError(int errorCode) 130 | : std::runtime_error(std::to_string(errorCode)), 131 | mErrorCode(errorCode) 132 | {} 133 | 134 | int getErrorCode() const 135 | { 136 | return mErrorCode; 137 | } 138 | 139 | private: 140 | int mErrorCode; 141 | }; 142 | 143 | class ListenSocket : public brynet::base::NonCopyable 144 | { 145 | private: 146 | class ListenSocketDeleter 147 | { 148 | public: 149 | void operator()(ListenSocket* ptr) const 150 | { 151 | delete ptr; 152 | } 153 | }; 154 | 155 | public: 156 | using Ptr = std::unique_ptr; 157 | 158 | public: 159 | TcpSocket::Ptr accept() 160 | { 161 | const auto clientFD = brynet::net::base::Accept(mFD, nullptr, nullptr); 162 | if (clientFD == BRYNET_INVALID_SOCKET) 163 | { 164 | #if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 165 | if (BRYNET_ERRNO == EMFILE) 166 | { 167 | // Thanks libev and muduo. 168 | // Read the section named "The special problem of 169 | // accept()ing when you can't" in libev's doc. 170 | // By Marc Lehmann, author of libev. 171 | mIdle.reset(); 172 | TcpSocket::Create(brynet::net::base::Accept(mFD, nullptr, nullptr), true); 173 | mIdle = brynet::net::TcpSocket::Create(::open("/dev/null", O_RDONLY | O_CLOEXEC), true); 174 | } 175 | #endif 176 | if (BRYNET_ERRNO == EINTR) 177 | { 178 | throw EintrError(); 179 | } 180 | else 181 | { 182 | throw AcceptError(BRYNET_ERRNO); 183 | } 184 | } 185 | 186 | return TcpSocket::Create(clientFD, true); 187 | } 188 | 189 | public: 190 | static Ptr Create(BrynetSocketFD fd) 191 | { 192 | class make_unique_enabler : public ListenSocket 193 | { 194 | public: 195 | explicit make_unique_enabler(BrynetSocketFD fd) 196 | : ListenSocket(fd) 197 | {} 198 | }; 199 | 200 | return Ptr(new make_unique_enabler(fd)); 201 | } 202 | 203 | protected: 204 | explicit ListenSocket(BrynetSocketFD fd) 205 | : mFD(fd) 206 | { 207 | #if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 208 | mIdle = brynet::net::TcpSocket::Create(::open("/dev/null", O_RDONLY | O_CLOEXEC), true); 209 | #endif 210 | } 211 | 212 | virtual ~ListenSocket() 213 | { 214 | brynet::net::base::SocketClose(mFD); 215 | } 216 | 217 | private: 218 | const BrynetSocketFD mFD; 219 | #if defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 220 | brynet::net::TcpSocket::Ptr mIdle; 221 | #endif 222 | 223 | friend class TcpConnection; 224 | }; 225 | 226 | }}// namespace brynet::net 227 | -------------------------------------------------------------------------------- /include/brynet/net/SocketLibTypes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef BRYNET_PLATFORM_WINDOWS 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #elif defined BRYNET_PLATFORM_LINUX 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #elif defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #else 50 | #error "Unsupported OS, please commit an issuse." 51 | #endif 52 | 53 | #ifdef BRYNET_PLATFORM_WINDOWS 54 | typedef SOCKET BrynetSocketFD; 55 | #define BRYNET_ERRNO WSAGetLastError() 56 | #define BRYNET_ENOTSOCK WSAENOTSOCK 57 | #define BRYNET_EWOULDBLOCK WSAEWOULDBLOCK 58 | #define BRYNET_EINTR WSAEINTR 59 | #define BRYNET_ECONNABORTED WSAECONNABORTED 60 | #define BRYNET_SOCKET_ERROR SOCKET_ERROR 61 | #define BRYNET_INVALID_SOCKET INVALID_SOCKET 62 | 63 | #elif defined BRYNET_PLATFORM_LINUX || defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 64 | #define BRYNET_ERRNO errno 65 | #define BRYNET_ENOTSOCK EBADF 66 | #define BRYNET_EWOULDBLOCK EAGAIN 67 | #define BRYNET_EINTR EINTR 68 | #define BRYNET_ECONNABORTED ECONNABORTED 69 | typedef int BrynetSocketFD; 70 | #define BRYNET_SOCKET_ERROR (-1) 71 | #define BRYNET_INVALID_SOCKET (-1) 72 | #endif 73 | -------------------------------------------------------------------------------- /include/brynet/net/TcpService.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace brynet { namespace net { 6 | 7 | using ConnectionOption = detail::ConnectionOption; 8 | 9 | class ITcpService 10 | { 11 | public: 12 | using Ptr = std::shared_ptr; 13 | 14 | public: 15 | virtual ~ITcpService() = default; 16 | virtual void addTcpConnection(TcpSocket::Ptr socket, ConnectionOption options) = 0; 17 | }; 18 | 19 | // use multi IO threads process IO 20 | class IOThreadTcpService : public ITcpService, 21 | public detail::TcpServiceDetail, 22 | public std::enable_shared_from_this 23 | { 24 | public: 25 | using Ptr = std::shared_ptr; 26 | using FrameCallback = detail::TcpServiceDetail::FrameCallback; 27 | 28 | public: 29 | static Ptr Create() 30 | { 31 | struct make_shared_enabler : public IOThreadTcpService 32 | { 33 | }; 34 | return std::make_shared(); 35 | } 36 | 37 | std::vector startWorkerThread(size_t threadNum, 38 | FrameCallback callback = nullptr) 39 | { 40 | return detail::TcpServiceDetail::startWorkerThread(threadNum, callback); 41 | } 42 | 43 | void stopWorkerThread() 44 | { 45 | detail::TcpServiceDetail::stopWorkerThread(); 46 | } 47 | 48 | void addTcpConnection(TcpSocket::Ptr socket, ConnectionOption options) override 49 | { 50 | return detail::TcpServiceDetail::addTcpConnection(std::move(socket), std::move(options)); 51 | } 52 | 53 | private: 54 | IOThreadTcpService() = default; 55 | }; 56 | 57 | // use specified eventloop for process IO 58 | class EventLoopTcpService : public ITcpService 59 | { 60 | public: 61 | using Ptr = std::shared_ptr; 62 | 63 | EventLoopTcpService(EventLoop::Ptr eventLoop) 64 | : mEventLoop(eventLoop) 65 | {} 66 | 67 | static Ptr Create(EventLoop::Ptr eventLoop) 68 | { 69 | return std::make_shared(eventLoop); 70 | } 71 | 72 | void addTcpConnection(TcpSocket::Ptr socket, ConnectionOption options) override 73 | { 74 | detail::HelperAddTcpConnection(mEventLoop, std::move(socket), std::move(options)); 75 | } 76 | 77 | private: 78 | EventLoop::Ptr mEventLoop; 79 | }; 80 | 81 | }}// namespace brynet::net 82 | -------------------------------------------------------------------------------- /include/brynet/net/detail/ConnectionOption.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace brynet { namespace net { namespace detail { 7 | 8 | class ConnectionOption final 9 | { 10 | public: 11 | std::vector enterCallback; 12 | std::function enterFailedCallback; 13 | SSLHelper::Ptr sslHelper; 14 | bool useSSL = false; 15 | bool forceSameThreadLoop = false; 16 | size_t maxRecvBufferSize = 128; 17 | }; 18 | 19 | }}}// namespace brynet::net::detail 20 | -------------------------------------------------------------------------------- /include/brynet/net/detail/ConnectorDetail.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace brynet { namespace net { namespace detail { 14 | 15 | class AsyncConnectorDetail : public brynet::base::NonCopyable 16 | { 17 | protected: 18 | void startWorkerThread() 19 | { 20 | std::lock_guard lck(mThreadGuard); 21 | 22 | if (mThread != nullptr) 23 | { 24 | throw std::runtime_error("connect thread already started"); 25 | } 26 | 27 | mIsRun = std::make_shared(true); 28 | mWorkInfo = std::make_shared(); 29 | mEventLoop = std::make_shared(); 30 | 31 | auto eventLoop = mEventLoop; 32 | auto workerInfo = mWorkInfo; 33 | auto isRun = mIsRun; 34 | 35 | auto wg = brynet::base::WaitGroup::Create(); 36 | wg->add(1); 37 | mThread = std::make_shared([wg, eventLoop, workerInfo, isRun]() { 38 | eventLoop->bindCurrentThread(); 39 | wg->done(); 40 | 41 | while (*isRun) 42 | { 43 | detail::RunOnceCheckConnect(eventLoop, workerInfo); 44 | } 45 | 46 | workerInfo->causeAllFailed(); 47 | }); 48 | 49 | wg->wait(); 50 | } 51 | 52 | void stopWorkerThread() 53 | { 54 | std::lock_guard lck(mThreadGuard); 55 | 56 | if (mThread == nullptr) 57 | { 58 | return; 59 | } 60 | 61 | mEventLoop->runAsyncFunctor([this]() { 62 | *mIsRun = false; 63 | }); 64 | 65 | try 66 | { 67 | if (mThread->joinable()) 68 | { 69 | mThread->join(); 70 | } 71 | } 72 | catch (std::system_error& e) 73 | { 74 | (void) e; 75 | } 76 | 77 | mEventLoop = nullptr; 78 | mWorkInfo = nullptr; 79 | mIsRun = nullptr; 80 | mThread = nullptr; 81 | } 82 | 83 | void asyncConnect(detail::ConnectOption option) 84 | { 85 | std::lock_guard lck(mThreadGuard); 86 | 87 | if (option.completedCallback == nullptr && option.failedCallback == nullptr) 88 | { 89 | throw ConnectException("all callback is nullptr"); 90 | } 91 | if (option.ip.empty()) 92 | { 93 | throw ConnectException("addr is empty"); 94 | } 95 | 96 | if (!(*mIsRun)) 97 | { 98 | throw ConnectException("work thread already stop"); 99 | } 100 | 101 | auto workInfo = mWorkInfo; 102 | auto address = detail::AsyncConnectAddr(std::move(option.ip), 103 | option.port, 104 | option.timeout, 105 | std::move(option.completedCallback), 106 | std::move(option.failedCallback), 107 | std::move(option.processCallbacks)); 108 | mEventLoop->runAsyncFunctor([workInfo, address]() { 109 | workInfo->processConnect(address); 110 | }); 111 | } 112 | 113 | protected: 114 | AsyncConnectorDetail() 115 | { 116 | mIsRun = std::make_shared(false); 117 | } 118 | 119 | virtual ~AsyncConnectorDetail() 120 | { 121 | stopWorkerThread(); 122 | } 123 | 124 | private: 125 | std::shared_ptr mEventLoop; 126 | 127 | std::shared_ptr mWorkInfo; 128 | std::shared_ptr mThread; 129 | std::mutex mThreadGuard; 130 | std::shared_ptr mIsRun; 131 | }; 132 | 133 | }}}// namespace brynet::net::detail 134 | -------------------------------------------------------------------------------- /include/brynet/net/detail/IOLoopData.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace brynet { namespace net { namespace detail { 9 | 10 | class TcpServiceDetail; 11 | 12 | class IOLoopData : public brynet::base::NonCopyable, 13 | public std::enable_shared_from_this 14 | { 15 | public: 16 | using Ptr = std::shared_ptr; 17 | 18 | static Ptr Create(EventLoop::Ptr eventLoop, 19 | std::shared_ptr ioThread) 20 | { 21 | class make_shared_enabler : public IOLoopData 22 | { 23 | public: 24 | make_shared_enabler(EventLoop::Ptr eventLoop, 25 | std::shared_ptr ioThread) 26 | : IOLoopData(std::move(eventLoop), std::move(ioThread)) 27 | {} 28 | }; 29 | 30 | return std::make_shared(std::move(eventLoop), 31 | std::move(ioThread)); 32 | } 33 | 34 | const EventLoop::Ptr& getEventLoop() const 35 | { 36 | return mEventLoop; 37 | } 38 | 39 | protected: 40 | const std::shared_ptr& getIOThread() const 41 | { 42 | return mIOThread; 43 | } 44 | 45 | IOLoopData(EventLoop::Ptr eventLoop, 46 | std::shared_ptr ioThread) 47 | : mEventLoop(std::move(eventLoop)), 48 | mIOThread(std::move(ioThread)) 49 | {} 50 | virtual ~IOLoopData() = default; 51 | 52 | const EventLoop::Ptr mEventLoop; 53 | 54 | private: 55 | std::shared_ptr mIOThread; 56 | 57 | friend class TcpServiceDetail; 58 | }; 59 | 60 | using IOLoopDataPtr = std::shared_ptr; 61 | 62 | }}}// namespace brynet::net::detail 63 | -------------------------------------------------------------------------------- /include/brynet/net/detail/ListenThreadDetail.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace brynet { namespace net { namespace detail { 17 | 18 | class ListenThreadDetail : public brynet::base::NonCopyable 19 | { 20 | protected: 21 | using AccepCallback = std::function; 22 | using TcpSocketProcessCallback = std::function; 23 | 24 | void startListen() 25 | { 26 | std::lock_guard lck(mListenThreadGuard); 27 | 28 | if (mListenThread != nullptr) 29 | { 30 | throw std::runtime_error("listen thread already started"); 31 | } 32 | 33 | const auto fd = brynet::net::base::Listen(mIsIPV6, mIP.c_str(), mPort, 512, mEnabledReusePort); 34 | if (fd == BRYNET_INVALID_SOCKET) 35 | { 36 | throw BrynetCommonException( 37 | std::string("listen error of:") + std::to_string(BRYNET_ERRNO)); 38 | } 39 | 40 | mRunListen = std::make_shared(true); 41 | 42 | auto listenSocket = std::shared_ptr(ListenSocket::Create(fd)); 43 | auto isRunListen = mRunListen; 44 | auto callback = mCallback; 45 | auto processCallbacks = mProcessCallbacks; 46 | mListenThread = std::make_shared( 47 | [isRunListen, listenSocket, callback, processCallbacks]() mutable { 48 | while (*isRunListen) 49 | { 50 | auto clientSocket = runOnceListen(listenSocket); 51 | if (clientSocket == nullptr) 52 | { 53 | continue; 54 | } 55 | 56 | if (*isRunListen) 57 | { 58 | for (const auto& process : processCallbacks) 59 | { 60 | process(*clientSocket); 61 | } 62 | callback(std::move(clientSocket)); 63 | } 64 | } 65 | }); 66 | } 67 | 68 | void stopListen() 69 | { 70 | std::lock_guard lck(mListenThreadGuard); 71 | 72 | if (mListenThread == nullptr) 73 | { 74 | return; 75 | } 76 | 77 | *mRunListen = false; 78 | auto selfIP = mIP; 79 | if (selfIP == "0.0.0.0") 80 | { 81 | selfIP = "127.0.0.1"; 82 | } 83 | 84 | auto connector = AsyncConnector::Create(); 85 | connector->startWorkerThread(); 86 | 87 | //TODO:: if the listen enable reuse_port, one time connect may be can't wakeup listen. 88 | wrapper::SocketConnectBuilder connectBuilder; 89 | (void) connectBuilder 90 | .WithConnector(connector) 91 | .WithTimeout(std::chrono::seconds(2)) 92 | .WithAddr(selfIP, mPort) 93 | .syncConnect(); 94 | 95 | try 96 | { 97 | if (mListenThread->joinable()) 98 | { 99 | mListenThread->join(); 100 | } 101 | } 102 | catch (std::system_error& e) 103 | { 104 | (void) e; 105 | } 106 | mListenThread = nullptr; 107 | } 108 | 109 | protected: 110 | ListenThreadDetail(bool isIPV6, 111 | const std::string& ip, 112 | int port, 113 | const AccepCallback& callback, 114 | const std::vector& processCallbacks, 115 | bool enabledReusePort) 116 | : mIsIPV6(isIPV6), 117 | mIP(ip), 118 | mPort(port), 119 | mCallback(callback), 120 | mProcessCallbacks(processCallbacks), 121 | mEnabledReusePort(enabledReusePort) 122 | { 123 | if (mCallback == nullptr) 124 | { 125 | throw BrynetCommonException("accept callback is nullptr"); 126 | } 127 | mRunListen = std::make_shared(false); 128 | } 129 | 130 | virtual ~ListenThreadDetail() 131 | { 132 | stopListen(); 133 | } 134 | 135 | private: 136 | static brynet::net::TcpSocket::Ptr runOnceListen(const std::shared_ptr& listenSocket) 137 | { 138 | try 139 | { 140 | return listenSocket->accept(); 141 | } 142 | catch (const EintrError& e) 143 | { 144 | std::cerr << "accept EINTR execption:" << e.what() << std::endl; 145 | } 146 | catch (const AcceptError& e) 147 | { 148 | std::cerr << "accept execption:" << e.what() << std::endl; 149 | } 150 | 151 | return nullptr; 152 | } 153 | 154 | private: 155 | const bool mIsIPV6; 156 | const std::string mIP; 157 | const int mPort; 158 | const AccepCallback mCallback; 159 | const std::vector mProcessCallbacks; 160 | const bool mEnabledReusePort; 161 | 162 | std::shared_ptr mRunListen; 163 | std::shared_ptr mListenThread; 164 | std::mutex mListenThreadGuard; 165 | }; 166 | 167 | }}}// namespace brynet::net::detail 168 | -------------------------------------------------------------------------------- /include/brynet/net/detail/TCPServiceDetail.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace brynet { namespace net { namespace detail { 17 | 18 | static void HelperAddTcpConnection(const EventLoop::Ptr& eventLoop, TcpSocket::Ptr socket, ConnectionOption option) 19 | { 20 | if (eventLoop == nullptr) 21 | { 22 | throw BrynetCommonException("event loop is null"); 23 | } 24 | if (option.maxRecvBufferSize <= 0) 25 | { 26 | throw BrynetCommonException("buffer size is zero"); 27 | } 28 | 29 | auto wrapperEnterCallback = [option](const TcpConnection::Ptr& tcpConnection) { 30 | for (const auto& callback : option.enterCallback) 31 | { 32 | callback(tcpConnection); 33 | } 34 | }; 35 | 36 | if (option.useSSL && option.sslHelper == nullptr) 37 | { 38 | option.sslHelper = SSLHelper::Create(); 39 | } 40 | 41 | TcpConnection::Create(std::move(socket), 42 | option.maxRecvBufferSize, 43 | wrapperEnterCallback, 44 | eventLoop, 45 | option.sslHelper, 46 | option.enterFailedCallback); 47 | } 48 | 49 | class TcpServiceDetail : public brynet::base::NonCopyable 50 | { 51 | protected: 52 | using FrameCallback = std::function; 53 | const static unsigned int sDefaultLoopTimeOutMS = 100; 54 | 55 | TcpServiceDetail() 56 | : mRandom(static_cast( 57 | std::chrono::system_clock::now().time_since_epoch().count())) 58 | { 59 | mRunIOLoop = std::make_shared(false); 60 | } 61 | 62 | virtual ~TcpServiceDetail() 63 | { 64 | stopWorkerThread(); 65 | } 66 | 67 | std::vector startWorkerThread(size_t threadNum, 68 | FrameCallback callback = nullptr) 69 | { 70 | if (threadNum == 0) 71 | { 72 | throw std::runtime_error("thread num is zero"); 73 | } 74 | 75 | std::vector eventLoops; 76 | 77 | std::lock_guard lck(mServiceGuard); 78 | std::lock_guard lock(mIOLoopGuard); 79 | 80 | if (!mIOLoopDatas.empty()) 81 | { 82 | throw std::runtime_error("worker thread already started"); 83 | } 84 | 85 | mRunIOLoop = std::make_shared(true); 86 | 87 | mIOLoopDatas.resize(threadNum); 88 | auto wg = brynet::base::WaitGroup::Create(); 89 | for (auto& v : mIOLoopDatas) 90 | { 91 | auto eventLoop = std::make_shared(); 92 | eventLoops.push_back(eventLoop); 93 | 94 | auto runIoLoop = mRunIOLoop; 95 | wg->add(1); 96 | v = IOLoopData::Create(eventLoop, 97 | std::make_shared( 98 | [wg, callback, runIoLoop, eventLoop]() { 99 | eventLoop->bindCurrentThread(); 100 | wg->done(); 101 | 102 | while (*runIoLoop) 103 | { 104 | eventLoop->loopCompareNearTimer(sDefaultLoopTimeOutMS); 105 | if (callback != nullptr) 106 | { 107 | callback(eventLoop); 108 | } 109 | } 110 | })); 111 | } 112 | wg->wait(); 113 | 114 | return eventLoops; 115 | } 116 | 117 | void stopWorkerThread() 118 | { 119 | std::lock_guard lck(mServiceGuard); 120 | std::lock_guard lock(mIOLoopGuard); 121 | 122 | *mRunIOLoop = false; 123 | 124 | for (const auto& v : mIOLoopDatas) 125 | { 126 | v->getEventLoop()->wakeup(); 127 | try 128 | { 129 | if (v->getIOThread()->joinable()) 130 | { 131 | v->getIOThread()->join(); 132 | } 133 | } 134 | catch (std::system_error& e) 135 | { 136 | (void) e; 137 | } 138 | } 139 | mIOLoopDatas.clear(); 140 | } 141 | 142 | void addTcpConnection(TcpSocket::Ptr socket, ConnectionOption option) 143 | { 144 | EventLoop::Ptr eventLoop; 145 | if (option.forceSameThreadLoop) 146 | { 147 | eventLoop = getSameThreadEventLoop(); 148 | } 149 | else 150 | { 151 | eventLoop = getRandomEventLoop(); 152 | } 153 | return HelperAddTcpConnection(eventLoop, std::move(socket), std::move(option)); 154 | } 155 | 156 | EventLoop::Ptr getRandomEventLoop() 157 | { 158 | std::lock_guard lock(mIOLoopGuard); 159 | 160 | if (mIOLoopDatas.empty()) 161 | { 162 | return nullptr; 163 | } 164 | else 165 | { 166 | return mIOLoopDatas[mRandom() % mIOLoopDatas.size()]->getEventLoop(); 167 | } 168 | } 169 | 170 | EventLoop::Ptr getSameThreadEventLoop() 171 | { 172 | std::lock_guard lock(mIOLoopGuard); 173 | for (const auto& v : mIOLoopDatas) 174 | { 175 | if (v->getEventLoop()->isInLoopThread()) 176 | { 177 | return v->getEventLoop(); 178 | } 179 | } 180 | return nullptr; 181 | } 182 | 183 | private: 184 | std::vector mIOLoopDatas; 185 | mutable std::mutex mIOLoopGuard; 186 | std::shared_ptr mRunIOLoop; 187 | 188 | std::mutex mServiceGuard; 189 | std::mt19937 mRandom; 190 | }; 191 | 192 | }}}// namespace brynet::net::detail 193 | -------------------------------------------------------------------------------- /include/brynet/net/detail/WakeupChannel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #ifdef BRYNET_PLATFORM_WINDOWS 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | namespace brynet { namespace net { namespace detail { 12 | 13 | #ifdef BRYNET_PLATFORM_WINDOWS 14 | class WakeupChannel final : public Channel, public brynet::base::NonCopyable 15 | { 16 | public: 17 | explicit WakeupChannel(HANDLE iocp) 18 | : mIOCP(iocp), 19 | mWakeupOvl(port::Win::OverlappedType::OverlappedRecv) 20 | { 21 | } 22 | 23 | bool wakeup() 24 | { 25 | return PostQueuedCompletionStatus(mIOCP, 26 | 0, 27 | reinterpret_cast(this), 28 | &mWakeupOvl.base); 29 | } 30 | 31 | private: 32 | void canRecv(bool) override 33 | { 34 | ; 35 | } 36 | 37 | void canSend() override 38 | { 39 | ; 40 | } 41 | 42 | void onClose() override 43 | { 44 | ; 45 | } 46 | 47 | HANDLE mIOCP; 48 | port::Win::OverlappedExt mWakeupOvl; 49 | }; 50 | #elif defined BRYNET_PLATFORM_LINUX 51 | class WakeupChannel final : public Channel, public brynet::base::NonCopyable 52 | { 53 | public: 54 | explicit WakeupChannel(BrynetSocketFD fd) 55 | : mUniqueFd(fd) 56 | { 57 | } 58 | 59 | bool wakeup() 60 | { 61 | uint64_t one = 1; 62 | return write(mUniqueFd.getFD(), &one, sizeof one) > 0; 63 | } 64 | 65 | private: 66 | void canRecv(bool) override 67 | { 68 | char temp[1024 * 10]; 69 | while (true) 70 | { 71 | auto n = read(mUniqueFd.getFD(), temp, sizeof(temp)); 72 | if (n == -1 || static_cast(n) < sizeof(temp)) 73 | { 74 | break; 75 | } 76 | } 77 | } 78 | 79 | void canSend() override 80 | { 81 | } 82 | 83 | void onClose() override 84 | { 85 | } 86 | 87 | private: 88 | UniqueFd mUniqueFd; 89 | }; 90 | 91 | #elif defined BRYNET_PLATFORM_DARWIN || defined BRYNET_PLATFORM_FREEBSD 92 | class WakeupChannel final : public Channel, public brynet::base::NonCopyable 93 | { 94 | public: 95 | explicit WakeupChannel(int kqueuefd, int ident) 96 | : mKqueueFd(kqueuefd), 97 | mUserEvent(ident) 98 | { 99 | } 100 | 101 | bool wakeup() 102 | { 103 | struct kevent ev; 104 | EV_SET(&ev, mUserEvent, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); 105 | 106 | struct timespec timeout = {0, 0}; 107 | return kevent(mKqueueFd, &ev, 1, NULL, 0, &timeout) == 0; 108 | } 109 | 110 | private: 111 | void canRecv(bool) override 112 | { 113 | } 114 | 115 | void canSend() override 116 | { 117 | } 118 | 119 | void onClose() override 120 | { 121 | } 122 | 123 | private: 124 | int mKqueueFd; 125 | int mUserEvent; 126 | }; 127 | #endif 128 | 129 | }}}// namespace brynet::net::detail 130 | -------------------------------------------------------------------------------- /include/brynet/net/http/HttpFormat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace brynet { namespace net { namespace http { 9 | 10 | class HttpQueryParameter final 11 | { 12 | public: 13 | void add(const std::string& k, const std::string& v) 14 | { 15 | if (!mParameter.empty()) 16 | { 17 | mParameter += "&"; 18 | } 19 | 20 | mParameter += k; 21 | mParameter += "="; 22 | mParameter += v; 23 | } 24 | 25 | const std::string& getResult() const 26 | { 27 | return mParameter; 28 | } 29 | 30 | private: 31 | std::string mParameter; 32 | }; 33 | 34 | class HttpRequest final 35 | { 36 | public: 37 | enum class HTTP_METHOD 38 | { 39 | HTTP_METHOD_HEAD, 40 | HTTP_METHOD_GET, 41 | HTTP_METHOD_POST, 42 | HTTP_METHOD_PUT, 43 | HTTP_METHOD_DELETE, 44 | HTTP_METHOD_MAX 45 | }; 46 | 47 | HttpRequest() 48 | { 49 | setMethod(HTTP_METHOD::HTTP_METHOD_GET); 50 | } 51 | 52 | void setMethod(HTTP_METHOD protocol) 53 | { 54 | mMethod = protocol; 55 | assert(mMethod >= HTTP_METHOD::HTTP_METHOD_HEAD && 56 | mMethod < HTTP_METHOD::HTTP_METHOD_MAX); 57 | } 58 | 59 | void setHost(const std::string& host) 60 | { 61 | addHeadValue("Host", host); 62 | } 63 | 64 | void setUrl(const std::string& url) 65 | { 66 | mUrl = url; 67 | } 68 | 69 | void setCookie(const std::string& v) 70 | { 71 | addHeadValue("Cookie", v); 72 | } 73 | 74 | void setContentType(const std::string& v) 75 | { 76 | addHeadValue("Content-Type", v); 77 | } 78 | 79 | void setQuery(const std::string& query) 80 | { 81 | mQuery = query; 82 | } 83 | 84 | void setBody(const std::string& body) 85 | { 86 | addHeadValue("Content-Length", std::to_string(body.size())); 87 | mBody = body; 88 | } 89 | 90 | void setBody(std::string&& body) 91 | { 92 | addHeadValue("Content-Length", std::to_string(body.size())); 93 | mBody = std::move(body); 94 | } 95 | 96 | void addHeadValue(const std::string& field, 97 | const std::string& value) 98 | { 99 | mHeadField[field] = value; 100 | } 101 | 102 | std::string getResult() const 103 | { 104 | const auto MethodMax = static_cast(HTTP_METHOD::HTTP_METHOD_MAX); 105 | const static std::array HttpMethodString = 106 | {"HEAD", "GET", "POST", "PUT", "DELETE"}; 107 | 108 | std::string ret; 109 | if (mMethod >= HTTP_METHOD::HTTP_METHOD_HEAD && 110 | mMethod < HTTP_METHOD::HTTP_METHOD_MAX) 111 | { 112 | ret += HttpMethodString[static_cast(mMethod)]; 113 | } 114 | 115 | ret += " "; 116 | ret += mUrl; 117 | if (!mQuery.empty()) 118 | { 119 | ret += "?"; 120 | ret += mQuery; 121 | } 122 | 123 | ret += " HTTP/1.1\r\n"; 124 | 125 | for (auto& v : mHeadField) 126 | { 127 | ret += v.first; 128 | ret += ": "; 129 | ret += v.second; 130 | ret += "\r\n"; 131 | } 132 | 133 | ret += "\r\n"; 134 | 135 | if (!mBody.empty()) 136 | { 137 | ret += mBody; 138 | } 139 | 140 | return ret; 141 | } 142 | 143 | private: 144 | std::string mUrl; 145 | std::string mQuery; 146 | std::string mBody; 147 | HTTP_METHOD mMethod; 148 | std::map mHeadField; 149 | }; 150 | 151 | class HttpResponse final 152 | { 153 | public: 154 | enum class HTTP_RESPONSE_STATUS 155 | { 156 | NONE, 157 | OK = 200, 158 | }; 159 | 160 | HttpResponse() 161 | : mStatus(HTTP_RESPONSE_STATUS::OK) 162 | { 163 | } 164 | 165 | void setStatus(HTTP_RESPONSE_STATUS status) 166 | { 167 | mStatus = status; 168 | } 169 | 170 | void setContentType(const std::string& v) 171 | { 172 | addHeadValue("Content-Type", v); 173 | } 174 | 175 | void addHeadValue(const std::string& field, 176 | const std::string& value) 177 | { 178 | mHeadField[field] = value; 179 | } 180 | 181 | void setBody(const std::string& body) 182 | { 183 | addHeadValue("Content-Length", std::to_string(body.size())); 184 | mBody = body; 185 | } 186 | 187 | void setBody(std::string&& body) 188 | { 189 | addHeadValue("Content-Length", std::to_string(body.size())); 190 | mBody = std::move(body); 191 | } 192 | 193 | std::string getResult() const 194 | { 195 | std::string ret = "HTTP/1.1 "; 196 | 197 | ret += std::to_string(static_cast(mStatus)); 198 | switch (mStatus) 199 | { 200 | case HTTP_RESPONSE_STATUS::OK: 201 | ret += " OK"; 202 | break; 203 | default: 204 | ret += "UNKNOWN"; 205 | break; 206 | } 207 | 208 | ret += "\r\n"; 209 | 210 | for (auto& v : mHeadField) 211 | { 212 | ret += v.first; 213 | ret += ": "; 214 | ret += v.second; 215 | ret += "\r\n"; 216 | } 217 | 218 | ret += "\r\n"; 219 | 220 | if (!mBody.empty()) 221 | { 222 | ret += mBody; 223 | } 224 | 225 | return ret; 226 | } 227 | 228 | private: 229 | HTTP_RESPONSE_STATUS mStatus; 230 | std::map mHeadField; 231 | std::string mBody; 232 | }; 233 | 234 | }}}// namespace brynet::net::http 235 | -------------------------------------------------------------------------------- /include/brynet/net/http/WebSocketFormat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace brynet { namespace net { namespace http { 12 | 13 | class WebSocketFormat 14 | { 15 | public: 16 | enum class WebSocketFrameType 17 | { 18 | ERROR_FRAME = 0xff, 19 | CONTINUATION_FRAME = 0x00, 20 | TEXT_FRAME = 0x01, 21 | BINARY_FRAME = 0x02, 22 | CLOSE_FRAME = 0x08, 23 | PING_FRAME = 0x09, 24 | PONG_FRAME = 0x0A 25 | }; 26 | 27 | static std::string wsHandshake(std::string secKey) 28 | { 29 | secKey.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); 30 | 31 | CSHA1 s1; 32 | s1.Update((unsigned char*) secKey.c_str(), static_cast(secKey.size())); 33 | s1.Final(); 34 | unsigned char puDest[20]; 35 | s1.GetHash(puDest); 36 | 37 | std::string base64Str = brynet::base::crypto::base64_encode((const unsigned char*) puDest, 20); 38 | 39 | std::string response = 40 | "HTTP/1.1 101 Switching Protocols\r\n" 41 | "Upgrade: websocket\r\n" 42 | "Connection: Upgrade\r\n" 43 | "Sec-WebSocket-Accept: "; 44 | 45 | response += base64Str; 46 | response += "\r\n\r\n"; 47 | 48 | return response; 49 | } 50 | 51 | static bool wsFrameBuild(const char* payload, 52 | size_t payloadLen, 53 | std::string& frame, 54 | WebSocketFrameType frame_type = WebSocketFrameType::TEXT_FRAME, 55 | bool isFin = true, 56 | bool masking = false) 57 | { 58 | const auto unixTime = std::chrono::system_clock::now().time_since_epoch().count(); 59 | static std::mt19937 random(static_cast(unixTime)); 60 | 61 | static_assert(std::is_same::value, ""); 62 | 63 | const uint8_t head = static_cast(frame_type) | (isFin ? 0x80 : 0x00); 64 | 65 | frame.clear(); 66 | frame.push_back(static_cast(head)); 67 | if (payloadLen <= 125) 68 | { 69 | // mask << 7 | payloadLen, mask = 0 70 | frame.push_back(static_cast(payloadLen)); 71 | } 72 | else if (payloadLen <= 0xFFFF) 73 | { 74 | // 126 + 16bit len 75 | frame.push_back(126); 76 | frame.push_back((payloadLen & 0xFF00) >> 8); 77 | frame.push_back(payloadLen & 0x00FF); 78 | } 79 | else 80 | { 81 | // 127 + 64bit len 82 | frame.push_back(127); 83 | // assume payload len is less than u_int32_max 84 | frame.push_back(0x00); 85 | frame.push_back(0x00); 86 | frame.push_back(0x00); 87 | frame.push_back(0x00); 88 | frame.push_back(static_cast((payloadLen & 0xFF000000) >> 24)); 89 | frame.push_back(static_cast((payloadLen & 0x00FF0000) >> 16)); 90 | frame.push_back(static_cast((payloadLen & 0x0000FF00) >> 8)); 91 | frame.push_back(static_cast(payloadLen & 0x000000FF)); 92 | } 93 | 94 | if (masking) 95 | { 96 | frame[1] = ((uint8_t) frame[1]) | 0x80; 97 | uint8_t mask[4]; 98 | for (auto& m : mask) 99 | { 100 | m = static_cast(random()); 101 | frame.push_back(m); 102 | } 103 | 104 | frame.reserve(frame.size() + payloadLen); 105 | 106 | for (size_t i = 0; i < payloadLen; i++) 107 | { 108 | frame.push_back(static_cast(payload[i]) ^ mask[i % 4]); 109 | } 110 | } 111 | else 112 | { 113 | frame.append(payload, payloadLen); 114 | } 115 | 116 | return true; 117 | } 118 | 119 | static std::string wsFrameBuild(const std::string& payload, 120 | bool isFin = true, 121 | bool masking = false) 122 | { 123 | std::string frame; 124 | wsFrameBuild(payload.c_str(), 125 | payload.size(), 126 | frame, 127 | WebSocketFormat::WebSocketFrameType::TEXT_FRAME, 128 | isFin, 129 | masking); 130 | return frame;//auto move 131 | } 132 | static bool wsFrameBuild(const std::string& payload, 133 | std::string& frame, 134 | WebSocketFrameType frame_type = WebSocketFrameType::TEXT_FRAME, 135 | bool isFin = true, 136 | bool masking = false) 137 | { 138 | return wsFrameBuild(payload.c_str(), 139 | payload.size(), 140 | frame, 141 | frame_type, 142 | isFin, 143 | masking); 144 | } 145 | 146 | static bool wsFrameExtractBuffer(const char* inbuffer, 147 | const size_t bufferSize, 148 | std::string& payload, 149 | WebSocketFrameType& outopcode, 150 | size_t& frameSize, 151 | bool& outfin) 152 | { 153 | const auto buffer = (const unsigned char*) inbuffer; 154 | 155 | if (bufferSize < 2) 156 | { 157 | return false; 158 | } 159 | 160 | outfin = (buffer[0] & 0x80) != 0; 161 | outopcode = (WebSocketFrameType)(buffer[0] & 0x0F); 162 | const bool isMasking = (buffer[1] & 0x80) != 0; 163 | uint32_t payloadlen = buffer[1] & 0x7F; 164 | 165 | uint32_t pos = 2; 166 | if (payloadlen == 126) 167 | { 168 | if (bufferSize < 4) 169 | { 170 | return false; 171 | } 172 | 173 | payloadlen = (buffer[2] << 8) + buffer[3]; 174 | pos = 4; 175 | } 176 | else if (payloadlen == 127) 177 | { 178 | if (bufferSize < 10) 179 | { 180 | return false; 181 | } 182 | 183 | if (buffer[2] != 0 || 184 | buffer[3] != 0 || 185 | buffer[4] != 0 || 186 | buffer[5] != 0) 187 | { 188 | return false; 189 | } 190 | 191 | if ((buffer[6] & 0x80) != 0) 192 | { 193 | return false; 194 | } 195 | 196 | payloadlen = (buffer[6] << 24) + 197 | (buffer[7] << 16) + 198 | (buffer[8] << 8) + 199 | buffer[9]; 200 | pos = 10; 201 | } 202 | 203 | uint8_t mask[4]; 204 | if (isMasking) 205 | { 206 | if (bufferSize < (pos + 4)) 207 | { 208 | return false; 209 | } 210 | 211 | mask[0] = buffer[pos++]; 212 | mask[1] = buffer[pos++]; 213 | mask[2] = buffer[pos++]; 214 | mask[3] = buffer[pos++]; 215 | } 216 | 217 | if (bufferSize < (pos + payloadlen)) 218 | { 219 | return false; 220 | } 221 | 222 | if (isMasking) 223 | { 224 | payload.reserve(payloadlen); 225 | for (size_t j = 0; j < payloadlen; j++) 226 | payload.push_back(buffer[pos + j] ^ mask[j % 4]); 227 | } 228 | else 229 | { 230 | payload.append((const char*) (buffer + pos), payloadlen); 231 | } 232 | 233 | frameSize = payloadlen + pos; 234 | 235 | return true; 236 | } 237 | 238 | static bool wsFrameExtractString(const std::string& buffer, 239 | std::string& payload, 240 | WebSocketFrameType& opcode, 241 | size_t& frameSize, bool& isFin) 242 | { 243 | return wsFrameExtractBuffer(buffer.c_str(), 244 | buffer.size(), 245 | payload, 246 | opcode, 247 | frameSize, 248 | isFin); 249 | } 250 | }; 251 | 252 | }}}// namespace brynet::net::http 253 | -------------------------------------------------------------------------------- /include/brynet/net/port/Win.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace brynet { namespace net { namespace port { 6 | 7 | #ifdef BRYNET_PLATFORM_WINDOWS 8 | class Win 9 | { 10 | public: 11 | enum class OverlappedType 12 | { 13 | OverlappedNone = 0, 14 | OverlappedRecv, 15 | OverlappedSend, 16 | }; 17 | 18 | struct OverlappedExt 19 | { 20 | OVERLAPPED base; 21 | const OverlappedType OP; 22 | 23 | OverlappedExt(OverlappedType op) 24 | : OP(op) 25 | { 26 | memset(&base, 0, sizeof(base)); 27 | } 28 | }; 29 | }; 30 | #endif 31 | 32 | }}}// namespace brynet::net::port 33 | -------------------------------------------------------------------------------- /include/brynet/net/wrapper/ConnectionBuilder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace brynet { namespace net { namespace wrapper { 10 | 11 | using CompletedCallback = detail::AsyncConnectAddr::CompletedCallback; 12 | using ProcessTcpSocketCallback = detail::AsyncConnectAddr::ProcessTcpSocketCallback; 13 | using FailedCallback = detail::AsyncConnectAddr::FailedCallback; 14 | 15 | template 16 | class BaseSocketConnectBuilder 17 | { 18 | public: 19 | virtual ~BaseSocketConnectBuilder() = default; 20 | 21 | Derived& WithConnector(AsyncConnector::Ptr connector) 22 | { 23 | mConnector = std::move(connector); 24 | return static_cast(*this); 25 | } 26 | 27 | Derived& WithAddr(std::string ip, size_t port) 28 | { 29 | mConnectOption.ip = std::move(ip); 30 | mConnectOption.port = port; 31 | return static_cast(*this); 32 | } 33 | 34 | Derived& WithTimeout(std::chrono::nanoseconds timeout) 35 | { 36 | mConnectOption.timeout = timeout; 37 | return static_cast(*this); 38 | } 39 | 40 | Derived& AddSocketProcessCallback(const ProcessTcpSocketCallback& callback) 41 | { 42 | mConnectOption.processCallbacks.push_back(callback); 43 | return static_cast(*this); 44 | } 45 | 46 | Derived& WithCompletedCallback(CompletedCallback callback) 47 | { 48 | mConnectOption.completedCallback = std::move(callback); 49 | return static_cast(*this); 50 | } 51 | 52 | Derived& WithFailedCallback(FailedCallback callback) 53 | { 54 | mConnectOption.failedCallback = std::move(callback); 55 | return static_cast(*this); 56 | } 57 | 58 | void asyncConnect() const 59 | { 60 | if (mConnector == nullptr) 61 | { 62 | throw BrynetCommonException("connector is nullptr"); 63 | } 64 | if (mConnectOption.ip.empty()) 65 | { 66 | throw BrynetCommonException("address is empty"); 67 | } 68 | 69 | mConnector->asyncConnect(mConnectOption); 70 | } 71 | 72 | TcpSocket::Ptr syncConnect() 73 | { 74 | if (mConnectOption.completedCallback != nullptr || mConnectOption.failedCallback != nullptr) 75 | { 76 | throw std::runtime_error("already setting completed callback or failed callback"); 77 | } 78 | 79 | auto socketPromise = std::make_shared>(); 80 | mConnectOption.completedCallback = [socketPromise](TcpSocket::Ptr socket) { 81 | socketPromise->set_value(std::move(socket)); 82 | }; 83 | mConnectOption.failedCallback = [socketPromise]() { 84 | socketPromise->set_value(nullptr); 85 | }; 86 | 87 | asyncConnect(); 88 | 89 | auto future = socketPromise->get_future(); 90 | if (future.wait_for(mConnectOption.timeout) != std::future_status::ready) 91 | { 92 | return nullptr; 93 | } 94 | 95 | return future.get(); 96 | } 97 | 98 | private: 99 | AsyncConnector::Ptr mConnector; 100 | ConnectOption mConnectOption; 101 | }; 102 | 103 | class SocketConnectBuilder : public BaseSocketConnectBuilder 104 | { 105 | }; 106 | 107 | template 108 | class BaseConnectionBuilder 109 | { 110 | public: 111 | Derived& WithService(ITcpService::Ptr service) 112 | { 113 | mTcpService = std::move(service); 114 | return static_cast(*this); 115 | } 116 | 117 | Derived& WithConnector(AsyncConnector::Ptr connector) 118 | { 119 | mConnectBuilder.WithConnector(std::move(connector)); 120 | return static_cast(*this); 121 | } 122 | 123 | Derived& WithAddr(std::string ip, size_t port) 124 | { 125 | mConnectBuilder.WithAddr(std::move(ip), port); 126 | return static_cast(*this); 127 | } 128 | 129 | Derived& WithTimeout(std::chrono::nanoseconds timeout) 130 | { 131 | mConnectBuilder.WithTimeout(timeout); 132 | return static_cast(*this); 133 | } 134 | 135 | Derived& AddSocketProcessCallback(const ProcessTcpSocketCallback& callback) 136 | { 137 | mConnectBuilder.AddSocketProcessCallback(callback); 138 | return static_cast(*this); 139 | } 140 | 141 | Derived& WithFailedCallback(FailedCallback callback) 142 | { 143 | mConnectBuilder.WithFailedCallback(std::move(callback)); 144 | return static_cast(*this); 145 | } 146 | 147 | Derived& WithMaxRecvBufferSize(size_t size) 148 | { 149 | mOption.maxRecvBufferSize = size; 150 | return static_cast(*this); 151 | } 152 | 153 | Derived& WithEnterFailedCallback(std::function callback) 154 | { 155 | mOption.enterFailedCallback = std::move(callback); 156 | return static_cast(*this); 157 | } 158 | 159 | #ifdef BRYNET_USE_OPENSSL 160 | Derived& WithSSL() 161 | { 162 | mOption.useSSL = true; 163 | return static_cast(*this); 164 | } 165 | #endif 166 | Derived& WithForceSameThreadLoop() 167 | { 168 | mOption.forceSameThreadLoop = true; 169 | return static_cast(*this); 170 | } 171 | 172 | Derived& AddEnterCallback(const TcpConnection::EnterCallback& callback) 173 | { 174 | mOption.enterCallback.push_back(callback); 175 | return static_cast(*this); 176 | } 177 | 178 | void asyncConnect() 179 | { 180 | auto service = mTcpService; 181 | auto option = mOption; 182 | mConnectBuilder.WithCompletedCallback([service, option](TcpSocket::Ptr socket) mutable { 183 | service->addTcpConnection(std::move(socket), std::move(option)); 184 | }); 185 | 186 | mConnectBuilder.asyncConnect(); 187 | } 188 | 189 | TcpConnection::Ptr syncConnect() 190 | { 191 | auto sessionPromise = std::make_shared>(); 192 | 193 | auto option = mOption; 194 | option.enterCallback.push_back([sessionPromise](const TcpConnection::Ptr& session) { 195 | sessionPromise->set_value(session); 196 | }); 197 | 198 | auto socket = mConnectBuilder.syncConnect(); 199 | if (socket == nullptr) 200 | { 201 | return nullptr; 202 | } 203 | mTcpService->addTcpConnection(std::move(socket), std::move(option)); 204 | 205 | return sessionPromise->get_future().get(); 206 | } 207 | 208 | private: 209 | ITcpService::Ptr mTcpService; 210 | ConnectionOption mOption; 211 | SocketConnectBuilder mConnectBuilder; 212 | }; 213 | 214 | class ConnectionBuilder : public BaseConnectionBuilder 215 | { 216 | }; 217 | 218 | }}}// namespace brynet::net::wrapper 219 | -------------------------------------------------------------------------------- /include/brynet/net/wrapper/HttpConnectionBuilder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace brynet { namespace net { namespace wrapper { 8 | 9 | class HttpConnectionBuilder 10 | { 11 | public: 12 | HttpConnectionBuilder& WithService(ITcpService::Ptr service) 13 | { 14 | mBuilder.WithService(std::move(service)); 15 | return *this; 16 | } 17 | 18 | HttpConnectionBuilder& WithConnector(AsyncConnector::Ptr connector) 19 | { 20 | mBuilder.WithConnector(std::move(connector)); 21 | return *this; 22 | } 23 | 24 | HttpConnectionBuilder& WithAddr(std::string ip, size_t port) 25 | { 26 | mBuilder.WithAddr(std::move(ip), port); 27 | return *this; 28 | } 29 | 30 | HttpConnectionBuilder& WithTimeout(std::chrono::nanoseconds timeout) 31 | { 32 | mBuilder.WithTimeout(timeout); 33 | return *this; 34 | } 35 | 36 | HttpConnectionBuilder& AddSocketProcessCallback(const ProcessTcpSocketCallback& callback) 37 | { 38 | mBuilder.AddSocketProcessCallback(callback); 39 | return *this; 40 | } 41 | 42 | HttpConnectionBuilder& WithEnterCallback(http::HttpSession::EnterCallback&& callback) 43 | { 44 | mHttpEnterCallback = std::move(callback); 45 | return *this; 46 | } 47 | 48 | HttpConnectionBuilder& WithFailedCallback(FailedCallback callback) 49 | { 50 | mBuilder.WithFailedCallback(std::move(callback)); 51 | return *this; 52 | } 53 | 54 | HttpConnectionBuilder& WithMaxRecvBufferSize(size_t size) 55 | { 56 | mBuilder.WithMaxRecvBufferSize(size); 57 | return *this; 58 | } 59 | 60 | #ifdef BRYNET_USE_OPENSSL 61 | HttpConnectionBuilder& WithSSL() 62 | { 63 | mBuilder.WithSSL(); 64 | return *this; 65 | } 66 | #endif 67 | HttpConnectionBuilder& WithForceSameThreadLoop() 68 | { 69 | mBuilder.WithForceSameThreadLoop(); 70 | return *this; 71 | } 72 | 73 | void asyncConnect() 74 | { 75 | if (mHttpEnterCallback == nullptr) 76 | { 77 | throw BrynetCommonException("not setting http enter callback"); 78 | } 79 | 80 | auto callback = mHttpEnterCallback; 81 | auto builder = mBuilder; 82 | builder.AddEnterCallback([callback](const TcpConnection::Ptr& session) { 83 | http::HttpService::setup(session, callback); 84 | }); 85 | builder.asyncConnect(); 86 | } 87 | 88 | private: 89 | http::HttpSession::EnterCallback mHttpEnterCallback; 90 | ConnectionBuilder mBuilder; 91 | }; 92 | 93 | }}}// namespace brynet::net::wrapper 94 | -------------------------------------------------------------------------------- /include/brynet/net/wrapper/HttpServiceBuilder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace brynet { namespace net { namespace wrapper { 8 | 9 | class HttpListenerBuilder 10 | { 11 | public: 12 | HttpListenerBuilder& WithService(ITcpService::Ptr service) 13 | { 14 | mBuilder.WithService(std::move(service)); 15 | return *this; 16 | } 17 | 18 | HttpListenerBuilder& WithEnterCallback(http::HttpSession::EnterCallback&& callback) 19 | { 20 | mHttpEnterCallback = std::move(callback); 21 | return *this; 22 | } 23 | 24 | HttpListenerBuilder& AddSocketProcess(const ListenThread::TcpSocketProcessCallback& callback) 25 | { 26 | mBuilder.AddSocketProcess(callback); 27 | return *this; 28 | } 29 | 30 | HttpListenerBuilder& WithMaxRecvBufferSize(size_t size) 31 | { 32 | mBuilder.WithMaxRecvBufferSize(size); 33 | return *this; 34 | } 35 | #ifdef BRYNET_USE_OPENSSL 36 | HttpListenerBuilder& WithSSL(SSLHelper::Ptr sslHelper) 37 | { 38 | mBuilder.WithSSL(std::move(sslHelper)); 39 | return *this; 40 | } 41 | #endif 42 | HttpListenerBuilder& WithForceSameThreadLoop() 43 | { 44 | mBuilder.WithForceSameThreadLoop(); 45 | return *this; 46 | } 47 | 48 | HttpListenerBuilder& WithAddr(bool ipV6, std::string ip, size_t port) 49 | { 50 | mBuilder.WithAddr(ipV6, std::move(ip), port); 51 | return *this; 52 | } 53 | 54 | HttpListenerBuilder& WithReusePort() 55 | { 56 | mBuilder.WithReusePort(); 57 | return *this; 58 | } 59 | 60 | void asyncRun() 61 | { 62 | if (mHttpEnterCallback == nullptr) 63 | { 64 | throw BrynetCommonException("not setting http enter callback"); 65 | } 66 | 67 | auto callback = mHttpEnterCallback; 68 | mBuilder.AddEnterCallback([callback](const TcpConnection::Ptr& session) { 69 | http::HttpService::setup(session, callback); 70 | }); 71 | mBuilder.asyncRun(); 72 | } 73 | 74 | private: 75 | http::HttpSession::EnterCallback mHttpEnterCallback; 76 | ListenerBuilder mBuilder; 77 | }; 78 | 79 | }}}// namespace brynet::net::wrapper 80 | -------------------------------------------------------------------------------- /include/brynet/net/wrapper/ServiceBuilder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace brynet { namespace net { namespace wrapper { 10 | 11 | template 12 | class BaseListenerBuilder 13 | { 14 | public: 15 | virtual ~BaseListenerBuilder() = default; 16 | 17 | Derived& WithService(ITcpService::Ptr service) 18 | { 19 | mTcpService = std::move(service); 20 | return static_cast(*this); 21 | } 22 | 23 | Derived& WithAddr(bool ipV6, std::string ip, size_t port) 24 | { 25 | mIsIpV6 = ipV6; 26 | mListenAddr = std::move(ip); 27 | mPort = port; 28 | return static_cast(*this); 29 | } 30 | 31 | Derived& WithReusePort() 32 | { 33 | mEnabledReusePort = true; 34 | return static_cast(*this); 35 | } 36 | 37 | Derived& AddSocketProcess(const ListenThread::TcpSocketProcessCallback& callback) 38 | { 39 | mSocketProcessCallbacks.push_back(callback); 40 | return static_cast(*this); 41 | } 42 | 43 | Derived& WithMaxRecvBufferSize(size_t size) 44 | { 45 | mSocketOption.maxRecvBufferSize = size; 46 | return static_cast(*this); 47 | } 48 | #ifdef BRYNET_USE_OPENSSL 49 | Derived& WithSSL(SSLHelper::Ptr sslHelper) 50 | { 51 | mSocketOption.sslHelper = std::move(sslHelper); 52 | mSocketOption.useSSL = true; 53 | return static_cast(*this); 54 | } 55 | #endif 56 | Derived& WithForceSameThreadLoop() 57 | { 58 | mSocketOption.forceSameThreadLoop = true; 59 | return static_cast(*this); 60 | } 61 | 62 | Derived& AddEnterCallback(const TcpConnection::EnterCallback& callback) 63 | { 64 | mSocketOption.enterCallback.push_back(callback); 65 | return static_cast(*this); 66 | } 67 | 68 | Derived& WithEnterFailedCallback(std::function callback) 69 | { 70 | mSocketOption.enterFailedCallback = std::move(callback); 71 | return static_cast(*this); 72 | } 73 | 74 | void asyncRun() 75 | { 76 | if (mTcpService == nullptr) 77 | { 78 | throw BrynetCommonException("tcp service is nullptr"); 79 | } 80 | if (mListenAddr.empty()) 81 | { 82 | throw BrynetCommonException("not config listen addr"); 83 | } 84 | 85 | auto service = mTcpService; 86 | auto option = mSocketOption; 87 | mListenThread = ListenThread::Create( 88 | mIsIpV6, 89 | mListenAddr, 90 | mPort, 91 | [service, option](brynet::net::TcpSocket::Ptr socket) { 92 | service->addTcpConnection(std::move(socket), option); 93 | }, 94 | mSocketProcessCallbacks, 95 | mEnabledReusePort); 96 | mListenThread->startListen(); 97 | } 98 | 99 | void stop() 100 | { 101 | if (mListenThread) 102 | { 103 | mListenThread->stopListen(); 104 | } 105 | } 106 | 107 | private: 108 | ITcpService::Ptr mTcpService; 109 | std::vector mSocketProcessCallbacks; 110 | ConnectionOption mSocketOption; 111 | std::string mListenAddr; 112 | int mPort = 0; 113 | bool mIsIpV6 = false; 114 | bool mEnabledReusePort = false; 115 | ListenThread::Ptr mListenThread; 116 | }; 117 | 118 | class ListenerBuilder : public BaseListenerBuilder 119 | { 120 | }; 121 | 122 | }}}// namespace brynet::net::wrapper 123 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | enable_testing() 2 | 3 | include_directories("${PROJECT_SOURCE_DIR}/include/") 4 | 5 | add_executable(test_timer test_timer.cpp) 6 | if(WIN32) 7 | target_link_libraries(test_timer ws2_32) 8 | elseif(UNIX) 9 | find_package(Threads REQUIRED) 10 | target_link_libraries(test_timer pthread) 11 | endif() 12 | add_test(TestTimer test_timer) 13 | 14 | add_executable(test_wait_group test_wait_group.cpp) 15 | if(WIN32) 16 | target_link_libraries(test_wait_group ws2_32) 17 | elseif(UNIX) 18 | find_package(Threads REQUIRED) 19 | target_link_libraries(test_wait_group pthread) 20 | endif() 21 | add_test(TestWaitGroup test_wait_group) 22 | 23 | add_executable(test_sync_connect test_sync_connect.cpp) 24 | if(WIN32) 25 | target_link_libraries(test_sync_connect ws2_32) 26 | elseif(UNIX) 27 | find_package(Threads REQUIRED) 28 | target_link_libraries(test_sync_connect pthread) 29 | endif() 30 | add_test(TestSyncConnect test_sync_connect) 31 | 32 | add_executable(test_http test_http.cpp) 33 | if(WIN32) 34 | target_link_libraries(test_http ws2_32) 35 | elseif(UNIX) 36 | find_package(Threads REQUIRED) 37 | target_link_libraries(test_http pthread) 38 | endif() 39 | add_test(TestHTTP test_http) 40 | 41 | add_executable(test_array test_array.cpp) 42 | add_test(TestArray test_array) 43 | 44 | add_executable(test_buffer test_buffer.cpp) 45 | add_test(TestBuffer test_buffer) 46 | 47 | add_executable(test_stack test_stack.cpp) 48 | add_test(TestStack test_stack) 49 | 50 | add_executable(test_packet test_packet.cpp) 51 | if(WIN32) 52 | target_link_libraries(test_packet ws2_32) 53 | endif() 54 | add_test(TestPacket test_packet) 55 | 56 | add_executable(test_endian test_endian.cpp) 57 | if(WIN32) 58 | target_link_libraries(test_endian ws2_32) 59 | endif() 60 | add_test(TestEndian test_endian) -------------------------------------------------------------------------------- /tests/test_array.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include "catch.hpp" 3 | 4 | // ignore assert in Array.hpp for test case 5 | #ifndef NDEBUG 6 | #define NDEBUG 7 | #endif 8 | 9 | #include 10 | 11 | TEST_CASE("Array are computed", "[Array]") 12 | { 13 | using namespace brynet::base; 14 | 15 | const size_t num = 10; 16 | auto array = array_new(num, sizeof(int)); 17 | REQUIRE(array_num(array) == num); 18 | 19 | int value = 5; 20 | REQUIRE(array_set(array, 0, &value)); 21 | auto v = array_at(array, 0); 22 | REQUIRE(v != nullptr); 23 | REQUIRE(*(int*) v == value); 24 | 25 | REQUIRE(!array_set(array, 10, &value)); 26 | REQUIRE(array_at(array, 10) == nullptr); 27 | 28 | REQUIRE(array_increase(array, num)); 29 | REQUIRE(array_num(array) == (num * 2)); 30 | v = array_at(array, 0); 31 | REQUIRE(v != nullptr); 32 | REQUIRE(*(int*) v == value); 33 | 34 | value++; 35 | REQUIRE(array_set(array, 19, &value)); 36 | v = array_at(array, 19); 37 | REQUIRE(v != nullptr); 38 | REQUIRE(*(int*) v == value); 39 | 40 | REQUIRE(array_at(array, 10) != nullptr); 41 | REQUIRE(array_at(array, 19) != nullptr); 42 | REQUIRE(array_at(array, 20) == nullptr); 43 | 44 | array_delete(array); 45 | array = nullptr; 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_buffer.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | 4 | #include "catch.hpp" 5 | 6 | TEST_CASE("Buffer are computed", "[Buffer]") 7 | { 8 | using namespace brynet::base; 9 | 10 | auto buffer = buffer_new(100); 11 | REQUIRE(buffer_getreadvalidcount(buffer) == 0); 12 | REQUIRE(buffer_getwritepos(buffer) == 0); 13 | REQUIRE(buffer_getreadpos(buffer) == 0); 14 | REQUIRE(buffer_getwritevalidcount(buffer) == 100); 15 | REQUIRE(buffer_getsize(buffer) == 100); 16 | 17 | int value = 5; 18 | REQUIRE(buffer_write(buffer, (const char*) &value, sizeof(value))); 19 | REQUIRE(buffer_getreadvalidcount(buffer) == sizeof(value)); 20 | REQUIRE(buffer_getwritepos(buffer) == sizeof(value)); 21 | REQUIRE(buffer_getreadpos(buffer) == 0); 22 | REQUIRE(buffer_getwritevalidcount(buffer) == (100 - sizeof(value))); 23 | REQUIRE(buffer_getsize(buffer) == 100); 24 | 25 | REQUIRE(*(int*) buffer_getreadptr(buffer) == 5); 26 | REQUIRE(buffer_getreadptr(buffer) + sizeof(value) == buffer_getwriteptr(buffer)); 27 | 28 | buffer_adjustto_head(buffer); 29 | REQUIRE(*(int*) buffer_getreadptr(buffer) == 5); 30 | REQUIRE(buffer_getreadptr(buffer) + sizeof(value) == buffer_getwriteptr(buffer)); 31 | 32 | char bigBuffer[200]; 33 | REQUIRE(!buffer_write(buffer, bigBuffer, sizeof(bigBuffer))); 34 | 35 | buffer_delete(buffer); 36 | buffer = nullptr; 37 | } 38 | -------------------------------------------------------------------------------- /tests/test_endian.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | 4 | #include "catch.hpp" 5 | 6 | TEST_CASE("Endian are computed", "[Endian]") 7 | { 8 | using namespace brynet::base::endian; 9 | 10 | auto littleEndian = false; 11 | { 12 | int a = 0x12345678; 13 | littleEndian = (*(char*) &a == 0x78); 14 | } 15 | 16 | REQUIRE(hostToNetwork64(0x1234567812345678, littleEndian) == 0x7856341278563412); 17 | REQUIRE(hostToNetwork32(0x12345678, littleEndian) == 0x78563412); 18 | REQUIRE(hostToNetwork16(0x1234, littleEndian) == 0x3412); 19 | 20 | REQUIRE(networkToHost64(0x1234567812345678, littleEndian) == 0x7856341278563412); 21 | REQUIRE(networkToHost32(0x12345678, littleEndian) == 0x78563412); 22 | REQUIRE(networkToHost16(0x1234, littleEndian) == 0x3412); 23 | } 24 | -------------------------------------------------------------------------------- /tests/test_http.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "catch.hpp" 13 | 14 | TEST_CASE("http server are computed", "[http_server]") 15 | { 16 | using namespace brynet; 17 | using namespace brynet::net; 18 | using namespace brynet::net::http; 19 | 20 | const std::string ip = "127.0.0.1"; 21 | const auto port = 9999; 22 | 23 | std::srand(std::time(nullptr)); 24 | // 开启监听服务 25 | { 26 | { 27 | static std::atomic_llong counter = ATOMIC_VAR_INIT(0); 28 | static std::atomic_bool useShutdown = ATOMIC_VAR_INIT(false); 29 | 30 | auto connector = AsyncConnector::Create(); 31 | connector->startWorkerThread(); 32 | 33 | auto service = IOThreadTcpService::Create(); 34 | service->startWorkerThread(1); 35 | 36 | auto httpEnterCallback = [](const HTTPParser& httpParser, 37 | const HttpSession::Ptr& session) { 38 | REQUIRE(httpParser.getPath() == "/ISteamUserAuth/AuthenticateUserTicket/v1/"); 39 | REQUIRE(httpParser.getQuery() == "key=DCD9C36F1F54A96F707DFBE833600167&appid=929390&ticket=140000006FC57764C95D45085373F10401001001359F745C1800000001000000020000009DACD3DE1202A8C0431E100003000000B200000032000000040000005373F104010010016E2E0E009DACD3DE1202A8C000000000AAA16F5C2A518B5C0100FC96040000000000061129B849B0397DD62E0B1B0373451EC08E1BAB70FC18E21094FC5F4674EDD50226ABB33D71C601B8E65542FB9A9F48BFF87AC30904D272FAD5F15CD2D5428D44827BA58A45886119D6244D672A0C1909C5D7BD9096D96EB8BAC30E006BE6D405E5B25659CF3D343C9627078C5FD4CE0120D80DDB2FA09E76111143F132CA0B"); 40 | REQUIRE(httpParser.method() == static_cast(HttpRequest::HTTP_METHOD::HTTP_METHOD_GET)); 41 | (void)httpParser; 42 | HttpResponse response; 43 | response.setBody(std::string("hello world ")); 44 | if (httpParser.isKeepAlive()) 45 | { 46 | response.addHeadValue("Connection", "Keep-Alive"); 47 | session->send(response.getResult()); 48 | } 49 | else 50 | { 51 | response.addHeadValue("Connection", "Close"); 52 | session->send(response.getResult(), [session]() { 53 | if (std::rand() / 2 == 0) 54 | { 55 | useShutdown.store(true); 56 | session->postShutdown(); 57 | } 58 | else 59 | { 60 | session->postClose(); 61 | } 62 | }); 63 | } 64 | }; 65 | 66 | auto wsEnterCallback = [](const HttpSession::Ptr& httpSession, 67 | WebSocketFormat::WebSocketFrameType opcode, 68 | const std::string& payload) { 69 | // echo frame 70 | std::string frame; 71 | WebSocketFormat::wsFrameBuild(payload.c_str(), 72 | payload.size(), 73 | frame, 74 | WebSocketFormat::WebSocketFrameType::TEXT_FRAME, 75 | true, 76 | false); 77 | httpSession->send(std::move(frame)); 78 | }; 79 | 80 | brynet::base::WaitGroup::Ptr wg = brynet::base::WaitGroup::Create(); 81 | wg->add(); 82 | 83 | HttpRequest request; 84 | request.setMethod(HttpRequest::HTTP_METHOD::HTTP_METHOD_GET); 85 | request.setUrl("/ISteamUserAuth/AuthenticateUserTicket/v1/"); 86 | request.addHeadValue("Host", "api.steampowered.com"); 87 | 88 | HttpQueryParameter p; 89 | p.add("key", "DCD9C36F1F54A96F707DFBE833600167"); 90 | p.add("appid", "929390"); 91 | p.add("ticket", 92 | "140000006FC57764C95D45085373F104" 93 | "01001001359F745C1800000001000000020000009" 94 | "DACD3DE1202A8C0431E100003000000B200000032" 95 | "000000040000005373F104010010016E2E0E009D" 96 | "ACD3DE1202A8C000000000AAA16F5C2A518B5C" 97 | "0100FC96040000000000061129B849B0397DD" 98 | "62E0B1B0373451EC08E1BAB70FC18E21094F" 99 | "C5F4674EDD50226ABB33D71C601B8E65542F" 100 | "B9A9F48BFF87AC30904D272FAD5F15CD2D5428" 101 | "D44827BA58A45886119D6244D672A0C1909C5D" 102 | "7BD9096D96EB8BAC30E006BE6D405E5B25659" 103 | "CF3D343C9627078C5FD4CE0120D80DDB2FA09E76111143F132CA0B"); 104 | request.setQuery(p.getResult()); 105 | 106 | std::string requestStr = request.getResult(); 107 | 108 | wrapper::HttpListenerBuilder listenBuilder; 109 | listenBuilder 110 | .WithService(service) 111 | .AddSocketProcess([](TcpSocket& socket) { 112 | socket.setNodelay(); 113 | }) 114 | .WithMaxRecvBufferSize(1024) 115 | .WithAddr(false, "0.0.0.0", port) 116 | .WithReusePort() 117 | .WithEnterCallback([httpEnterCallback, wsEnterCallback](const HttpSession::Ptr& httpSession, HttpSessionHandlers& handlers) { 118 | handlers.setHttpEndCallback(httpEnterCallback); 119 | handlers.setWSCallback(wsEnterCallback); 120 | }) 121 | .asyncRun(); 122 | 123 | wrapper::HttpConnectionBuilder() 124 | .WithConnector(connector) 125 | .WithService(service) 126 | .WithAddr("127.0.0.1", port) 127 | .WithTimeout(std::chrono::seconds(10)) 128 | .WithFailedCallback([]() { 129 | }) 130 | .WithMaxRecvBufferSize(10) 131 | .WithEnterCallback([requestStr, wg](const HttpSession::Ptr& session, HttpSessionHandlers& handlers) { 132 | (void)session; 133 | session->send(requestStr); 134 | handlers.setClosedCallback([wg](const HttpSession::Ptr& session) { 135 | wg->done(); 136 | }); 137 | handlers.setHttpEndCallback([wg](const HTTPParser& httpParser, 138 | const HttpSession::Ptr& session) { 139 | (void)session; 140 | REQUIRE(httpParser.getBody() == "hello world "); 141 | counter.fetch_add(1); 142 | session->postClose(); 143 | }); 144 | }) 145 | .asyncConnect(); 146 | wg->wait(); 147 | if (useShutdown.load()) 148 | { 149 | REQUIRE(counter.load() == 1); 150 | } 151 | } 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /tests/test_packet.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | 4 | #include "catch.hpp" 5 | 6 | TEST_CASE("Packet are computed", "[Packet]") 7 | { 8 | using namespace brynet::base; 9 | BigPacket packet; 10 | packet.writeBool(true); 11 | packet.writeINT8(1); 12 | packet.writeUINT8(2); 13 | packet.writeINT16(3); 14 | packet.writeUINT16(4); 15 | packet.writeINT32(5); 16 | packet.writeUINT32(6); 17 | packet.writeINT64(7); 18 | packet.writeUINT64(8); 19 | 20 | REQUIRE(packet.getPos() == 31); 21 | 22 | BasePacketReader reader(packet.getData(), packet.getPos()); 23 | REQUIRE(reader.getLeft() == 31); 24 | REQUIRE(reader.readBool() == true); 25 | REQUIRE(reader.readINT8() == 1); 26 | REQUIRE(reader.readUINT8() == 2); 27 | REQUIRE(reader.readINT16() == 3); 28 | REQUIRE(reader.readUINT16() == 4); 29 | REQUIRE(reader.readINT32() == 5); 30 | REQUIRE(reader.readUINT32() == 6); 31 | REQUIRE(reader.readINT64() == 7); 32 | REQUIRE(reader.readUINT64() == 8); 33 | 34 | REQUIRE(reader.getLeft() == 0); 35 | REQUIRE(reader.currentPos() == 31); 36 | } 37 | -------------------------------------------------------------------------------- /tests/test_stack.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | 4 | #include "catch.hpp" 5 | 6 | TEST_CASE("Stack are computed", "[Stack]") 7 | { 8 | using namespace brynet::base; 9 | 10 | const size_t StackNum = 10; 11 | 12 | auto stack = stack_new(StackNum, sizeof(size_t)); 13 | REQUIRE(stack_num(stack) == 0); 14 | REQUIRE(stack_size(stack) == StackNum); 15 | REQUIRE(!stack_isfull(stack)); 16 | REQUIRE(stack_popback(stack) == nullptr); 17 | REQUIRE(stack_popfront(stack) == nullptr); 18 | 19 | for (size_t i = 0; i < StackNum; i++) 20 | { 21 | REQUIRE(stack_push(stack, &i)); 22 | } 23 | REQUIRE(stack_num(stack) == StackNum); 24 | REQUIRE(stack_isfull(stack)); 25 | 26 | for (size_t i = 0; i < StackNum; i++) 27 | { 28 | REQUIRE(*(size_t*) stack_popfront(stack) == i); 29 | } 30 | REQUIRE(stack_popback(stack) == nullptr); 31 | REQUIRE(stack_popfront(stack) == nullptr); 32 | 33 | stack_init(stack); 34 | 35 | REQUIRE(!stack_isfull(stack)); 36 | REQUIRE(stack_popback(stack) == nullptr); 37 | REQUIRE(stack_popfront(stack) == nullptr); 38 | for (size_t i = 0; i < StackNum; i++) 39 | { 40 | REQUIRE(stack_push(stack, &i)); 41 | } 42 | for (int i = StackNum - 1; i >= 0; i--) 43 | { 44 | REQUIRE(*(size_t*) stack_popback(stack) == (size_t) i); 45 | } 46 | REQUIRE(stack_popback(stack) == nullptr); 47 | REQUIRE(stack_popfront(stack) == nullptr); 48 | 49 | stack_delete(stack); 50 | stack = nullptr; 51 | } 52 | -------------------------------------------------------------------------------- /tests/test_sync_connect.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | #include 4 | #include 5 | 6 | #include "catch.hpp" 7 | 8 | TEST_CASE("SyncConnector are computed", "[sync_connect]") 9 | { 10 | 11 | using namespace brynet::net; 12 | 13 | const std::string ip = "127.0.0.1"; 14 | const auto port = 9999; 15 | 16 | { 17 | {}} 18 | // listen service not open 19 | { 20 | {auto connector = AsyncConnector::Create(); 21 | connector->startWorkerThread(); 22 | wrapper::SocketConnectBuilder connectBuilder; 23 | auto socket = connectBuilder 24 | .WithConnector(connector) 25 | .WithTimeout(std::chrono::seconds(2)) 26 | .WithAddr(ip, port) 27 | .syncConnect(); 28 | 29 | REQUIRE(socket == nullptr); 30 | } 31 | 32 | { 33 | auto connector = AsyncConnector::Create(); 34 | connector->startWorkerThread(); 35 | auto service = IOThreadTcpService::Create(); 36 | service->startWorkerThread(1); 37 | 38 | wrapper::ConnectionBuilder connectionBuilder; 39 | auto session = connectionBuilder 40 | .WithService(service) 41 | .WithConnector(connector) 42 | .WithTimeout(std::chrono::seconds(2)) 43 | .WithAddr(ip, port) 44 | .syncConnect(); 45 | 46 | REQUIRE(session == nullptr); 47 | } 48 | } 49 | 50 | // use ListenerBuilder for open listen 51 | { 52 | auto service = IOThreadTcpService::Create(); 53 | service->startWorkerThread(1); 54 | wrapper::ListenerBuilder listenerBuilder; 55 | listenerBuilder.WithService(service) 56 | .WithMaxRecvBufferSize(10) 57 | .WithAddr(false, ip, port) 58 | .asyncRun(); 59 | 60 | { 61 | auto connector = AsyncConnector::Create(); 62 | connector->startWorkerThread(); 63 | wrapper::SocketConnectBuilder connectBuilder; 64 | auto socket = connectBuilder 65 | .WithConnector(connector) 66 | .WithTimeout(std::chrono::seconds(2)) 67 | .WithAddr(ip, port) 68 | .syncConnect(); 69 | 70 | REQUIRE(socket != nullptr); 71 | } 72 | } 73 | 74 | { 75 | auto connector = AsyncConnector::Create(); 76 | connector->startWorkerThread(); 77 | wrapper::SocketConnectBuilder connectBuilder; 78 | auto socket = connectBuilder 79 | .WithConnector(connector) 80 | .WithTimeout(std::chrono::seconds(2)) 81 | .WithAddr(ip, port) 82 | .syncConnect(); 83 | 84 | REQUIRE(socket == nullptr); 85 | } 86 | 87 | // open listen thread 88 | { 89 | auto listenThread = brynet::net::ListenThread::Create(false, 90 | ip, 91 | port, 92 | [](brynet::net::TcpSocket::Ptr) { 93 | }); 94 | listenThread->startListen(); 95 | 96 | { 97 | auto connector = AsyncConnector::Create(); 98 | connector->startWorkerThread(); 99 | wrapper::SocketConnectBuilder connectBuilder; 100 | auto socket = connectBuilder 101 | .WithConnector(connector) 102 | .WithTimeout(std::chrono::seconds(2)) 103 | .WithAddr(ip, port) 104 | .syncConnect(); 105 | 106 | REQUIRE(socket != nullptr); 107 | } 108 | 109 | // Tcp Service start io work thread 110 | { 111 | auto connector = AsyncConnector::Create(); 112 | connector->startWorkerThread(); 113 | auto service = IOThreadTcpService::Create(); 114 | service->startWorkerThread(1); 115 | 116 | wrapper::ConnectionBuilder connectionBuilder; 117 | auto session = connectionBuilder 118 | .WithService(service) 119 | .WithConnector(connector) 120 | .WithTimeout(std::chrono::seconds(2)) 121 | .WithAddr(ip, port) 122 | .syncConnect(); 123 | 124 | REQUIRE(session != nullptr); 125 | } 126 | 127 | // Tcp Service not open io work thread 128 | { 129 | auto connector = AsyncConnector::Create(); 130 | connector->startWorkerThread(); 131 | auto service = IOThreadTcpService::Create(); 132 | 133 | wrapper::ConnectionBuilder connectionBuilder; 134 | TcpConnection::Ptr session; 135 | try 136 | { 137 | session = connectionBuilder 138 | .WithService(service) 139 | .WithConnector(connector) 140 | .WithTimeout(std::chrono::seconds(2)) 141 | .WithAddr(ip, port) 142 | .syncConnect(); 143 | } 144 | catch (...) 145 | { 146 | } 147 | 148 | REQUIRE(session == nullptr); 149 | } 150 | 151 | // use EventLoop for Tcp Service 152 | { 153 | auto connector = AsyncConnector::Create(); 154 | connector->startWorkerThread(); 155 | auto eventLoop = std::make_shared(); 156 | eventLoop->bindCurrentThread(); 157 | auto service = EventLoopTcpService::Create(eventLoop); 158 | 159 | wrapper::ConnectionBuilder connectionBuilder; 160 | auto session = connectionBuilder 161 | .WithService(service) 162 | .WithConnector(connector) 163 | .WithTimeout(std::chrono::seconds(2)) 164 | .WithAddr(ip, port) 165 | .syncConnect(); 166 | 167 | REQUIRE(session != nullptr); 168 | } 169 | } 170 | 171 | // listen thread exit 172 | { 173 | auto connector = AsyncConnector::Create(); 174 | connector->startWorkerThread(); 175 | wrapper::SocketConnectBuilder connectBuilder; 176 | auto socket = connectBuilder 177 | .WithConnector(connector) 178 | .WithTimeout(std::chrono::seconds(2)) 179 | .WithAddr(ip, port) 180 | .syncConnect(); 181 | 182 | REQUIRE(socket == nullptr); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /tests/test_timer.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "catch.hpp" 9 | 10 | TEST_CASE("Timer are computed", "[timer]") 11 | { 12 | auto timerMgr = std::make_shared(); 13 | 14 | int upvalue = 0; 15 | auto timer = timerMgr->addTimer(std::chrono::seconds(1), [&upvalue]() { 16 | upvalue++; 17 | }); 18 | std::function fuck; 19 | bool testCancelFlag = false; 20 | timer = timerMgr->addTimer(std::chrono::seconds(1), [&upvalue, &fuck]() { 21 | fuck(); 22 | upvalue++; 23 | }); 24 | 25 | fuck = [&]() { 26 | auto t = timer.lock(); 27 | if (t != nullptr) 28 | { 29 | t->cancel(); 30 | testCancelFlag = true; 31 | } 32 | }; 33 | auto leftTime = timerMgr->nearLeftTime(); 34 | REQUIRE_FALSE(leftTime > std::chrono::seconds(1)); 35 | REQUIRE(timerMgr->isEmpty() == false); 36 | while (!timerMgr->isEmpty()) 37 | { 38 | timerMgr->schedule(); 39 | } 40 | REQUIRE(upvalue == 2); 41 | REQUIRE(testCancelFlag); 42 | 43 | timer = timerMgr->addTimer(std::chrono::seconds(1), [&upvalue]() { 44 | upvalue++; 45 | }); 46 | timer.lock()->cancel(); 47 | REQUIRE(timerMgr->isEmpty() == false); 48 | while (!timerMgr->isEmpty()) 49 | { 50 | timerMgr->schedule(); 51 | } 52 | 53 | REQUIRE(upvalue == 2); 54 | } 55 | 56 | TEST_CASE("repeat timer are computed", "[repeat timer]") 57 | { 58 | auto timerMgr = std::make_shared(); 59 | auto wg = brynet::base::WaitGroup::Create(); 60 | wg->add(1); 61 | 62 | std::atomic_int value = ATOMIC_VAR_INIT(0); 63 | auto timer = timerMgr->addIntervalTimer(std::chrono::milliseconds(100), [&]() { 64 | if (value.load() < 10) 65 | { 66 | value.fetch_add(1); 67 | } 68 | else 69 | { 70 | wg->done(); 71 | } 72 | }); 73 | 74 | std::thread t([&]() { 75 | wg->wait(); 76 | timer->cancel(); 77 | }); 78 | while (!timerMgr->isEmpty()) 79 | { 80 | timerMgr->schedule(); 81 | } 82 | t.join(); 83 | REQUIRE(value.load() == 10); 84 | } 85 | -------------------------------------------------------------------------------- /tests/test_wait_group.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN// This tells Catch to provide a main() - only do this in one cpp file 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "catch.hpp" 9 | 10 | TEST_CASE("WaitGroup are computed", "[waitgroup]") 11 | { 12 | auto wg = brynet::base::WaitGroup::Create(); 13 | wg->wait(); 14 | 15 | wg->add(2); 16 | 17 | std::atomic upvalue = ATOMIC_VAR_INIT(0); 18 | auto a = std::thread([&]() { 19 | upvalue++; 20 | wg->done(); 21 | }); 22 | auto b = std::thread([&]() { 23 | upvalue++; 24 | wg->done(); 25 | }); 26 | wg->wait(); 27 | 28 | REQUIRE(upvalue == 2); 29 | wg->wait(); 30 | if (a.joinable()) 31 | { 32 | a.join(); 33 | } 34 | if (b.joinable()) 35 | { 36 | b.join(); 37 | } 38 | } 39 | --------------------------------------------------------------------------------