# CppServer
**Repository Path**: codergeek/CppServer
## Basic Information
- **Project Name**: CppServer
- **Description**: No description available
- **Primary Language**: C++
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-07-21
- **Last Updated**: 2024-07-21
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# CppServer
[](https://github.com/fffaraz/awesome-cpp)
[](LICENSE)
[](https://github.com/chronoxor/CppServer/releases)
[](https://github.com/chronoxor/CppServer/actions/workflows/build-linux-clang.yml)
[](https://github.com/chronoxor/CppServer/actions/workflows/build-linux-gcc.yml)
[](https://github.com/chronoxor/CppServer/actions/workflows/build-macos.yml)
[](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-msys2.yml)
[](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-mingw.yml)
[](https://github.com/chronoxor/CppServer/actions/workflows/build-windows-vs.yml)
Ultra fast and low latency asynchronous socket server & client C++ library with
support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and [10K connections problem](https://en.wikipedia.org/wiki/C10k_problem)
solution.
Has integration with high-level message protocol based on [Fast Binary Encoding](https://github.com/chronoxor/FastBinaryEncoding)
[CppServer API reference](https://chronoxor.github.io/CppServer/index.html)
# Contents
* [Features](#features)
* [Requirements](#requirements)
* [How to build?](#how-to-build)
* [Examples](#examples)
* [Example: Asio service](#example-asio-service)
* [Example: Asio timer](#example-asio-timer)
* [Example: TCP chat server](#example-tcp-chat-server)
* [Example: TCP chat client](#example-tcp-chat-client)
* [Example: SSL chat server](#example-ssl-chat-server)
* [Example: SSL chat client](#example-ssl-chat-client)
* [Example: UDP echo server](#example-udp-echo-server)
* [Example: UDP echo client](#example-udp-echo-client)
* [Example: UDP multicast server](#example-udp-multicast-server)
* [Example: UDP multicast client](#example-udp-multicast-client)
* [Example: Simple protocol](#example-simple-protocol)
* [Example: Simple protocol server](#example-simple-protocol-server)
* [Example: Simple protocol client](#example-simple-protocol-client)
* [Example: HTTP server](#example-http-server)
* [Example: HTTP client](#example-http-client)
* [Example: HTTPS server](#example-https-server)
* [Example: HTTPS client](#example-https-client)
* [Example: WebSocket chat server](#example-websocket-chat-server)
* [Example: WebSocket chat client](#example-websocket-chat-client)
* [Example: WebSocket secure chat server](#example-websocket-secure-chat-server)
* [Example: WebSocket secure chat client](#example-websocket-secure-chat-client)
* [Performance](#performance)
* [Benchmark: Round-Trip](#benchmark-round-trip)
* [TCP echo server](#tcp-echo-server)
* [SSL echo server](#ssl-echo-server)
* [UDP echo server](#udp-echo-server)
* [Simple protocol server](#simple-protocol-server)
* [WebSocket echo server](#websocket-echo-server)
* [WebSocket secure echo server](#websocket-secure-echo-server)
* [Benchmark: Multicast](#benchmark-multicast)
* [TCP multicast server](#tcp-multicast-server)
* [SSL multicast server](#ssl-multicast-server)
* [UDP multicast server](#udp-multicast-server)
* [WebSocket multicast server](#websocket-multicast-server)
* [WebSocket secure multicast server](#websocket-secure-multicast-server)
* [Benchmark: Web Server](#benchmark-web-server)
* [HTTP Trace server](#http-trace-server)
* [HTTPS Trace server](#https-trace-server)
* [OpenSSL certificates](#openssl-certificates)
* [Production](#production)
* [Development](#development)
* [Certificate Authority](#certificate-authority)
* [SSL Server certificate](#ssl-server-certificate)
* [SSL Client certificate](#ssl-client-certificate)
* [Diffie-Hellman key exchange](#diffie-hellman-key-exchange)
# Features
* Cross platform (Linux, MacOS, Windows)
* [Asynchronous communication](https://think-async.com)
* Supported CPU scalability designs: IO service per thread, thread pool
* Supported transport protocols: [TCP](#example-tcp-chat-server), [SSL](#example-ssl-chat-server),
[UDP](#example-udp-echo-server), [UDP multicast](#example-udp-multicast-server)
* Supported Web protocols: [HTTP](#example-http-server), [HTTPS](#example-https-server),
[WebSocket](#example-websocket-chat-server), [WebSocket secure](#example-websocket-secure-chat-server)
* Supported [Swagger OpenAPI](https://swagger.io/specification/) iterative documentation
* Supported message protocol based on [Fast Binary Encoding](https://github.com/chronoxor/FastBinaryEncoding)
# Requirements
* Linux
* MacOS
* Windows
* [cmake](https://www.cmake.org)
* [gcc](https://gcc.gnu.org)
* [git](https://git-scm.com)
* [gil](https://github.com/chronoxor/gil.git)
* [python3](https://www.python.org)
Optional:
* [clang](https://clang.llvm.org)
* [CLion](https://www.jetbrains.com/clion)
* [MSYS2](https://www.msys2.org)
* [MinGW](https://mingw-w64.org/doku.php)
* [Visual Studio](https://www.visualstudio.com)
# How to build?
### Linux: install required packages
```shell
sudo apt-get install -y binutils-dev uuid-dev libssl-dev
```
### Install [gil (git links) tool](https://github.com/chronoxor/gil)
```shell
pip3 install gil
```
### Setup repository
```shell
git clone https://github.com/chronoxor/CppServer.git
cd CppServer
gil update
```
### Linux
```shell
cd build
./unix.sh
```
### MacOS
```shell
cd build
./unix.sh
```
### Windows (MSYS2)
```shell
cd build
unix.bat
```
### Windows (MinGW)
```shell
cd build
mingw.bat
```
### Windows (Visual Studio)
```shell
cd build
vs.bat
```
# Examples
## Example: Asio service
Asio service is used to host all clients/servers based on [Asio C++ library](https://think-async.com).
It is implemented based on Asio C++ Library and use a separate thread to
perform all asynchronous IO operations and communications.
The common usecase is to instantiate one Asio service, start the service and
attach TCP/UDP/WebSocket servers or/and clients to it. One Asio service can
handle several servers and clients asynchronously at the same time in one I/O
thread. If you want to scale your servers or clients it is possible to create
and use more than one Asio services to handle your servers/clients in balance.
Also it is possible to dispatch or post your custom handler into I/O thread.
Dispatch will execute the handler immediately if the current thread is I/O one.
Otherwise the handler will be enqueued to the I/O queue. In opposite the post
method will always enqueue the handler into the I/O queue.
Here comes an example of using custom Asio service with dispatch/post methods:
```c++
#include "server/asio/service.h"
#include "threads/thread.h"
#include
int main(int argc, char** argv)
{
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Dispatch
std::cout << "1 - Dispatch from the main thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
service->Dispatch([service]()
{
std::cout << "1.1 - Dispatched in thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
std::cout << "1.2 - Dispatch from thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
service->Dispatch([service]()
{
std::cout << "1.2.1 - Dispatched in thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
});
std::cout << "1.3 - Post from thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
service->Post([service]()
{
std::cout << "1.3.1 - Posted in thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
});
});
// Post
std::cout << "2 - Post from the main thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
service->Post([service]()
{
std::cout << "2.1 - Posted in thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
std::cout << "2.2 - Dispatch from thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
service->Dispatch([service]()
{
std::cout << "2.2.1 - Dispatched in thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
});
std::cout << "2.3 - Post from thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
service->Post([service]()
{
std::cout << "2.3.1 - Posted in thread with Id " << CppCommon::Thread::CurrentThreadId() << std::endl;
});
});
// Wait for a while...
CppCommon::Thread::Sleep(1000);
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
Output of the above example is the following:
```
Asio service started!
1 - Dispatch from the main thread with Id 16744
2 - Post from the main thread with Id 16744
1.1 - Dispatched in thread with Id 19920
1.2 - Dispatch from thread with Id 19920
1.2.1 - Dispatched in thread with Id 19920
1.3 - Post from thread with Id 19920
2.1 - Posted in thread with Id 19920
2.2 - Dispatch from thread with Id 19920
2.2.1 - Dispatched in thread with Id 19920
2.3 - Post from thread with Id 19920
1.3.1 - Posted in thread with Id 19920
2.3.1 - Posted in thread with Id 19920
Asio service stopped!
```
## Example: Asio timer
Here comes the example of Asio timer. It can be used to wait for some action
in future with providing absolute time or relative time span. Asio timer can
be used in synchronous or asynchronous modes.
```c++
#include "server/asio/timer.h"
#include "threads/thread.h"
#include
class AsioTimer : public CppServer::Asio::Timer
{
public:
using CppServer::Asio::Timer::Timer;
protected:
void onTimer(bool canceled) override
{
std::cout << "Asio timer " << (canceled ? "canceled" : "expired") << std::endl;
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Asio timer caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
int main(int argc, char** argv)
{
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new Asio timer
auto timer = std::make_shared(service);
// Setup and synchronously wait for the timer
timer->Setup(CppCommon::UtcTime() + CppCommon::Timespan::seconds(1));
timer->WaitSync();
// Setup and asynchronously wait for the timer
timer->Setup(CppCommon::Timespan::seconds(1));
timer->WaitAsync();
// Wait for a while...
CppCommon::Thread::Sleep(2000);
// Setup and asynchronously wait for the timer
timer->Setup(CppCommon::Timespan::seconds(1));
timer->WaitAsync();
// Wait for a while...
CppCommon::Thread::Sleep(500);
// Cancel the timer
timer->Cancel();
// Wait for a while...
CppCommon::Thread::Sleep(500);
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
Output of the above example is the following:
```
Asio service starting...Done!
Timer was expired
Timer was canceled
Asio service stopping...Done!
```
## Example: TCP chat server
Here comes the example of the TCP chat server. It handles multiple TCP client
sessions and multicast received message from any session to all ones. Also it
is possible to send admin message directly from the server.
```c++
#include "server/asio/tcp_server.h"
#include "threads/thread.h"
#include
class ChatSession : public CppServer::Asio::TCPSession
{
public:
using CppServer::Asio::TCPSession::TCPSession;
protected:
void onConnected() override
{
std::cout << "Chat TCP session with Id " << id() << " connected!" << std::endl;
// Send invite message
std::string message("Hello from TCP chat! Please send a message or '!' to disconnect the client!");
SendAsync(message);
}
void onDisconnected() override
{
std::cout << "Chat TCP session with Id " << id() << " disconnected!" << std::endl;
}
void onReceived(const void* buffer, size_t size) override
{
std::string message((const char*)buffer, size);
std::cout << "Incoming: " << message << std::endl;
// Multicast message to all connected sessions
server()->Multicast(message);
// If the buffer starts with '!' the disconnect the current session
if (message == "!")
DisconnectAsync();
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Chat TCP session caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
class ChatServer : public CppServer::Asio::TCPServer
{
public:
using CppServer::Asio::TCPServer::TCPServer;
protected:
std::shared_ptr CreateSession(std::shared_ptr server) override
{
return std::make_shared(server);
}
protected:
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Chat TCP server caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
int main(int argc, char** argv)
{
// TCP server port
int port = 1111;
if (argc > 1)
port = std::atoi(argv[1]);
std::cout << "TCP server port: " << port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new TCP chat server
auto server = std::make_shared(service, port);
// Start the server
std::cout << "Server starting...";
server->Start();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the server or '!' to restart the server..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Restart the server
if (line == "!")
{
std::cout << "Server restarting...";
server->Restart();
std::cout << "Done!" << std::endl;
continue;
}
// Multicast admin message to all sessions
line = "(admin) " + line;
server->Multicast(line);
}
// Stop the server
std::cout << "Server stopping...";
server->Stop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: TCP chat client
Here comes the example of the TCP chat client. It connects to the TCP chat
server and allows to send message to it and receive new messages.
```c++
#include "server/asio/tcp_client.h"
#include "threads/thread.h"
#include
#include
class ChatClient : public CppServer::Asio::TCPClient
{
public:
ChatClient(std::shared_ptr service, const std::string& address, int port)
: CppServer::Asio::TCPClient(service, address, port)
{
_stop = false;
}
void DisconnectAndStop()
{
_stop = true;
DisconnectAsync();
while (IsConnected())
CppCommon::Thread::Yield();
}
protected:
void onConnected() override
{
std::cout << "Chat TCP client connected a new session with Id " << id() << std::endl;
}
void onDisconnected() override
{
std::cout << "Chat TCP client disconnected a session with Id " << id() << std::endl;
// Wait for a while...
CppCommon::Thread::Sleep(1000);
// Try to connect again
if (!_stop)
ConnectAsync();
}
void onReceived(const void* buffer, size_t size) override
{
std::cout << "Incoming: " << std::string((const char*)buffer, size) << std::endl;
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Chat TCP client caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
private:
std::atomic _stop;
};
int main(int argc, char** argv)
{
// TCP server address
std::string address = "127.0.0.1";
if (argc > 1)
address = argv[1];
// TCP server port
int port = 1111;
if (argc > 2)
port = std::atoi(argv[2]);
std::cout << "TCP server address: " << address << std::endl;
std::cout << "TCP server port: " << port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new TCP chat client
auto client = std::make_shared(service, address, port);
// Connect the client
std::cout << "Client connecting...";
client->ConnectAsync();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the client or '!' to reconnect the client..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Disconnect the client
if (line == "!")
{
std::cout << "Client disconnecting...";
client->DisconnectAsync();
std::cout << "Done!" << std::endl;
continue;
}
// Send the entered text to the chat server
client->SendAsync(line);
}
// Disconnect the client
std::cout << "Client disconnecting...";
client->DisconnectAndStop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: SSL chat server
Here comes the example of the SSL chat server. It handles multiple SSL client
sessions and multicast received message from any session to all ones. Also it
is possible to send admin message directly from the server.
This example is very similar to the TCP one except the code that prepares SSL
context and handshake handler.
```c++
#include "server/asio/ssl_server.h"
#include "threads/thread.h"
#include
class ChatSession : public CppServer::Asio::SSLSession
{
public:
using CppServer::Asio::SSLSession::SSLSession;
protected:
void onConnected() override
{
std::cout << "Chat SSL session with Id " << id() << " connected!" << std::endl;
}
void onHandshaked() override
{
std::cout << "Chat SSL session with Id " << id() << " handshaked!" << std::endl;
// Send invite message
std::string message("Hello from SSL chat! Please send a message or '!' to disconnect the client!");
SendAsync(message.data(), message.size());
}
void onDisconnected() override
{
std::cout << "Chat SSL session with Id " << id() << " disconnected!" << std::endl;
}
void onReceived(const void* buffer, size_t size) override
{
std::string message((const char*)buffer, size);
std::cout << "Incoming: " << message << std::endl;
// Multicast message to all connected sessions
server()->Multicast(message);
// If the buffer starts with '!' the disconnect the current session
if (message == "!")
DisconnectAsync();
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Chat SSL session caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
class ChatServer : public CppServer::Asio::SSLServer
{
public:
using CppServer::Asio::SSLServer::SSLServer;
protected:
std::shared_ptr CreateSession(std::shared_ptr server) override
{
return std::make_shared(server);
}
protected:
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Chat TCP server caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
int main(int argc, char** argv)
{
// SSL server port
int port = 2222;
if (argc > 1)
port = std::atoi(argv[1]);
std::cout << "SSL server port: " << port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create and prepare a new SSL server context
auto context = std::make_shared(asio::ssl::context::tlsv12);
context->set_password_callback([](size_t max_length, asio::ssl::context::password_purpose purpose) -> std::string { return "qwerty"; });
context->use_certificate_chain_file("../tools/certificates/server.pem");
context->use_private_key_file("../tools/certificates/server.pem", asio::ssl::context::pem);
context->use_tmp_dh_file("../tools/certificates/dh4096.pem");
// Create a new SSL chat server
auto server = std::make_shared(service, context, port);
// Start the server
std::cout << "Server starting...";
server->Start();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the server or '!' to restart the server..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Restart the server
if (line == "!")
{
std::cout << "Server restarting...";
server->Restart();
std::cout << "Done!" << std::endl;
continue;
}
// Multicast admin message to all sessions
line = "(admin) " + line;
server->Multicast(line);
}
// Stop the server
std::cout << "Server stopping...";
server->Stop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: SSL chat client
Here comes the example of the SSL chat client. It connects to the SSL chat
server and allows to send message to it and receive new messages.
This example is very similar to the TCP one except the code that prepares SSL
context and handshake handler.
```c++
#include "server/asio/ssl_client.h"
#include "threads/thread.h"
#include
#include
class ChatClient : public CppServer::Asio::SSLClient
{
public:
ChatClient(std::shared_ptr service, std::shared_ptr context, const std::string& address, int port)
: CppServer::Asio::SSLClient(service, context, address, port)
{
_stop = false;
}
void DisconnectAndStop()
{
_stop = true;
DisconnectAsync();
while (IsConnected())
CppCommon::Thread::Yield();
}
protected:
void onConnected() override
{
std::cout << "Chat SSL client connected a new session with Id " << id() << std::endl;
}
void onHandshaked() override
{
std::cout << "Chat SSL client handshaked a new session with Id " << id() << std::endl;
}
void onDisconnected() override
{
std::cout << "Chat SSL client disconnected a session with Id " << id() << std::endl;
// Wait for a while...
CppCommon::Thread::Sleep(1000);
// Try to connect again
if (!_stop)
ConnectAsync();
}
void onReceived(const void* buffer, size_t size) override
{
std::cout << "Incoming: " << std::string((const char*)buffer, size) << std::endl;
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Chat SSL client caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
private:
std::atomic _stop;
};
int main(int argc, char** argv)
{
// SSL server address
std::string address = "127.0.0.1";
if (argc > 1)
address = argv[1];
// SSL server port
int port = 2222;
if (argc > 2)
port = std::atoi(argv[2]);
std::cout << "SSL server address: " << address << std::endl;
std::cout << "SSL server port: " << port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create and prepare a new SSL client context
auto context = std::make_shared(asio::ssl::context::tlsv12);
context->set_default_verify_paths();
context->set_root_certs();
context->set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert);
context->load_verify_file("../tools/certificates/ca.pem");
// Create a new SSL chat client
auto client = std::make_shared(service, context, address, port);
// Connect the client
std::cout << "Client connecting...";
client->ConnectAsync();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the client or '!' to reconnect the client..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Disconnect the client
if (line == "!")
{
std::cout << "Client disconnecting...";
client->DisconnectAsync();
std::cout << "Done!" << std::endl;
continue;
}
// Send the entered text to the chat server
client->SendAsync(line);
}
// Disconnect the client
std::cout << "Client disconnecting...";
client->DisconnectAndStop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: UDP echo server
Here comes the example of the UDP echo server. It receives a datagram mesage
from any UDP client and resend it back without any changes.
```c++
#include "server/asio/udp_server.h"
#include "threads/thread.h"
#include
class EchoServer : public CppServer::Asio::UDPServer
{
public:
using CppServer::Asio::UDPServer::UDPServer;
protected:
void onStarted() override
{
// Start receive datagrams
ReceiveAsync();
}
void onReceived(const asio::ip::udp::endpoint& endpoint, const void* buffer, size_t size) override
{
std::string message((const char*)buffer, size);
std::cout << "Incoming: " << message << std::endl;
// Echo the message back to the sender
SendAsync(endpoint, message);
}
void onSent(const asio::ip::udp::endpoint& endpoint, size_t sent) override
{
// Continue receive datagrams
ReceiveAsync();
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Echo UDP server caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
int main(int argc, char** argv)
{
// UDP server port
int port = 3333;
if (argc > 1)
port = std::atoi(argv[1]);
std::cout << "UDP server port: " << port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new UDP echo server
auto server = std::make_shared(service, port);
// Start the server
std::cout << "Server starting...";
server->Start();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the server or '!' to restart the server..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Restart the server
if (line == "!")
{
std::cout << "Server restarting...";
server->Restart();
std::cout << "Done!" << std::endl;
continue;
}
}
// Stop the server
std::cout << "Server stopping...";
server->Stop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: UDP echo client
Here comes the example of the UDP echo client. It sends user datagram message
to UDP server and listen for response.
```c++
#include "server/asio/udp_client.h"
#include "threads/thread.h"
#include
#include
class EchoClient : public CppServer::Asio::UDPClient
{
public:
EchoClient(std::shared_ptr service, const std::string& address, int port)
: CppServer::Asio::UDPClient(service, address, port)
{
_stop = false;
}
void DisconnectAndStop()
{
_stop = true;
DisconnectAsync();
while (IsConnected())
CppCommon::Thread::Yield();
}
protected:
void onConnected() override
{
std::cout << "Echo UDP client connected a new session with Id " << id() << std::endl;
// Start receive datagrams
ReceiveAsync();
}
void onDisconnected() override
{
std::cout << "Echo UDP client disconnected a session with Id " << id() << std::endl;
// Wait for a while...
CppCommon::Thread::Sleep(1000);
// Try to connect again
if (!_stop)
ConnectAsync();
}
void onReceived(const asio::ip::udp::endpoint& endpoint, const void* buffer, size_t size) override
{
std::cout << "Incoming: " << std::string((const char*)buffer, size) << std::endl;
// Continue receive datagrams
ReceiveAsync();
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Echo UDP client caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
private:
std::atomic _stop;
};
int main(int argc, char** argv)
{
// UDP server address
std::string address = "127.0.0.1";
if (argc > 1)
address = argv[1];
// UDP server port
int port = 3333;
if (argc > 2)
port = std::atoi(argv[2]);
std::cout << "UDP server address: " << address << std::endl;
std::cout << "UDP server port: " << port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new UDP echo client
auto client = std::make_shared(service, address, port);
// Connect the client
std::cout << "Client connecting...";
client->ConnectAsync();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the client or '!' to reconnect the client..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Disconnect the client
if (line == "!")
{
std::cout << "Client disconnecting...";
client->DisconnectAsync();
std::cout << "Done!" << std::endl;
continue;
}
// Send the entered text to the echo server
client->SendSync(line);
}
// Disconnect the client
std::cout << "Client disconnecting...";
client->DisconnectAndStop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: UDP multicast server
Here comes the example of the UDP multicast server. It use multicast IP address
to multicast datagram messages to all client that joined corresponding UDP
multicast group.
```c++
#include "server/asio/udp_server.h"
#include "threads/thread.h"
#include
class MulticastServer : public CppServer::Asio::UDPServer
{
public:
using CppServer::Asio::UDPServer::UDPServer;
protected:
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Multicast UDP server caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
};
int main(int argc, char** argv)
{
// UDP multicast address
std::string multicast_address = "239.255.0.1";
if (argc > 1)
multicast_address = argv[1];
// UDP multicast port
int multicast_port = 3334;
if (argc > 2)
multicast_port = std::atoi(argv[2]);
std::cout << "UDP multicast address: " << multicast_address << std::endl;
std::cout << "UDP multicast port: " << multicast_port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new UDP multicast server
auto server = std::make_shared(service, 0);
// Start the multicast server
std::cout << "Server starting...";
server->Start(multicast_address, multicast_port);
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the server or '!' to restart the server..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Restart the server
if (line == "!")
{
std::cout << "Server restarting...";
server->Restart();
std::cout << "Done!" << std::endl;
continue;
}
// Multicast admin message to all sessions
line = "(admin) " + line;
server->MulticastSync(line);
}
// Stop the server
std::cout << "Server stopping...";
server->Stop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: UDP multicast client
Here comes the example of the UDP multicast client. It use multicast IP address
and joins UDP multicast group in order to receive multicasted datagram messages
from UDP server.
```c++
#include "server/asio/udp_client.h"
#include "threads/thread.h"
#include
#include
class MulticastClient : public CppServer::Asio::UDPClient
{
public:
MulticastClient(std::shared_ptr service, const std::string& address, const std::string& multicast, int port)
: CppServer::Asio::UDPClient(service, address, port),
_multicast(multicast)
{
_stop = false;
}
void DisconnectAndStop()
{
_stop = true;
DisconnectAsync();
while (IsConnected())
CppCommon::Thread::Yield();
}
protected:
void onConnected() override
{
std::cout << "Multicast UDP client connected a new session with Id " << id() << std::endl;
// Join UDP multicast group
JoinMulticastGroupAsync(_multicast);
// Start receive datagrams
ReceiveAsync();
}
void onDisconnected() override
{
std::cout << "Multicast UDP client disconnected a session with Id " << id() << std::endl;
// Wait for a while...
CppCommon::Thread::Sleep(1000);
// Try to connect again
if (!_stop)
ConnectAsync();
}
void onReceived(const asio::ip::udp::endpoint& endpoint, const void* buffer, size_t size) override
{
std::cout << "Incoming: " << std::string((const char*)buffer, size) << std::endl;
// Continue receive datagrams
ReceiveAsync();
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Multicast UDP client caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
private:
std::atomic _stop;
std::string _multicast;
};
int main(int argc, char** argv)
{
// UDP listen address
std::string listen_address = "0.0.0.0";
if (argc > 1)
listen_address = argv[1];
// UDP multicast address
std::string multicast_address = "239.255.0.1";
if (argc > 2)
multicast_address = argv[2];
// UDP multicast port
int multicast_port = 3334;
if (argc > 3)
multicast_port = std::atoi(argv[3]);
std::cout << "UDP listen address: " << listen_address << std::endl;
std::cout << "UDP multicast address: " << multicast_address << std::endl;
std::cout << "UDP multicast port: " << multicast_port << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new UDP multicast client
auto client = std::make_shared(service, listen_address, multicast_address, multicast_port);
client->SetupMulticast(true);
// Connect the client
std::cout << "Client connecting...";
client->ConnectAsync();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the client or '!' to reconnect the client..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Disconnect the client
if (line == "!")
{
std::cout << "Client disconnecting...";
client->DisconnectAsync();
std::cout << "Done!" << std::endl;
continue;
}
}
// Disconnect the client
std::cout << "Client disconnecting...";
client->DisconnectAndStop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: Simple protocol
Simple protocol is defined in [simple.fbe](https://github.com/chronoxor/CppServer/blob/master/proto/simple.fbe) file:
```proto
/*
Simple Fast Binary Encoding protocol for CppServer
https://github.com/chronoxor/FastBinaryEncoding
Generate protocol command: fbec --cpp --proto --input=simple.fbe --output=.
*/
// Domain declaration
domain com.chronoxor
// Package declaration
package simple
// Protocol version
version 1.0
// Simple request message
[request]
[response(SimpleResponse)]
[reject(SimpleReject)]
message SimpleRequest
{
// Request Id
uuid [id] = uuid1;
// Request message
string Message;
}
// Simple response
message SimpleResponse
{
// Response Id
uuid [id] = uuid1;
// Calculated message hash
uint32 Hash;
}
// Simple reject
message SimpleReject
{
// Reject Id
uuid [id] = uuid1;
// Error message
string Error;
}
// Simple notification
message SimpleNotify
{
// Server notification
string Notification;
}
// Disconnect request message
[request]
message DisconnectRequest
{
// Request Id
uuid [id] = uuid1;
}
```
## Example: Simple protocol server
Here comes the example of the simple protocol server. It process client
requests, answer with corresponding responses and send server notifications
back to clients.
```c++
#include "asio_service.h"
#include "server/asio/tcp_server.h"
#include "../proto/simple_protocol.h"
#include
class SimpleProtoSession : public CppServer::Asio::TCPSession, public FBE::simple::Sender, public FBE::simple::Receiver
{
public:
using CppServer::Asio::TCPSession::TCPSession;
protected:
void onConnected() override
{
std::cout << "Simple protocol session with Id " << id() << " connected!" << std::endl;
// Send invite notification
simple::SimpleNotify notify;
notify.Notification = "Hello from Simple protocol server! Please send a message or '!' to disconnect the client!";
send(notify);
}
void onDisconnected() override
{
std::cout << "Simple protocol session with Id " << id() << " disconnected!" << std::endl;
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Simple protocol session caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
// Protocol handlers
void onReceive(const ::simple::DisconnectRequest& request) override { Disconnect(); }
void onReceive(const ::simple::SimpleRequest& request) override
{
std::cout << "Received: " << request << std::endl;
// Validate request
if (request.Message.empty())
{
// Send reject
simple::SimpleReject reject;
reject.id = request.id;
reject.Error = "Request message is empty!";
send(reject);
return;
}
static std::hash hasher;
// Send response
simple::SimpleResponse response;
response.id = request.id;
response.Hash = (uint32_t)hasher(request.Message);
send(response);
}
// Protocol implementation
void onReceived(const void* buffer, size_t size) override { receive(buffer, size); }
size_t onSend(const void* data, size_t size) override { return SendAsync(data, size) ? size : 0; }
};
class SimpleProtoServer : public CppServer::Asio::TCPServer, public FBE::simple::Sender
{
public:
using CppServer::Asio::TCPServer::TCPServer;
protected:
std::shared_ptr CreateSession(const std::shared_ptr& server) override
{
return std::make_shared(server);
}
protected:
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Simple protocol server caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
// Protocol implementation
size_t onSend(const void* data, size_t size) override { Multicast(data, size); return size; }
};
int main(int argc, char** argv)
{
// Simple protocol server port
int port = 4444;
if (argc > 1)
port = std::atoi(argv[1]);
std::cout << "Simple protocol server port: " << port << std::endl;
std::cout << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new simple protocol server
auto server = std::make_shared(service, port);
// Start the server
std::cout << "Server starting...";
server->Start();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the server or '!' to restart the server..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Restart the server
if (line == "!")
{
std::cout << "Server restarting...";
server->Restart();
std::cout << "Done!" << std::endl;
continue;
}
// Multicast admin notification to all sessions
simple::SimpleNotify notify;
notify.Notification = "(admin) " + line;
server->send(notify);
}
// Stop the server
std::cout << "Server stopping...";
server->Stop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: Simple protocol client
Here comes the example of the simple protocol client. It connects to the
simple protocol server and allows to send requests to it and receive
corresponding responses.
```c++
#include "asio_service.h"
#include "server/asio/tcp_client.h"
#include "threads/thread.h"
#include "../proto/simple_protocol.h"
#include
#include
class SimpleProtoClient : public CppServer::Asio::TCPClient, public FBE::simple::Client
{
public:
using CppServer::Asio::TCPClient::TCPClient;
void DisconnectAndStop()
{
_stop = true;
DisconnectAsync();
while (IsConnected())
CppCommon::Thread::Yield();
}
protected:
void onConnected() override
{
std::cout << "Simple protocol client connected a new session with Id " << id() << std::endl;
// Reset FBE protocol buffers
reset();
}
void onDisconnected() override
{
std::cout << "Simple protocol client disconnected a session with Id " << id() << std::endl;
// Wait for a while...
CppCommon::Thread::Sleep(1000);
// Try to connect again
if (!_stop)
ConnectAsync();
}
void onError(int error, const std::string& category, const std::string& message) override
{
std::cout << "Simple protocol client caught an error with code " << error << " and category '" << category << "': " << message << std::endl;
}
// Protocol handlers
void onReceive(const ::simple::DisconnectRequest& request) override { Client::onReceive(request); std::cout << "Received: " << request << std::endl; DisconnectAsync(); }
void onReceive(const ::simple::SimpleResponse& response) override { Client::onReceive(response); std::cout << "Received: " << response << std::endl; }
void onReceive(const ::simple::SimpleReject& reject) override { Client::onReceive(reject); std::cout << "Received: " << reject << std::endl; }
void onReceive(const ::simple::SimpleNotify& notify) override { Client::onReceive(notify); std::cout << "Received: " << notify << std::endl; }
// Protocol implementation
void onReceived(const void* buffer, size_t size) override { receive(buffer, size); }
size_t onSend(const void* data, size_t size) override { return SendAsync(data, size) ? size : 0; }
private:
std::atomic _stop{false};
};
int main(int argc, char** argv)
{
// TCP server address
std::string address = "127.0.0.1";
if (argc > 1)
address = argv[1];
// Simple protocol server port
int port = 4444;
if (argc > 2)
port = std::atoi(argv[2]);
std::cout << "Simple protocol server address: " << address << std::endl;
std::cout << "Simple protocol server port: " << port << std::endl;
std::cout << std::endl;
// Create a new Asio service
auto service = std::make_shared();
// Start the Asio service
std::cout << "Asio service starting...";
service->Start();
std::cout << "Done!" << std::endl;
// Create a new simple protocol client
auto client = std::make_shared(service, address, port);
// Connect the client
std::cout << "Client connecting...";
client->ConnectAsync();
std::cout << "Done!" << std::endl;
std::cout << "Press Enter to stop the client or '!' to reconnect the client..." << std::endl;
// Perform text input
std::string line;
while (getline(std::cin, line))
{
if (line.empty())
break;
// Reconnect the client
if (line == "!")
{
std::cout << "Client reconnecting...";
client->IsConnected() ? client->ReconnectAsync() : client->ConnectAsync();
std::cout << "Done!" << std::endl;
continue;
}
// Send request to the simple protocol server
simple::SimpleRequest request;
request.Message = line;
auto response = client->request(request).get();
// Show string hash calculation result
std::cout << "Hash of '" << line << "' = " << std::format("0x{:8X}", response.Hash) << std::endl;
}
// Disconnect the client
std::cout << "Client disconnecting...";
client->DisconnectAndStop();
std::cout << "Done!" << std::endl;
// Stop the Asio service
std::cout << "Asio service stopping...";
service->Stop();
std::cout << "Done!" << std::endl;
return 0;
}
```
## Example: HTTP server
Here comes the example of the HTTP cache server. It allows to manipulate
cache data with HTTP methods (GET, POST, PUT and DELETE).
Use the following link to open [Swagger OpenAPI](https://swagger.io/specification/) iterative documentation: http://localhost:8080/api/index.html

```c++
#include "server/http/http_server.h"
#include "string/string_utils.h"
#include "utility/singleton.h"
#include
#include