├── .gitignore ├── LICENSE ├── README.md ├── async-server-rooms ├── CMakeLists.txt ├── Dockerfile ├── README.md ├── src │ ├── App.cpp │ ├── AppComponent.hpp │ ├── controller │ │ └── RoomsController.hpp │ └── rooms │ │ ├── Lobby.cpp │ │ ├── Lobby.hpp │ │ ├── Peer.cpp │ │ ├── Peer.hpp │ │ ├── Room.cpp │ │ └── Room.hpp ├── test │ ├── WSTest.cpp │ ├── WSTest.hpp │ └── tests.cpp └── utility │ └── install-oatpp-modules.sh ├── async-server ├── CMakeLists.txt ├── README.md ├── src │ ├── App.cpp │ ├── AppComponent.hpp │ ├── controller │ │ └── MyController.hpp │ └── websocket │ │ ├── WSListener.cpp │ │ └── WSListener.hpp ├── test │ ├── WSTest.cpp │ ├── WSTest.hpp │ └── tests.cpp └── utility │ └── install-oatpp-modules.sh ├── azure-pipelines.yml ├── ci-install-mbedtls.sh ├── ci-install-oatpp-modules.sh ├── client-binance.com ├── CMakeLists.txt ├── README.md ├── src │ ├── AggregateTradesListener.hpp │ ├── App.cpp │ ├── KlineCandlestickListener.hpp │ ├── Model.hpp │ ├── TradesListener.hpp │ ├── WSEventListener.hpp │ ├── WSListener.cpp │ └── WSListener.hpp ├── test │ ├── WSTest.cpp │ ├── WSTest.hpp │ └── tests.cpp └── utility │ └── install-oatpp-modules.sh ├── client-mbedtls ├── CMakeLists.txt ├── README.md ├── src │ ├── App.cpp │ ├── WSListener.cpp │ └── WSListener.hpp ├── test │ ├── WSTest.cpp │ ├── WSTest.hpp │ └── tests.cpp └── utility │ └── install-oatpp-modules.sh ├── client ├── CMakeLists.txt ├── README.md ├── src │ ├── App.cpp │ ├── WSListener.cpp │ └── WSListener.hpp ├── test │ ├── WSTest.cpp │ ├── WSTest.hpp │ └── tests.cpp └── utility │ └── install-oatpp-modules.sh ├── cmake └── module │ └── Findmbedtls.cmake └── server ├── CMakeLists.txt ├── README.md ├── src ├── App.cpp ├── AppComponent.hpp ├── controller │ └── MyController.hpp └── websocket │ ├── WSListener.cpp │ └── WSListener.hpp ├── test ├── WSTest.cpp ├── WSTest.hpp └── tests.cpp └── utility └── install-oatpp-modules.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Idea 35 | 36 | **/.idea/ 37 | **/cmake-build-debug/ 38 | **/build/ 39 | 40 | 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # example-websocket [![Build Status](https://dev.azure.com/lganzzzo/lganzzzo/_apis/build/status/oatpp.example-websocket?branchName=master)](https://dev.azure.com/lganzzzo/lganzzzo/_build/latest?definitionId=20&branchName=master) 2 | 3 | Collection of oatpp WebSocket examples. 4 | 5 | In the box: 6 | 7 | - [async-server](https://github.com/oatpp/example-websocket/tree/master/async-server) - Asynchronous WebSocket server example. 8 | - [async-server-rooms](https://github.com/oatpp/example-websocket/tree/master/async-server-rooms) - Asynchronous Room-Based chat server. 9 | - [server](https://github.com/oatpp/example-websocket/tree/master/server) - Multithreaded WebSocket server example. 10 | - [client](https://github.com/oatpp/example-websocket/tree/master/client) - Simple WebSocket client example. 11 | - [client-binance.com](https://github.com/oatpp/example-websocket/tree/master/client-binance.com) - Example client for reading crypto trading event stream from binance.com. 12 | - [client-mbedtls](https://github.com/oatpp/example-websocket/tree/master/client-mbedtls) - Secure WebSocket client example. TLS backend - MbedTLS. 13 | 14 | 15 | See more: 16 | 17 | - [Oat++ Website](https://oatpp.io/) 18 | - [Oat++ Github Repository](https://github.com/oatpp/oatpp) 19 | - [Get Started With Oat++](https://oatpp.io/docs/start) 20 | - [Can-Chat](https://github.com/lganzzzo/canchat) - Feature-complete rooms-based chat for tens of thousands users. Client plus Server. 21 | -------------------------------------------------------------------------------- /async-server-rooms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | set(project_name async-server-rooms) ## rename your project here 4 | 5 | project(${project_name}) 6 | 7 | set(CMAKE_CXX_STANDARD 17) 8 | 9 | include_directories(src) 10 | 11 | add_library(${project_name}-lib 12 | src/AppComponent.hpp 13 | src/controller/RoomsController.hpp 14 | src/rooms/Peer.cpp 15 | src/rooms/Peer.hpp 16 | src/rooms/Room.cpp 17 | src/rooms/Room.hpp 18 | src/rooms/Lobby.cpp 19 | src/rooms/Lobby.hpp 20 | ) 21 | 22 | ## link libs 23 | 24 | find_package(oatpp 1.4.0 REQUIRED) 25 | find_package(oatpp-websocket 1.4.0 REQUIRED) 26 | 27 | target_link_libraries(${project_name}-lib 28 | PUBLIC oatpp::oatpp 29 | PUBLIC oatpp::oatpp-test 30 | PUBLIC oatpp::oatpp-websocket 31 | ) 32 | 33 | ## add executables 34 | 35 | add_executable(${project_name}-exe 36 | src/App.cpp 37 | ) 38 | target_link_libraries(${project_name}-exe ${project_name}-lib) 39 | add_dependencies(${project_name}-exe ${project_name}-lib) 40 | 41 | add_executable(${project_name}-test 42 | test/tests.cpp 43 | test/WSTest.cpp 44 | test/WSTest.hpp 45 | ) 46 | target_link_libraries(${project_name}-test ${project_name}-lib) 47 | add_dependencies(${project_name}-test ${project_name}-lib) 48 | 49 | set_target_properties(${project_name}-lib ${project_name}-exe ${project_name}-test PROPERTIES 50 | CXX_STANDARD 17 51 | CXX_EXTENSIONS OFF 52 | CXX_STANDARD_REQUIRED ON 53 | LINKER_LANGUAGE CXX 54 | ) 55 | 56 | enable_testing() 57 | add_test(tests ${project_name}-test) 58 | -------------------------------------------------------------------------------- /async-server-rooms/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM lganzzzo/alpine-cmake:latest 2 | 3 | ADD . /service 4 | 5 | WORKDIR /service/utility 6 | 7 | RUN ./install-oatpp-modules.sh 8 | 9 | WORKDIR /service/build 10 | 11 | RUN cmake .. 12 | RUN make 13 | 14 | EXPOSE 8000 8000 15 | 16 | ENTRYPOINT ["./async-server-rooms-exe"] 17 | -------------------------------------------------------------------------------- /async-server-rooms/README.md: -------------------------------------------------------------------------------- 1 | # Room-Based Asynchronous Chat Server 2 | 3 | Simple example of a high performance room-based chat server. Built with oat++ (AKA oatpp) web framework using Async API. 4 | Server can handle a large number of simultaneous connections. 5 | 6 | See more: 7 | 8 | - [Oat++ Website](https://oatpp.io/) 9 | - [Oat++ Github Repository](https://github.com/oatpp/oatpp) 10 | - [Get Started With Oat++](https://oatpp.io/docs/start) 11 | 12 | ## Overview 13 | 14 | User connectes on a websocket endpoint `ws://localhost:8000/ws/chat/{room_name}/?nickname={nickname}` and 15 | enters the chat room `room_name`. User will appear in the chat as a `nickname`. 16 | 17 | If user is the first user in the room,- new room will be automatically created. 18 | 19 | Each message that user send to the chat room will be delivered to every participant of the room. 20 | 21 | ### Project layout 22 | 23 | ``` 24 | |- CMakeLists.txt // projects CMakeLists.txt 25 | |- src/ 26 | | | 27 | | |- controller/ 28 | | | | 29 | | | |- RoomsController.hpp // ApiController with websocket endpoint 30 | | | 31 | | |- rooms/ 32 | | | | 33 | | | |- Lobby.hpp // Class for managing new peers and assigning them to rooms 34 | | | |- Peer.hpp // Class representing one peer of a chat 35 | | | |- Room.hpp // Class representing one chat room 36 | | | 37 | | |- AppComponent.hpp // Application config. 38 | | |- App.cpp // main() is here 39 | | 40 | |- utility/install-oatpp-modules.sh // utility script to install required oatpp-modules. 41 | ``` 42 | 43 | ## Build and Run 44 | 45 | ### Using CMake 46 | 47 | **Requires:** [oatpp](https://github.com/oatpp/oatpp), and [oatpp-websocket](https://github.com/oatpp/oatpp-websocket) 48 | modules installed. You may run `utility/install-oatpp-modules.sh` 49 | script to install required oatpp modules. 50 | 51 | After all dependencies satisfied: 52 | 53 | ``` 54 | $ mkdir build && cd build 55 | $ cmake .. 56 | $ make 57 | $ ./async-server-rooms-exe # - run application. 58 | ``` 59 | 60 | ### In Docker 61 | 62 | ``` 63 | $ docker build -t rooms-server . 64 | $ docker run -p 8000:8000 -t rooms-server 65 | ``` 66 | 67 | ## How to test 68 | 69 | Browser tab1: 70 | 71 | - Goto [http://www.websocket.org/echo.html](http://www.websocket.org/echo.html). 72 | - In the "location" field put - `ws://localhost:8000/ws/chat/Room1/?nickname=Nick` 73 | - Press "Connect" button. 74 | - Verify recieved `RECEIVED: Nick joined Room1` 75 | 76 | Browser tab2: 77 | 78 | - Goto [http://www.websocket.org/echo.html](http://www.websocket.org/echo.html). 79 | - In the "location" field put - `ws://localhost:8000/ws/chat/Room1/?nickname=Alex` 80 | - Press "Connect" button. 81 | - Verify recieved `RECEIVED: Alex joined Room1` 82 | 83 | Now try to send messages from both browser tabs and see that messages are delivered to both participants. 84 | 85 | -------------------------------------------------------------------------------- /async-server-rooms/src/App.cpp: -------------------------------------------------------------------------------- 1 | #include "controller/RoomsController.hpp" 2 | #include "./AppComponent.hpp" 3 | 4 | #include "oatpp/network/Server.hpp" 5 | 6 | #include 7 | 8 | void run() { 9 | 10 | /* Register Components in scope of run() method */ 11 | AppComponent components; 12 | 13 | /* Get router component */ 14 | OATPP_COMPONENT(std::shared_ptr, router); 15 | 16 | /* Create RoomsController and add all of its endpoints to router */ 17 | router->addController(std::make_shared()); 18 | 19 | /* Get connection handler component */ 20 | OATPP_COMPONENT(std::shared_ptr, connectionHandler, "http"); 21 | 22 | /* Get connection provider component */ 23 | OATPP_COMPONENT(std::shared_ptr, connectionProvider); 24 | 25 | /* Create server which takes provided TCP connections and passes them to HTTP connection handler */ 26 | oatpp::network::Server server(connectionProvider, connectionHandler); 27 | 28 | /* Priny info about server port */ 29 | OATPP_LOGi("MyApp", "Server running on port {}", connectionProvider->getProperty("port").toString()); 30 | 31 | /* Run server */ 32 | server.run(); 33 | 34 | } 35 | 36 | int main(int argc, const char * argv[]) { 37 | 38 | oatpp::Environment::init(); 39 | 40 | run(); 41 | 42 | oatpp::Environment::destroy(); 43 | 44 | return 0; 45 | } -------------------------------------------------------------------------------- /async-server-rooms/src/AppComponent.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AppComponent_hpp 2 | #define AppComponent_hpp 3 | 4 | #include "rooms/Lobby.hpp" 5 | 6 | #include "oatpp/web/server/AsyncHttpConnectionHandler.hpp" 7 | #include "oatpp/web/server/HttpRouter.hpp" 8 | #include "oatpp/network/tcp/server/ConnectionProvider.hpp" 9 | 10 | #include "oatpp/json/ObjectMapper.hpp" 11 | 12 | #include "oatpp/macro/component.hpp" 13 | 14 | /** 15 | * Class which creates and holds Application components and registers components in oatpp::Environment 16 | * Order of components initialization is from top to bottom 17 | */ 18 | class AppComponent { 19 | public: 20 | 21 | /** 22 | * Create Async Executor 23 | */ 24 | OATPP_CREATE_COMPONENT(std::shared_ptr, executor)([] { 25 | return std::make_shared( 26 | 4 /* Data-Processing threads */, 27 | 1 /* I/O threads */, 28 | 1 /* Timer threads */ 29 | ); 30 | }()); 31 | 32 | /** 33 | * Create ConnectionProvider component which listens on the port 34 | */ 35 | OATPP_CREATE_COMPONENT(std::shared_ptr, serverConnectionProvider)([] { 36 | return oatpp::network::tcp::server::ConnectionProvider::createShared({"0.0.0.0", 8000, oatpp::network::Address::IP_4}); 37 | }()); 38 | 39 | /** 40 | * Create Router component 41 | */ 42 | OATPP_CREATE_COMPONENT(std::shared_ptr, httpRouter)([] { 43 | return oatpp::web::server::HttpRouter::createShared(); 44 | }()); 45 | 46 | /** 47 | * Create ConnectionHandler component which uses Router component to route requests 48 | */ 49 | OATPP_CREATE_COMPONENT(std::shared_ptr, serverConnectionHandler)("http", [] { 50 | OATPP_COMPONENT(std::shared_ptr, router); // get Router component 51 | OATPP_COMPONENT(std::shared_ptr, executor); // get Async executor component 52 | return oatpp::web::server::AsyncHttpConnectionHandler::createShared(router, executor); 53 | }()); 54 | 55 | /** 56 | * Create ObjectMapper component to serialize/deserialize DTOs in Contoller's API 57 | */ 58 | OATPP_CREATE_COMPONENT(std::shared_ptr, apiObjectMapper)([] { 59 | return std::make_shared(); 60 | }()); 61 | 62 | /** 63 | * Create websocket connection handler 64 | */ 65 | OATPP_CREATE_COMPONENT(std::shared_ptr, websocketConnectionHandler)("websocket", [] { 66 | OATPP_COMPONENT(std::shared_ptr, executor); 67 | auto connectionHandler = oatpp::websocket::AsyncConnectionHandler::createShared(executor); 68 | connectionHandler->setSocketInstanceListener(std::make_shared()); 69 | return connectionHandler; 70 | }()); 71 | 72 | }; 73 | 74 | #endif /* AppComponent_hpp */ -------------------------------------------------------------------------------- /async-server-rooms/src/controller/RoomsController.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RoomsController_hpp 3 | #define RoomsController_hpp 4 | 5 | #include "oatpp-websocket/Handshaker.hpp" 6 | 7 | #include "oatpp/web/server/api/ApiController.hpp" 8 | 9 | #include "oatpp/network/ConnectionHandler.hpp" 10 | 11 | #include "oatpp/macro/codegen.hpp" 12 | #include "oatpp/macro/component.hpp" 13 | 14 | 15 | #include OATPP_CODEGEN_BEGIN(ApiController) //<-- codegen begin 16 | 17 | /** 18 | * Controller with WebSocket-connect endpoint. 19 | */ 20 | class RoomsController : public oatpp::web::server::api::ApiController { 21 | private: 22 | typedef RoomsController __ControllerType; 23 | private: 24 | OATPP_COMPONENT(std::shared_ptr, websocketConnectionHandler, "websocket"); 25 | public: 26 | RoomsController(OATPP_COMPONENT(std::shared_ptr, objectMapper)) 27 | : oatpp::web::server::api::ApiController(objectMapper) 28 | {} 29 | public: 30 | 31 | ENDPOINT_ASYNC("GET", "/", Root) { 32 | 33 | ENDPOINT_ASYNC_INIT(Root) 34 | 35 | const char* pageTemplate = 36 | "" 37 | "" 38 | "" 39 | "" 40 | "" 41 | "

