{"id":15034817,"url":"https://github.com/chronoxor/cppserver","last_synced_at":"2025-04-08T09:06:31.371Z","repository":{"id":40523611,"uuid":"76032739","full_name":"chronoxor/CppServer","owner":"chronoxor","description":"Ultra fast and low latency asynchronous socket server \u0026 client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and 10K connections problem solution","archived":false,"fork":false,"pushed_at":"2024-03-09T11:08:45.000Z","size":199002,"stargazers_count":1508,"open_issues_count":58,"forks_count":299,"subscribers_count":72,"default_branch":"master","last_synced_at":"2025-04-08T09:06:22.329Z","etag":null,"topics":["async","http","http-server","https","https-server","low-latency","performance","socket-client","socket-server","ssl","tcp-client","tcp-server","tls","udp-client","udp-server","websocket","websocket-server"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chronoxor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-12-09T12:14:09.000Z","updated_at":"2025-04-08T04:50:33.000Z","dependencies_parsed_at":"2023-12-23T11:54:22.549Z","dependency_job_id":"e4b16b56-68b1-4abb-b1d3-73904e7394be","html_url":"https://github.com/chronoxor/CppServer","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCppServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCppServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCppServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chronoxor%2FCppServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chronoxor","download_url":"https://codeload.github.com/chronoxor/CppServer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247809964,"owners_count":20999816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","http","http-server","https","https-server","low-latency","performance","socket-client","socket-server","ssl","tcp-client","tcp-server","tls","udp-client","udp-server","websocket","websocket-server"],"created_at":"2024-09-24T20:26:28.136Z","updated_at":"2025-04-08T09:06:31.349Z","avatar_url":"https://github.com/chronoxor.png","language":"C++","readme":"# CppServer\n\n[![Awesome C++](https://awesome.re/badge.svg)](https://github.com/fffaraz/awesome-cpp)\n[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n[![Release](https://img.shields.io/github/release/chronoxor/CppServer.svg?sort=semver)](https://github.com/chronoxor/CppServer/releases)\n\u003cbr/\u003e\n[![Linux (clang)](https://github.com/chronoxor/CppServer/actions/workflows/build-linux-clang.yml/badge.svg)](https://github.com/chronoxor/CppServer/actions/workflows/build-linux-clang.yml)\n[![Linux (gcc)](https://github.com/chronoxor/CppServer/actions/workflows/build-linux-gcc.yml/badge.svg)](https://github.com/chronoxor/CppServer/actions/workflows/build-linux-gcc.yml)\n[![MacOS](https://github.com/chronoxor/CppServer/actions/workflows/build-macos.yml/badge.svg)](https://github.com/chronoxor/CppServer/actions/workflows/build-macos.yml)\n\u003cbr/\u003e\n[![Windows (MSYS2)](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-msys2.yml/badge.svg)](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-msys2.yml)\n[![Windows (MinGW)](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-mingw.yml/badge.svg)](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-mingw.yml)\n[![Windows (Visual Studio)](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-vs.yml/badge.svg)](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-vs.yml)\n\nUltra fast and low latency asynchronous socket server \u0026 client C++ library with\nsupport TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and [10K connections problem](https://en.wikipedia.org/wiki/C10k_problem)\nsolution.\n\nHas integration with high-level message protocol based on [Fast Binary Encoding](https://github.com/chronoxor/FastBinaryEncoding)\n\n[CppServer API reference](https://chronoxor.github.io/CppServer/index.html)\n\n# Contents\n  * [Features](#features)\n  * [Requirements](#requirements)\n  * [How to build?](#how-to-build)\n  * [Examples](#examples)\n    * [Example: Asio service](#example-asio-service)\n    * [Example: Asio timer](#example-asio-timer)\n    * [Example: TCP chat server](#example-tcp-chat-server)\n    * [Example: TCP chat client](#example-tcp-chat-client)\n    * [Example: SSL chat server](#example-ssl-chat-server)\n    * [Example: SSL chat client](#example-ssl-chat-client)\n    * [Example: UDP echo server](#example-udp-echo-server)\n    * [Example: UDP echo client](#example-udp-echo-client)\n    * [Example: UDP multicast server](#example-udp-multicast-server)\n    * [Example: UDP multicast client](#example-udp-multicast-client)\n    * [Example: Simple protocol](#example-simple-protocol)\n    * [Example: Simple protocol server](#example-simple-protocol-server)\n    * [Example: Simple protocol client](#example-simple-protocol-client)\n    * [Example: HTTP server](#example-http-server)\n    * [Example: HTTP client](#example-http-client)\n    * [Example: HTTPS server](#example-https-server)\n    * [Example: HTTPS client](#example-https-client)\n    * [Example: WebSocket chat server](#example-websocket-chat-server)\n    * [Example: WebSocket chat client](#example-websocket-chat-client)\n    * [Example: WebSocket secure chat server](#example-websocket-secure-chat-server)\n    * [Example: WebSocket secure chat client](#example-websocket-secure-chat-client)\n  * [Performance](#performance)\n    * [Benchmark: Round-Trip](#benchmark-round-trip)\n      * [TCP echo server](#tcp-echo-server)\n      * [SSL echo server](#ssl-echo-server)\n      * [UDP echo server](#udp-echo-server)\n      * [Simple protocol server](#simple-protocol-server)\n      * [WebSocket echo server](#websocket-echo-server)\n      * [WebSocket secure echo server](#websocket-secure-echo-server)\n    * [Benchmark: Multicast](#benchmark-multicast)\n      * [TCP multicast server](#tcp-multicast-server)\n      * [SSL multicast server](#ssl-multicast-server)\n      * [UDP multicast server](#udp-multicast-server)\n      * [WebSocket multicast server](#websocket-multicast-server)\n      * [WebSocket secure multicast server](#websocket-secure-multicast-server)\n    * [Benchmark: Web Server](#benchmark-web-server)\n      * [HTTP Trace server](#http-trace-server)\n      * [HTTPS Trace server](#https-trace-server)\n  * [OpenSSL certificates](#openssl-certificates)\n    * [Production](#production)\n    * [Development](#development)\n    * [Certificate Authority](#certificate-authority)\n    * [SSL Server certificate](#ssl-server-certificate)\n    * [SSL Client certificate](#ssl-client-certificate)\n    * [Diffie-Hellman key exchange](#diffie-hellman-key-exchange)\n\n# Features\n* Cross platform (Linux, MacOS, Windows)\n* [Asynchronous communication](https://think-async.com)\n* Supported CPU scalability designs: IO service per thread, thread pool\n* Supported transport protocols: [TCP](#example-tcp-chat-server), [SSL](#example-ssl-chat-server),\n  [UDP](#example-udp-echo-server), [UDP multicast](#example-udp-multicast-server)\n* Supported Web protocols: [HTTP](#example-http-server), [HTTPS](#example-https-server),\n  [WebSocket](#example-websocket-chat-server), [WebSocket secure](#example-websocket-secure-chat-server)\n* Supported [Swagger OpenAPI](https://swagger.io/specification/) iterative documentation\n* Supported message protocol based on [Fast Binary Encoding](https://github.com/chronoxor/FastBinaryEncoding)\n\n# Requirements\n* Linux\n* MacOS\n* Windows\n* [cmake](https://www.cmake.org)\n* [gcc](https://gcc.gnu.org)\n* [git](https://git-scm.com)\n* [gil](https://github.com/chronoxor/gil.git)\n* [python3](https://www.python.org)\n\nOptional:\n* [clang](https://clang.llvm.org)\n* [CLion](https://www.jetbrains.com/clion)\n* [MSYS2](https://www.msys2.org)\n* [MinGW](https://mingw-w64.org/doku.php)\n* [Visual Studio](https://www.visualstudio.com)\n\n# How to build?\n\n### Linux: install required packages\n```shell\nsudo apt-get install -y binutils-dev uuid-dev libssl-dev\n```\n\n### Install [gil (git links) tool](https://github.com/chronoxor/gil)\n```shell\npip3 install gil\n```\n\n### Setup repository\n```shell\ngit clone https://github.com/chronoxor/CppServer.git\ncd CppServer\ngil update\n```\n\n### Linux\n```shell\ncd build\n./unix.sh\n```\n\n### MacOS\n```shell\ncd build\n./unix.sh\n```\n\n### Windows (MSYS2)\n```shell\ncd build\nunix.bat\n```\n\n### Windows (MinGW)\n```shell\ncd build\nmingw.bat\n```\n\n### Windows (Visual Studio)\n```shell\ncd build\nvs.bat\n```\n\n# Examples\n\n## Example: Asio service\nAsio service is used to host all clients/servers based on [Asio C++ library](https://think-async.com).\nIt is implemented based on Asio C++ Library and use a separate thread to\nperform all asynchronous IO operations and communications.\n\nThe common usecase is to instantiate one Asio service, start the service and\nattach TCP/UDP/WebSocket servers or/and clients to it. One Asio service can\nhandle several servers and clients asynchronously at the same time in one I/O\nthread. If you want to scale your servers or clients it is possible to create\nand use more than one Asio services to handle your servers/clients in balance.\n\nAlso it is possible to dispatch or post your custom handler into I/O thread.\nDispatch will execute the handler immediately if the current thread is I/O one.\nOtherwise the handler will be enqueued to the I/O queue. In opposite the post\nmethod will always enqueue the handler into the I/O queue.\n\nHere comes an example of using custom Asio service with dispatch/post methods:\n```c++\n#include \"server/asio/service.h\"\n#include \"threads/thread.h\"\n\n#include \u003ciostream\u003e\n\nint main(int argc, char** argv)\n{\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Dispatch\n    std::cout \u003c\u003c \"1 - Dispatch from the main thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n    service-\u003eDispatch([service]()\n    {\n        std::cout \u003c\u003c \"1.1 - Dispatched in thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n\n        std::cout \u003c\u003c \"1.2 - Dispatch from thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        service-\u003eDispatch([service]()\n        {\n            std::cout \u003c\u003c \"1.2.1 - Dispatched in thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        });\n\n        std::cout \u003c\u003c \"1.3 - Post from thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        service-\u003ePost([service]()\n        {\n            std::cout \u003c\u003c \"1.3.1 - Posted in thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        });\n    });\n\n    // Post\n    std::cout \u003c\u003c \"2 - Post from the main thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n    service-\u003ePost([service]()\n    {\n        std::cout \u003c\u003c \"2.1 - Posted in thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n\n        std::cout \u003c\u003c \"2.2 - Dispatch from thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        service-\u003eDispatch([service]()\n        {\n            std::cout \u003c\u003c \"2.2.1 - Dispatched in thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        });\n\n        std::cout \u003c\u003c \"2.3 - Post from thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        service-\u003ePost([service]()\n        {\n            std::cout \u003c\u003c \"2.3.1 - Posted in thread with Id \" \u003c\u003c CppCommon::Thread::CurrentThreadId() \u003c\u003c std::endl;\n        });\n    });\n\n    // Wait for a while...\n    CppCommon::Thread::Sleep(1000);\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\nOutput of the above example is the following:\n```\nAsio service started!\n1 - Dispatch from the main thread with Id 16744\n2 - Post from the main thread with Id 16744\n1.1 - Dispatched in thread with Id 19920\n1.2 - Dispatch from thread with Id 19920\n1.2.1 - Dispatched in thread with Id 19920\n1.3 - Post from thread with Id 19920\n2.1 - Posted in thread with Id 19920\n2.2 - Dispatch from thread with Id 19920\n2.2.1 - Dispatched in thread with Id 19920\n2.3 - Post from thread with Id 19920\n1.3.1 - Posted in thread with Id 19920\n2.3.1 - Posted in thread with Id 19920\nAsio service stopped!\n```\n\n## Example: Asio timer\nHere comes the example of Asio timer. It can be used to wait for some action\nin future with providing absolute time or relative time span. Asio timer can\nbe used in synchronous or asynchronous modes.\n```c++\n#include \"server/asio/timer.h\"\n#include \"threads/thread.h\"\n\n#include \u003ciostream\u003e\n\nclass AsioTimer : public CppServer::Asio::Timer\n{\npublic:\n    using CppServer::Asio::Timer::Timer;\n\nprotected:\n    void onTimer(bool canceled) override\n    {\n        std::cout \u003c\u003c \"Asio timer \" \u003c\u003c (canceled ? \"canceled\" : \"expired\") \u003c\u003c std::endl;\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Asio timer caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new Asio timer\n    auto timer = std::make_shared\u003cAsioTimer\u003e(service);\n\n    // Setup and synchronously wait for the timer\n    timer-\u003eSetup(CppCommon::UtcTime() + CppCommon::Timespan::seconds(1));\n    timer-\u003eWaitSync();\n\n    // Setup and asynchronously wait for the timer\n    timer-\u003eSetup(CppCommon::Timespan::seconds(1));\n    timer-\u003eWaitAsync();\n\n    // Wait for a while...\n    CppCommon::Thread::Sleep(2000);\n\n    // Setup and asynchronously wait for the timer\n    timer-\u003eSetup(CppCommon::Timespan::seconds(1));\n    timer-\u003eWaitAsync();\n\n    // Wait for a while...\n    CppCommon::Thread::Sleep(500);\n\n    // Cancel the timer\n    timer-\u003eCancel();\n\n    // Wait for a while...\n    CppCommon::Thread::Sleep(500);\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\nOutput of the above example is the following:\n```\nAsio service starting...Done!\nTimer was expired\nTimer was canceled\nAsio service stopping...Done!\n```\n\n## Example: TCP chat server\nHere comes the example of the TCP chat server. It handles multiple TCP client\nsessions and multicast received message from any session to all ones. Also it\nis possible to send admin message directly from the server.\n\n```c++\n#include \"server/asio/tcp_server.h\"\n#include \"threads/thread.h\"\n\n#include \u003ciostream\u003e\n\nclass ChatSession : public CppServer::Asio::TCPSession\n{\npublic:\n    using CppServer::Asio::TCPSession::TCPSession;\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Chat TCP session with Id \" \u003c\u003c id() \u003c\u003c \" connected!\" \u003c\u003c std::endl;\n\n        // Send invite message\n        std::string message(\"Hello from TCP chat! Please send a message or '!' to disconnect the client!\");\n        SendAsync(message);\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat TCP session with Id \" \u003c\u003c id() \u003c\u003c \" disconnected!\" \u003c\u003c std::endl;\n    }\n\n    void onReceived(const void* buffer, size_t size) override\n    {\n        std::string message((const char*)buffer, size);\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c message \u003c\u003c std::endl;\n\n        // Multicast message to all connected sessions\n        server()-\u003eMulticast(message);\n\n        // If the buffer starts with '!' the disconnect the current session\n        if (message == \"!\")\n            DisconnectAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat TCP session caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nclass ChatServer : public CppServer::Asio::TCPServer\n{\npublic:\n    using CppServer::Asio::TCPServer::TCPServer;\n\nprotected:\n    std::shared_ptr\u003cCppServer::Asio::TCPSession\u003e CreateSession(std::shared_ptr\u003cCppServer::Asio::TCPServer\u003e server) override\n    {\n        return std::make_shared\u003cChatSession\u003e(server);\n    }\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat TCP server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // TCP server port\n    int port = 1111;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n\n    std::cout \u003c\u003c \"TCP server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new TCP chat server\n    auto server = std::make_shared\u003cChatServer\u003e(service, port);\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Multicast admin message to all sessions\n        line = \"(admin) \" + line;\n        server-\u003eMulticast(line);\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: TCP chat client\nHere comes the example of the TCP chat client. It connects to the TCP chat\nserver and allows to send message to it and receive new messages.\n\n```c++\n#include \"server/asio/tcp_client.h\"\n#include \"threads/thread.h\"\n\n#include \u003catomic\u003e\n#include \u003ciostream\u003e\n\nclass ChatClient : public CppServer::Asio::TCPClient\n{\npublic:\n    ChatClient(std::shared_ptr\u003cCppServer::Asio::Service\u003e service, const std::string\u0026 address, int port)\n        : CppServer::Asio::TCPClient(service, address, port)\n    {\n        _stop = false;\n    }\n\n    void DisconnectAndStop()\n    {\n        _stop = true;\n        DisconnectAsync();\n        while (IsConnected())\n            CppCommon::Thread::Yield();\n    }\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Chat TCP client connected a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat TCP client disconnected a session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Wait for a while...\n        CppCommon::Thread::Sleep(1000);\n\n        // Try to connect again\n        if (!_stop)\n            ConnectAsync();\n    }\n\n    void onReceived(const void* buffer, size_t size) override\n    {\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c std::string((const char*)buffer, size) \u003c\u003c std::endl;\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat TCP client caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\nprivate:\n    std::atomic\u003cbool\u003e _stop;\n};\n\nint main(int argc, char** argv)\n{\n    // TCP server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    // TCP server port\n    int port = 1111;\n    if (argc \u003e 2)\n        port = std::atoi(argv[2]);\n\n    std::cout \u003c\u003c \"TCP server address: \" \u003c\u003c address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"TCP server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new TCP chat client\n    auto client = std::make_shared\u003cChatClient\u003e(service, address, port);\n\n    // Connect the client\n    std::cout \u003c\u003c \"Client connecting...\";\n    client-\u003eConnectAsync();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Disconnect the client\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Client disconnecting...\";\n            client-\u003eDisconnectAsync();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Send the entered text to the chat server\n        client-\u003eSendAsync(line);\n    }\n\n    // Disconnect the client\n    std::cout \u003c\u003c \"Client disconnecting...\";\n    client-\u003eDisconnectAndStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: SSL chat server\nHere comes the example of the SSL chat server. It handles multiple SSL client\nsessions and multicast received message from any session to all ones. Also it\nis possible to send admin message directly from the server.\n\nThis example is very similar to the TCP one except the code that prepares SSL\ncontext and handshake handler.\n\n```c++\n#include \"server/asio/ssl_server.h\"\n#include \"threads/thread.h\"\n\n#include \u003ciostream\u003e\n\nclass ChatSession : public CppServer::Asio::SSLSession\n{\npublic:\n    using CppServer::Asio::SSLSession::SSLSession;\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Chat SSL session with Id \" \u003c\u003c id() \u003c\u003c \" connected!\" \u003c\u003c std::endl;\n    }\n\n    void onHandshaked() override\n    {\n        std::cout \u003c\u003c \"Chat SSL session with Id \" \u003c\u003c id() \u003c\u003c \" handshaked!\" \u003c\u003c std::endl;\n\n        // Send invite message\n        std::string message(\"Hello from SSL chat! Please send a message or '!' to disconnect the client!\");\n        SendAsync(message.data(), message.size());\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat SSL session with Id \" \u003c\u003c id() \u003c\u003c \" disconnected!\" \u003c\u003c std::endl;\n    }\n\n    void onReceived(const void* buffer, size_t size) override\n    {\n        std::string message((const char*)buffer, size);\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c message \u003c\u003c std::endl;\n\n        // Multicast message to all connected sessions\n        server()-\u003eMulticast(message);\n\n        // If the buffer starts with '!' the disconnect the current session\n        if (message == \"!\")\n            DisconnectAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat SSL session caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nclass ChatServer : public CppServer::Asio::SSLServer\n{\npublic:\n    using CppServer::Asio::SSLServer::SSLServer;\n\nprotected:\n    std::shared_ptr\u003cCppServer::Asio::SSLSession\u003e CreateSession(std::shared_ptr\u003cCppServer::Asio::SSLServer\u003e server) override\n    {\n        return std::make_shared\u003cChatSession\u003e(server);\n    }\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat TCP server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // SSL server port\n    int port = 2222;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n\n    std::cout \u003c\u003c \"SSL server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create and prepare a new SSL server context\n    auto context = std::make_shared\u003cCppServer::Asio::SSLContext\u003e(asio::ssl::context::tlsv12);\n    context-\u003eset_password_callback([](size_t max_length, asio::ssl::context::password_purpose purpose) -\u003e std::string { return \"qwerty\"; });\n    context-\u003euse_certificate_chain_file(\"../tools/certificates/server.pem\");\n    context-\u003euse_private_key_file(\"../tools/certificates/server.pem\", asio::ssl::context::pem);\n    context-\u003euse_tmp_dh_file(\"../tools/certificates/dh4096.pem\");\n\n    // Create a new SSL chat server\n    auto server = std::make_shared\u003cChatServer\u003e(service, context, port);\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Multicast admin message to all sessions\n        line = \"(admin) \" + line;\n        server-\u003eMulticast(line);\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: SSL chat client\nHere comes the example of the SSL chat client. It connects to the SSL chat\nserver and allows to send message to it and receive new messages.\n\nThis example is very similar to the TCP one except the code that prepares SSL\ncontext and handshake handler.\n\n```c++\n#include \"server/asio/ssl_client.h\"\n#include \"threads/thread.h\"\n\n#include \u003catomic\u003e\n#include \u003ciostream\u003e\n\nclass ChatClient : public CppServer::Asio::SSLClient\n{\npublic:\n    ChatClient(std::shared_ptr\u003cCppServer::Asio::Service\u003e service, std::shared_ptr\u003cCppServer::Asio::SSLContext\u003e context, const std::string\u0026 address, int port)\n        : CppServer::Asio::SSLClient(service, context, address, port)\n    {\n        _stop = false;\n    }\n\n    void DisconnectAndStop()\n    {\n        _stop = true;\n        DisconnectAsync();\n        while (IsConnected())\n            CppCommon::Thread::Yield();\n    }\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Chat SSL client connected a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n    }\n\n    void onHandshaked() override\n    {\n        std::cout \u003c\u003c \"Chat SSL client handshaked a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat SSL client disconnected a session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Wait for a while...\n        CppCommon::Thread::Sleep(1000);\n\n        // Try to connect again\n        if (!_stop)\n            ConnectAsync();\n    }\n\n    void onReceived(const void* buffer, size_t size) override\n    {\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c std::string((const char*)buffer, size) \u003c\u003c std::endl;\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat SSL client caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\nprivate:\n    std::atomic\u003cbool\u003e _stop;\n};\n\nint main(int argc, char** argv)\n{\n    // SSL server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    // SSL server port\n    int port = 2222;\n    if (argc \u003e 2)\n        port = std::atoi(argv[2]);\n\n    std::cout \u003c\u003c \"SSL server address: \" \u003c\u003c address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"SSL server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create and prepare a new SSL client context\n    auto context = std::make_shared\u003cCppServer::Asio::SSLContext\u003e(asio::ssl::context::tlsv12);\n    context-\u003eset_default_verify_paths();\n    context-\u003eset_root_certs();\n    context-\u003eset_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);\n    context-\u003eload_verify_file(\"../tools/certificates/ca.pem\");\n\n    // Create a new SSL chat client\n    auto client = std::make_shared\u003cChatClient\u003e(service, context, address, port);\n\n    // Connect the client\n    std::cout \u003c\u003c \"Client connecting...\";\n    client-\u003eConnectAsync();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Disconnect the client\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Client disconnecting...\";\n            client-\u003eDisconnectAsync();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Send the entered text to the chat server\n        client-\u003eSendAsync(line);\n    }\n\n    // Disconnect the client\n    std::cout \u003c\u003c \"Client disconnecting...\";\n    client-\u003eDisconnectAndStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: UDP echo server\nHere comes the example of the UDP echo server. It receives a datagram mesage\nfrom any UDP client and resend it back without any changes.\n\n```c++\n#include \"server/asio/udp_server.h\"\n#include \"threads/thread.h\"\n\n#include \u003ciostream\u003e\n\nclass EchoServer : public CppServer::Asio::UDPServer\n{\npublic:\n    using CppServer::Asio::UDPServer::UDPServer;\n\nprotected:\n    void onStarted() override\n    {\n        // Start receive datagrams\n        ReceiveAsync();\n    }\n\n    void onReceived(const asio::ip::udp::endpoint\u0026 endpoint, const void* buffer, size_t size) override\n    {\n        std::string message((const char*)buffer, size);\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c message \u003c\u003c std::endl;\n\n        // Echo the message back to the sender\n        SendAsync(endpoint, message);\n    }\n\n    void onSent(const asio::ip::udp::endpoint\u0026 endpoint, size_t sent) override\n    {\n        // Continue receive datagrams\n        ReceiveAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Echo UDP server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // UDP server port\n    int port = 3333;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n\n    std::cout \u003c\u003c \"UDP server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new UDP echo server\n    auto server = std::make_shared\u003cEchoServer\u003e(service, port);\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: UDP echo client\nHere comes the example of the UDP echo client. It sends user datagram message\nto UDP server and listen for response.\n\n```c++\n#include \"server/asio/udp_client.h\"\n#include \"threads/thread.h\"\n\n#include \u003catomic\u003e\n#include \u003ciostream\u003e\n\nclass EchoClient : public CppServer::Asio::UDPClient\n{\npublic:\n    EchoClient(std::shared_ptr\u003cCppServer::Asio::Service\u003e service, const std::string\u0026 address, int port)\n        : CppServer::Asio::UDPClient(service, address, port)\n    {\n        _stop = false;\n    }\n\n    void DisconnectAndStop()\n    {\n        _stop = true;\n        DisconnectAsync();\n        while (IsConnected())\n            CppCommon::Thread::Yield();\n    }\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Echo UDP client connected a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Start receive datagrams\n        ReceiveAsync();\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Echo UDP client disconnected a session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Wait for a while...\n        CppCommon::Thread::Sleep(1000);\n\n        // Try to connect again\n        if (!_stop)\n            ConnectAsync();\n    }\n\n    void onReceived(const asio::ip::udp::endpoint\u0026 endpoint, const void* buffer, size_t size) override\n    {\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c std::string((const char*)buffer, size) \u003c\u003c std::endl;\n\n        // Continue receive datagrams\n        ReceiveAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Echo UDP client caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\nprivate:\n    std::atomic\u003cbool\u003e _stop;\n};\n\nint main(int argc, char** argv)\n{\n    // UDP server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    // UDP server port\n    int port = 3333;\n    if (argc \u003e 2)\n        port = std::atoi(argv[2]);\n\n    std::cout \u003c\u003c \"UDP server address: \" \u003c\u003c address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"UDP server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new UDP echo client\n    auto client = std::make_shared\u003cEchoClient\u003e(service, address, port);\n\n    // Connect the client\n    std::cout \u003c\u003c \"Client connecting...\";\n    client-\u003eConnectAsync();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Disconnect the client\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Client disconnecting...\";\n            client-\u003eDisconnectAsync();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Send the entered text to the echo server\n        client-\u003eSendSync(line);\n    }\n\n    // Disconnect the client\n    std::cout \u003c\u003c \"Client disconnecting...\";\n    client-\u003eDisconnectAndStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: UDP multicast server\nHere comes the example of the UDP multicast server. It use multicast IP address\nto multicast datagram messages to all client that joined corresponding UDP\nmulticast group.\n\n```c++\n#include \"server/asio/udp_server.h\"\n#include \"threads/thread.h\"\n\n#include \u003ciostream\u003e\n\nclass MulticastServer : public CppServer::Asio::UDPServer\n{\npublic:\n    using CppServer::Asio::UDPServer::UDPServer;\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Multicast UDP server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // UDP multicast address\n    std::string multicast_address = \"239.255.0.1\";\n    if (argc \u003e 1)\n        multicast_address = argv[1];\n\n    // UDP multicast port\n    int multicast_port = 3334;\n    if (argc \u003e 2)\n        multicast_port = std::atoi(argv[2]);\n\n    std::cout \u003c\u003c \"UDP multicast address: \" \u003c\u003c multicast_address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"UDP multicast port: \" \u003c\u003c multicast_port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new UDP multicast server\n    auto server = std::make_shared\u003cMulticastServer\u003e(service, 0);\n\n    // Start the multicast server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart(multicast_address, multicast_port);\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Multicast admin message to all sessions\n        line = \"(admin) \" + line;\n        server-\u003eMulticastSync(line);\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: UDP multicast client\nHere comes the example of the UDP multicast client. It use multicast IP address\nand joins UDP multicast group in order to receive multicasted datagram messages\nfrom UDP server.\n\n```c++\n#include \"server/asio/udp_client.h\"\n#include \"threads/thread.h\"\n\n#include \u003catomic\u003e\n#include \u003ciostream\u003e\n\nclass MulticastClient : public CppServer::Asio::UDPClient\n{\npublic:\n    MulticastClient(std::shared_ptr\u003cCppServer::Asio::Service\u003e service, const std::string\u0026 address, const std::string\u0026 multicast, int port)\n        : CppServer::Asio::UDPClient(service, address, port),\n          _multicast(multicast)\n    {\n        _stop = false;\n    }\n\n    void DisconnectAndStop()\n    {\n        _stop = true;\n        DisconnectAsync();\n        while (IsConnected())\n            CppCommon::Thread::Yield();\n    }\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Multicast UDP client connected a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Join UDP multicast group\n        JoinMulticastGroupAsync(_multicast);\n\n        // Start receive datagrams\n        ReceiveAsync();\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Multicast UDP client disconnected a session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Wait for a while...\n        CppCommon::Thread::Sleep(1000);\n\n        // Try to connect again\n        if (!_stop)\n            ConnectAsync();\n    }\n\n    void onReceived(const asio::ip::udp::endpoint\u0026 endpoint, const void* buffer, size_t size) override\n    {\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c std::string((const char*)buffer, size) \u003c\u003c std::endl;\n\n        // Continue receive datagrams\n        ReceiveAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Multicast UDP client caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\nprivate:\n    std::atomic\u003cbool\u003e _stop;\n    std::string _multicast;\n};\n\nint main(int argc, char** argv)\n{\n    // UDP listen address\n    std::string listen_address = \"0.0.0.0\";\n    if (argc \u003e 1)\n        listen_address = argv[1];\n\n    // UDP multicast address\n    std::string multicast_address = \"239.255.0.1\";\n    if (argc \u003e 2)\n        multicast_address = argv[2];\n\n    // UDP multicast port\n    int multicast_port = 3334;\n    if (argc \u003e 3)\n        multicast_port = std::atoi(argv[3]);\n\n    std::cout \u003c\u003c \"UDP listen address: \" \u003c\u003c listen_address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"UDP multicast address: \" \u003c\u003c multicast_address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"UDP multicast port: \" \u003c\u003c multicast_port \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new UDP multicast client\n    auto client = std::make_shared\u003cMulticastClient\u003e(service, listen_address, multicast_address, multicast_port);\n    client-\u003eSetupMulticast(true);\n\n    // Connect the client\n    std::cout \u003c\u003c \"Client connecting...\";\n    client-\u003eConnectAsync();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Disconnect the client\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Client disconnecting...\";\n            client-\u003eDisconnectAsync();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n    }\n\n    // Disconnect the client\n    std::cout \u003c\u003c \"Client disconnecting...\";\n    client-\u003eDisconnectAndStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: Simple protocol\nSimple protocol is defined in [simple.fbe](https://github.com/chronoxor/CppServer/blob/master/proto/simple.fbe) file:\n\n```proto\n/*\n   Simple Fast Binary Encoding protocol for CppServer\n   https://github.com/chronoxor/FastBinaryEncoding\n\n   Generate protocol command: fbec --cpp --proto --input=simple.fbe --output=.\n*/\n\n// Domain declaration\ndomain com.chronoxor\n\n// Package declaration\npackage simple\n\n// Protocol version\nversion 1.0\n\n// Simple request message\n[request]\n[response(SimpleResponse)]\n[reject(SimpleReject)]\nmessage SimpleRequest\n{\n    // Request Id\n    uuid [id] = uuid1;\n    // Request message\n    string Message;\n}\n\n// Simple response\nmessage SimpleResponse\n{\n    // Response Id\n    uuid [id] = uuid1;\n    // Calculated message hash\n    uint32 Hash;\n}\n\n// Simple reject\nmessage SimpleReject\n{\n    // Reject Id\n    uuid [id] = uuid1;\n    // Error message\n    string Error;\n}\n\n// Simple notification\nmessage SimpleNotify\n{\n    // Server notification\n    string Notification;\n}\n\n// Disconnect request message\n[request]\nmessage DisconnectRequest\n{\n    // Request Id\n    uuid [id] = uuid1;\n}\n```\n\n## Example: Simple protocol server\nHere comes the example of  the  simple  protocol  server.  It  process  client\nrequests, answer with corresponding responses and  send  server  notifications\nback to clients.\n\n```c++\n#include \"asio_service.h\"\n\n#include \"server/asio/tcp_server.h\"\n\n#include \"../proto/simple_protocol.h\"\n\n#include \u003ciostream\u003e\n\nclass SimpleProtoSession : public CppServer::Asio::TCPSession, public FBE::simple::Sender, public FBE::simple::Receiver\n{\npublic:\n    using CppServer::Asio::TCPSession::TCPSession;\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Simple protocol session with Id \" \u003c\u003c id() \u003c\u003c \" connected!\" \u003c\u003c std::endl;\n\n        // Send invite notification\n        simple::SimpleNotify notify;\n        notify.Notification = \"Hello from Simple protocol server! Please send a message or '!' to disconnect the client!\";\n        send(notify);\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Simple protocol session with Id \" \u003c\u003c id() \u003c\u003c \" disconnected!\" \u003c\u003c std::endl;\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Simple protocol session caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\n    // Protocol handlers\n    void onReceive(const ::simple::DisconnectRequest\u0026 request) override { Disconnect(); }\n    void onReceive(const ::simple::SimpleRequest\u0026 request) override\n    {\n        std::cout \u003c\u003c \"Received: \" \u003c\u003c request \u003c\u003c std::endl;\n\n        // Validate request\n        if (request.Message.empty())\n        {\n            // Send reject\n            simple::SimpleReject reject;\n            reject.id = request.id;\n            reject.Error = \"Request message is empty!\";\n            send(reject);\n            return;\n        }\n\n        static std::hash\u003cstd::string\u003e hasher;\n\n        // Send response\n        simple::SimpleResponse response;\n        response.id = request.id;\n        response.Hash = (uint32_t)hasher(request.Message);\n        send(response);\n    }\n\n    // Protocol implementation\n    void onReceived(const void* buffer, size_t size) override { receive(buffer, size); }\n    size_t onSend(const void* data, size_t size) override { return SendAsync(data, size) ? size : 0; }\n};\n\nclass SimpleProtoServer : public CppServer::Asio::TCPServer, public FBE::simple::Sender\n{\npublic:\n    using CppServer::Asio::TCPServer::TCPServer;\n\nprotected:\n    std::shared_ptr\u003cCppServer::Asio::TCPSession\u003e CreateSession(const std::shared_ptr\u003cCppServer::Asio::TCPServer\u003e\u0026 server) override\n    {\n        return std::make_shared\u003cSimpleProtoSession\u003e(server);\n    }\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Simple protocol server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\n    // Protocol implementation\n    size_t onSend(const void* data, size_t size) override { Multicast(data, size); return size; }\n};\n\nint main(int argc, char** argv)\n{\n    // Simple protocol server port\n    int port = 4444;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n\n    std::cout \u003c\u003c \"Simple protocol server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cAsioService\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new simple protocol server\n    auto server = std::make_shared\u003cSimpleProtoServer\u003e(service, port);\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Multicast admin notification to all sessions\n        simple::SimpleNotify notify;\n        notify.Notification = \"(admin) \" + line;\n        server-\u003esend(notify);\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: Simple protocol client\nHere comes the example of the simple  protocol  client.  It  connects  to  the\nsimple protocol  server  and  allows  to  send  requests  to  it  and  receive\ncorresponding responses.\n\n```c++\n#include \"asio_service.h\"\n\n#include \"server/asio/tcp_client.h\"\n#include \"threads/thread.h\"\n\n#include \"../proto/simple_protocol.h\"\n\n#include \u003catomic\u003e\n#include \u003ciostream\u003e\n\nclass SimpleProtoClient : public CppServer::Asio::TCPClient, public FBE::simple::Client\n{\npublic:\n    using CppServer::Asio::TCPClient::TCPClient;\n\n    void DisconnectAndStop()\n    {\n        _stop = true;\n        DisconnectAsync();\n        while (IsConnected())\n            CppCommon::Thread::Yield();\n    }\n\nprotected:\n    void onConnected() override\n    {\n        std::cout \u003c\u003c \"Simple protocol client connected a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Reset FBE protocol buffers\n        reset();\n    }\n\n    void onDisconnected() override\n    {\n        std::cout \u003c\u003c \"Simple protocol client disconnected a session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n\n        // Wait for a while...\n        CppCommon::Thread::Sleep(1000);\n\n        // Try to connect again\n        if (!_stop)\n            ConnectAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Simple protocol client caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\n    // Protocol handlers\n    void onReceive(const ::simple::DisconnectRequest\u0026 request) override { Client::onReceive(request); std::cout \u003c\u003c \"Received: \" \u003c\u003c request \u003c\u003c std::endl; DisconnectAsync(); }\n    void onReceive(const ::simple::SimpleResponse\u0026 response) override { Client::onReceive(response); std::cout \u003c\u003c \"Received: \" \u003c\u003c response \u003c\u003c std::endl; }\n    void onReceive(const ::simple::SimpleReject\u0026 reject) override { Client::onReceive(reject); std::cout \u003c\u003c \"Received: \" \u003c\u003c reject \u003c\u003c std::endl; }\n    void onReceive(const ::simple::SimpleNotify\u0026 notify) override { Client::onReceive(notify); std::cout \u003c\u003c \"Received: \" \u003c\u003c notify \u003c\u003c std::endl; }\n\n    // Protocol implementation\n    void onReceived(const void* buffer, size_t size) override { receive(buffer, size); }\n    size_t onSend(const void* data, size_t size) override { return SendAsync(data, size) ? size : 0; }\n\nprivate:\n    std::atomic\u003cbool\u003e _stop{false};\n};\n\nint main(int argc, char** argv)\n{\n    // TCP server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    // Simple protocol server port\n    int port = 4444;\n    if (argc \u003e 2)\n        port = std::atoi(argv[2]);\n\n    std::cout \u003c\u003c \"Simple protocol server address: \" \u003c\u003c address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"Simple protocol server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cAsioService\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new simple protocol client\n    auto client = std::make_shared\u003cSimpleProtoClient\u003e(service, address, port);\n\n    // Connect the client\n    std::cout \u003c\u003c \"Client connecting...\";\n    client-\u003eConnectAsync();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Reconnect the client\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Client reconnecting...\";\n            client-\u003eIsConnected() ? client-\u003eReconnectAsync() : client-\u003eConnectAsync();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Send request to the simple protocol server\n        simple::SimpleRequest request;\n        request.Message = line;\n        auto response = client-\u003erequest(request).get();\n\n        // Show string hash calculation result\n        std::cout \u003c\u003c \"Hash of '\" \u003c\u003c line \u003c\u003c \"' = \" \u003c\u003c std::format(\"0x{:8X}\", response.Hash) \u003c\u003c std::endl;\n    }\n\n    // Disconnect the client\n    std::cout \u003c\u003c \"Client disconnecting...\";\n    client-\u003eDisconnectAndStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: HTTP server\nHere comes the example of the HTTP cache server. It allows to manipulate\ncache data with HTTP methods (GET, POST, PUT and DELETE).\n\nUse the following link to open [Swagger OpenAPI](https://swagger.io/specification/) iterative documentation: http://localhost:8080/api/index.html\n\n![OpenAPI-HTTP](https://github.com/chronoxor/CppServer/raw/master/images/openapi-http.png)\n\n```c++\n#include \"server/http/http_server.h\"\n#include \"string/string_utils.h\"\n#include \"utility/singleton.h\"\n\n#include \u003ciostream\u003e\n#include \u003cmap\u003e\n#include \u003cmutex\u003e\n\nclass Cache : public CppCommon::Singleton\u003cCache\u003e\n{\n   friend CppCommon::Singleton\u003cCache\u003e;\n\npublic:\n    std::string GetAllCache()\n    {\n        std::scoped_lock locker(_cache_lock);\n        std::string result;\n        result += \"[\\n\";\n        for (const auto\u0026 item : _cache)\n        {\n            result += \"  {\\n\";\n            result += \"    \\\"key\\\": \\\"\" + item.first + \"\\\",\\n\";\n            result += \"    \\\"value\\\": \\\"\" + item.second + \"\\\",\\n\";\n            result += \"  },\\n\";\n        }\n        result += \"]\\n\";\n        return result;\n    }\n\n    bool GetCacheValue(std::string_view key, std::string\u0026 value)\n    {\n        std::scoped_lock locker(_cache_lock);\n        auto it = _cache.find(key);\n        if (it != _cache.end())\n        {\n            value = it-\u003esecond;\n            return true;\n        }\n        else\n            return false;\n    }\n\n    void PutCacheValue(std::string_view key, std::string_view value)\n    {\n        std::scoped_lock locker(_cache_lock);\n        auto it = _cache.emplace(key, value);\n        if (!it.second)\n            it.first-\u003esecond = value;\n    }\n\n    bool DeleteCacheValue(std::string_view key, std::string\u0026 value)\n    {\n        std::scoped_lock locker(_cache_lock);\n        auto it = _cache.find(key);\n        if (it != _cache.end())\n        {\n            value = it-\u003esecond;\n            _cache.erase(it);\n            return true;\n        }\n        else\n            return false;\n    }\n\nprivate:\n    std::mutex _cache_lock;\n    std::map\u003cstd::string, std::string, std::less\u003c\u003e\u003e _cache;\n};\n\nclass HTTPCacheSession : public CppServer::HTTP::HTTPSession\n{\npublic:\n    using CppServer::HTTP::HTTPSession::HTTPSession;\n\nprotected:\n    void onReceivedRequest(const CppServer::HTTP::HTTPRequest\u0026 request) override\n    {\n        // Show HTTP request content\n        std::cout \u003c\u003c std::endl \u003c\u003c request;\n\n        // Process HTTP request methods\n        if (request.method() == \"HEAD\")\n            SendResponseAsync(response().MakeHeadResponse());\n        else if (request.method() == \"GET\")\n        {\n            std::string key(request.url());\n            std::string value;\n\n            // Decode the key value\n            key = CppCommon::Encoding::URLDecode(key);\n            CppCommon::StringUtils::ReplaceFirst(key, \"/api/cache\", \"\");\n            CppCommon::StringUtils::ReplaceFirst(key, \"?key=\", \"\");\n\n            if (key.empty())\n            {\n                // Response with all cache values\n                SendResponseAsync(response().MakeGetResponse(Cache::GetInstance().GetAllCache(), \"application/json; charset=UTF-8\"));\n            }\n            // Get the cache value by the given key\n            else if (Cache::GetInstance().GetCacheValue(key, value))\n            {\n                // Response with the cache value\n                SendResponseAsync(response().MakeGetResponse(value));\n            }\n            else\n                SendResponseAsync(response().MakeErrorResponse(404, \"Required cache value was not found for the key: \" + key));\n        }\n        else if ((request.method() == \"POST\") || (request.method() == \"PUT\"))\n        {\n            std::string key(request.url());\n            std::string value(request.body());\n\n            // Decode the key value\n            key = CppCommon::Encoding::URLDecode(key);\n            CppCommon::StringUtils::ReplaceFirst(key, \"/api/cache\", \"\");\n            CppCommon::StringUtils::ReplaceFirst(key, \"?key=\", \"\");\n\n            // Put the cache value\n            Cache::GetInstance().PutCacheValue(key, value);\n\n            // Response with the cache value\n            SendResponseAsync(response().MakeOKResponse());\n        }\n        else if (request.method() == \"DELETE\")\n        {\n            std::string key(request.url());\n            std::string value;\n\n            // Decode the key value\n            key = CppCommon::Encoding::URLDecode(key);\n            CppCommon::StringUtils::ReplaceFirst(key, \"/api/cache\", \"\");\n            CppCommon::StringUtils::ReplaceFirst(key, \"?key=\", \"\");\n\n            // Delete the cache value\n            if (Cache::GetInstance().DeleteCacheValue(key, value))\n            {\n                // Response with the cache value\n                SendResponseAsync(response().MakeGetResponse(value));\n            }\n            else\n                SendResponseAsync(response().MakeErrorResponse(404, \"Deleted cache value was not found for the key: \" + key));\n        }\n        else if (request.method() == \"OPTIONS\")\n            SendResponseAsync(response().MakeOptionsResponse());\n        else if (request.method() == \"TRACE\")\n            SendResponseAsync(response().MakeTraceResponse(request.cache()));\n        else\n            SendResponseAsync(response().MakeErrorResponse(\"Unsupported HTTP method: \" + std::string(request.method())));\n    }\n\n    void onReceivedRequestError(const CppServer::HTTP::HTTPRequest\u0026 request, const std::string\u0026 error) override\n    {\n        std::cout \u003c\u003c \"Request error: \" \u003c\u003c error \u003c\u003c std::endl;\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"HTTP session caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nclass HTTPCacheServer : public CppServer::HTTP::HTTPServer\n{\npublic:\n    using CppServer::HTTP::HTTPServer::HTTPServer;\n\nprotected:\n    std::shared_ptr\u003cCppServer::Asio::TCPSession\u003e CreateSession(const std::shared_ptr\u003cCppServer::Asio::TCPServer\u003e\u0026 server) override\n    {\n        return std::make_shared\u003cHTTPCacheSession\u003e(std::dynamic_pointer_cast\u003cCppServer::HTTP::HTTPServer\u003e(server));\n    }\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"HTTP server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // HTTP server port\n    int port = 8080;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n    // HTTP server content path\n    std::string www = \"../www/api\";\n    if (argc \u003e 2)\n        www = argv[2];\n\n    std::cout \u003c\u003c \"HTTP server port: \" \u003c\u003c port \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"HTTP server static content path: \" \u003c\u003c www \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"HTTP server website: \" \u003c\u003c \"http://localhost:\" \u003c\u003c port \u003c\u003c \"/api/index.html\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new HTTP server\n    auto server = std::make_shared\u003cHTTPCacheServer\u003e(service, port);\n    server-\u003eAddStaticContent(www, \"/api\");\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: HTTP client\nHere comes the example of the HTTP client. It allows to send HTTP requests\n(GET, POST, PUT and DELETE) and receive HTTP responses.\n\n```c++\n#include \"server/http/http_client.h\"\n#include \"string/string_utils.h\"\n\n#include \u003ciostream\u003e\n\nint main(int argc, char** argv)\n{\n    // HTTP server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    std::cout \u003c\u003c \"HTTP server address: \" \u003c\u003c address \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new HTTP client\n    auto client = std::make_shared\u003cCppServer::HTTP::HTTPClientEx\u003e(service, address, \"http\");\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    try\n    {\n        // Perform text input\n        std::string line;\n        while (getline(std::cin, line))\n        {\n            if (line.empty())\n                break;\n\n            // Reconnect the client\n            if (line == \"!\")\n            {\n                std::cout \u003c\u003c \"Client reconnecting...\";\n                client-\u003eReconnectAsync();\n                std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n                continue;\n            }\n\n            auto commands = CppCommon::StringUtils::Split(line, ' ', true);\n            if (commands.size() \u003c 2)\n            {\n                std::cout \u003c\u003c \"HTTP method and URL must be entered!\" \u003c\u003c std::endl;\n                continue;\n            }\n\n            if (CppCommon::StringUtils::ToUpper(commands[0]) == \"HEAD\")\n            {\n                auto response = client-\u003eSendHeadRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"GET\")\n            {\n                auto response = client-\u003eSendGetRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"POST\")\n            {\n                if (commands.size() \u003c 3)\n                {\n                    std::cout \u003c\u003c \"HTTP method, URL and body must be entered!\" \u003c\u003c std::endl;\n                    continue;\n                }\n                auto response = client-\u003eSendPostRequest(commands[1], commands[2]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"PUT\")\n            {\n                if (commands.size() \u003c 3)\n                {\n                    std::cout \u003c\u003c \"HTTP method, URL and body must be entered!\" \u003c\u003c std::endl;\n                    continue;\n                }\n                auto response = client-\u003eSendPutRequest(commands[1], commands[2]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"DELETE\")\n            {\n                auto response = client-\u003eSendDeleteRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"OPTIONS\")\n            {\n                auto response = client-\u003eSendOptionsRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"TRACE\")\n            {\n                auto response = client-\u003eSendTraceRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else\n                std::cout \u003c\u003c \"Unknown HTTP method: \" \u003c\u003c commands[0] \u003c\u003c std::endl;\n        }\n    }\n    catch (const std::exception\u0026 ex)\n    {\n        std::cerr \u003c\u003c ex.what() \u003c\u003c std::endl;\n    }\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: HTTPS server\nHere comes the example of the HTTPS cache server. It allows to manipulate\ncache data with HTTP methods (GET, POST, PUT and DELETE) with secured\ntransport protocol.\n\nUse the following link to open [Swagger OpenAPI](https://swagger.io/specification/) iterative documentation: https://localhost:8443/api/index.html\n\n![OpenAPI-HTTPS](https://github.com/chronoxor/CppServer/raw/master/images/openapi-https.png)\n\n```c++\n#include \"server/http/https_server.h\"\n#include \"string/string_utils.h\"\n#include \"utility/singleton.h\"\n\n#include \u003ciostream\u003e\n#include \u003cmap\u003e\n#include \u003cmutex\u003e\n\nclass Cache : public CppCommon::Singleton\u003cCache\u003e\n{\n   friend CppCommon::Singleton\u003cCache\u003e;\n\npublic:\n    std::string GetAllCache()\n    {\n        std::scoped_lock locker(_cache_lock);\n        std::string result;\n        result += \"[\\n\";\n        for (const auto\u0026 item : _cache)\n        {\n            result += \"  {\\n\";\n            result += \"    \\\"key\\\": \\\"\" + item.first + \"\\\",\\n\";\n            result += \"    \\\"value\\\": \\\"\" + item.second + \"\\\",\\n\";\n            result += \"  },\\n\";\n        }\n        result += \"]\\n\";\n        return result;\n    }\n\n    bool GetCacheValue(std::string_view key, std::string\u0026 value)\n    {\n        std::scoped_lock locker(_cache_lock);\n        auto it = _cache.find(key);\n        if (it != _cache.end())\n        {\n            value = it-\u003esecond;\n            return true;\n        }\n        else\n            return false;\n    }\n\n    void PutCacheValue(std::string_view key, std::string_view value)\n    {\n        std::scoped_lock locker(_cache_lock);\n        auto it = _cache.emplace(key, value);\n        if (!it.second)\n            it.first-\u003esecond = value;\n    }\n\n    bool DeleteCacheValue(std::string_view key, std::string\u0026 value)\n    {\n        std::scoped_lock locker(_cache_lock);\n        auto it = _cache.find(key);\n        if (it != _cache.end())\n        {\n            value = it-\u003esecond;\n            _cache.erase(it);\n            return true;\n        }\n        else\n            return false;\n    }\n\nprivate:\n    std::mutex _cache_lock;\n    std::map\u003cstd::string, std::string, std::less\u003c\u003e\u003e _cache;\n};\n\nclass HTTPSCacheSession : public CppServer::HTTP::HTTPSSession\n{\npublic:\n    using CppServer::HTTP::HTTPSSession::HTTPSSession;\n\nprotected:\n    void onReceivedRequest(const CppServer::HTTP::HTTPRequest\u0026 request) override\n    {\n        // Show HTTP request content\n        std::cout \u003c\u003c std::endl \u003c\u003c request;\n\n        // Process HTTP request methods\n        if (request.method() == \"HEAD\")\n            SendResponseAsync(response().MakeHeadResponse());\n        else if (request.method() == \"GET\")\n        {\n            std::string key(request.url());\n            std::string value;\n\n            // Decode the key value\n            key = CppCommon::Encoding::URLDecode(key);\n            CppCommon::StringUtils::ReplaceFirst(key, \"/api/cache\", \"\");\n            CppCommon::StringUtils::ReplaceFirst(key, \"?key=\", \"\");\n\n            if (key.empty())\n            {\n                // Response with all cache values\n                SendResponseAsync(response().MakeGetResponse(Cache::GetInstance().GetAllCache(), \"application/json; charset=UTF-8\"));\n            }\n            // Get the cache value by the given key\n            else if (Cache::GetInstance().GetCacheValue(key, value))\n            {\n                // Response with the cache value\n                SendResponseAsync(response().MakeGetResponse(value));\n            }\n            else\n                SendResponseAsync(response().MakeErrorResponse(404, \"Required cache value was not found for the key: \" + key));\n        }\n        else if ((request.method() == \"POST\") || (request.method() == \"PUT\"))\n        {\n            std::string key(request.url());\n            std::string value(request.body());\n\n            // Decode the key value\n            key = CppCommon::Encoding::URLDecode(key);\n            CppCommon::StringUtils::ReplaceFirst(key, \"/api/cache\", \"\");\n            CppCommon::StringUtils::ReplaceFirst(key, \"?key=\", \"\");\n\n            // Put the cache value\n            Cache::GetInstance().PutCacheValue(key, value);\n\n            // Response with the cache value\n            SendResponseAsync(response().MakeOKResponse());\n        }\n        else if (request.method() == \"DELETE\")\n        {\n            std::string key(request.url());\n            std::string value;\n\n            // Decode the key value\n            key = CppCommon::Encoding::URLDecode(key);\n            CppCommon::StringUtils::ReplaceFirst(key, \"/api/cache\", \"\");\n            CppCommon::StringUtils::ReplaceFirst(key, \"?key=\", \"\");\n\n            // Delete the cache value\n            if (Cache::GetInstance().DeleteCacheValue(key, value))\n            {\n                // Response with the cache value\n                SendResponseAsync(response().MakeGetResponse(value));\n            }\n            else\n                SendResponseAsync(response().MakeErrorResponse(404, \"Deleted cache value was not found for the key: \" + key));\n        }\n        else if (request.method() == \"OPTIONS\")\n            SendResponseAsync(response().MakeOptionsResponse());\n        else if (request.method() == \"TRACE\")\n            SendResponseAsync(response().MakeTraceResponse(request.cache()));\n        else\n            SendResponseAsync(response().MakeErrorResponse(\"Unsupported HTTP method: \" + std::string(request.method())));\n    }\n\n    void onReceivedRequestError(const CppServer::HTTP::HTTPRequest\u0026 request, const std::string\u0026 error) override\n    {\n        std::cout \u003c\u003c \"Request error: \" \u003c\u003c error \u003c\u003c std::endl;\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"HTTPS session caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nclass HTTPSCacheServer : public CppServer::HTTP::HTTPSServer\n{\npublic:\n    using CppServer::HTTP::HTTPSServer::HTTPSServer;\n\nprotected:\n    std::shared_ptr\u003cCppServer::Asio::SSLSession\u003e CreateSession(const std::shared_ptr\u003cCppServer::Asio::SSLServer\u003e\u0026 server) override\n    {\n        return std::make_shared\u003cHTTPSCacheSession\u003e(std::dynamic_pointer_cast\u003cCppServer::HTTP::HTTPSServer\u003e(server));\n    }\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"HTTPS server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // HTTPS server port\n    int port = 8443;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n    // HTTPS server content path\n    std::string www = \"../www/api\";\n    if (argc \u003e 2)\n        www = argv[2];\n\n    std::cout \u003c\u003c \"HTTPS server port: \" \u003c\u003c port \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"HTTPS server static content path: \" \u003c\u003c www \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"HTTPS server website: \" \u003c\u003c \"https://localhost:\" \u003c\u003c port \u003c\u003c \"/api/index.html\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create and prepare a new SSL server context\n    auto context = std::make_shared\u003cCppServer::Asio::SSLContext\u003e(asio::ssl::context::tlsv12);\n    context-\u003eset_password_callback([](size_t max_length, asio::ssl::context::password_purpose purpose) -\u003e std::string { return \"qwerty\"; });\n    context-\u003euse_certificate_chain_file(\"../tools/certificates/server.pem\");\n    context-\u003euse_private_key_file(\"../tools/certificates/server.pem\", asio::ssl::context::pem);\n    context-\u003euse_tmp_dh_file(\"../tools/certificates/dh4096.pem\");\n\n    // Create a new HTTPS server\n    auto server = std::make_shared\u003cHTTPSCacheServer\u003e(service, context, port);\n    server-\u003eAddStaticContent(www, \"/api\");\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: HTTPS client\nHere comes the example of the HTTPS client. It allows to send HTTP requests\n(GET, POST, PUT and DELETE) and receive HTTP responses with secured\ntransport protocol.\n\n```c++\n#include \"server/http/https_client.h\"\n#include \"string/string_utils.h\"\n\n#include \u003ciostream\u003e\n\nint main(int argc, char** argv)\n{\n    // HTTP server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    std::cout \u003c\u003c \"HTTPS server address: \" \u003c\u003c address \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create and prepare a new SSL client context\n    auto context = std::make_shared\u003cCppServer::Asio::SSLContext\u003e(asio::ssl::context::tlsv12);\n    context-\u003eset_default_verify_paths();\n    context-\u003eset_root_certs();\n    context-\u003eset_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);\n    context-\u003eload_verify_file(\"../tools/certificates/ca.pem\");\n\n    // Create a new HTTP client\n    auto client = std::make_shared\u003cCppServer::HTTP::HTTPSClientEx\u003e(service, context, address, \"https\");\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    try\n    {\n        // Perform text input\n        std::string line;\n        while (getline(std::cin, line))\n        {\n            if (line.empty())\n                break;\n\n            // Reconnect the client\n            if (line == \"!\")\n            {\n                std::cout \u003c\u003c \"Client reconnecting...\";\n                client-\u003eReconnectAsync();\n                std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n                continue;\n            }\n\n            auto commands = CppCommon::StringUtils::Split(line, ' ', true);\n            if (commands.size() \u003c 2)\n            {\n                std::cout \u003c\u003c \"HTTP method and URL must be entered!\" \u003c\u003c std::endl;\n                continue;\n            }\n\n            if (CppCommon::StringUtils::ToUpper(commands[0]) == \"HEAD\")\n            {\n                auto response = client-\u003eSendHeadRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"GET\")\n            {\n                auto response = client-\u003eSendGetRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"POST\")\n            {\n                if (commands.size() \u003c 3)\n                {\n                    std::cout \u003c\u003c \"HTTP method, URL and body must be entered!\" \u003c\u003c std::endl;\n                    continue;\n                }\n                auto response = client-\u003eSendPostRequest(commands[1], commands[2]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"PUT\")\n            {\n                if (commands.size() \u003c 3)\n                {\n                    std::cout \u003c\u003c \"HTTP method, URL and body must be entered!\" \u003c\u003c std::endl;\n                    continue;\n                }\n                auto response = client-\u003eSendPutRequest(commands[1], commands[2]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"DELETE\")\n            {\n                auto response = client-\u003eSendDeleteRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"OPTIONS\")\n            {\n                auto response = client-\u003eSendOptionsRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else if (CppCommon::StringUtils::ToUpper(commands[0]) == \"TRACE\")\n            {\n                auto response = client-\u003eSendTraceRequest(commands[1]).get();\n                std::cout \u003c\u003c response \u003c\u003c std::endl;\n            }\n            else\n                std::cout \u003c\u003c \"Unknown HTTP method: \" \u003c\u003c commands[0] \u003c\u003c std::endl;\n        }\n    }\n    catch (const std::exception\u0026 ex)\n    {\n        std::cerr \u003c\u003c ex.what() \u003c\u003c std::endl;\n    }\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: WebSocket chat server\nHere comes the example of the WebSocket chat server. It handles multiple\nWebSocket client sessions and multicast received message from any session\nto all ones. Also it is possible to send admin message directly from the\nserver.\n\nUse the following link to open WebSocket chat server example: http://localhost:8080/chat/index.html\n\n![ws-chat](https://github.com/chronoxor/CppServer/raw/master/images/ws-chat.png)\n\n```c++\n#include \"server/ws/ws_server.h\"\n\n#include \u003ciostream\u003e\n\nclass ChatSession : public CppServer::WS::WSSession\n{\npublic:\n    using CppServer::WS::WSSession::WSSession;\n\nprotected:\n    void onWSConnected(const CppServer::HTTP::HTTPRequest\u0026 request) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket session with Id \" \u003c\u003c id() \u003c\u003c \" connected!\" \u003c\u003c std::endl;\n\n        // Send invite message\n        std::string message(\"Hello from WebSocket chat! Please send a message or '!' to disconnect the client!\");\n        SendTextAsync(message);\n    }\n\n    void onWSDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket session with Id \" \u003c\u003c id() \u003c\u003c \" disconnected!\" \u003c\u003c std::endl;\n    }\n\n    void onWSReceived(const void* buffer, size_t size) override\n    {\n        std::string message((const char*)buffer, size);\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c message \u003c\u003c std::endl;\n\n        // Multicast message to all connected sessions\n        std::dynamic_pointer_cast\u003cCppServer::WS::WSServer\u003e(server())-\u003eMulticastText(message);\n\n        // If the buffer starts with '!' the disconnect the current session\n        if (message == \"!\")\n            Close(1000);\n    }\n\n    void onWSPing(const void* buffer, size_t size) override\n    {\n        SendPongAsync(buffer, size);\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket session caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nclass ChatServer : public CppServer::WS::WSServer\n{\npublic:\n    using CppServer::WS::WSServer::WSServer;\n\nprotected:\n    std::shared_ptr\u003cCppServer::Asio::TCPSession\u003e CreateSession(std::shared_ptr\u003cCppServer::Asio::TCPServer\u003e server) override\n    {\n        return std::make_shared\u003cChatSession\u003e(std::dynamic_pointer_cast\u003cCppServer::WS::WSServer\u003e(server));\n    }\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // WebSocket server port\n    int port = 8080;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n    // WebSocket server content path\n    std::string www = \"../www/ws\";\n    if (argc \u003e 2)\n        www = argv[2];\n\n    std::cout \u003c\u003c \"WebSocket server port: \" \u003c\u003c port \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"WebSocket server static content path: \" \u003c\u003c www \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"WebSocket server website: \" \u003c\u003c \"http://localhost:\" \u003c\u003c port \u003c\u003c \"/chat/index.html\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new WebSocket chat server\n    auto server = std::make_shared\u003cChatServer\u003e(service, port);\n    server-\u003eAddStaticContent(www, \"/chat\");\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Multicast admin message to all sessions\n        line = \"(admin) \" + line;\n        server-\u003eMulticastText(line);\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: WebSocket chat client\nHere comes the example of the WebSocket chat client. It connects to the\nWebSocket chat server and allows to send message to it and receive new\nmessages.\n\n```c++\n#include \"server/ws/ws_client.h\"\n#include \"threads/thread.h\"\n\n#include \u003catomic\u003e\n#include \u003ciostream\u003e\n\nclass ChatClient : public CppServer::WS::WSClient\n{\npublic:\n    using CppServer::WS::WSClient::WSClient;\n\n    void DisconnectAndStop()\n    {\n        _stop = true;\n        CloseAsync(1000);\n        while (IsConnected())\n            CppCommon::Thread::Yield();\n    }\n\nprotected:\n    void onWSConnecting(CppServer::HTTP::HTTPRequest\u0026 request) override\n    {\n        request.SetBegin(\"GET\", \"/\");\n        request.SetHeader(\"Host\", \"localhost\");\n        request.SetHeader(\"Origin\", \"http://localhost\");\n        request.SetHeader(\"Upgrade\", \"websocket\");\n        request.SetHeader(\"Connection\", \"Upgrade\");\n        request.SetHeader(\"Sec-WebSocket-Key\", CppCommon::Encoding::Base64Encode(ws_nonce()));\n        request.SetHeader(\"Sec-WebSocket-Protocol\", \"chat, superchat\");\n        request.SetHeader(\"Sec-WebSocket-Version\", \"13\");\n    }\n\n    void onWSConnected(const CppServer::HTTP::HTTPResponse\u0026 response) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket client connected a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n    }\n\n    void onWSDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket client disconnected a session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n    }\n\n    void onWSReceived(const void* buffer, size_t size) override\n    {\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c std::string((const char*)buffer, size) \u003c\u003c std::endl;\n    }\n\n    void onWSPing(const void* buffer, size_t size) override\n    {\n        SendPongAsync(buffer, size);\n    }\n\n    void onDisconnected() override\n    {\n        WSClient::onDisconnected();\n\n        // Wait for a while...\n        CppCommon::Thread::Sleep(1000);\n\n        // Try to connect again\n        if (!_stop)\n            ConnectAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket client caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\nprivate:\n    std::atomic\u003cbool\u003e _stop{false};\n};\n\nint main(int argc, char** argv)\n{\n    // WebSocket server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    // WebSocket server port\n    int port = 8080;\n    if (argc \u003e 2)\n        port = std::atoi(argv[2]);\n\n    std::cout \u003c\u003c \"WebSocket server address: \" \u003c\u003c address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"WebSocket server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create a new WebSocket chat client\n    auto client = std::make_shared\u003cChatClient\u003e(service, address, port);\n\n    // Connect the client\n    std::cout \u003c\u003c \"Client connecting...\";\n    client-\u003eConnectAsync();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Reconnect the client\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Client reconnecting...\";\n            client-\u003eReconnectAsync();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Send the entered text to the chat server\n        client-\u003eSendTextAsync(line);\n    }\n\n    // Disconnect the client\n    std::cout \u003c\u003c \"Client disconnecting...\";\n    client-\u003eDisconnectAndStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: WebSocket secure chat server\nHere comes the example of the WebSocket secure chat server. It handles\nmultiple WebSocket secure client sessions and multicast received message\nfrom any session to all ones. Also it is possible to send admin message\ndirectly from the server.\n\nThis example is very similar to the WebSocket one except the code that\nprepares WebSocket secure context and handshake handler.\n\nUse the following link to open WebSocket secure chat server example: https://localhost:8443/chat/index.html\n\n![wss-chat](https://github.com/chronoxor/CppServer/raw/master/images/wss-chat.png)\n\n```c++\n#include \"server/ws/wss_server.h\"\n\n#include \u003ciostream\u003e\n\nclass ChatSession : public CppServer::WS::WSSSession\n{\npublic:\n    using CppServer::WS::WSSSession::WSSSession;\n\nprotected:\n    void onWSConnected(const CppServer::HTTP::HTTPRequest\u0026 request) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket secure session with Id \" \u003c\u003c id() \u003c\u003c \" connected!\" \u003c\u003c std::endl;\n\n        // Send invite message\n        std::string message(\"Hello from WebSocket secure chat! Please send a message or '!' to disconnect the client!\");\n        SendTextAsync(message);\n    }\n\n    void onWSDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket secure session with Id \" \u003c\u003c id() \u003c\u003c \" disconnected!\" \u003c\u003c std::endl;\n    }\n\n    void onWSReceived(const void* buffer, size_t size) override\n    {\n        std::string message((const char*)buffer, size);\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c message \u003c\u003c std::endl;\n\n        // Multicast message to all connected sessions\n        std::dynamic_pointer_cast\u003cCppServer::WS::WSSServer\u003e(server())-\u003eMulticastText(message);\n\n        // If the buffer starts with '!' the disconnect the current session\n        if (message == \"!\")\n            Close(1000);\n    }\n\n    void onWSPing(const void* buffer, size_t size) override\n    {\n        SendPongAsync(buffer, size);\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket secure session caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nclass ChatServer : public CppServer::WS::WSSServer\n{\npublic:\n    using CppServer::WS::WSSServer::WSSServer;\n\nprotected:\n    std::shared_ptr\u003cCppServer::Asio::SSLSession\u003e CreateSession(std::shared_ptr\u003cCppServer::Asio::SSLServer\u003e server) override\n    {\n        return std::make_shared\u003cChatSession\u003e(std::dynamic_pointer_cast\u003cCppServer::WS::WSSServer\u003e(server));\n    }\n\nprotected:\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket secure server caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n};\n\nint main(int argc, char** argv)\n{\n    // WebSocket secure server port\n    int port = 8443;\n    if (argc \u003e 1)\n        port = std::atoi(argv[1]);\n    // WebSocket secure server content path\n    std::string www = \"../www/wss\";\n    if (argc \u003e 2)\n        www = argv[2];\n\n    std::cout \u003c\u003c \"WebSocket secure server port: \" \u003c\u003c port \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"WebSocket secure server static content path: \" \u003c\u003c www \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"WebSocket server website: \" \u003c\u003c \"https://localhost:\" \u003c\u003c port \u003c\u003c \"/chat/index.html\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create and prepare a new SSL server context\n    auto context = std::make_shared\u003cCppServer::Asio::SSLContext\u003e(asio::ssl::context::tlsv12);\n    context-\u003eset_password_callback([](size_t max_length, asio::ssl::context::password_purpose purpose) -\u003e std::string { return \"qwerty\"; });\n    context-\u003euse_certificate_chain_file(\"../tools/certificates/server.pem\");\n    context-\u003euse_private_key_file(\"../tools/certificates/server.pem\", asio::ssl::context::pem);\n    context-\u003euse_tmp_dh_file(\"../tools/certificates/dh4096.pem\");\n\n    // Create a new WebSocket secure chat server\n    auto server = std::make_shared\u003cChatServer\u003e(service, context, port);\n    server-\u003eAddStaticContent(www, \"/chat\");\n\n    // Start the server\n    std::cout \u003c\u003c \"Server starting...\";\n    server-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the server or '!' to restart the server...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Restart the server\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Server restarting...\";\n            server-\u003eRestart();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Multicast admin message to all sessions\n        line = \"(admin) \" + line;\n        server-\u003eMulticastText(line);\n    }\n\n    // Stop the server\n    std::cout \u003c\u003c \"Server stopping...\";\n    server-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n## Example: WebSocket secure chat client\nHere comes the example of the WebSocket secure chat client. It connects to\nthe WebSocket secure chat server and allows to send message to it and receive\nnew messages.\n\nThis example is very similar to the WebSocket one except the code that\nprepares WebSocket secure context and handshake handler.\n\n```c++\n#include \"server/ws/wss_client.h\"\n#include \"threads/thread.h\"\n\n#include \u003catomic\u003e\n#include \u003ciostream\u003e\n\nclass ChatClient : public CppServer::WS::WSSClient\n{\npublic:\n    using CppServer::WS::WSSClient::WSSClient;\n\n    void DisconnectAndStop()\n    {\n        _stop = true;\n        CloseAsync(1000);\n        while (IsConnected())\n            CppCommon::Thread::Yield();\n    }\n\nprotected:\n    void onWSConnecting(CppServer::HTTP::HTTPRequest\u0026 request) override\n    {\n        request.SetBegin(\"GET\", \"/\");\n        request.SetHeader(\"Host\", \"localhost\");\n        request.SetHeader(\"Origin\", \"https://localhost\");\n        request.SetHeader(\"Upgrade\", \"websocket\");\n        request.SetHeader(\"Connection\", \"Upgrade\");\n        request.SetHeader(\"Sec-WebSocket-Key\", CppCommon::Encoding::Base64Encode(ws_nonce()));\n        request.SetHeader(\"Sec-WebSocket-Protocol\", \"chat, superchat\");\n        request.SetHeader(\"Sec-WebSocket-Version\", \"13\");\n    }\n\n    void onWSConnected(const CppServer::HTTP::HTTPResponse\u0026 response) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket secure client connected a new session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n    }\n\n    void onWSDisconnected() override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket secure client disconnected a session with Id \" \u003c\u003c id() \u003c\u003c std::endl;\n    }\n\n    void onWSReceived(const void* buffer, size_t size) override\n    {\n        std::cout \u003c\u003c \"Incoming: \" \u003c\u003c std::string((const char*)buffer, size) \u003c\u003c std::endl;\n    }\n\n    void onWSPing(const void* buffer, size_t size) override\n    {\n        SendPongAsync(buffer, size);\n    }\n\n    void onDisconnected() override\n    {\n        WSSClient::onDisconnected();\n\n        // Wait for a while...\n        CppCommon::Thread::Sleep(1000);\n\n        // Try to connect again\n        if (!_stop)\n            ConnectAsync();\n    }\n\n    void onError(int error, const std::string\u0026 category, const std::string\u0026 message) override\n    {\n        std::cout \u003c\u003c \"Chat WebSocket secure client caught an error with code \" \u003c\u003c error \u003c\u003c \" and category '\" \u003c\u003c category \u003c\u003c \"': \" \u003c\u003c message \u003c\u003c std::endl;\n    }\n\nprivate:\n    std::atomic\u003cbool\u003e _stop{false};\n};\n\nint main(int argc, char** argv)\n{\n    // WebSocket server address\n    std::string address = \"127.0.0.1\";\n    if (argc \u003e 1)\n        address = argv[1];\n\n    // WebSocket server port\n    int port = 8443;\n    if (argc \u003e 2)\n        port = std::atoi(argv[2]);\n\n    std::cout \u003c\u003c \"WebSocket secure server address: \" \u003c\u003c address \u003c\u003c std::endl;\n    std::cout \u003c\u003c \"WebSocket secure server port: \" \u003c\u003c port \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c std::endl;\n\n    // Create a new Asio service\n    auto service = std::make_shared\u003cCppServer::Asio::Service\u003e();\n\n    // Start the Asio service\n    std::cout \u003c\u003c \"Asio service starting...\";\n    service-\u003eStart();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Create and prepare a new SSL client context\n    auto context = std::make_shared\u003cCppServer::Asio::SSLContext\u003e(asio::ssl::context::tlsv12);\n    context-\u003eset_default_verify_paths();\n    context-\u003eset_root_certs();\n    context-\u003eset_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);\n    context-\u003eload_verify_file(\"../tools/certificates/ca.pem\");\n\n    // Create a new WebSocket chat client\n    auto client = std::make_shared\u003cChatClient\u003e(service, context, address, port);\n\n    // Connect the client\n    std::cout \u003c\u003c \"Client connecting...\";\n    client-\u003eConnectAsync();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    std::cout \u003c\u003c \"Press Enter to stop the client or '!' to reconnect the client...\" \u003c\u003c std::endl;\n\n    // Perform text input\n    std::string line;\n    while (getline(std::cin, line))\n    {\n        if (line.empty())\n            break;\n\n        // Reconnect the client\n        if (line == \"!\")\n        {\n            std::cout \u003c\u003c \"Client reconnecting...\";\n            client-\u003eReconnectAsync();\n            std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n            continue;\n        }\n\n        // Send the entered text to the chat server\n        client-\u003eSendTextAsync(line);\n    }\n\n    // Disconnect the client\n    std::cout \u003c\u003c \"Client disconnecting...\";\n    client-\u003eDisconnectAndStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    // Stop the Asio service\n    std::cout \u003c\u003c \"Asio service stopping...\";\n    service-\u003eStop();\n    std::cout \u003c\u003c \"Done!\" \u003c\u003c std::endl;\n\n    return 0;\n}\n```\n\n# Performance\n\nHere comes several communication scenarios with timing measurements.\n\nBenchmark environment is the following:\n```\nCPU architecutre: Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz\nCPU logical cores: 8\nCPU physical cores: 4\nCPU clock speed: 3.998 GHz\nCPU Hyper-Threading: enabled\nRAM total: 31.962 GiB\nRAM free: 21.623 GiB\n\nOS version: Microsoft Windows 8 Enterprise Edition (build 9200), 64-bit\nOS bits: 64-bit\nProcess bits: 64-bit\nProcess configuaraion: release\n```\n\n## Benchmark: Round-Trip\n\n![Round-trip](https://github.com/chronoxor/CppServer/raw/master/images/round-trip.png)\n\nThis scenario sends lots of messages from several clients to a server.\nThe server responses to each message and resend the similar response to\nthe client. The benchmark measures total round-trip time to send all\nmessages and receive all responses, messages \u0026 data throughput, count\nof errors.\n\n### TCP echo server\n\n* [cppserver-performance-tcp_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_echo_server.cpp)\n* [cppserver-performance-tcp_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_echo_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.001 s\nTotal data: 1.692 GiB\nTotal messages: 56261685\nData throughput: 171.693 MiB/s\nMessage latency: 177 ns\nMessage throughput: 5625528 msg/s\n```\n\n* [cppserver-performance-tcp_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_echo_server.cpp)\n* [cppserver-performance-tcp_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_echo_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.007 s\nTotal data: 1.151 GiB\nTotal messages: 38503396\nData throughput: 117.423 MiB/s\nMessage latency: 259 ns\nMessage throughput: 3847402 msg/s\n```\n\n### SSL echo server\n\n* [cppserver-performance-ssl_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_echo_server.cpp)\n* [cppserver-performance-ssl_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_echo_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.012 s\nTotal data: 296.350 MiB\nTotal messages: 9710535\nData throughput: 29.612 MiB/s\nMessage latency: 1.031 mcs\nMessage throughput: 969878 msg/s\n```\n\n* [cppserver-performance-ssl_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_echo_server.cpp)\n* [cppserver-performance-ssl_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_echo_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.341 s\nTotal data: 390.660 MiB\nTotal messages: 12800660\nData throughput: 37.792 MiB/s\nMessage latency: 807 ns\nMessage throughput: 1237782 msg/s\n```\n\n### UDP echo server\n\n* [cppserver-performance-udp_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/udp_echo_server.cpp)\n* [cppserver-performance-udp_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/udp_echo_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 3333\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.002 s\nTotal data: 46.032 MiB\nTotal messages: 1508355\nData throughput: 4.616 MiB/s\nMessage latency: 6.631 mcs\nMessage throughput: 150801 msg/s\n```\n\n* [cppserver-performance-udp_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/udp_echo_server.cpp)\n* [cppserver-performance-udp_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/udp_echo_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 3333\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.152 s\nTotal data: 32.185 MiB\nTotal messages: 1054512\nData throughput: 3.173 MiB/s\nMessage latency: 9.627 mcs\nMessage throughput: 103867 msg/s\n```\n\n### Simple protocol server\n\n* [cppserver-performance-proto_server](https://github.com/chronoxor/CppServer/blob/master/performance/proto_server.cpp)\n* [cppserver-performance-proto_client](https://github.com/chronoxor/CppServer/blob/master/performance/proto_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 4444\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.002 s\nTotal data: 497.096 MiB\nTotal messages: 16288783\nData throughput: 49.715 MiB/s\nMessage latency: 614 ns\nMessage throughput: 1628542 msg/s\n```\n\n* [cppserver-performance-proto_server](https://github.com/chronoxor/CppServer/blob/master/performance/proto_server.cpp)\n* [cppserver-performance-proto_client](https://github.com/chronoxor/CppServer/blob/master/performance/proto_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 4444\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.066 s\nTotal data: 997.384 MiB\nTotal messages: 32681995\nData throughput: 99.078 MiB/s\nMessage latency: 308 ns\nMessage throughput: 3246558 msg/s\n```\n\n### WebSocket echo server\n\n* [cppserver-performance-ws_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/ws_echo_server.cpp)\n* [cppserver-performance-ws_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/ws_echo_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 8080\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 9.994 s\nTotal data: 48.958 MiB\nTotal messages: 1603548\nData throughput: 4.918 MiB/s\nMessage latency: 6.232 mcs\nMessage throughput: 160448 msg/s\n```\n\n* [cppserver-performance-ws_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/ws_echo_server.cpp)\n* [cppserver-performance-ws_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/ws_echo_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 8080\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 11.402 s\nTotal data: 206.827 MiB\nTotal messages: 6776702\nData throughput: 18.140 MiB/s\nMessage latency: 1.682 mcs\nMessage throughput: 594328 msg/s\n```\n\n### WebSocket secure echo server\n\n* [cppserver-performance-wss_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/wss_echo_server.cpp)\n* [cppserver-performance-wss_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/wss_echo_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 8443\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.001 s\nTotal data: 62.068 MiB\nTotal messages: 2033811\nData throughput: 6.210 MiB/s\nMessage latency: 4.917 mcs\nMessage throughput: 203343 msg/s\n```\n\n* [cppserver-performance-wss_echo_server](https://github.com/chronoxor/CppServer/blob/master/performance/wss_echo_server.cpp)\n* [cppserver-performance-wss_echo_client](https://github.com/chronoxor/CppServer/blob/master/performance/wss_echo_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 8443\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1000\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.011 s\nTotal data: 249.1023 MiB\nTotal messages: 8191971\nData throughput: 24.993 MiB/s\nMessage latency: 1.222 mcs\nMessage throughput: 818230 msg/s\n```\n\n## Benchmark: Multicast\n\n![Multicast](https://github.com/chronoxor/CppServer/raw/master/images/multicast.png)\n\nIn this scenario server multicasts messages to all connected clients.\nThe benchmark counts total messages received by all clients for all\nthe working time and measures messages \u0026 data throughput, count\nof errors.\n\n### TCP multicast server\n\n* [cppserver-performance-tcp_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_multicast_server.cpp)\n* [cppserver-performance-tcp_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_multicast_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.001 s\nTotal data: 1.907 GiB\nTotal messages: 63283367\nData throughput: 193.103 MiB/s\nMessage latency: 158 ns\nMessage throughput: 6327549 msg/s\n```\n\n* [cppserver-performance-tcp_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_multicast_server.cpp)\n* [cppserver-performance-tcp_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/tcp_multicast_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 1111\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.006 s\nTotal data: 1.1006 GiB\nTotal messages: 66535013\nData throughput: 202.930 MiB/s\nMessage latency: 150 ns\nMessage throughput: 6648899 msg/s\n```\n\n### SSL multicast server\n\n* [cppserver-performance-ssl_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_multicast_server.cpp)\n* [cppserver-performance-ssl_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_multicast_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.014 s\nTotal data: 1.535 GiB\nTotal messages: 51100073\nData throughput: 155.738 MiB/s\nMessage latency: 195 ns\nMessage throughput: 5102683 msg/s\n```\n\n* [cppserver-performance-ssl_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_multicast_server.cpp)\n* [cppserver-performance-ssl_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/ssl_multicast_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 2222\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.691 s\nTotal data: 1.878 GiB\nTotal messages: 62334478\nData throughput: 177.954 MiB/s\nMessage latency: 171 ns\nMessage throughput: 5830473 msg/s\n```\n\n### UDP multicast server\n\n* [cppserver-performance-udp_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/udp_multicast_server.cpp)\n* [cppserver-performance-udp_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/udp_multicast_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 239.255.0.1\nServer port: 3333\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.002 s\nTotal data: 23.777 MiB\nTotal messages: 778555\nData throughput: 2.384 MiB/s\nMessage latency: 12.847 mcs\nMessage throughput: 77833 msg/s\n```\n\n* [cppserver-performance-udp_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/udp_multicast_server.cpp)\n* [cppserver-performance-udp_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/udp_multicast_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 239.255.0.1\nServer port: 3333\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.004 s\nTotal data: 52.457 MiB\nTotal messages: 1718575\nData throughput: 5.248 MiB/s\nMessage latency: 5.821 mcs\nMessage throughput: 171784 msg/s\n```\n\n### WebSocket multicast server\n\n* [cppserver-performance-ws_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/ws_multicast_server.cpp)\n* [cppserver-performance-ws_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/ws_multicast_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 8080\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.001 s\nTotal data: 960.902 MiB\nTotal messages: 31486166\nData throughput: 96.075 MiB/s\nMessage latency: 317 ns\nMessage throughput: 3148135 msg/s\n```\n\n* [cppserver-performance-ws_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/ws_multicast_server.cpp)\n* [cppserver-performance-ws_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/ws_multicast_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 8080\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.020 s\nTotal data: 986.489 MiB\nTotal messages: 32324898\nData throughput: 98.459 MiB/s\nMessage latency: 309 ns\nMessage throughput: 3225965 msg/s\n```\n\n### WebSocket secure multicast server\n\n* [cppserver-performance-wss_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/wss_multicast_server.cpp)\n* [cppserver-performance-wss_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/wss_multicast_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 8443\nWorking threads: 1\nWorking clients: 1\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.002 s\nTotal data: 1.041 GiB\nTotal messages: 34903186\nData throughput: 106.505 MiB/s\nMessage latency: 286 ns\nMessage throughput: 3489578 msg/s\n```\n\n* [cppserver-performance-wss_multicast_server](https://github.com/chronoxor/CppServer/blob/master/performance/wss_multicast_server.cpp)\n* [cppserver-performance-wss_multicast_client](https://github.com/chronoxor/CppServer/blob/master/performance/wss_multicast_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 8443\nWorking threads: 4\nWorking clients: 100\nMessage size: 32\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.013 s\nTotal data: 1.569 GiB\nTotal messages: 52225588\nData throughput: 159.172 MiB/s\nMessage latency: 191 ns\nMessage throughput: 5215639 msg/s\n```\n\n## Benchmark: Web Server\n\n### HTTP Trace server\n\n* [cppserver-performance-http_trace_server](https://github.com/chronoxor/CppServer/blob/master/performance/http_trace_server.cpp)\n* [cppserver-performance-http_trace_client](https://github.com/chronoxor/CppServer/blob/master/performance/http_trace_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 80\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.001 s\nTotal data: 58.476 MiB\nTotal messages: 578353\nData throughput: 5.865 MiB/s\nMessage latency: 17.293 mcs\nMessage throughput: 57825 msg/s\n```\n\n* [cppserver-performance-http_trace_server](https://github.com/chronoxor/CppServer/blob/master/performance/http_trace_server.cpp)\n* [cppserver-performance-http_trace_client](https://github.com/chronoxor/CppServer/blob/master/performance/http_trace_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 80\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.006 s\nTotal data: 310.730 MiB\nTotal messages: 3073650\nData throughput: 31.051 MiB/s\nMessage latency: 3.255 mcs\nMessage throughput: 307154 msg/s\n```\n\n### HTTPS Trace server\n\n* [cppserver-performance-https_trace_server](https://github.com/chronoxor/CppServer/blob/master/performance/https_trace_server.cpp)\n* [cppserver-performance-https_trace_client](https://github.com/chronoxor/CppServer/blob/master/performance/https_trace_client.cpp) --clients 1 --threads 1\n\n```\nServer address: 127.0.0.1\nServer port: 443\nWorking threads: 1\nWorking clients: 1\nWorking messages: 1\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.003 s\nTotal data: 37.475 MiB\nTotal messages: 370602\nData throughput: 3.763 MiB/s\nMessage latency: 26.992 mcs\nMessage throughput: 37047 msg/s\n```\n\n* [cppserver-performance-https_trace_server](https://github.com/chronoxor/CppServer/blob/master/performance/https_trace_server.cpp)\n* [cppserver-performance-https_trace_client](https://github.com/chronoxor/CppServer/blob/master/performance/https_trace_client.cpp) --clients 100 --threads 4\n\n```\nServer address: 127.0.0.1\nServer port: 443\nWorking threads: 4\nWorking clients: 100\nWorking messages: 1\nSeconds to benchmarking: 10\n\nErrors: 0\n\nTotal time: 10.035 s\nTotal data: 204.531 MiB\nTotal messages: 2023152\nData throughput: 20.389 MiB/s\nMessage latency: 4.960 mcs\nMessage throughput: 201602 msg/s\n```\n\n# OpenSSL certificates\nIn order to create OpenSSL based server and client you should prepare a set of\nSSL certificates.\n\n## Production\nDepending on your project, you may need to purchase a traditional SSL\ncertificate signed by a Certificate Authority. If you, for instance,\nwant some else's web browser to talk to your WebSocket project, you'll\nneed a traditional SSL certificate.\n\n## Development\nThe commands below entered in the order they are listed will generate a\nself-signed certificate for development or testing purposes.\n\n## Certificate Authority\n\n* Create CA private key\n```shell\nopenssl genrsa -passout pass:qwerty -out ca-secret.key 4096\n```\n\n* Remove passphrase\n```shell\nopenssl rsa -passin pass:qwerty -in ca-secret.key -out ca.key\n```\n\n* Create CA self-signed certificate\n```shell\nopenssl req -new -x509 -days 3650 -subj '/C=BY/ST=Belarus/L=Minsk/O=Example root CA/OU=Example CA unit/CN=example.com' -key ca.key -out ca.crt\n```\n\n* Convert CA self-signed certificate to PFX\n```shell\nopenssl pkcs12 -export -passout pass:qwerty -inkey ca.key -in ca.crt -out ca.pfx\n```\n\n* Convert CA self-signed certificate to PEM\n```shell\nopenssl pkcs12 -passin pass:qwerty -passout pass:qwerty -in ca.pfx -out ca.pem\n```\n\n## SSL Server certificate\n\n* Create private key for the server\n```shell\nopenssl genrsa -passout pass:qwerty -out server-secret.key 4096\n```\n\n* Remove passphrase\n```shell\nopenssl rsa -passin pass:qwerty -in server-secret.key -out server.key\n```\n\n* Create CSR for the server\n```shell\nopenssl req -new -subj '/C=BY/ST=Belarus/L=Minsk/O=Example server/OU=Example server unit/CN=server.example.com' -key server.key -out server.csr\n```\n\n* Create certificate for the server\n```shell\nopenssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt\n```\n\n* Convert the server certificate to PFX\n```shell\nopenssl pkcs12 -export -passout pass:qwerty -inkey server.key -in server.crt -out server.pfx\n```\n\n* Convert the server certificate to PEM\n```shell\nopenssl pkcs12 -passin pass:qwerty -passout pass:qwerty -in server.pfx -out server.pem\n```\n\n## SSL Client certificate\n\n* Create private key for the client\n```shell\nopenssl genrsa -passout pass:qwerty -out client-secret.key 4096\n```\n\n* Remove passphrase\n```shell\nopenssl rsa -passin pass:qwerty -in client-secret.key -out client.key\n```\n\n* Create CSR for the client\n```shell\nopenssl req -new -subj '/C=BY/ST=Belarus/L=Minsk/O=Example client/OU=Example client unit/CN=client.example.com' -key client.key -out client.csr\n```\n\n* Create the client certificate\n```shell\nopenssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt\n```\n\n* Convert the client certificate to PFX\n```shell\nopenssl pkcs12 -export -passout pass:qwerty -inkey client.key -in client.crt -out client.pfx\n```\n\n* Convert the client certificate to PEM\n```shell\nopenssl pkcs12 -passin pass:qwerty -passout pass:qwerty -in client.pfx -out client.pem\n```\n\n## Diffie-Hellman key exchange\n\n* Create DH parameters\n```shell\nopenssl dhparam -out dh4096.pem 4096\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchronoxor%2Fcppserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchronoxor%2Fcppserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchronoxor%2Fcppserver/lists"}