Hello Async WebSocket Rooms Server!

" 42 | "

Connect to chat room:

" 43 | "localhost:8000/ws/chat/{room_name}/?nickname={nickname}" 44 | "" 45 | ""; 46 | 47 | Action act() override { 48 | return _return(controller->createResponse(Status::CODE_200, pageTemplate)); 49 | } 50 | 51 | }; 52 | 53 | ENDPOINT_ASYNC("GET", "ws/chat/{room-name}/*", WS) { 54 | 55 | ENDPOINT_ASYNC_INIT(WS) 56 | 57 | Action act() override { 58 | 59 | auto roomName = request->getPathVariable("room-name"); 60 | auto nickname = request->getQueryParameter("nickname"); 61 | 62 | OATPP_ASSERT_HTTP(nickname, Status::CODE_400, "No nickname specified."); 63 | 64 | /* Websocket handshake */ 65 | auto response = oatpp::websocket::Handshaker::serversideHandshake(request->getHeaders(), controller->websocketConnectionHandler); 66 | 67 | auto parameters = std::make_shared(); 68 | 69 | (*parameters)["roomName"] = roomName; 70 | (*parameters)["nickname"] = nickname; 71 | 72 | /* Set connection upgrade params */ 73 | response->setConnectionUpgradeParameters(parameters); 74 | 75 | return _return(response); 76 | 77 | } 78 | 79 | }; 80 | 81 | // TODO Insert Your endpoints here !!! 82 | 83 | }; 84 | 85 | #include OATPP_CODEGEN_END(ApiController) //<-- codegen end 86 | 87 | #endif /* RoomsController_hpp */ 88 | -------------------------------------------------------------------------------- /async-server-rooms/src/rooms/Lobby.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Lobby.hpp" 3 | 4 | v_int32 Lobby::obtainNewUserId() { 5 | return m_userIdCounter ++; 6 | } 7 | 8 | std::shared_ptr Lobby::getOrCreateRoom(const oatpp::String& roomName) { 9 | std::lock_guard lock(m_roomsMutex); 10 | std::shared_ptr& room = m_rooms[roomName]; 11 | if(!room) { 12 | room = std::make_shared(roomName); 13 | } 14 | return room; 15 | } 16 | 17 | void Lobby::onAfterCreate_NonBlocking(const std::shared_ptr& socket, const std::shared_ptr& params) { 18 | 19 | auto roomName = params->find("roomName")->second; 20 | auto nickname = params->find("nickname")->second; 21 | auto room = getOrCreateRoom(roomName); 22 | 23 | auto peer = std::make_shared(socket, room, nickname, obtainNewUserId()); 24 | socket->setListener(peer); 25 | 26 | room->addPeer(peer); 27 | room->sendMessage(nickname + " joined " + roomName); 28 | 29 | } 30 | 31 | void Lobby::onBeforeDestroy_NonBlocking(const std::shared_ptr& socket) { 32 | 33 | auto peer = std::static_pointer_cast(socket->getListener()); 34 | auto nickname = peer->getNickname(); 35 | auto room = peer->getRoom(); 36 | 37 | room->removePeerByUserId(peer->getUserId()); 38 | 39 | room->sendMessage(nickname + " left the room"); 40 | 41 | /* Remove circle `std::shared_ptr` dependencies */ 42 | socket->setListener(nullptr); 43 | 44 | } -------------------------------------------------------------------------------- /async-server-rooms/src/rooms/Lobby.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ASYNC_SERVER_ROOMS_LOBBY_HPP 3 | #define ASYNC_SERVER_ROOMS_LOBBY_HPP 4 | 5 | #include "./Room.hpp" 6 | 7 | #include "oatpp-websocket/AsyncConnectionHandler.hpp" 8 | 9 | #include 10 | #include 11 | 12 | class Lobby : public oatpp::websocket::AsyncConnectionHandler::SocketInstanceListener { 13 | public: 14 | std::atomic m_userIdCounter; 15 | std::unordered_map> m_rooms; 16 | std::mutex m_roomsMutex; 17 | public: 18 | 19 | Lobby() 20 | : m_userIdCounter(0) 21 | {} 22 | 23 | /** 24 | * Generate id for new user 25 | * @return 26 | */ 27 | v_int32 obtainNewUserId(); 28 | 29 | /** 30 | * Get room by name or create new one if not exists. 31 | * @param roomName 32 | * @return 33 | */ 34 | std::shared_ptr getOrCreateRoom(const oatpp::String& roomName); 35 | 36 | public: 37 | 38 | /** 39 | * Called when socket is created 40 | */ 41 | void onAfterCreate_NonBlocking(const std::shared_ptr& socket, const std::shared_ptr& params) override; 42 | 43 | /** 44 | * Called before socket instance is destroyed. 45 | */ 46 | void onBeforeDestroy_NonBlocking(const std::shared_ptr& socket) override; 47 | 48 | }; 49 | 50 | 51 | #endif //ASYNC_SERVER_ROOMS_LOBBY_HPP 52 | -------------------------------------------------------------------------------- /async-server-rooms/src/rooms/Peer.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Peer.hpp" 3 | #include "Room.hpp" 4 | 5 | void Peer::sendMessage(const oatpp::String& message) { 6 | 7 | class SendMessageCoroutine : public oatpp::async::Coroutine { 8 | private: 9 | oatpp::async::Lock* m_lock; 10 | std::shared_ptr m_websocket; 11 | oatpp::String m_message; 12 | public: 13 | 14 | SendMessageCoroutine(oatpp::async::Lock* lock, 15 | const std::shared_ptr& websocket, 16 | const oatpp::String& message) 17 | : m_lock(lock) 18 | , m_websocket(websocket) 19 | , m_message(message) 20 | {} 21 | 22 | Action act() override { 23 | return oatpp::async::synchronize(m_lock, m_websocket->sendOneFrameTextAsync(m_message)).next(finish()); 24 | } 25 | 26 | }; 27 | 28 | m_asyncExecutor->execute(&m_writeLock, m_socket, message); 29 | 30 | } 31 | 32 | std::shared_ptr Peer::getRoom() { 33 | return m_room; 34 | } 35 | 36 | oatpp::String Peer::getNickname() { 37 | return m_nickname; 38 | } 39 | 40 | v_int32 Peer::getUserId() { 41 | return m_userId; 42 | } 43 | 44 | oatpp::async::CoroutineStarter Peer::onPing(const std::shared_ptr& socket, const oatpp::String& message) { 45 | return oatpp::async::synchronize(&m_writeLock, socket->sendPongAsync(message)); 46 | } 47 | 48 | oatpp::async::CoroutineStarter Peer::onPong(const std::shared_ptr& socket, const oatpp::String& message) { 49 | return nullptr; // do nothing 50 | } 51 | 52 | oatpp::async::CoroutineStarter Peer::onClose(const std::shared_ptr& socket, v_uint16 code, const oatpp::String& message) { 53 | return nullptr; // do nothing 54 | } 55 | 56 | oatpp::async::CoroutineStarter Peer::readMessage(const std::shared_ptr& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) { 57 | 58 | if(size == 0) { // message transfer finished 59 | 60 | auto wholeMessage = m_messageBuffer.toString(); 61 | m_messageBuffer.setCurrentPosition(0); 62 | 63 | m_room->sendMessage(m_nickname + ": " + wholeMessage); 64 | 65 | } else if(size > 0) { // message frame received 66 | m_messageBuffer.writeSimple(data, size); 67 | } 68 | 69 | return nullptr; // do nothing 70 | 71 | } -------------------------------------------------------------------------------- /async-server-rooms/src/rooms/Peer.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ASYNC_SERVER_ROOMS_PEER_HPP 3 | #define ASYNC_SERVER_ROOMS_PEER_HPP 4 | 5 | #include "oatpp-websocket/AsyncWebSocket.hpp" 6 | 7 | #include "oatpp/async/Lock.hpp" 8 | #include "oatpp/async/Executor.hpp" 9 | 10 | #include "oatpp/macro/component.hpp" 11 | 12 | class Room; // FWD 13 | 14 | class Peer : public oatpp::websocket::AsyncWebSocket::Listener { 15 | private: 16 | 17 | /** 18 | * Buffer for messages. Needed for multi-frame messages. 19 | */ 20 | oatpp::data::stream::BufferOutputStream m_messageBuffer; 21 | 22 | /** 23 | * Lock for synchronization of writes to the web socket. 24 | */ 25 | oatpp::async::Lock m_writeLock; 26 | 27 | private: 28 | std::shared_ptr m_socket; 29 | std::shared_ptr m_room; 30 | oatpp::String m_nickname; 31 | v_int32 m_userId; 32 | private: 33 | 34 | /** 35 | * Inject async executor object. 36 | */ 37 | OATPP_COMPONENT(std::shared_ptr, m_asyncExecutor); 38 | 39 | public: 40 | 41 | Peer(const std::shared_ptr& socket, 42 | const std::shared_ptr& room, 43 | const oatpp::String& nickname, 44 | v_int32 userId) 45 | : m_socket(socket) 46 | , m_room(room) 47 | , m_nickname(nickname) 48 | , m_userId(userId) 49 | {} 50 | 51 | /** 52 | * Send message to peer (to user). 53 | * @param message 54 | */ 55 | void sendMessage(const oatpp::String& message); 56 | 57 | /** 58 | * Get room of the peer. 59 | * @return 60 | */ 61 | std::shared_ptr getRoom(); 62 | 63 | /** 64 | * Get peer nickname. 65 | * @return 66 | */ 67 | oatpp::String getNickname(); 68 | 69 | /** 70 | * Get peer userId. 71 | * @return 72 | */ 73 | v_int32 getUserId(); 74 | 75 | public: // WebSocket Listener methods 76 | 77 | CoroutineStarter onPing(const std::shared_ptr& socket, const oatpp::String& message) override; 78 | CoroutineStarter onPong(const std::shared_ptr& socket, const oatpp::String& message) override; 79 | CoroutineStarter onClose(const std::shared_ptr& socket, v_uint16 code, const oatpp::String& message) override; 80 | CoroutineStarter readMessage(const std::shared_ptr& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) override; 81 | 82 | }; 83 | 84 | 85 | #endif //ASYNC_SERVER_ROOMS_PEER_HPP 86 | -------------------------------------------------------------------------------- /async-server-rooms/src/rooms/Room.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Room.hpp" 3 | 4 | void Room::addPeer(const std::shared_ptr& peer) { 5 | std::lock_guard guard(m_peerByIdLock); 6 | m_peerById[peer->getUserId()] = peer; 7 | } 8 | 9 | void Room::removePeerByUserId(v_int32 userId) { 10 | std::lock_guard guard(m_peerByIdLock); 11 | m_peerById.erase(userId); 12 | } 13 | 14 | void Room::sendMessage(const oatpp::String& message) { 15 | std::lock_guard guard(m_peerByIdLock); 16 | for(auto& pair : m_peerById) { 17 | pair.second->sendMessage(message); 18 | } 19 | } -------------------------------------------------------------------------------- /async-server-rooms/src/rooms/Room.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ASYNC_SERVER_ROOMS_ROOM_HPP 3 | #define ASYNC_SERVER_ROOMS_ROOM_HPP 4 | 5 | #include "./Peer.hpp" 6 | 7 | #include 8 | 9 | class Room { 10 | private: 11 | oatpp::String m_name; 12 | std::unordered_map> m_peerById; 13 | std::mutex m_peerByIdLock; 14 | public: 15 | 16 | Room(const oatpp::String& name) 17 | : m_name(name) 18 | {} 19 | 20 | /** 21 | * Add peer to the room. 22 | * @param peer 23 | */ 24 | void addPeer(const std::shared_ptr& peer); 25 | 26 | /** 27 | * Remove peer from the room. 28 | * @param userId 29 | */ 30 | void removePeerByUserId(v_int32 userId); 31 | 32 | /** 33 | * Send message to all peers in the room. 34 | * @param message 35 | */ 36 | void sendMessage(const oatpp::String& message); 37 | 38 | }; 39 | 40 | #endif //ASYNC_SERVER_ROOMS_ROOM_HPP 41 | -------------------------------------------------------------------------------- /async-server-rooms/test/WSTest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Leonid on 2019-03-25. 3 | // 4 | 5 | #include "WSTest.hpp" 6 | 7 | void WSTest::onRun() { 8 | 9 | OATPP_LOGd(TAG, "TODO - write tests"); 10 | 11 | } -------------------------------------------------------------------------------- /async-server-rooms/test/WSTest.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MY_PROJECT_WEBSOCKETTEST_HPP 3 | #define MY_PROJECT_WEBSOCKETTEST_HPP 4 | 5 | 6 | #include "oatpp-test/UnitTest.hpp" 7 | 8 | class WSTest : public oatpp::test::UnitTest { 9 | public: 10 | 11 | WSTest():UnitTest("TEST[WSTest]"){} 12 | void onRun() override; 13 | 14 | }; 15 | 16 | 17 | #endif //MY_PROJECT_WEBSOCKETTEST_HPP 18 | -------------------------------------------------------------------------------- /async-server-rooms/test/tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | #include "oatpp-test/UnitTest.hpp" 5 | #include 6 | 7 | 8 | void runTests() { 9 | OATPP_RUN_TEST(WSTest); 10 | } 11 | 12 | int main() { 13 | 14 | oatpp::Environment::init(); 15 | 16 | runTests(); 17 | 18 | /* Print how much objects were created during app running, and what have left-probably leaked */ 19 | /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ 20 | std::cout << "\nEnvironment:\n"; 21 | std::cout << "objectsCount = " << oatpp::Environment::getObjectsCount() << "\n"; 22 | std::cout << "objectsCreated = " << oatpp::Environment::getObjectsCreated() << "\n\n"; 23 | 24 | OATPP_ASSERT(oatpp::Environment::getObjectsCount() == 0); 25 | 26 | oatpp::Environment::destroy(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /async-server-rooms/utility/install-oatpp-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | ########################################################## 9 | ## install oatpp 10 | 11 | MODULE_NAME="oatpp" 12 | 13 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 14 | 15 | cd $MODULE_NAME 16 | mkdir build 17 | cd build 18 | 19 | cmake .. 20 | make install 21 | 22 | cd ../../ 23 | 24 | ########################################################## 25 | ## install oatpp-websocket 26 | 27 | MODULE_NAME="oatpp-websocket" 28 | 29 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 30 | 31 | cd $MODULE_NAME 32 | mkdir build 33 | cd build 34 | 35 | cmake .. 36 | make install 37 | 38 | cd ../../ 39 | 40 | ########################################################## 41 | 42 | cd ../ 43 | 44 | rm -rf tmp 45 | -------------------------------------------------------------------------------- /async-server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | set(project_name async-websocket-server) ## rename your project here 4 | 5 | project(${project_name}) 6 | 7 | set(CMAKE_CXX_STANDARD 17) 8 | 9 | include_directories(src) 10 | 11 | add_library(${project_name}-lib 12 | src/AppComponent.hpp 13 | src/controller/MyController.hpp 14 | src/websocket/WSListener.cpp 15 | src/websocket/WSListener.hpp 16 | ) 17 | 18 | ## link libs 19 | 20 | find_package(oatpp 1.4.0 REQUIRED) 21 | find_package(oatpp-websocket 1.4.0 REQUIRED) 22 | 23 | target_link_libraries(${project_name}-lib 24 | PUBLIC oatpp::oatpp 25 | PUBLIC oatpp::oatpp-test 26 | PUBLIC oatpp::oatpp-websocket 27 | ) 28 | 29 | ## add executables 30 | 31 | add_executable(${project_name}-exe 32 | src/App.cpp 33 | ) 34 | target_link_libraries(${project_name}-exe ${project_name}-lib) 35 | add_dependencies(${project_name}-exe ${project_name}-lib) 36 | 37 | add_executable(${project_name}-test 38 | test/tests.cpp 39 | test/WSTest.cpp 40 | test/WSTest.hpp 41 | ) 42 | target_link_libraries(${project_name}-test ${project_name}-lib) 43 | add_dependencies(${project_name}-test ${project_name}-lib) 44 | 45 | set_target_properties(${project_name}-lib ${project_name}-exe ${project_name}-test PROPERTIES 46 | CXX_STANDARD 17 47 | CXX_EXTENSIONS OFF 48 | CXX_STANDARD_REQUIRED ON 49 | LINKER_LANGUAGE CXX 50 | ) 51 | 52 | enable_testing() 53 | add_test(tests ${project_name}-test) 54 | -------------------------------------------------------------------------------- /async-server/README.md: -------------------------------------------------------------------------------- 1 | # websocket async-server 2 | 3 | Simple example of the **asynchronous** websocket echo server. Built with oat++ (AKA oatpp) web framework. 4 | 5 | See more: 6 | 7 | - [Oat++ Website](https://oatpp.io/) 8 | - [Oat++ Github Repository](https://github.com/oatpp/oatpp) 9 | - [Get Started With Oat++](https://oatpp.io/docs/start) 10 | 11 | ## Overview 12 | 13 | ### Project layout 14 | 15 | ``` 16 | |- CMakeLists.txt // projects CMakeLists.txt 17 | |- src/ 18 | | | 19 | | |- controller/ 20 | | | | 21 | | | |- MyController.hpp // ApiController with websocket endpoint 22 | | | 23 | | |- websocket/ 24 | | | | 25 | | | |- WSListener.hpp // WebSocket listeners are defined here 26 | | | 27 | | |- AppComponent.hpp // Application config. 28 | | |- App.cpp // main() is here 29 | | 30 | |- utility/install-oatpp-modules.sh // utility script to install required oatpp-modules. 31 | ``` 32 | 33 | ## Build and Run 34 | 35 | ### Using CMake 36 | 37 | **Requires:** [oatpp](https://github.com/oatpp/oatpp), and [oatpp-websocket](https://github.com/oatpp/oatpp-websocket) 38 | modules installed. You may run `utility/install-oatpp-modules.sh` 39 | script to install required oatpp modules. 40 | 41 | After all dependencies satisfied: 42 | 43 | ``` 44 | $ mkdir build && cd build 45 | $ cmake .. 46 | $ make 47 | $ ./async-websocket-server-exe # - run application. 48 | ``` 49 | -------------------------------------------------------------------------------- /async-server/src/App.cpp: -------------------------------------------------------------------------------- 1 | #include "./controller/MyController.hpp" 2 | #include "./AppComponent.hpp" 3 | 4 | #include "oatpp/network/Server.hpp" 5 | 6 | #include 7 | 8 | void run() { 9 | 10 | /* Register Components in scope of run() method */ 11 | AppComponent components; 12 | 13 | /* Get router component */ 14 | OATPP_COMPONENT(std::shared_ptr, router); 15 | 16 | /* Create MyController and add all of its endpoints to router */ 17 | router->addController(std::make_shared()); 18 | 19 | /* Get connection handler component */ 20 | OATPP_COMPONENT(std::shared_ptr, connectionHandler, "http"); 21 | 22 | /* Get connection provider component */ 23 | OATPP_COMPONENT(std::shared_ptr, connectionProvider); 24 | 25 | /* Create server which takes provided TCP connections and passes them to HTTP connection handler */ 26 | oatpp::network::Server server(connectionProvider, connectionHandler); 27 | 28 | /* Priny info about server port */ 29 | OATPP_LOGi("MyApp", "Server running on port {}", connectionProvider->getProperty("port").toString()); 30 | 31 | /* Run server */ 32 | server.run(); 33 | 34 | } 35 | 36 | int main(int argc, const char * argv[]) { 37 | 38 | oatpp::Environment::init(); 39 | 40 | run(); 41 | 42 | oatpp::Environment::destroy(); 43 | 44 | return 0; 45 | } -------------------------------------------------------------------------------- /async-server/src/AppComponent.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AppComponent_hpp 2 | #define AppComponent_hpp 3 | 4 | #include "websocket/WSListener.hpp" 5 | 6 | #include "oatpp/web/server/AsyncHttpConnectionHandler.hpp" 7 | #include "oatpp/web/server/HttpRouter.hpp" 8 | #include "oatpp/network/tcp/server/ConnectionProvider.hpp" 9 | 10 | #include "oatpp/json/ObjectMapper.hpp" 11 | 12 | #include "oatpp/macro/component.hpp" 13 | 14 | /** 15 | * Class which creates and holds Application components and registers components in oatpp::Environment 16 | * Order of components initialization is from top to bottom 17 | */ 18 | class AppComponent { 19 | public: 20 | 21 | /** 22 | * Create Async Executor 23 | */ 24 | OATPP_CREATE_COMPONENT(std::shared_ptr, executor)([] { 25 | return std::make_shared( 26 | 4 /* Data-Processing threads */, 27 | 1 /* I/O threads */, 28 | 1 /* Timer threads */ 29 | ); 30 | }()); 31 | 32 | /** 33 | * Create ConnectionProvider component which listens on the port 34 | */ 35 | OATPP_CREATE_COMPONENT(std::shared_ptr, serverConnectionProvider)([] { 36 | return oatpp::network::tcp::server::ConnectionProvider::createShared({"0.0.0.0", 8000, oatpp::network::Address::IP_4}); 37 | }()); 38 | 39 | /** 40 | * Create Router component 41 | */ 42 | OATPP_CREATE_COMPONENT(std::shared_ptr, httpRouter)([] { 43 | return oatpp::web::server::HttpRouter::createShared(); 44 | }()); 45 | 46 | /** 47 | * Create ConnectionHandler component which uses Router component to route requests 48 | */ 49 | OATPP_CREATE_COMPONENT(std::shared_ptr, serverConnectionHandler)("http", [] { 50 | OATPP_COMPONENT(std::shared_ptr, router); // get Router component 51 | OATPP_COMPONENT(std::shared_ptr, executor); // get Async executor component 52 | return oatpp::web::server::AsyncHttpConnectionHandler::createShared(router, executor); 53 | }()); 54 | 55 | /** 56 | * Create ObjectMapper component to serialize/deserialize DTOs in Contoller's API 57 | */ 58 | OATPP_CREATE_COMPONENT(std::shared_ptr, apiObjectMapper)([] { 59 | return std::make_shared(); 60 | }()); 61 | 62 | /** 63 | * Create websocket connection handler 64 | */ 65 | OATPP_CREATE_COMPONENT(std::shared_ptr, websocketConnectionHandler)("websocket", [] { 66 | OATPP_COMPONENT(std::shared_ptr, executor); 67 | auto connectionHandler = oatpp::websocket::AsyncConnectionHandler::createShared(executor); 68 | connectionHandler->setSocketInstanceListener(std::make_shared()); 69 | return connectionHandler; 70 | }()); 71 | 72 | }; 73 | 74 | #endif /* AppComponent_hpp */ -------------------------------------------------------------------------------- /async-server/src/controller/MyController.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MyController_hpp 3 | #define MyController_hpp 4 | 5 | #include "oatpp-websocket/Handshaker.hpp" 6 | 7 | #include "oatpp/web/server/api/ApiController.hpp" 8 | 9 | #include "oatpp/network/ConnectionHandler.hpp" 10 | 11 | #include "oatpp/macro/codegen.hpp" 12 | #include "oatpp/macro/component.hpp" 13 | 14 | #include OATPP_CODEGEN_BEGIN(ApiController) //<-- codegen begin 15 | 16 | /** 17 | * Controller with WebSocket-connect endpoint. 18 | */ 19 | class MyController : public oatpp::web::server::api::ApiController { 20 | private: 21 | typedef MyController __ControllerType; 22 | private: 23 | OATPP_COMPONENT(std::shared_ptr, websocketConnectionHandler, "websocket"); 24 | public: 25 | MyController(OATPP_COMPONENT(std::shared_ptr, objectMapper)) 26 | : oatpp::web::server::api::ApiController(objectMapper) 27 | {} 28 | public: 29 | 30 | ENDPOINT_ASYNC("GET", "/", Root) { 31 | 32 | ENDPOINT_ASYNC_INIT(Root) 33 | 34 | const char* pageTemplate = 35 | "" 36 | "" 37 | "" 38 | "" 39 | "" 40 | "

Hello Async WebSocket Server!

" 41 | "

" 42 | "websocket endpoint is: localhost:8000/ws" 43 | "

" 44 | "" 45 | ""; 46 | 47 | Action act() override { 48 | return _return(controller->createResponse(Status::CODE_200, pageTemplate)); 49 | } 50 | 51 | }; 52 | 53 | ENDPOINT_ASYNC("GET", "ws", WS) { 54 | 55 | ENDPOINT_ASYNC_INIT(WS) 56 | 57 | Action act() override { 58 | auto response = oatpp::websocket::Handshaker::serversideHandshake(request->getHeaders(), controller->websocketConnectionHandler); 59 | return _return(response); 60 | } 61 | 62 | }; 63 | 64 | // TODO Insert Your endpoints here !!! 65 | 66 | }; 67 | 68 | #include OATPP_CODEGEN_END(ApiController) //<-- codegen end 69 | 70 | #endif /* MyController_hpp */ 71 | -------------------------------------------------------------------------------- /async-server/src/websocket/WSListener.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | #include "oatpp/base/Log.hpp" 4 | 5 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 6 | // WSListener 7 | 8 | oatpp::async::CoroutineStarter WSListener::onPing(const std::shared_ptr& socket, const oatpp::String& message) { 9 | OATPP_LOGd(TAG, "onPing"); 10 | return socket->sendPongAsync(message); 11 | } 12 | 13 | oatpp::async::CoroutineStarter WSListener::onPong(const std::shared_ptr& socket, const oatpp::String& message) { 14 | OATPP_LOGd(TAG, "onPong"); 15 | return nullptr; // do nothing 16 | } 17 | 18 | oatpp::async::CoroutineStarter WSListener::onClose(const std::shared_ptr& socket, v_uint16 code, const oatpp::String& message) { 19 | OATPP_LOGd(TAG, "onClose code={}", code); 20 | return nullptr; // do nothing 21 | } 22 | 23 | oatpp::async::CoroutineStarter WSListener::readMessage(const std::shared_ptr& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) { 24 | 25 | if(size == 0) { // message transfer finished 26 | 27 | auto wholeMessage = m_messageBuffer.toString(); 28 | m_messageBuffer.setCurrentPosition(0); 29 | 30 | OATPP_LOGd(TAG, "onMessage message='{}'", wholeMessage) 31 | 32 | /* Send message in reply */ 33 | return socket->sendOneFrameTextAsync( "Hello from oatpp!: " + wholeMessage); 34 | 35 | } else if(size > 0) { // message frame received 36 | m_messageBuffer.writeSimple(data, size); 37 | } 38 | 39 | return nullptr; // do nothing 40 | 41 | } 42 | 43 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 44 | // WSInstanceListener 45 | 46 | std::atomic WSInstanceListener::SOCKETS(0); 47 | 48 | void WSInstanceListener::onAfterCreate_NonBlocking(const std::shared_ptr& socket, const std::shared_ptr& params) { 49 | 50 | SOCKETS ++; 51 | OATPP_LOGd(TAG, "New Incoming Connection. Connection count={}", SOCKETS.load()); 52 | 53 | /* In this particular case we create one WSListener per each connection */ 54 | /* Which may be redundant in many cases */ 55 | socket->setListener(std::make_shared()); 56 | } 57 | 58 | void WSInstanceListener::onBeforeDestroy_NonBlocking(const std::shared_ptr& socket) { 59 | 60 | SOCKETS --; 61 | OATPP_LOGd(TAG, "Connection closed. Connection count={}", SOCKETS.load()); 62 | 63 | } -------------------------------------------------------------------------------- /async-server/src/websocket/WSListener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WSListener_hpp 3 | #define WSListener_hpp 4 | 5 | #include "oatpp-websocket/AsyncConnectionHandler.hpp" 6 | #include "oatpp-websocket/AsyncWebSocket.hpp" 7 | 8 | /** 9 | * WebSocket listener listens on incoming WebSocket events. 10 | */ 11 | class WSListener : public oatpp::websocket::AsyncWebSocket::Listener { 12 | private: 13 | static constexpr const char* TAG = "Server_WSListener"; 14 | private: 15 | /** 16 | * Buffer for messages. Needed for multi-frame messages. 17 | */ 18 | oatpp::data::stream::BufferOutputStream m_messageBuffer; 19 | public: 20 | 21 | /** 22 | * Called on "ping" frame. 23 | */ 24 | CoroutineStarter onPing(const std::shared_ptr& socket, const oatpp::String& message) override; 25 | 26 | /** 27 | * Called on "pong" frame 28 | */ 29 | CoroutineStarter onPong(const std::shared_ptr& socket, const oatpp::String& message) override; 30 | 31 | /** 32 | * Called on "close" frame 33 | */ 34 | CoroutineStarter onClose(const std::shared_ptr& socket, v_uint16 code, const oatpp::String& message) override; 35 | 36 | /** 37 | * Called on each message frame. After the last message will be called once-again with size == 0 to designate end of the message. 38 | */ 39 | CoroutineStarter readMessage(const std::shared_ptr& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) override; 40 | 41 | }; 42 | 43 | /** 44 | * Listener on new WebSocket connections. 45 | */ 46 | class WSInstanceListener : public oatpp::websocket::AsyncConnectionHandler::SocketInstanceListener { 47 | private: 48 | static constexpr const char* TAG = "Server_WSInstanceListener"; 49 | public: 50 | /** 51 | * Counter for connected clients. 52 | */ 53 | static std::atomic SOCKETS; 54 | public: 55 | 56 | /** 57 | * Called when socket is created 58 | */ 59 | void onAfterCreate_NonBlocking(const std::shared_ptr& socket, const std::shared_ptr& params) override; 60 | 61 | /** 62 | * Called before socket instance is destroyed. 63 | */ 64 | void onBeforeDestroy_NonBlocking(const std::shared_ptr& socket) override; 65 | 66 | }; 67 | 68 | #endif // WSListener_hpp 69 | -------------------------------------------------------------------------------- /async-server/test/WSTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | void WSTest::onRun() { 5 | 6 | OATPP_LOGd(TAG, "TODO - write tests"); 7 | 8 | } -------------------------------------------------------------------------------- /async-server/test/WSTest.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MY_PROJECT_WEBSOCKETTEST_HPP 3 | #define MY_PROJECT_WEBSOCKETTEST_HPP 4 | 5 | 6 | #include "oatpp-test/UnitTest.hpp" 7 | 8 | class WSTest : public oatpp::test::UnitTest { 9 | public: 10 | 11 | WSTest():UnitTest("TEST[WSTest]"){} 12 | void onRun() override; 13 | 14 | }; 15 | 16 | 17 | #endif //MY_PROJECT_WEBSOCKETTEST_HPP 18 | -------------------------------------------------------------------------------- /async-server/test/tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | #include "oatpp-test/UnitTest.hpp" 5 | #include 6 | 7 | 8 | void runTests() { 9 | OATPP_RUN_TEST(WSTest); 10 | } 11 | 12 | int main() { 13 | 14 | oatpp::Environment::init(); 15 | 16 | runTests(); 17 | 18 | /* Print how much objects were created during app running, and what have left-probably leaked */ 19 | /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ 20 | std::cout << "\nEnvironment:\n"; 21 | std::cout << "objectsCount = " << oatpp::Environment::getObjectsCount() << "\n"; 22 | std::cout << "objectsCreated = " << oatpp::Environment::getObjectsCreated() << "\n\n"; 23 | 24 | OATPP_ASSERT(oatpp::Environment::getObjectsCount() == 0); 25 | 26 | oatpp::Environment::destroy(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /async-server/utility/install-oatpp-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | ########################################################## 9 | ## install oatpp 10 | 11 | MODULE_NAME="oatpp" 12 | 13 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 14 | 15 | cd $MODULE_NAME 16 | mkdir build 17 | cd build 18 | 19 | cmake .. 20 | make install 21 | 22 | cd ../../ 23 | 24 | ########################################################## 25 | ## install oatpp-websocket 26 | 27 | MODULE_NAME="oatpp-websocket" 28 | 29 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 30 | 31 | cd $MODULE_NAME 32 | mkdir build 33 | cd build 34 | 35 | cmake .. 36 | make install 37 | 38 | cd ../../ 39 | 40 | ########################################################## 41 | 42 | cd ../ 43 | 44 | rm -rf tmp 45 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: ubuntu_20_04 3 | displayName: 'Build - Ubuntu 20.04' 4 | continueOnError: false 5 | pool: 6 | vmImage: 'ubuntu-20.04' 7 | workspace: 8 | clean: all 9 | steps: 10 | - script: | 11 | sudo ./ci-install-mbedtls.sh 12 | displayName: 'install mbedtls' 13 | - script: | 14 | sudo ./ci-install-oatpp-modules.sh 15 | displayName: 'install oatpp modules' 16 | - script: | 17 | mkdir -p async-server/build 18 | mkdir -p async-server-rooms/build 19 | mkdir -p client/build 20 | mkdir -p client-binance.com/build 21 | mkdir -p client-mbedtls/build 22 | mkdir -p server/build 23 | displayName: 'make dirs build' 24 | - script: | 25 | sudo cmake .. 26 | sudo make 27 | make test ARGS="-V" 28 | workingDirectory: async-server/build 29 | displayName: 'build async-server' 30 | - script: | 31 | sudo cmake .. 32 | sudo make 33 | make test ARGS="-V" 34 | workingDirectory: async-server-rooms/build 35 | displayName: 'build async-server-rooms' 36 | - script: | 37 | sudo cmake .. 38 | sudo make 39 | make test ARGS="-V" 40 | workingDirectory: client/build 41 | displayName: 'build client' 42 | - script: | 43 | sudo cmake .. 44 | sudo make 45 | make test ARGS="-V" 46 | workingDirectory: client-binance.com/build 47 | displayName: 'build client-binance.com' 48 | - script: | 49 | sudo cmake .. 50 | sudo make 51 | make test ARGS="-V" 52 | workingDirectory: client-mbedtls/build 53 | displayName: 'build client-mbedtls' 54 | - script: | 55 | sudo cmake .. 56 | sudo make 57 | make test ARGS="-V" 58 | workingDirectory: server/build 59 | displayName: 'build server' 60 | -------------------------------------------------------------------------------- /ci-install-mbedtls.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | git clone -b 'mbedtls-2.16.1' --single-branch --depth 1 --recurse-submodules https://github.com/ARMmbed/mbedtls 9 | 10 | cd mbedtls 11 | mkdir build && cd build 12 | 13 | cmake .. 14 | make 15 | make test 16 | make install 17 | 18 | cd ../../../ 19 | -------------------------------------------------------------------------------- /ci-install-oatpp-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | ########################################################## 9 | ## install oatpp 10 | 11 | MODULE_NAME="oatpp" 12 | 13 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 14 | 15 | cd $MODULE_NAME 16 | mkdir build 17 | cd build 18 | 19 | cmake .. 20 | make install 21 | 22 | cd ../../ 23 | 24 | ########################################################## 25 | ## install oatpp-websocket 26 | 27 | MODULE_NAME="oatpp-websocket" 28 | 29 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 30 | 31 | cd $MODULE_NAME 32 | mkdir build 33 | cd build 34 | 35 | cmake .. 36 | make install 37 | 38 | cd ../../ 39 | 40 | ########################################################## 41 | ## install oatpp-mbedtls 42 | 43 | MODULE_NAME="oatpp-mbedtls" 44 | 45 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 46 | 47 | cd $MODULE_NAME 48 | mkdir build 49 | cd build 50 | 51 | cmake .. 52 | make install 53 | 54 | cd ../../ 55 | 56 | ########################################################## 57 | 58 | cd ../ 59 | 60 | rm -rf tmp 61 | -------------------------------------------------------------------------------- /client-binance.com/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | set(project_name ws-client-binance.com) ## rename your project here 4 | 5 | project(${project_name}) 6 | 7 | set(CMAKE_CXX_STANDARD 11) 8 | 9 | include_directories(src) 10 | 11 | add_library(${project_name}-lib 12 | src/WSListener.cpp 13 | src/WSListener.hpp 14 | src/Model.hpp 15 | src/WSEventListener.hpp 16 | src/AggregateTradesListener.hpp 17 | src/TradesListener.hpp 18 | src/KlineCandlestickListener.hpp 19 | ) 20 | 21 | ## link libs 22 | 23 | include(FindPkgConfig) 24 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake/module") # <-- use Findmbedtls.cmake in /cmake/module folder 25 | 26 | find_package(mbedtls 2.16.0 REQUIRED) 27 | 28 | find_package(oatpp 1.3.0 REQUIRED) 29 | find_package(oatpp-websocket 1.3.0 REQUIRED) 30 | find_package(oatpp-mbedtls 1.3.0 REQUIRED) 31 | 32 | target_link_libraries(${project_name}-lib 33 | ## oatpp 34 | PUBLIC oatpp::oatpp 35 | PUBLIC oatpp::oatpp-test 36 | PUBLIC oatpp::oatpp-websocket 37 | PUBLIC oatpp::oatpp-mbedtls 38 | 39 | ## mbedtls 40 | PUBLIC mbedtls::TLS 41 | PUBLIC mbedtls::X509 42 | PUBLIC mbedtls::Crypto 43 | ) 44 | 45 | ################################################################# 46 | ## link mbedtls 47 | 48 | link_directories( 49 | /usr/local/lib/ 50 | ) 51 | 52 | ################################################################# 53 | 54 | ## add executables 55 | 56 | add_executable(${project_name}-exe 57 | src/App.cpp 58 | ) 59 | target_link_libraries(${project_name}-exe ${project_name}-lib) 60 | add_dependencies(${project_name}-exe ${project_name}-lib) 61 | 62 | add_executable(${project_name}-test 63 | test/tests.cpp 64 | test/WSTest.cpp 65 | test/WSTest.hpp 66 | ) 67 | target_link_libraries(${project_name}-test ${project_name}-lib) 68 | add_dependencies(${project_name}-test ${project_name}-lib) 69 | 70 | set_target_properties(${project_name}-lib ${project_name}-exe ${project_name}-test PROPERTIES 71 | CXX_STANDARD 11 72 | CXX_EXTENSIONS OFF 73 | CXX_STANDARD_REQUIRED ON 74 | LINKER_LANGUAGE CXX 75 | ) 76 | 77 | enable_testing() 78 | add_test(tests ${project_name}-test) 79 | -------------------------------------------------------------------------------- /client-binance.com/README.md: -------------------------------------------------------------------------------- 1 | # binance.com websocket client 2 | 3 | Client for binance.com public WebSocket APIs. Built with oat++ (AKA oatpp) web framework. 4 | 5 | See more: 6 | 7 | - [Oat++ Website](https://oatpp.io/) 8 | - [Oat++ Github Repository](https://github.com/oatpp/oatpp) 9 | - [Get Started With Oat++](https://oatpp.io/docs/start) 10 | 11 | ## Overview 12 | 13 | This websocket client connects to stream.binance.com, listens to events and prints data to log-output. 14 | 15 | Listeners are implemented for the following streams: 16 | 17 | - Aggregate Trade Streams 18 | - Trade Streams 19 | - Kline/Candlestick Streams 20 | 21 | *more detailes on binance.com API [here](https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md)* 22 | 23 | ### Project layout 24 | 25 | ``` 26 | |- CMakeLists.txt // projects CMakeLists.txt 27 | |- src/ 28 | | | 29 | | |- Model.hpp // DTOs for binance.com API models 30 | | |- AggregateTradesListener.hpp // WebSocket listener for AggregateTrades streams 31 | | |- TradesListener.hpp // WebSocket listener for Trades streams 32 | | |- KlineCandlestickListener.hpp // WebSocket listener for Kline/Candlestick streams 33 | | |- WSEventListener.hpp // Template listener for stream objects. Deserialize received message to an event object. 34 | | |- WSListener.hpp // Base WebSocket listener. Implements "pongs" and reads messages. 35 | | |- App.cpp 36 | | 37 | |- utility/install-oatpp-modules.sh // utility script to install required oatpp-modules. 38 | ``` 39 | 40 | ## Build and Run 41 | 42 | ### Using CMake 43 | 44 | **Requires** 45 | - MbedTLS installed - see [install mbedtls](#install-mbedtls). 46 | - [oatpp](https://github.com/oatpp/oatpp), [oatpp-websocket](https://github.com/oatpp/oatpp-websocket), 47 | and [oatpp-mbedtls](https://github.com/oatpp/oatpp-mbedtls) modules installed. You may run `utility/install-oatpp-modules.sh` 48 | script to install required oatpp modules. 49 | 50 | ``` 51 | $ mkdir build && cd build 52 | $ cmake .. 53 | $ make 54 | $ ./ws-client-binance.com-exe # - run application. 55 | ``` 56 | 57 | ## Install MbedTLS 58 | 59 | ```bash 60 | git clone -b 'mbedtls-2.16.1' --single-branch --depth 1 --recurse-submodules https://github.com/ARMmbed/mbedtls 61 | 62 | cd mbedtls 63 | mkdir build && cd build 64 | 65 | cmake .. 66 | make install 67 | ``` 68 | 69 | -------------------------------------------------------------------------------- /client-binance.com/src/AggregateTradesListener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef AggregateTradesListener_hpp 3 | #define AggregateTradesListener_hpp 4 | 5 | #include "Model.hpp" 6 | #include "WSEventListener.hpp" 7 | 8 | /** 9 | * Listener for Aggregate Trade events 10 | */ 11 | class AggregateTradesListener : public WSEventListener> { 12 | public: 13 | 14 | AggregateTradesListener(const std::shared_ptr& mapper) 15 | : WSEventListener>(mapper) 16 | {} 17 | 18 | void onEvent(const oatpp::Object& trade) override { 19 | 20 | OATPP_LOGI("AggregateTrades", "%s - quantity=%s, price=%s, time=%d / firstTradeId=%d, lastTradeId=%d", 21 | trade->symbol->c_str(), 22 | trade->quantity->c_str(), 23 | trade->price->c_str(), 24 | *trade->tradeTime, 25 | *trade->firstTradeId, 26 | *trade->lastTradeId 27 | ); 28 | 29 | } 30 | 31 | }; 32 | 33 | #endif // AggregateTradesListener_hpp 34 | -------------------------------------------------------------------------------- /client-binance.com/src/App.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | 4 | #include "KlineCandlestickListener.hpp" 5 | #include "AggregateTradesListener.hpp" 6 | #include "TradesListener.hpp" 7 | 8 | #include "oatpp-websocket/WebSocket.hpp" 9 | #include "oatpp-websocket/Connector.hpp" 10 | 11 | #include "oatpp-mbedtls/client/ConnectionProvider.hpp" 12 | #include "oatpp-mbedtls/Config.hpp" 13 | 14 | #include "oatpp/json/ObjectMapper.hpp" 15 | 16 | #include 17 | 18 | namespace { 19 | 20 | const char* TAG = "websocket-client-binance.com"; 21 | 22 | } 23 | 24 | /** 25 | * Connect to "aggregate trade" stream and listen for trading events 26 | * @param connector 27 | * @param objectMapper 28 | */ 29 | void readAggregateTrades(const std::shared_ptr& connector, 30 | const std::shared_ptr& objectMapper) 31 | { 32 | 33 | auto connection = connector->connect("ws/bnbbtc@aggTrade"); 34 | oatpp::websocket::WebSocket socket(connection, true /* maskOutgoingMessages must be true for clients */); 35 | socket.setListener(std::make_shared(objectMapper)); 36 | socket.listen(); 37 | 38 | } 39 | 40 | /** 41 | * Connect to "trade" stream and listen for trading events 42 | * @param connector 43 | * @param objectMapper 44 | */ 45 | void readTrades(const std::shared_ptr& connector, 46 | const std::shared_ptr& objectMapper) 47 | { 48 | 49 | auto connection = connector->connect("ws/bnbbtc@trade"); 50 | oatpp::websocket::WebSocket socket(connection, true /* maskOutgoingMessages must be true for clients */); 51 | socket.setListener(std::make_shared(objectMapper)); 52 | socket.listen(); 53 | 54 | } 55 | 56 | /** 57 | * Connect to "Kline/Candlestick" stream and listen for events 58 | * @param connector 59 | * @param objectMapper 60 | */ 61 | void readCandlesticks(const std::shared_ptr& connector, 62 | const std::shared_ptr& objectMapper) 63 | { 64 | 65 | auto connection = connector->connect("ws/bnbbtc@kline_1m"); 66 | oatpp::websocket::WebSocket socket(connection, true /* maskOutgoingMessages must be true for clients */); 67 | socket.setListener(std::make_shared(objectMapper)); 68 | socket.listen(); 69 | 70 | } 71 | 72 | // TODO - add other stream tasks here 73 | 74 | void run() { 75 | 76 | OATPP_LOGI(TAG, "Application Started"); 77 | 78 | /* mbedtls config */ 79 | auto config = oatpp::mbedtls::Config::createDefaultClientConfigShared(); 80 | 81 | /* secure connection provider for stream.binance.com */ 82 | oatpp::network::Address address("stream.binance.com", 9443 /* port */); 83 | auto connectionProvider = oatpp::mbedtls::client::ConnectionProvider::createShared(config, address); 84 | 85 | /* websocket connector */ 86 | auto connector = oatpp::websocket::Connector::createShared(connectionProvider); 87 | 88 | /* object mapper for DTO objects */ 89 | auto objectMapper = std::make_shared(); 90 | 91 | 92 | /* Start Stream Reading Tasks */ 93 | 94 | std::thread aggTradesThread(readAggregateTrades, connector, objectMapper); 95 | std::thread tradesThread(readTrades, connector, objectMapper); 96 | std::thread candlesticksThread(readCandlesticks, connector, objectMapper); 97 | 98 | /* join task threads */ 99 | aggTradesThread.join(); 100 | tradesThread.join(); 101 | candlesticksThread.join(); 102 | 103 | } 104 | 105 | int main() { 106 | oatpp::Environment::init(); 107 | run(); 108 | oatpp::Environment::destroy(); 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /client-binance.com/src/KlineCandlestickListener.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KlineCandlestickListener_hpp 2 | #define KlineCandlestickListener_hpp 3 | 4 | #include "Model.hpp" 5 | #include "WSEventListener.hpp" 6 | 7 | /** 8 | * Listener for Trade events 9 | */ 10 | class KlineCandlestickListener : public WSEventListener> { 11 | public: 12 | 13 | KlineCandlestickListener(const std::shared_ptr& mapper) 14 | : WSEventListener>(mapper) 15 | {} 16 | 17 | void onEvent(const oatpp::Object& obj) override { 18 | 19 | OATPP_LOGI("Kline/Candlesticks", "%s - startTime=%d, closeTime=%d / openPrice=%s, closePrice=%s, highPrice=%s, lowPrice=%s", 20 | obj->symbol->c_str(), 21 | *obj->kline->klineStartTime, 22 | *obj->kline->klineCloseTime, 23 | obj->kline->openPrice->c_str(), 24 | obj->kline->closePrice->c_str(), 25 | obj->kline->highPrice->c_str(), 26 | obj->kline->lowPrice->c_str() 27 | ); 28 | 29 | } 30 | 31 | }; 32 | 33 | #endif // KlineCandlestickListener_hpp 34 | -------------------------------------------------------------------------------- /client-binance.com/src/Model.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef Model_hpp 3 | #define Model_hpp 4 | 5 | #include "oatpp/Types.hpp" 6 | #include "oatpp/macro/codegen.hpp" 7 | 8 | #include OATPP_CODEGEN_BEGIN(DTO) 9 | 10 | /** 11 | * DTO representing "Aggregate Trade Stream" object from binance-exchange. 12 | * See https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#aggregate-trade-streams 13 | */ 14 | class AggregateTrade : public oatpp::DTO { 15 | 16 | DTO_INIT(AggregateTrade, DTO) 17 | 18 | DTO_FIELD(String, eventType, "e"); 19 | DTO_FIELD(Int64, eventTime, "E"); 20 | DTO_FIELD(String, symbol, "s"); 21 | DTO_FIELD(Int64, aggregateTradeId, "a"); 22 | DTO_FIELD(String, price, "p"); 23 | DTO_FIELD(String, quantity, "q"); 24 | DTO_FIELD(Int64, firstTradeId, "f"); 25 | DTO_FIELD(Int64, lastTradeId, "l"); 26 | DTO_FIELD(Int64, tradeTime, "T"); 27 | DTO_FIELD(Boolean, marketMaker, "m"); 28 | DTO_FIELD(Boolean, ignore, "M"); 29 | 30 | }; 31 | 32 | /** 33 | * DTO representing "Trade Stream" object from binance-exchange. 34 | * See https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#trade-streams 35 | */ 36 | class Trade : public oatpp::DTO { 37 | 38 | DTO_INIT(Trade, DTO) 39 | 40 | DTO_FIELD(String, eventType, "e"); 41 | DTO_FIELD(Int64, eventTime, "E"); 42 | DTO_FIELD(String, symbol, "s"); 43 | DTO_FIELD(Int64, tradeId, "t"); 44 | DTO_FIELD(String, price, "p"); 45 | DTO_FIELD(String, quantity, "q"); 46 | DTO_FIELD(Int64, buyerOrderId, "b"); 47 | DTO_FIELD(Int64, sellerOrderId, "a"); 48 | DTO_FIELD(Int64, tradeTime, "T"); 49 | DTO_FIELD(Boolean, marketMaker, "m"); 50 | DTO_FIELD(Boolean, ignore, "M"); 51 | 52 | }; 53 | 54 | /** 55 | * Nested object for Candlestick 56 | */ 57 | class Kline : public oatpp::DTO { 58 | 59 | DTO_INIT(Kline, DTO) 60 | 61 | DTO_FIELD(Int64, klineStartTime, "t"); 62 | DTO_FIELD(Int64, klineCloseTime, "T"); 63 | DTO_FIELD(String, symbol, "s"); 64 | DTO_FIELD(String, interval, "i"); 65 | 66 | DTO_FIELD(Int64, firstTradeId, "f"); 67 | DTO_FIELD(Int64, lastTradeId, "L"); 68 | 69 | DTO_FIELD(String, openPrice, "o"); 70 | DTO_FIELD(String, closePrice, "c"); 71 | DTO_FIELD(String, highPrice, "h"); 72 | DTO_FIELD(String, lowPrice, "l"); 73 | DTO_FIELD(String, baseAssetVolume, "v"); 74 | 75 | DTO_FIELD(Int64, numberOfTrades, "n"); 76 | DTO_FIELD(Boolean, isKlineClosed, "x"); 77 | 78 | DTO_FIELD(String, quoteAssetVolume, "q"); 79 | DTO_FIELD(String, takerBuyBaseAssetVolume, "V"); 80 | DTO_FIELD(String, takerBuyQuoteAssetVolume, "Q"); 81 | DTO_FIELD(String, ignore, "B"); 82 | 83 | }; 84 | 85 | /** 86 | * DTO representing "Kline/Candlestick Stream" object from binance-exchange. 87 | * See https://github.com/binance-exchange/binance-official-api-docs/blob/master/web-socket-streams.md#klinecandlestick-streams 88 | */ 89 | class Candlestick : public oatpp::DTO { 90 | 91 | DTO_INIT(Candlestick, DTO) 92 | 93 | DTO_FIELD(String, eventType, "e"); 94 | DTO_FIELD(Int64, eventTime, "E"); 95 | DTO_FIELD(String, symbol, "s"); 96 | DTO_FIELD(Object, kline, "k"); 97 | 98 | }; 99 | 100 | #include OATPP_CODEGEN_END(DTO) ///< End DTO codegen section 101 | 102 | #endif // Model_hpp 103 | -------------------------------------------------------------------------------- /client-binance.com/src/TradesListener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef TradesListener_hpp 3 | #define TradesListener_hpp 4 | 5 | #include "Model.hpp" 6 | #include "WSEventListener.hpp" 7 | 8 | /** 9 | * Listener for Trade events 10 | */ 11 | class TradesListener : public WSEventListener> { 12 | public: 13 | 14 | TradesListener(const std::shared_ptr& mapper) 15 | : WSEventListener>(mapper) 16 | {} 17 | 18 | void onEvent(const oatpp::Object& trade) override { 19 | 20 | OATPP_LOGI("Trades", "%s - quantity=%s, price=%s, time=%d", 21 | trade->symbol->c_str(), 22 | trade->quantity->c_str(), 23 | trade->price->c_str(), 24 | *trade->tradeTime 25 | ); 26 | 27 | } 28 | 29 | }; 30 | 31 | #endif // TradesListener_hpp 32 | -------------------------------------------------------------------------------- /client-binance.com/src/WSEventListener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WSEventListener_hpp 3 | #define WSEventListener_hpp 4 | 5 | #include "WSListener.hpp" 6 | 7 | #include "oatpp/data/mapping/ObjectMapper.hpp" 8 | 9 | /** 10 | * Template event listener class. 11 | * Basically it waits for a message and deserializes message to a given DTO (model) class. 12 | * @tparam T 13 | */ 14 | template 15 | class WSEventListener : public WSListener { 16 | private: 17 | std::shared_ptr m_mapper; 18 | public: 19 | 20 | WSEventListener(const std::shared_ptr& mapper) 21 | : m_mapper(mapper) 22 | {} 23 | 24 | void onCompleteMessage(const oatpp::String& message) override { 25 | auto eventObject = m_mapper->readFromString(message); 26 | onEvent(eventObject); 27 | } 28 | 29 | virtual void onEvent(const T& eventObject) = 0; 30 | 31 | }; 32 | 33 | #endif // WSEventListener_hpp 34 | -------------------------------------------------------------------------------- /client-binance.com/src/WSListener.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | 4 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 5 | // WSListener 6 | 7 | void WSListener::onPing(const WebSocket& socket, const oatpp::String& message) { 8 | OATPP_LOGd(TAG, "onPing"); 9 | socket.sendPong(message); 10 | } 11 | 12 | void WSListener::onPong(const WebSocket& socket, const oatpp::String& message) { 13 | OATPP_LOGd(TAG, "onPong"); 14 | } 15 | 16 | void WSListener::onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) { 17 | OATPP_LOGd(TAG, "onClose code={}", code); 18 | } 19 | 20 | void WSListener::readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) { 21 | 22 | if(size == 0) { // message transfer finished 23 | 24 | auto wholeMessage = m_messageBuffer.toString(); 25 | m_messageBuffer.setCurrentPosition(0); 26 | onCompleteMessage(wholeMessage); 27 | 28 | } else if(size > 0) { // message frame received 29 | m_messageBuffer.writeSimple(data, size); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /client-binance.com/src/WSListener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WSListener_hpp 3 | #define WSListener_hpp 4 | 5 | #include "oatpp-websocket/ConnectionHandler.hpp" 6 | #include "oatpp-websocket/WebSocket.hpp" 7 | 8 | /** 9 | * WebSocket listener listens on incoming WebSocket events. 10 | */ 11 | class WSListener : public oatpp::websocket::WebSocket::Listener { 12 | private: 13 | static constexpr const char* TAG = "Client_WSListener"; 14 | private: 15 | /** 16 | * Buffer for messages. Needed for multi-frame messages. 17 | */ 18 | oatpp::data::stream::BufferOutputStream m_messageBuffer; 19 | public: 20 | 21 | /** 22 | * Called on "ping" frame. 23 | */ 24 | void onPing(const WebSocket& socket, const oatpp::String& message) override; 25 | 26 | /** 27 | * Called on "pong" frame 28 | */ 29 | void onPong(const WebSocket& socket, const oatpp::String& message) override; 30 | 31 | /** 32 | * Called on "close" frame 33 | */ 34 | void onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) override; 35 | 36 | /** 37 | * Called on each message frame. After the last message will be called once-again with size == 0 to designate end of the message. 38 | */ 39 | void readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) override; 40 | 41 | public: 42 | 43 | virtual void onCompleteMessage(const oatpp::String& message) = 0; 44 | 45 | }; 46 | 47 | #endif // WSListener_hpp 48 | -------------------------------------------------------------------------------- /client-binance.com/test/WSTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | void WSTest::onRun() { 5 | 6 | OATPP_LOGd(TAG, "TODO - write tests"); 7 | 8 | } -------------------------------------------------------------------------------- /client-binance.com/test/WSTest.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MY_PROJECT_WEBSOCKETTEST_HPP 3 | #define MY_PROJECT_WEBSOCKETTEST_HPP 4 | 5 | 6 | #include "oatpp-test/UnitTest.hpp" 7 | 8 | class WSTest : public oatpp::test::UnitTest { 9 | public: 10 | 11 | WSTest():UnitTest("TEST[WSTest]"){} 12 | void onRun() override; 13 | 14 | }; 15 | 16 | 17 | #endif //MY_PROJECT_WEBSOCKETTEST_HPP 18 | -------------------------------------------------------------------------------- /client-binance.com/test/tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | #include "oatpp-test/UnitTest.hpp" 5 | #include 6 | 7 | 8 | void runTests() { 9 | OATPP_RUN_TEST(WSTest); 10 | } 11 | 12 | int main() { 13 | 14 | oatpp::Environment::init(); 15 | 16 | runTests(); 17 | 18 | /* Print how much objects were created during app running, and what have left-probably leaked */ 19 | /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ 20 | std::cout << "\nEnvironment:\n"; 21 | std::cout << "objectsCount = " << oatpp::Environment::getObjectsCount() << "\n"; 22 | std::cout << "objectsCreated = " << oatpp::Environment::getObjectsCreated() << "\n\n"; 23 | 24 | OATPP_ASSERT(oatpp::Environment::getObjectsCount() == 0); 25 | 26 | oatpp::Environment::destroy(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /client-binance.com/utility/install-oatpp-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | ########################################################## 9 | ## install oatpp 10 | 11 | MODULE_NAME="oatpp" 12 | 13 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 14 | 15 | cd $MODULE_NAME 16 | mkdir build 17 | cd build 18 | 19 | cmake .. 20 | make install 21 | 22 | cd ../../ 23 | 24 | ########################################################## 25 | ## install oatpp-websocket 26 | 27 | MODULE_NAME="oatpp-websocket" 28 | 29 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 30 | 31 | cd $MODULE_NAME 32 | mkdir build 33 | cd build 34 | 35 | cmake .. 36 | make install 37 | 38 | cd ../../ 39 | 40 | ########################################################## 41 | ## install oatpp-mbedtls 42 | 43 | MODULE_NAME="oatpp-mbedtls" 44 | 45 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 46 | 47 | cd $MODULE_NAME 48 | mkdir build 49 | cd build 50 | 51 | cmake .. 52 | make install 53 | 54 | cd ../../ 55 | 56 | ########################################################## 57 | 58 | cd ../ 59 | 60 | rm -rf tmp 61 | -------------------------------------------------------------------------------- /client-mbedtls/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | set(project_name websocket-client-mbedtls) ## rename your project here 4 | 5 | project(${project_name}) 6 | 7 | set(CMAKE_CXX_STANDARD 11) 8 | 9 | include_directories(src) 10 | 11 | add_library(${project_name}-lib 12 | src/WSListener.cpp 13 | src/WSListener.hpp 14 | ) 15 | 16 | ## link libs 17 | 18 | include(FindPkgConfig) 19 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../cmake/module") # <-- use Findmbedtls.cmake in /cmake/module folder 20 | 21 | find_package(mbedtls 2.16.0 REQUIRED) 22 | 23 | find_package(oatpp 1.3.0 REQUIRED) 24 | find_package(oatpp-websocket 1.3.0 REQUIRED) 25 | find_package(oatpp-mbedtls 1.3.0 REQUIRED) 26 | 27 | target_link_libraries(${project_name}-lib 28 | ## oatpp 29 | PUBLIC oatpp::oatpp 30 | PUBLIC oatpp::oatpp-test 31 | PUBLIC oatpp::oatpp-websocket 32 | PUBLIC oatpp::oatpp-mbedtls 33 | 34 | ## mbedtls 35 | PUBLIC mbedtls::TLS 36 | PUBLIC mbedtls::X509 37 | PUBLIC mbedtls::Crypto 38 | ) 39 | 40 | ################################################################# 41 | ## link mbedtls 42 | 43 | link_directories( 44 | /usr/local/lib/ 45 | ) 46 | 47 | ################################################################# 48 | 49 | ## add executables 50 | 51 | add_executable(${project_name}-exe 52 | src/App.cpp 53 | ) 54 | target_link_libraries(${project_name}-exe ${project_name}-lib) 55 | add_dependencies(${project_name}-exe ${project_name}-lib) 56 | 57 | add_executable(${project_name}-test 58 | test/tests.cpp 59 | test/WSTest.cpp 60 | test/WSTest.hpp 61 | ) 62 | target_link_libraries(${project_name}-test ${project_name}-lib) 63 | add_dependencies(${project_name}-test ${project_name}-lib) 64 | 65 | set_target_properties(${project_name}-lib ${project_name}-exe ${project_name}-test PROPERTIES 66 | CXX_STANDARD 11 67 | CXX_EXTENSIONS OFF 68 | CXX_STANDARD_REQUIRED ON 69 | LINKER_LANGUAGE CXX 70 | ) 71 | 72 | enable_testing() 73 | add_test(tests ${project_name}-test) 74 | -------------------------------------------------------------------------------- /client-mbedtls/README.md: -------------------------------------------------------------------------------- 1 | # secure websocket client with mbedtls 2 | 3 | Simple example of the secure websocket client. Built with oat++ (AKA oatpp) web framework. 4 | TLS backend - MbedTLS. 5 | 6 | See more: 7 | 8 | - [Oat++ Website](https://oatpp.io/) 9 | - [Oat++ Github Repository](https://github.com/oatpp/oatpp) 10 | - [Get Started With Oat++](https://oatpp.io/docs/start) 11 | 12 | ## Overview 13 | 14 | ### Project layout 15 | 16 | ``` 17 | |- CMakeLists.txt // projects CMakeLists.txt 18 | |- src/ 19 | | | 20 | | |- WSListener.hpp // WebSocket listeners are defined here 21 | | |- App.cpp // main() is here 22 | | 23 | |- utility/install-oatpp-modules.sh // utility script to install required oatpp-modules. 24 | ``` 25 | 26 | ## Build and Run 27 | 28 | ### Using CMake 29 | 30 | **Requires** 31 | - MbedTLS installed - see [install mbedtls](#install-mbedtls). 32 | - [oatpp](https://github.com/oatpp/oatpp), [oatpp-websocket](https://github.com/oatpp/oatpp-websocket), 33 | and [oatpp-mbedtls](https://github.com/oatpp/oatpp-mbedtls) modules installed. You may run `utility/install-oatpp-modules.sh` 34 | script to install required oatpp modules. 35 | 36 | ``` 37 | $ mkdir build && cd build 38 | $ cmake .. 39 | $ make 40 | $ ./websocket-client-mbedtls-exe # - run application. 41 | ``` 42 | 43 | ## Install MbedTLS 44 | 45 | ```bash 46 | git clone -b 'mbedtls-2.16.1' --single-branch --depth 1 --recurse-submodules https://github.com/ARMmbed/mbedtls 47 | 48 | cd mbedtls 49 | mkdir build && cd build 50 | 51 | cmake .. 52 | make install 53 | ``` 54 | -------------------------------------------------------------------------------- /client-mbedtls/src/App.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | 4 | #include "oatpp-websocket/WebSocket.hpp" 5 | #include "oatpp-websocket/Connector.hpp" 6 | 7 | #include "oatpp-mbedtls/client/ConnectionProvider.hpp" 8 | #include "oatpp-mbedtls/Config.hpp" 9 | 10 | #include 11 | 12 | namespace { 13 | 14 | const char* TAG = "websocket-client-mbedtls"; 15 | 16 | bool finished = false; 17 | 18 | void socketTask(const std::shared_ptr& websocket) { 19 | websocket->listen(); 20 | OATPP_LOGd(TAG, "SOCKET CLOSED!!!"); 21 | finished = true; 22 | } 23 | 24 | } 25 | 26 | void run() { 27 | 28 | OATPP_LOGI(TAG, "Application Started"); 29 | 30 | auto config = oatpp::mbedtls::Config::createDefaultClientConfigShared(); 31 | 32 | oatpp::network::Address address("demo.piesocket.com", 443); 33 | auto connectionProvider = oatpp::mbedtls::client::ConnectionProvider::createShared(config, address); 34 | 35 | auto connector = oatpp::websocket::Connector::createShared(connectionProvider); 36 | 37 | auto connection = connector->connect("v3/channel_1?api_key=oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm¬ify_self"); 38 | 39 | OATPP_LOGI(TAG, "Connected"); 40 | 41 | auto socket = oatpp::websocket::WebSocket::createShared(connection, true /* maskOutgoingMessages must be true for clients */); 42 | 43 | std::mutex socketWriteMutex; 44 | 45 | socket->setListener(std::make_shared(socketWriteMutex)); 46 | 47 | std::thread thread(socketTask, socket); 48 | 49 | while(!finished) { 50 | { 51 | OATPP_LOGd(TAG, "sending message..."); 52 | std::lock_guard lock(socketWriteMutex); 53 | socket->sendOneFrameText("hello"); 54 | } 55 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 56 | } 57 | 58 | thread.join(); 59 | 60 | } 61 | 62 | int main() { 63 | oatpp::Environment::init(); 64 | run(); 65 | oatpp::Environment::destroy(); 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /client-mbedtls/src/WSListener.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | #include "oatpp/base/Log.hpp" 4 | 5 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 6 | // WSListener 7 | 8 | void WSListener::onPing(const WebSocket& socket, const oatpp::String& message) { 9 | OATPP_LOGd(TAG, "onPing"); 10 | std::lock_guard lock(m_writeMutex); 11 | socket.sendPong(message); 12 | } 13 | 14 | void WSListener::onPong(const WebSocket& socket, const oatpp::String& message) { 15 | OATPP_LOGd(TAG, "onPong"); 16 | } 17 | 18 | void WSListener::onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) { 19 | OATPP_LOGd(TAG, "onClose code={}", code); 20 | } 21 | 22 | void WSListener::readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) { 23 | 24 | if(size == 0) { // message transfer finished 25 | 26 | auto wholeMessage = m_messageBuffer.toString(); 27 | m_messageBuffer.setCurrentPosition(0); 28 | 29 | OATPP_LOGd(TAG, "on message received '{}'", wholeMessage->c_str()); 30 | 31 | /* Send message in reply */ 32 | //std::lock_guard lock(m_writeMutex); 33 | //socket.sendOneFrameText( "Hello from oatpp!: " + wholeMessage); 34 | 35 | } else if(size > 0) { // message frame received 36 | m_messageBuffer.writeSimple(data, size); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /client-mbedtls/src/WSListener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WSListener_hpp 3 | #define WSListener_hpp 4 | 5 | #include "oatpp-websocket/ConnectionHandler.hpp" 6 | #include "oatpp-websocket/WebSocket.hpp" 7 | 8 | /** 9 | * WebSocket listener listens on incoming WebSocket events. 10 | */ 11 | class WSListener : public oatpp::websocket::WebSocket::Listener { 12 | private: 13 | static constexpr const char* TAG = "Client_WSListener"; 14 | private: 15 | std::mutex& m_writeMutex; 16 | /** 17 | * Buffer for messages. Needed for multi-frame messages. 18 | */ 19 | oatpp::data::stream::BufferOutputStream m_messageBuffer; 20 | public: 21 | 22 | WSListener(std::mutex& writeMutex) 23 | : m_writeMutex(writeMutex) 24 | {} 25 | 26 | /** 27 | * Called on "ping" frame. 28 | */ 29 | void onPing(const WebSocket& socket, const oatpp::String& message) override; 30 | 31 | /** 32 | * Called on "pong" frame 33 | */ 34 | void onPong(const WebSocket& socket, const oatpp::String& message) override; 35 | 36 | /** 37 | * Called on "close" frame 38 | */ 39 | void onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) override; 40 | 41 | /** 42 | * Called on each message frame. After the last message will be called once-again with size == 0 to designate end of the message. 43 | */ 44 | void readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) override; 45 | 46 | }; 47 | 48 | #endif // WSListener_hpp 49 | -------------------------------------------------------------------------------- /client-mbedtls/test/WSTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | void WSTest::onRun() { 5 | 6 | OATPP_LOGD(TAG, "TODO - write tests"); 7 | 8 | } -------------------------------------------------------------------------------- /client-mbedtls/test/WSTest.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MY_PROJECT_WEBSOCKETTEST_HPP 3 | #define MY_PROJECT_WEBSOCKETTEST_HPP 4 | 5 | 6 | #include "oatpp-test/UnitTest.hpp" 7 | 8 | class WSTest : public oatpp::test::UnitTest { 9 | public: 10 | 11 | WSTest():UnitTest("TEST[WSTest]"){} 12 | void onRun() override; 13 | 14 | }; 15 | 16 | 17 | #endif //MY_PROJECT_WEBSOCKETTEST_HPP 18 | -------------------------------------------------------------------------------- /client-mbedtls/test/tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | #include "oatpp-test/UnitTest.hpp" 5 | #include 6 | 7 | 8 | void runTests() { 9 | OATPP_RUN_TEST(WSTest); 10 | } 11 | 12 | int main() { 13 | 14 | oatpp::Environment::init(); 15 | 16 | runTests(); 17 | 18 | /* Print how much objects were created during app running, and what have left-probably leaked */ 19 | /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ 20 | std::cout << "\nEnvironment:\n"; 21 | std::cout << "objectsCount = " << oatpp::Environment::getObjectsCount() << "\n"; 22 | std::cout << "objectsCreated = " << oatpp::Environment::getObjectsCreated() << "\n\n"; 23 | 24 | OATPP_ASSERT(oatpp::Environment::getObjectsCount() == 0); 25 | 26 | oatpp::Environment::destroy(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /client-mbedtls/utility/install-oatpp-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | ########################################################## 9 | ## install oatpp 10 | 11 | MODULE_NAME="oatpp" 12 | 13 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 14 | 15 | cd $MODULE_NAME 16 | mkdir build 17 | cd build 18 | 19 | cmake .. 20 | make install 21 | 22 | cd ../../ 23 | 24 | ########################################################## 25 | ## install oatpp-websocket 26 | 27 | MODULE_NAME="oatpp-websocket" 28 | 29 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 30 | 31 | cd $MODULE_NAME 32 | mkdir build 33 | cd build 34 | 35 | cmake .. 36 | make install 37 | 38 | cd ../../ 39 | 40 | ########################################################## 41 | ## install oatpp-mbedtls 42 | 43 | MODULE_NAME="oatpp-mbedtls" 44 | 45 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 46 | 47 | cd $MODULE_NAME 48 | mkdir build 49 | cd build 50 | 51 | cmake .. 52 | make install 53 | 54 | cd ../../ 55 | 56 | ########################################################## 57 | 58 | cd ../ 59 | 60 | rm -rf tmp 61 | -------------------------------------------------------------------------------- /client/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | set(project_name websocket-client) ## rename your project here 4 | 5 | project(${project_name}) 6 | 7 | set(CMAKE_CXX_STANDARD 17) 8 | 9 | include_directories(src) 10 | 11 | add_library(${project_name}-lib 12 | src/WSListener.cpp 13 | src/WSListener.hpp 14 | ) 15 | 16 | ## link libs 17 | 18 | find_package(oatpp 1.4.0 REQUIRED) 19 | find_package(oatpp-websocket 1.4.0 REQUIRED) 20 | 21 | target_link_libraries(${project_name}-lib 22 | PUBLIC oatpp::oatpp 23 | PUBLIC oatpp::oatpp-test 24 | PUBLIC oatpp::oatpp-websocket 25 | ) 26 | 27 | ################################################################# 28 | 29 | ## add executables 30 | 31 | add_executable(${project_name}-exe 32 | src/App.cpp 33 | ) 34 | target_link_libraries(${project_name}-exe ${project_name}-lib) 35 | add_dependencies(${project_name}-exe ${project_name}-lib) 36 | 37 | add_executable(${project_name}-test 38 | test/tests.cpp 39 | test/WSTest.cpp 40 | test/WSTest.hpp 41 | ) 42 | target_link_libraries(${project_name}-test ${project_name}-lib) 43 | add_dependencies(${project_name}-test ${project_name}-lib) 44 | 45 | set_target_properties(${project_name}-lib ${project_name}-exe ${project_name}-test PROPERTIES 46 | CXX_STANDARD 17 47 | CXX_EXTENSIONS OFF 48 | CXX_STANDARD_REQUIRED ON 49 | LINKER_LANGUAGE CXX 50 | ) 51 | 52 | enable_testing() 53 | add_test(tests ${project_name}-test) 54 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # websocket client 2 | 3 | Simple example of the websocket client. Built with oat++ (AKA oatpp) web framework. 4 | 5 | See more: 6 | 7 | - [Oat++ Website](https://oatpp.io/) 8 | - [Oat++ Github Repository](https://github.com/oatpp/oatpp) 9 | - [Get Started With Oat++](https://oatpp.io/docs/start) 10 | 11 | ## Overview 12 | 13 | ### Project layout 14 | 15 | ``` 16 | |- CMakeLists.txt // projects CMakeLists.txt 17 | |- src/ 18 | | | 19 | | |- WSListener.hpp // WebSocket listeners are defined here 20 | | |- App.cpp // main() is here 21 | | 22 | |- utility/install-oatpp-modules.sh // utility script to install required oatpp-modules. 23 | ``` 24 | 25 | ## Build and Run 26 | 27 | ### Using CMake 28 | 29 | **Requires:** [oatpp](https://github.com/oatpp/oatpp), and [oatpp-websocket](https://github.com/oatpp/oatpp-websocket) 30 | modules installed. You may run `utility/install-oatpp-modules.sh` 31 | script to install required oatpp modules. 32 | 33 | After all dependencies satisfied: 34 | 35 | ``` 36 | $ mkdir build && cd build 37 | $ cmake .. 38 | $ make 39 | $ ./websocket-client-exe # - run application. 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /client/src/App.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | 4 | #include "oatpp-websocket/WebSocket.hpp" 5 | #include "oatpp-websocket/Connector.hpp" 6 | 7 | #include "oatpp/network/tcp/client/ConnectionProvider.hpp" 8 | 9 | #include "oatpp/base/Log.hpp" 10 | 11 | #include 12 | 13 | namespace { 14 | 15 | const char* TAG = "websocket-client"; 16 | 17 | bool finished = false; 18 | 19 | void socketTask(const std::shared_ptr& websocket) { 20 | websocket->listen(); 21 | OATPP_LOGd(TAG, "SOCKET CLOSED!!!"); 22 | finished = true; 23 | } 24 | 25 | } 26 | 27 | void run() { 28 | 29 | OATPP_LOGi(TAG, "Application Started"); 30 | 31 | auto connectionProvider = oatpp::network::tcp::client::ConnectionProvider::createShared({"demo.piesocket.com", 80}); 32 | 33 | auto connector = oatpp::websocket::Connector::createShared(connectionProvider); 34 | 35 | auto connection = connector->connect("v3/channel_1?api_key=VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV¬ify_self"); 36 | 37 | OATPP_LOGi(TAG, "Connected"); 38 | 39 | auto socket = oatpp::websocket::WebSocket::createShared(connection, true /* maskOutgoingMessages must be true for clients */); 40 | 41 | std::mutex socketWriteMutex; 42 | 43 | socket->setListener(std::make_shared(socketWriteMutex)); 44 | 45 | std::thread thread(socketTask, socket); 46 | 47 | while(!finished) { 48 | { 49 | OATPP_LOGd(TAG, "sending message..."); 50 | std::lock_guard lock(socketWriteMutex); 51 | socket->sendOneFrameText("hello"); 52 | } 53 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 54 | } 55 | 56 | thread.join(); 57 | 58 | } 59 | 60 | int main() { 61 | oatpp::Environment::init(); 62 | run(); 63 | oatpp::Environment::destroy(); 64 | return 0; 65 | } 66 | -------------------------------------------------------------------------------- /client/src/WSListener.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | #include "oatpp/base/Log.hpp" 4 | 5 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 6 | // WSListener 7 | 8 | void WSListener::onPing(const WebSocket& socket, const oatpp::String& message) { 9 | OATPP_LOGd(TAG, "onPing"); 10 | std::lock_guard lock(m_writeMutex); 11 | socket.sendPong(message); 12 | } 13 | 14 | void WSListener::onPong(const WebSocket& socket, const oatpp::String& message) { 15 | OATPP_LOGd(TAG, "onPong"); 16 | } 17 | 18 | void WSListener::onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) { 19 | OATPP_LOGd(TAG, "onClose code={}", code); 20 | } 21 | 22 | void WSListener::readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) { 23 | 24 | if(size == 0) { // message transfer finished 25 | 26 | auto wholeMessage = m_messageBuffer.toString(); 27 | m_messageBuffer.setCurrentPosition(0); 28 | 29 | OATPP_LOGd(TAG, "on message received '{}'", wholeMessage); 30 | 31 | /* Send message in reply */ 32 | //std::lock_guard lock(m_writeMutex); 33 | //socket.sendOneFrameText( "Hello from oatpp!: " + wholeMessage); 34 | 35 | } else if(size > 0) { // message frame received 36 | m_messageBuffer.writeSimple(data, size); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /client/src/WSListener.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WSListener_hpp 3 | #define WSListener_hpp 4 | 5 | #include "oatpp-websocket/ConnectionHandler.hpp" 6 | #include "oatpp-websocket/WebSocket.hpp" 7 | 8 | /** 9 | * WebSocket listener listens on incoming WebSocket events. 10 | */ 11 | class WSListener : public oatpp::websocket::WebSocket::Listener { 12 | private: 13 | static constexpr const char* TAG = "Client_WSListener"; 14 | private: 15 | std::mutex& m_writeMutex; 16 | /** 17 | * Buffer for messages. Needed for multi-frame messages. 18 | */ 19 | oatpp::data::stream::BufferOutputStream m_messageBuffer; 20 | public: 21 | 22 | WSListener(std::mutex& writeMutex) 23 | : m_writeMutex(writeMutex) 24 | {} 25 | 26 | /** 27 | * Called on "ping" frame. 28 | */ 29 | void onPing(const WebSocket& socket, const oatpp::String& message) override; 30 | 31 | /** 32 | * Called on "pong" frame 33 | */ 34 | void onPong(const WebSocket& socket, const oatpp::String& message) override; 35 | 36 | /** 37 | * Called on "close" frame 38 | */ 39 | void onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) override; 40 | 41 | /** 42 | * Called on each message frame. After the last message will be called once-again with size == 0 to designate end of the message. 43 | */ 44 | void readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) override; 45 | 46 | }; 47 | 48 | #endif // WSListener_hpp 49 | -------------------------------------------------------------------------------- /client/test/WSTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | void WSTest::onRun() { 5 | 6 | OATPP_LOGD(TAG, "TODO - write tests"); 7 | 8 | } -------------------------------------------------------------------------------- /client/test/WSTest.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MY_PROJECT_WEBSOCKETTEST_HPP 3 | #define MY_PROJECT_WEBSOCKETTEST_HPP 4 | 5 | 6 | #include "oatpp-test/UnitTest.hpp" 7 | 8 | class WSTest : public oatpp::test::UnitTest { 9 | public: 10 | 11 | WSTest():UnitTest("TEST[WSTest]"){} 12 | void onRun() override; 13 | 14 | }; 15 | 16 | 17 | #endif //MY_PROJECT_WEBSOCKETTEST_HPP 18 | -------------------------------------------------------------------------------- /client/test/tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | #include "oatpp-test/UnitTest.hpp" 5 | #include 6 | 7 | 8 | void runTests() { 9 | OATPP_RUN_TEST(WSTest); 10 | } 11 | 12 | int main() { 13 | 14 | oatpp::Environment::init(); 15 | 16 | runTests(); 17 | 18 | /* Print how much objects were created during app running, and what have left-probably leaked */ 19 | /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ 20 | std::cout << "\nEnvironment:\n"; 21 | std::cout << "objectsCount = " << oatpp::Environment::getObjectsCount() << "\n"; 22 | std::cout << "objectsCreated = " << oatpp::Environment::getObjectsCreated() << "\n\n"; 23 | 24 | OATPP_ASSERT(oatpp::Environment::getObjectsCount() == 0); 25 | 26 | oatpp::Environment::destroy(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /client/utility/install-oatpp-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | ########################################################## 9 | ## install oatpp 10 | 11 | MODULE_NAME="oatpp" 12 | 13 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 14 | 15 | cd $MODULE_NAME 16 | mkdir build 17 | cd build 18 | 19 | cmake .. 20 | make install 21 | 22 | cd ../../ 23 | 24 | ########################################################## 25 | ## install oatpp-websocket 26 | 27 | MODULE_NAME="oatpp-websocket" 28 | 29 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 30 | 31 | cd $MODULE_NAME 32 | mkdir build 33 | cd build 34 | 35 | cmake .. 36 | make install 37 | 38 | cd ../../ 39 | 40 | ########################################################## 41 | 42 | cd ../ 43 | 44 | rm -rf tmp 45 | -------------------------------------------------------------------------------- /cmake/module/Findmbedtls.cmake: -------------------------------------------------------------------------------- 1 | #[=======================================================================[ 2 | 3 | Copyright (c) 2020 Benedikt-Alexander Mokroß 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | FindmbedTLS 18 | ------------ 19 | 20 | Find the mbedTLS encryption library. 21 | 22 | Optional Components 23 | ^^^^^^^^^^^^^^^^^^^ 24 | 25 | This module supports two optional components: SSL and TLS. Both 26 | components have associated imported targets, as described below. 27 | 28 | Imported Targets 29 | ^^^^^^^^^^^^^^^^ 30 | 31 | This module defines the following imported targets: 32 | 33 | mbedtls::Crypto 34 | The mbedTLS crypto library, if found. 35 | 36 | mbedtls::X509 37 | The mbedTLS x509 library, if found. 38 | 39 | mbedtls::SSL 40 | The mbedTLS ssl library, if found. Requires and includes mbedtls::Crypto automatically. 41 | 42 | mbedtls::TLS 43 | The mbedTLS tls library, if found. Requires and includes mbedtls::SSL and mbedtls::Crypto automatically. 44 | 45 | Result Variables 46 | ^^^^^^^^^^^^^^^^ 47 | 48 | This module will set the following variables in your project: 49 | 50 | MBEDTLS_FOUND 51 | System has the mbedTLS library. If no components are requested it only requires the crypto library. 52 | MBEDTLS_INCLUDE_DIR 53 | The mbedTLS include directory. 54 | MBEDTLS_X509_LIBRARY 55 | The mbedTLS crypto library. 56 | MBEDTLS_CRYPTO_LIBRARY 57 | The mbedTLS crypto library. 58 | MBEDTLS_SSL_LIBRARY 59 | The mbedTLS SSL library. 60 | MBEDTLS_TLS_LIBRARY 61 | The mbedTLS TLS library. 62 | MBEDTLS_LIBRARIES 63 | All mbedTLS libraries. 64 | MBEDTLS_VERSION 65 | This is set to $major.$minor.$revision (e.g. 2.6.8). 66 | 67 | Hints 68 | ^^^^^ 69 | 70 | Set MBEDTLS_ROOT_DIR to the root directory of an mbedTLS installation. 71 | 72 | ]=======================================================================] 73 | 74 | # Set Hints 75 | set(_MBEDTLS_ROOT_HINTS 76 | ${MBEDTLS_ROOT_DIR} 77 | ENV MBEDTLS_ROOT_DIR 78 | ) 79 | 80 | # Set Paths 81 | if (WIN32) 82 | file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) 83 | set(_MBEDTLS_ROOT_PATHS 84 | "${_programfiles}/mbedTLS" 85 | ) 86 | unset(_programfiles) 87 | else() 88 | set(_MBEDTLS_ROOT_PATHS 89 | "/usr/local/" 90 | ) 91 | endif() 92 | 93 | # Combine 94 | set(_MBEDTLS_ROOT_HINTS_AND_PATHS 95 | HINTS ${_MBEDTLS_ROOT_HINTS} 96 | PATHS ${_MBEDTLS_ROOT_PATHS} 97 | ) 98 | 99 | # Find Include Path 100 | find_path(MBEDTLS_INCLUDE_DIR 101 | NAMES 102 | mbedtls 103 | ${_MBEDTLS_ROOT_HINTS_AND_PATHS} 104 | PATH_SUFFIXES 105 | include 106 | ) 107 | 108 | # Find Crypto Library 109 | find_library(MBEDTLS_CRYPTO_LIBRARY 110 | NAMES 111 | libmbedcrypto 112 | mbedcrypto 113 | NAMES_PER_DIR 114 | ${_MBEDTLS_ROOT_HINTS_AND_PATHS} 115 | PATH_SUFFIXES 116 | lib 117 | ) 118 | 119 | # Find x509 Library 120 | find_library(MBEDTLS_X509_LIBRARY 121 | NAMES 122 | libmbedx509 123 | mbedx509 124 | NAMES_PER_DIR 125 | ${_MBEDTLS_ROOT_HINTS_AND_PATHS} 126 | PATH_SUFFIXES 127 | lib 128 | ) 129 | 130 | # Find SSL Library 131 | find_library(MBEDTLS_SSL_LIBRARY 132 | NAMES 133 | libmbedx509 134 | mbedx509 135 | NAMES_PER_DIR 136 | ${_MBEDTLS_ROOT_HINTS_AND_PATHS} 137 | PATH_SUFFIXES 138 | lib 139 | ) 140 | 141 | # Find TLS Library 142 | find_library(MBEDTLS_TLS_LIBRARY 143 | NAMES 144 | libmbedtls 145 | mbedtls 146 | NAMES_PER_DIR 147 | ${_MBEDTLS_ROOT_HINTS_AND_PATHS} 148 | PATH_SUFFIXES 149 | lib 150 | ) 151 | 152 | # Set Libraries 153 | set(MBEDTLS_LIBRARIES ${MBEDTLS_CRYPTO_LIBRARY} ${MBEDTLS_X509_LIBRARY} ${MBEDTLS_SSL_LIBRARY} ${MBEDTLS_TLS_LIBRARY}) 154 | 155 | # Mark Variables As Advanced 156 | mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_CRYPTO_LIBRARY MBEDTLS_X509_LIBRARY MBEDTLS_SSL_LIBRARY MBEDTLS_TLS_LIBRARY) 157 | 158 | # Find Version File 159 | if(MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") 160 | 161 | # Get Version From File 162 | file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h" VERSIONH REGEX "#define MBEDTLS_VERSION_STRING[ ]+\".*\"") 163 | 164 | # Match Version String 165 | string(REGEX REPLACE ".*\".*([0-9]+)\\.([0-9]+)\\.([0-9]+)\"" "\\1;\\2;\\3" MBEDTLS_VERSION_LIST "${VERSIONH}") 166 | 167 | # Split Parts 168 | list(GET MBEDTLS_VERSION_LIST 0 MBEDTLS_VERSION_MAJOR) 169 | list(GET MBEDTLS_VERSION_LIST 1 MBEDTLS_VERSION_MINOR) 170 | list(GET MBEDTLS_VERSION_LIST 2 MBEDTLS_VERSION_REVISION) 171 | 172 | # Set Version String 173 | set(MBEDTLS_VERSION "${MBEDTLS_VERSION_MAJOR}.${MBEDTLS_VERSION_MINOR}.${MBEDTLS_VERSION_REVISION}") 174 | 175 | endif() 176 | 177 | # Set Find Package Arguments 178 | find_package_handle_standard_args(mbedTLS 179 | REQUIRED_VARS 180 | MBEDTLS_X509_LIBRARY 181 | MBEDTLS_TLS_LIBRARY 182 | MBEDTLS_SSL_LIBRARY 183 | MBEDTLS_INCLUDE_DIR 184 | VERSION_VAR 185 | MBEDTLS_VERSION 186 | HANDLE_COMPONENTS 187 | FAIL_MESSAGE 188 | "Could NOT find mbedTLS, try setting the path to mbedTLS using the MBEDTLS_ROOT_DIR environment variable" 189 | ) 190 | 191 | # mbedTLS Found 192 | if(MBEDTLS_FOUND) 193 | 194 | # Set mbedtls::Crypto 195 | if(NOT TARGET mbedtls::Crypto AND EXISTS "${MBEDTLS_CRYPTO_LIBRARY}") 196 | 197 | # Add Library 198 | add_library(mbedtls::Crypto UNKNOWN IMPORTED) 199 | 200 | # Set Properties 201 | set_target_properties( 202 | mbedtls::Crypto 203 | PROPERTIES 204 | INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}" 205 | IMPORTED_LINK_INTERFACE_LANGUAGES "C" 206 | IMPORTED_LOCATION "${MBEDTLS_CRYPTO_LIBRARY}" 207 | ) 208 | 209 | endif() # mbedtls::Crypto 210 | 211 | # Set mbedtls::X509 212 | if(NOT TARGET mbedtls::X509 AND EXISTS "${MBEDTLS_X509_LIBRARY}") 213 | 214 | # Add Library 215 | add_library(mbedtls::X509 UNKNOWN IMPORTED) 216 | 217 | # Set Properties 218 | set_target_properties( 219 | mbedtls::X509 220 | PROPERTIES 221 | INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}" 222 | IMPORTED_LINK_INTERFACE_LANGUAGES "C" 223 | IMPORTED_LOCATION "${MBEDTLS_X509_LIBRARY}" 224 | ) 225 | 226 | endif() # mbedtls::X509 227 | 228 | # Set mbedtls::SSL 229 | if(NOT TARGET mbedtls::SSL AND EXISTS "${MBEDTLS_SSL_LIBRARY}") 230 | 231 | # Add Library 232 | add_library(mbedtls::SSL UNKNOWN IMPORTED) 233 | 234 | # Set Properties 235 | set_target_properties( 236 | mbedtls::SSL 237 | PROPERTIES 238 | INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}" 239 | IMPORTED_LINK_INTERFACE_LANGUAGES "C" 240 | IMPORTED_LOCATION "${MBEDTLS_SSL_LIBRARY}" 241 | INTERFACE_LINK_LIBRARIES mbedtls::Crypto 242 | ) 243 | 244 | endif() # mbedtls::SSL 245 | 246 | # Set mbedtls::TLS 247 | if(NOT TARGET mbedtls::TLS AND EXISTS "${MBEDTLS_TLS_LIBRARY}") 248 | add_library(mbedtls::TLS UNKNOWN IMPORTED) 249 | set_target_properties( 250 | mbedtls::TLS 251 | PROPERTIES 252 | INTERFACE_INCLUDE_DIRECTORIES "${MBEDTLS_INCLUDE_DIR}" 253 | IMPORTED_LINK_INTERFACE_LANGUAGES "C" 254 | IMPORTED_LOCATION "${MBEDTLS_TLS_LIBRARY}" 255 | INTERFACE_LINK_LIBRARIES mbedtls::SSL 256 | ) 257 | 258 | endif() # mbedtls::TLS 259 | 260 | endif(MBEDTLS_FOUND) 261 | -------------------------------------------------------------------------------- /server/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.20) 2 | 3 | set(project_name websocket-server) ## rename your project here 4 | 5 | project(${project_name}) 6 | 7 | set(CMAKE_CXX_STANDARD 17) 8 | 9 | include_directories(src) 10 | 11 | add_library(${project_name}-lib 12 | src/AppComponent.hpp 13 | src/controller/MyController.hpp 14 | src/websocket/WSListener.cpp 15 | src/websocket/WSListener.hpp 16 | ) 17 | 18 | ## link libs 19 | 20 | find_package(oatpp 1.4.0 REQUIRED) 21 | find_package(oatpp-websocket 1.4.0 REQUIRED) 22 | 23 | target_link_libraries(${project_name}-lib 24 | PUBLIC oatpp::oatpp 25 | PUBLIC oatpp::oatpp-test 26 | PUBLIC oatpp::oatpp-websocket 27 | ) 28 | 29 | ## add executables 30 | 31 | add_executable(${project_name}-exe 32 | src/App.cpp 33 | ) 34 | target_link_libraries(${project_name}-exe ${project_name}-lib) 35 | add_dependencies(${project_name}-exe ${project_name}-lib) 36 | 37 | add_executable(${project_name}-test 38 | test/tests.cpp 39 | test/WSTest.cpp 40 | test/WSTest.hpp 41 | ) 42 | target_link_libraries(${project_name}-test ${project_name}-lib) 43 | add_dependencies(${project_name}-test ${project_name}-lib) 44 | 45 | set_target_properties(${project_name}-lib ${project_name}-exe ${project_name}-test PROPERTIES 46 | CXX_STANDARD 17 47 | CXX_EXTENSIONS OFF 48 | CXX_STANDARD_REQUIRED ON 49 | LINKER_LANGUAGE CXX 50 | ) 51 | 52 | enable_testing() 53 | add_test(tests ${project_name}-test) 54 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # websocket server 2 | 3 | Simple example of the **multithreaded** websocket echo server. Built with oat++ (AKA oatpp) web framework. 4 | 5 | See more: 6 | 7 | - [Oat++ Website](https://oatpp.io/) 8 | - [Oat++ Github Repository](https://github.com/oatpp/oatpp) 9 | - [Get Started With Oat++](https://oatpp.io/docs/start) 10 | 11 | ## Overview 12 | 13 | ### Project layout 14 | 15 | ``` 16 | |- CMakeLists.txt // projects CMakeLists.txt 17 | |- src/ 18 | | | 19 | | |- controller/ 20 | | | | 21 | | | |- MyController.hpp // ApiController with websocket endpoint 22 | | | 23 | | |- websocket/ 24 | | | | 25 | | | |- WSListener.hpp // WebSocket listeners are defined here 26 | | | 27 | | |- AppComponent.hpp // Application config. 28 | | |- App.cpp // main() is here 29 | | 30 | |- utility/install-oatpp-modules.sh // utility script to install required oatpp-modules. 31 | ``` 32 | 33 | ## Build and Run 34 | 35 | ### Using CMake 36 | 37 | **Requires:** [oatpp](https://github.com/oatpp/oatpp), and [oatpp-websocket](https://github.com/oatpp/oatpp-websocket) 38 | modules installed. You may run `utility/install-oatpp-modules.sh` 39 | script to install required oatpp modules. 40 | 41 | After all dependencies satisfied: 42 | 43 | ``` 44 | $ mkdir build && cd build 45 | $ cmake .. 46 | $ make 47 | $ ./websocket-server-exe # - run application. 48 | ``` 49 | -------------------------------------------------------------------------------- /server/src/App.cpp: -------------------------------------------------------------------------------- 1 | #include "./controller/MyController.hpp" 2 | #include "./AppComponent.hpp" 3 | 4 | #include "oatpp/network/Server.hpp" 5 | 6 | #include 7 | 8 | void run() { 9 | 10 | /* Register Components in scope of run() method */ 11 | AppComponent components; 12 | 13 | /* Get router component */ 14 | OATPP_COMPONENT(std::shared_ptr, router); 15 | 16 | /* Create MyController and add all of its endpoints to router */ 17 | router->addController(std::make_shared()); 18 | 19 | /* Get connection handler component */ 20 | OATPP_COMPONENT(std::shared_ptr, connectionHandler, "http"); 21 | 22 | /* Get connection provider component */ 23 | OATPP_COMPONENT(std::shared_ptr, connectionProvider); 24 | 25 | /* Create server which takes provided TCP connections and passes them to HTTP connection handler */ 26 | oatpp::network::Server server(connectionProvider, connectionHandler); 27 | 28 | /* Priny info about server port */ 29 | OATPP_LOGi("MyApp", "Server running on port {}", connectionProvider->getProperty("port").toString()) 30 | 31 | /* Run server */ 32 | server.run(); 33 | 34 | } 35 | 36 | int main(int argc, const char * argv[]) { 37 | 38 | oatpp::Environment::init(); 39 | 40 | run(); 41 | 42 | oatpp::Environment::destroy(); 43 | 44 | return 0; 45 | } -------------------------------------------------------------------------------- /server/src/AppComponent.hpp: -------------------------------------------------------------------------------- 1 | #ifndef AppComponent_hpp 2 | #define AppComponent_hpp 3 | 4 | #include "websocket/WSListener.hpp" 5 | 6 | #include "oatpp-websocket/ConnectionHandler.hpp" 7 | 8 | #include "oatpp/web/server/HttpConnectionHandler.hpp" 9 | #include "oatpp/web/server/HttpRouter.hpp" 10 | #include "oatpp/network/tcp/server/ConnectionProvider.hpp" 11 | 12 | #include "oatpp/json/ObjectMapper.hpp" 13 | 14 | #include "oatpp/macro/component.hpp" 15 | 16 | /** 17 | * Class which creates and holds Application components and registers components in oatpp::Environment 18 | * Order of components initialization is from top to bottom 19 | */ 20 | class AppComponent { 21 | public: 22 | 23 | /** 24 | * Create ConnectionProvider component which listens on the port 25 | */ 26 | OATPP_CREATE_COMPONENT(std::shared_ptr, serverConnectionProvider)([] { 27 | return oatpp::network::tcp::server::ConnectionProvider::createShared({"0.0.0.0", 8000, oatpp::network::Address::IP_4}); 28 | }()); 29 | 30 | /** 31 | * Create Router component 32 | */ 33 | OATPP_CREATE_COMPONENT(std::shared_ptr, httpRouter)([] { 34 | return oatpp::web::server::HttpRouter::createShared(); 35 | }()); 36 | 37 | /** 38 | * Create http ConnectionHandler 39 | */ 40 | OATPP_CREATE_COMPONENT(std::shared_ptr, httpConnectionHandler)("http" /* qualifier */, [] { 41 | OATPP_COMPONENT(std::shared_ptr, router); // get Router component 42 | return oatpp::web::server::HttpConnectionHandler::createShared(router); 43 | }()); 44 | 45 | /** 46 | * Create ObjectMapper component to serialize/deserialize DTOs in Contoller's API 47 | */ 48 | OATPP_CREATE_COMPONENT(std::shared_ptr, apiObjectMapper)([] { 49 | return std::make_shared(); 50 | }()); 51 | 52 | /** 53 | * Create websocket connection handler 54 | */ 55 | OATPP_CREATE_COMPONENT(std::shared_ptr, websocketConnectionHandler)("websocket" /* qualifier */, [] { 56 | auto connectionHandler = oatpp::websocket::ConnectionHandler::createShared(); 57 | connectionHandler->setSocketInstanceListener(std::make_shared()); 58 | return connectionHandler; 59 | }()); 60 | 61 | }; 62 | 63 | #endif /* AppComponent_hpp */ -------------------------------------------------------------------------------- /server/src/controller/MyController.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MyController_hpp 3 | #define MyController_hpp 4 | 5 | #include "oatpp-websocket/Handshaker.hpp" 6 | 7 | #include "oatpp/web/server/api/ApiController.hpp" 8 | 9 | #include "oatpp/network/ConnectionHandler.hpp" 10 | 11 | #include "oatpp/macro/codegen.hpp" 12 | #include "oatpp/macro/component.hpp" 13 | 14 | 15 | #include OATPP_CODEGEN_BEGIN(ApiController) //<-- codegen begin 16 | 17 | /** 18 | * Controller with WebSocket-connect endpoint. 19 | */ 20 | class MyController : public oatpp::web::server::api::ApiController { 21 | private: 22 | OATPP_COMPONENT(std::shared_ptr, websocketConnectionHandler, "websocket"); 23 | public: 24 | MyController(OATPP_COMPONENT(std::shared_ptr, objectMapper)) 25 | : oatpp::web::server::api::ApiController(objectMapper) 26 | {} 27 | public: 28 | 29 | ENDPOINT("GET", "/", root) { 30 | 31 | const char* pageTemplate = 32 | "" 33 | "" 34 | "" 35 | "" 36 | "" 37 | "

Hello Multithreaded WebSocket Server!

" 38 | "

" 39 | "websocket endpoint is: localhost:8000/ws" 40 | "

" 41 | "" 42 | ""; 43 | 44 | return createResponse(Status::CODE_200, pageTemplate); 45 | 46 | }; 47 | 48 | ENDPOINT("GET", "ws", ws, REQUEST(std::shared_ptr, request)) { 49 | return oatpp::websocket::Handshaker::serversideHandshake(request->getHeaders(), websocketConnectionHandler); 50 | }; 51 | 52 | // TODO Insert Your endpoints here !!! 53 | 54 | }; 55 | 56 | #include OATPP_CODEGEN_END(ApiController) //<-- codegen end 57 | 58 | #endif /* MyController_hpp */ 59 | -------------------------------------------------------------------------------- /server/src/websocket/WSListener.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSListener.hpp" 3 | #include "oatpp/base/Log.hpp" 4 | 5 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 6 | // WSListener 7 | 8 | void WSListener::onPing(const WebSocket& socket, const oatpp::String& message) { 9 | OATPP_LOGd(TAG, "onPing"); 10 | socket.sendPong(message); 11 | } 12 | 13 | void WSListener::onPong(const WebSocket& socket, const oatpp::String& message) { 14 | OATPP_LOGd(TAG, "onPong"); 15 | } 16 | 17 | void WSListener::onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) { 18 | OATPP_LOGd(TAG, "onClose code={}", code); 19 | } 20 | 21 | void WSListener::readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) { 22 | 23 | if(size == 0) { // message transfer finished 24 | 25 | auto wholeMessage = m_messageBuffer.toString(); 26 | m_messageBuffer.setCurrentPosition(0); 27 | 28 | OATPP_LOGd(TAG, "onMessage message='{}'", wholeMessage); 29 | 30 | /* Send message in reply */ 31 | socket.sendOneFrameText( "Hello from oatpp!: " + wholeMessage); 32 | 33 | } else if(size > 0) { // message frame received 34 | m_messageBuffer.writeSimple(data, size); 35 | } 36 | 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 40 | // WSInstanceListener 41 | 42 | std::atomic WSInstanceListener::SOCKETS(0); 43 | 44 | void WSInstanceListener::onAfterCreate(const oatpp::websocket::WebSocket& socket, const std::shared_ptr& params) { 45 | 46 | SOCKETS ++; 47 | OATPP_LOGd(TAG, "New Incoming Connection. Connection count={}", SOCKETS.load()); 48 | 49 | /* In this particular case we create one WSListener per each connection */ 50 | /* Which may be redundant in many cases */ 51 | socket.setListener(std::make_shared()); 52 | } 53 | 54 | void WSInstanceListener::onBeforeDestroy(const oatpp::websocket::WebSocket& socket) { 55 | 56 | SOCKETS --; 57 | OATPP_LOGd(TAG, "Connection closed. Connection count={}", SOCKETS.load()); 58 | 59 | } -------------------------------------------------------------------------------- /server/src/websocket/WSListener.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WSListener_hpp 2 | #define WSListener_hpp 3 | 4 | #include "oatpp-websocket/ConnectionHandler.hpp" 5 | #include "oatpp-websocket/WebSocket.hpp" 6 | 7 | /** 8 | * WebSocket listener listens on incoming WebSocket events. 9 | */ 10 | class WSListener : public oatpp::websocket::WebSocket::Listener { 11 | private: 12 | static constexpr const char* TAG = "Server_WSListener"; 13 | private: 14 | /** 15 | * Buffer for messages. Needed for multi-frame messages. 16 | */ 17 | oatpp::data::stream::BufferOutputStream m_messageBuffer; 18 | public: 19 | 20 | /** 21 | * Called on "ping" frame. 22 | */ 23 | void onPing(const WebSocket& socket, const oatpp::String& message) override; 24 | 25 | /** 26 | * Called on "pong" frame 27 | */ 28 | void onPong(const WebSocket& socket, const oatpp::String& message) override; 29 | 30 | /** 31 | * Called on "close" frame 32 | */ 33 | void onClose(const WebSocket& socket, v_uint16 code, const oatpp::String& message) override; 34 | 35 | /** 36 | * Called on each message frame. After the last message will be called once-again with size == 0 to designate end of the message. 37 | */ 38 | void readMessage(const WebSocket& socket, v_uint8 opcode, p_char8 data, oatpp::v_io_size size) override; 39 | 40 | }; 41 | 42 | /** 43 | * Listener on new WebSocket connections. 44 | */ 45 | class WSInstanceListener : public oatpp::websocket::ConnectionHandler::SocketInstanceListener { 46 | private: 47 | static constexpr const char* TAG = "Server_WSInstanceListener"; 48 | public: 49 | /** 50 | * Counter for connected clients. 51 | */ 52 | static std::atomic SOCKETS; 53 | public: 54 | 55 | /** 56 | * Called when socket is created 57 | */ 58 | void onAfterCreate(const oatpp::websocket::WebSocket& socket, const std::shared_ptr& params) override; 59 | 60 | /** 61 | * Called before socket instance is destroyed. 62 | */ 63 | void onBeforeDestroy(const oatpp::websocket::WebSocket& socket) override; 64 | 65 | }; 66 | 67 | #endif // WSListener_hpp 68 | -------------------------------------------------------------------------------- /server/test/WSTest.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | void WSTest::onRun() { 5 | 6 | OATPP_LOGD(TAG, "TODO - write tests"); 7 | 8 | } -------------------------------------------------------------------------------- /server/test/WSTest.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MY_PROJECT_WEBSOCKETTEST_HPP 2 | #define MY_PROJECT_WEBSOCKETTEST_HPP 3 | 4 | #include "oatpp-test/UnitTest.hpp" 5 | 6 | class WSTest : public oatpp::test::UnitTest { 7 | public: 8 | 9 | WSTest():UnitTest("TEST[WSTest]"){} 10 | void onRun() override; 11 | 12 | }; 13 | 14 | #endif //MY_PROJECT_WEBSOCKETTEST_HPP 15 | -------------------------------------------------------------------------------- /server/test/tests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "WSTest.hpp" 3 | 4 | #include "oatpp-test/UnitTest.hpp" 5 | #include 6 | 7 | 8 | void runTests() { 9 | OATPP_RUN_TEST(WSTest); 10 | } 11 | 12 | int main() { 13 | 14 | oatpp::Environment::init(); 15 | 16 | runTests(); 17 | 18 | /* Print how much objects were created during app running, and what have left-probably leaked */ 19 | /* Disable object counting for release builds using '-D OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ 20 | std::cout << "\nEnvironment:\n"; 21 | std::cout << "objectsCount = " << oatpp::Environment::getObjectsCount() << "\n"; 22 | std::cout << "objectsCreated = " << oatpp::Environment::getObjectsCreated() << "\n\n"; 23 | 24 | OATPP_ASSERT(oatpp::Environment::getObjectsCount() == 0); 25 | 26 | oatpp::Environment::destroy(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /server/utility/install-oatpp-modules.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf tmp 4 | 5 | mkdir tmp 6 | cd tmp 7 | 8 | ########################################################## 9 | ## install oatpp 10 | 11 | MODULE_NAME="oatpp" 12 | 13 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 14 | 15 | cd $MODULE_NAME 16 | mkdir build 17 | cd build 18 | 19 | cmake .. 20 | make install 21 | 22 | cd ../../ 23 | 24 | ########################################################## 25 | ## install oatpp-websocket 26 | 27 | MODULE_NAME="oatpp-websocket" 28 | 29 | git clone --depth=1 https://github.com/oatpp/$MODULE_NAME 30 | 31 | cd $MODULE_NAME 32 | mkdir build 33 | cd build 34 | 35 | cmake .. 36 | make install 37 | 38 | cd ../../ 39 | 40 | ########################################################## 41 | 42 | cd ../ 43 | 44 | rm -rf tmp 45 | --------------------------------------------------------------------------------