| doc_id | NET-GUID-003 |
|---|---|
| doc_title | Facade API Guide |
| doc_version | 1.0.0 |
| doc_date | 2026-04-04 |
| doc_status | Released |
| project | network_system |
| category | GUID |
SSOT: This document is the single source of truth for Facade API Guide.
Language: English
A comprehensive guide to the Facade v2.0 API layer, which provides simplified, high-level interfaces for each network protocol in the network_system library.
- Facade Pattern Overview
- TCP Facade
- UDP Facade
- HTTP Facade
- WebSocket Facade
- QUIC Facade
- Common Patterns Across Facades
- Migration from Low-Level API
The network_system library provides powerful, low-level protocol implementations with extensive template parameters, protocol tags, and TLS policies. While flexible, this complexity creates a steep learning curve for common use cases. The facade layer eliminates this complexity by providing:
- No template parameters: Concrete classes instead of template-heavy instantiation
- Unified API: Same
create_client()/create_server()pattern across all protocols - Automatic TLS handling: SSL/TLS configuration via simple boolean flags
- Zero-cost abstraction: No runtime overhead compared to direct instantiation
┌──────────────────────────────────────────────────┐
│ User Application │
├──────────────────────────────────────────────────┤
│ tcp_facade udp_facade http_facade ws_facade │ ← Facade Layer
│ quic_facade │
├──────────────────────────────────────────────────┤
│ i_protocol_client / i_protocol_server │ ← Unified Interfaces
├──────────────────────────────────────────────────┤
│ messaging_client messaging_udp_client │
│ messaging_server messaging_udp_server │ ← Protocol Implementations
│ http_client/server messaging_ws_client/server │
│ messaging_quic_client / messaging_quic_server │
└──────────────────────────────────────────────────┘
| Scenario | Recommended |
|---|---|
| Simple client/server setup | Facade |
| Standard protocol usage | Facade |
| Custom template configurations | Direct API |
| Protocol-specific advanced features (e.g., QUIC multi-stream) | Direct API |
| Connection pooling (TCP) | Facade (create_connection_pool) |
| Rapid prototyping | Facade |
All facades share the same structural pattern:
class <protocol>_facade {
public:
struct client_config { /* protocol-specific options */ };
struct server_config { /* protocol-specific options */ };
auto create_client(const client_config&) -> Result<std::shared_ptr<i_protocol_client>>;
auto create_server(const server_config&) -> Result<std::shared_ptr<i_protocol_server>>;
};#include <kcenon/network/facade/tcp_facade.h>
#include <kcenon/network/facade/udp_facade.h>
#include <kcenon/network/facade/http_facade.h>
#include <kcenon/network/facade/websocket_facade.h>
#include <kcenon/network/facade/quic_facade.h>All facades reside in the kcenon::network::facade namespace.
The tcp_facade provides simplified TCP client/server creation with optional SSL/TLS support and connection pooling.
Header: kcenon/network/facade/tcp_facade.h
| Field | Type | Default | Description |
|---|---|---|---|
host |
std::string |
(required) | Server hostname or IP address |
port |
uint16_t |
0 |
Server port number |
client_id |
std::string |
auto-generated | Client identifier |
timeout |
std::chrono::milliseconds |
30s |
Connection timeout |
use_ssl |
bool |
false |
Enable SSL/TLS encryption |
ca_cert_path |
std::optional<std::string> |
nullopt |
CA certificate file path |
verify_certificate |
bool |
true |
Verify server SSL certificate |
| Field | Type | Default | Description |
|---|---|---|---|
port |
uint16_t |
0 |
Port to listen on |
server_id |
std::string |
auto-generated | Server identifier |
use_ssl |
bool |
false |
Enable SSL/TLS encryption |
cert_path |
std::optional<std::string> |
nullopt |
Server certificate file (required if use_ssl=true) |
key_path |
std::optional<std::string> |
nullopt |
Server private key file (required if use_ssl=true) |
tls_version |
std::optional<std::string> |
nullopt |
TLS protocol version (default: TLS 1.2+) |
| Field | Type | Default | Description |
|---|---|---|---|
host |
std::string |
(required) | Server hostname or IP address |
port |
uint16_t |
0 |
Server port number |
pool_size |
size_t |
10 |
Number of connections to maintain |
#include <kcenon/network/facade/tcp_facade.h>
#include <iostream>
#include <vector>
using namespace kcenon::network::facade;
int main()
{
tcp_facade facade;
// Create server
auto result = facade.create_server({.port = 8080});
if (result.is_err())
{
std::cerr << "Failed to create server" << std::endl;
return 1;
}
auto server = result.value();
// Echo received data back to the client
server->set_receive_callback(
[&](std::string_view session_id, const std::vector<uint8_t>& data) {
// In a real implementation, look up the session and send data back
std::cout << "Received " << data.size() << " bytes from "
<< session_id << std::endl;
});
server->set_connection_callback(
[](std::shared_ptr<kcenon::network::interfaces::i_session> session) {
std::cout << "Client connected: " << session->id() << std::endl;
});
// Start listening
if (auto result = server->start(8080); !result)
{
std::cerr << "Failed to start server" << std::endl;
return 1;
}
std::cout << "Echo server running on port 8080" << std::endl;
// Block until stopped
return 0;
}#include <kcenon/network/facade/tcp_facade.h>
#include <kcenon/network/interfaces/connection_observer.h>
#include <iostream>
using namespace kcenon::network;
int main()
{
facade::tcp_facade facade;
auto result = facade.create_client({
.host = "example.com",
.port = 8443,
.client_id = "secure-client",
.use_ssl = true,
.verify_certificate = true
});
if (result.is_err())
{
std::cerr << "Failed to create client" << std::endl;
return 1;
}
auto client = result.value();
// Use observer pattern for event handling
auto observer = std::make_shared<interfaces::callback_adapter>();
observer->on_connected([]() {
std::cout << "Connected to server" << std::endl;
}).on_receive([](std::span<const uint8_t> data) {
std::cout << "Received " << data.size() << " bytes" << std::endl;
}).on_error([](std::error_code ec) {
std::cerr << "Error: " << ec.message() << std::endl;
});
client->set_observer(observer);
if (auto result = client->start("example.com", 8443); !result)
{
std::cerr << "Connection failed" << std::endl;
return 1;
}
// Send data
std::vector<uint8_t> payload = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello"
client->send(std::move(payload));
client->stop();
return 0;
}#include <kcenon/network/facade/tcp_facade.h>
#include <iostream>
using namespace kcenon::network::facade;
int main()
{
tcp_facade facade;
auto pool_result = facade.create_connection_pool({
.host = "127.0.0.1",
.port = 5555,
.pool_size = 10
});
if (pool_result.is_err())
{
std::cerr << "Failed to create connection pool" << std::endl;
return 1;
}
auto pool = pool_result.value();
if (auto result = pool->initialize(); !result)
{
std::cerr << "Pool initialization failed" << std::endl;
return 1;
}
// Acquire a connection from the pool
auto client = pool->acquire();
// Use the connection
std::vector<uint8_t> data = {0x01, 0x02, 0x03};
// client->send_packet(data);
// Release back to pool when done
pool->release(std::move(client));
std::cout << "Active connections: " << pool->active_count()
<< "/" << pool->pool_size() << std::endl;
return 0;
}The udp_facade provides simplified UDP datagram client/server creation.
Header: kcenon/network/facade/udp_facade.h
| Field | Type | Default | Description |
|---|---|---|---|
host |
std::string |
(required) | Target hostname or IP address |
port |
uint16_t |
0 |
Target port number |
client_id |
std::string |
auto-generated | Client identifier |
| Field | Type | Default | Description |
|---|---|---|---|
port |
uint16_t |
0 |
Port to listen on |
server_id |
std::string |
auto-generated | Server identifier |
#include <kcenon/network/facade/udp_facade.h>
#include <kcenon/network/interfaces/connection_observer.h>
#include <iostream>
using namespace kcenon::network;
// Discovery server: listens for broadcast queries and responds
int main()
{
facade::udp_facade facade;
// Create UDP server for discovery
auto result = facade.create_server({
.port = 9999,
.server_id = "discovery-server"
});
if (result.is_err())
{
std::cerr << "Failed to create server" << std::endl;
return 1;
}
auto server = result.value();
server->set_receive_callback(
[](std::string_view session_id, const std::vector<uint8_t>& data) {
std::cout << "Discovery query from " << session_id
<< " (" << data.size() << " bytes)" << std::endl;
});
if (auto result = server->start(9999); !result)
{
std::cerr << "Failed to start discovery server" << std::endl;
return 1;
}
std::cout << "Discovery server listening on port 9999" << std::endl;
return 0;
}#include <kcenon/network/facade/udp_facade.h>
#include <kcenon/network/interfaces/connection_observer.h>
#include <iostream>
using namespace kcenon::network;
int main()
{
facade::udp_facade facade;
auto result = facade.create_client({
.host = "127.0.0.1",
.port = 9999,
.client_id = "discovery-client"
});
if (result.is_err())
{
std::cerr << "Failed to create client" << std::endl;
return 1;
}
auto client = result.value();
auto observer = std::make_shared<interfaces::callback_adapter>();
observer->on_receive([](std::span<const uint8_t> data) {
std::cout << "Response: " << data.size() << " bytes" << std::endl;
});
client->set_observer(observer);
if (auto result = client->start("127.0.0.1", 9999); !result)
{
std::cerr << "Failed to start UDP client" << std::endl;
return 1;
}
// Send discovery query
std::vector<uint8_t> query = {'D', 'I', 'S', 'C', 'O', 'V', 'E', 'R'};
client->send(std::move(query));
client->stop();
return 0;
}Note: UDP is connectionless. The
start()method sets the default target endpoint rather than establishing a persistent connection. Message boundaries are preserved (eachsend()produces one datagram).
The http_facade provides simplified HTTP/1.1 client/server creation through the unified protocol interface.
Header: kcenon/network/facade/http_facade.h
| Field | Type | Default | Description |
|---|---|---|---|
client_id |
std::string |
auto-generated | Client identifier |
timeout |
std::chrono::milliseconds |
30s |
Request timeout |
use_ssl |
bool |
false |
Enable HTTPS |
path |
std::string |
"/" |
HTTP path |
| Field | Type | Default | Description |
|---|---|---|---|
port |
uint16_t |
0 |
Port to listen on |
server_id |
std::string |
auto-generated | Server identifier |
#include <kcenon/network/facade/http_facade.h>
#include <iostream>
#include <string>
using namespace kcenon::network;
int main()
{
facade::http_facade facade;
auto result = facade.create_server({
.port = 8080,
.server_id = "api-server"
});
if (result.is_err())
{
std::cerr << "Failed to create server" << std::endl;
return 1;
}
auto server = result.value();
// Handle incoming HTTP requests
server->set_receive_callback(
[](std::string_view session_id, const std::vector<uint8_t>& data) {
std::string body(data.begin(), data.end());
std::cout << "Request from " << session_id << ": " << body << std::endl;
});
server->set_connection_callback(
[](std::shared_ptr<interfaces::i_session> session) {
// Send response
std::string response = R"({"status": "ok"})";
std::vector<uint8_t> response_data(response.begin(), response.end());
session->send(std::move(response_data));
});
if (auto result = server->start(8080); !result)
{
std::cerr << "Failed to start HTTP server" << std::endl;
return 1;
}
std::cout << "HTTP server running on port 8080" << std::endl;
return 0;
}#include <kcenon/network/facade/http_facade.h>
#include <kcenon/network/interfaces/connection_observer.h>
#include <iostream>
using namespace kcenon::network;
int main()
{
facade::http_facade facade;
auto result = facade.create_client({
.client_id = "api-client",
.timeout = std::chrono::seconds(10),
.path = "/api/data"
});
if (result.is_err())
{
std::cerr << "Failed to create client" << std::endl;
return 1;
}
auto client = result.value();
auto observer = std::make_shared<interfaces::callback_adapter>();
observer->on_receive([](std::span<const uint8_t> data) {
std::string response(reinterpret_cast<const char*>(data.data()), data.size());
std::cout << "Response: " << response << std::endl;
}).on_error([](std::error_code ec) {
std::cerr << "HTTP error: " << ec.message() << std::endl;
});
client->set_observer(observer);
// Connect to server
if (auto result = client->start("localhost", 8080); !result)
{
std::cerr << "Connection failed" << std::endl;
return 1;
}
// Send POST request with binary data
std::string body = R"({"key": "value"})";
std::vector<uint8_t> payload(body.begin(), body.end());
client->send(std::move(payload));
client->stop();
return 0;
}Protocol-Specific Notes:
start()sets the base URL (host:port) for subsequent requestssend()performs an HTTP POST request with the provided binary data- Received response body is delivered via the receive callback/observer
The websocket_facade provides simplified WebSocket client/server creation with automatic ping/pong keepalive.
Header: kcenon/network/facade/websocket_facade.h
| Field | Type | Default | Description |
|---|---|---|---|
client_id |
std::string |
auto-generated | Client identifier |
ping_interval |
std::chrono::milliseconds |
30s |
Ping keepalive interval |
| Field | Type | Default | Description |
|---|---|---|---|
port |
uint16_t |
0 |
Port to listen on |
path |
std::string |
"/" |
WebSocket endpoint path |
server_id |
std::string |
auto-generated | Server identifier |
#include <kcenon/network/facade/websocket_facade.h>
#include <iostream>
#include <mutex>
#include <unordered_map>
using namespace kcenon::network;
int main()
{
facade::websocket_facade facade;
auto result = facade.create_server({
.port = 8080,
.path = "/chat",
.server_id = "chat-server"
});
if (result.is_err())
{
std::cerr << "Failed to create server" << std::endl;
return 1;
}
auto server = result.value();
// Track connected sessions
std::mutex sessions_mutex;
std::unordered_map<std::string, std::shared_ptr<interfaces::i_session>> sessions;
server->set_connection_callback(
[&](std::shared_ptr<interfaces::i_session> session) {
std::lock_guard lock(sessions_mutex);
sessions[std::string(session->id())] = session;
std::cout << "User joined: " << session->id() << std::endl;
});
server->set_disconnection_callback(
[&](std::string_view session_id) {
std::lock_guard lock(sessions_mutex);
sessions.erase(std::string(session_id));
std::cout << "User left: " << session_id << std::endl;
});
// Broadcast received messages to all connected clients
server->set_receive_callback(
[&](std::string_view sender_id, const std::vector<uint8_t>& data) {
std::lock_guard lock(sessions_mutex);
for (auto& [id, session] : sessions)
{
if (id != sender_id && session->is_connected())
{
std::vector<uint8_t> copy(data);
session->send(std::move(copy));
}
}
});
if (auto result = server->start(8080); !result)
{
std::cerr << "Failed to start chat server" << std::endl;
return 1;
}
std::cout << "Chat server running on ws://localhost:8080/chat" << std::endl;
return 0;
}#include <kcenon/network/facade/websocket_facade.h>
#include <kcenon/network/interfaces/connection_observer.h>
#include <iostream>
using namespace kcenon::network;
int main()
{
facade::websocket_facade facade;
auto result = facade.create_client({
.client_id = "chat-user-1",
.ping_interval = std::chrono::seconds(15)
});
if (result.is_err())
{
std::cerr << "Failed to create client" << std::endl;
return 1;
}
auto client = result.value();
auto observer = std::make_shared<interfaces::callback_adapter>();
observer->on_connected([]() {
std::cout << "Connected to chat server" << std::endl;
}).on_receive([](std::span<const uint8_t> data) {
std::string message(reinterpret_cast<const char*>(data.data()), data.size());
std::cout << "Message: " << message << std::endl;
}).on_disconnected([](std::optional<std::string_view> reason) {
std::cout << "Disconnected";
if (reason) std::cout << ": " << *reason;
std::cout << std::endl;
});
client->set_observer(observer);
if (auto result = client->start("localhost", 8080); !result)
{
std::cerr << "Failed to connect" << std::endl;
return 1;
}
// Send a chat message
std::string msg = "Hello, everyone!";
std::vector<uint8_t> payload(msg.begin(), msg.end());
client->send(std::move(payload));
client->stop();
return 0;
}Protocol-Specific Notes:
send()transmits data as binary WebSocket frames via the unified interface- For text messages or WebSocket-specific features, cast to
i_websocket_client/i_websocket_server- The
pathfield in server config must start with'/'- Automatic ping/pong keepalive prevents connection timeouts
The quic_facade provides simplified QUIC client/server creation with built-in TLS 1.3, ALPN negotiation, and optional 0-RTT support.
Header: kcenon/network/facade/quic_facade.h
| Field | Type | Default | Description |
|---|---|---|---|
host |
std::string |
(required) | Server hostname or IP address |
port |
uint16_t |
0 |
Server port number |
client_id |
std::string |
auto-generated | Client identifier |
ca_cert_path |
std::optional<std::string> |
nullopt |
CA certificate for server verification |
client_cert_path |
std::optional<std::string> |
nullopt |
Client certificate for mutual TLS |
client_key_path |
std::optional<std::string> |
nullopt |
Client private key for mutual TLS |
verify_server |
bool |
true |
Verify server certificate |
alpn |
std::string |
(required) | ALPN protocol identifier (e.g., "h3") |
max_idle_timeout_ms |
uint64_t |
30000 |
Maximum idle timeout in milliseconds |
enable_0rtt |
bool |
false |
Enable 0-RTT early data |
| Field | Type | Default | Description |
|---|---|---|---|
port |
uint16_t |
0 |
Port to listen on |
server_id |
std::string |
auto-generated | Server identifier |
cert_path |
std::string |
(required) | Server certificate file (PEM) |
key_path |
std::string |
(required) | Server private key file (PEM) |
ca_cert_path |
std::optional<std::string> |
nullopt |
CA certificate for client verification |
require_client_cert |
bool |
false |
Require mutual TLS |
alpn |
std::string |
(required) | ALPN protocol identifier |
max_idle_timeout_ms |
uint64_t |
30000 |
Maximum idle timeout in milliseconds |
max_connections |
size_t |
10000 |
Maximum concurrent connections |
#include <kcenon/network/facade/quic_facade.h>
#include <iostream>
using namespace kcenon::network;
int main()
{
facade::quic_facade facade;
auto result = facade.create_server({
.port = 4433,
.server_id = "quic-server",
.cert_path = "/path/to/cert.pem",
.key_path = "/path/to/key.pem",
.alpn = "h3",
.max_idle_timeout_ms = 60000,
.max_connections = 5000
});
if (result.is_err())
{
std::cerr << "Failed to create QUIC server" << std::endl;
return 1;
}
auto server = result.value();
server->set_connection_callback(
[](std::shared_ptr<interfaces::i_session> session) {
std::cout << "QUIC client connected: " << session->id() << std::endl;
});
server->set_receive_callback(
[](std::string_view session_id, const std::vector<uint8_t>& data) {
std::cout << "Received " << data.size() << " bytes from "
<< session_id << std::endl;
});
if (auto result = server->start(4433); !result)
{
std::cerr << "Failed to start QUIC server" << std::endl;
return 1;
}
std::cout << "QUIC server running on port 4433" << std::endl;
return 0;
}#include <kcenon/network/facade/quic_facade.h>
#include <kcenon/network/interfaces/connection_observer.h>
#include <iostream>
using namespace kcenon::network;
int main()
{
facade::quic_facade facade;
auto result = facade.create_client({
.host = "127.0.0.1",
.port = 4433,
.client_id = "quic-client",
.ca_cert_path = "/path/to/ca.pem",
.alpn = "h3",
.max_idle_timeout_ms = 60000,
.enable_0rtt = true
});
if (result.is_err())
{
std::cerr << "Failed to create QUIC client" << std::endl;
return 1;
}
auto client = result.value();
auto observer = std::make_shared<interfaces::callback_adapter>();
observer->on_connected([]() {
std::cout << "QUIC connection established" << std::endl;
}).on_receive([](std::span<const uint8_t> data) {
std::cout << "Received " << data.size() << " bytes" << std::endl;
}).on_error([](std::error_code ec) {
std::cerr << "QUIC error: " << ec.message() << std::endl;
});
client->set_observer(observer);
if (auto result = client->start("127.0.0.1", 4433); !result)
{
std::cerr << "QUIC connection failed" << std::endl;
return 1;
}
// Send data over QUIC
std::vector<uint8_t> payload = {0x01, 0x02, 0x03, 0x04};
client->send(std::move(payload));
client->stop();
return 0;
}Protocol-Specific Notes:
- QUIC always uses TLS 1.3 encryption (no plaintext mode)
- Server requires
cert_pathandkey_path(mandatory)- For QUIC-specific features (multi-stream, stream multiplexing, connection migration), use
messaging_quic_client/messaging_quic_serverdirectly instead of the facade
Facade create_client() / create_server() methods return Result<std::shared_ptr<T>>, and interface methods like start() / send() return VoidResult (alias for Result<void>). The Result<T> type provides a type-safe alternative to exceptions:
// create_client returns Result<shared_ptr> — check before using
auto result = facade.create_client({.host = "127.0.0.1", .port = 8080});
if (result.is_err())
{
std::cerr << "Failed to create client" << std::endl;
return 1;
}
auto client = result.value();
// start() and send() return VoidResult — check with bool conversion
if (auto start_result = client->start("127.0.0.1", 8080); !start_result)
{
std::cerr << "Failed to start client" << std::endl;
return 1;
}
if (auto send_result = client->send(std::move(data)); !send_result)
{
std::cerr << "Send failed" << std::endl;
}The connection_observer interface is the recommended approach for handling client events. It centralizes all event handling in one place:
// Option 1: Implement connection_observer directly
class my_observer : public kcenon::network::interfaces::connection_observer
{
public:
void on_receive(std::span<const uint8_t> data) override
{
// Handle received data
}
void on_connected() override
{
// Handle connection established
}
void on_disconnected(std::optional<std::string_view> reason) override
{
// Handle disconnection
}
void on_error(std::error_code ec) override
{
// Handle error
}
};
auto observer = std::make_shared<my_observer>();
client->set_observer(observer);For simpler cases, use callback_adapter with a fluent builder pattern:
auto adapter = std::make_shared<interfaces::callback_adapter>();
adapter->on_connected([]() {
std::cout << "Connected" << std::endl;
}).on_receive([](std::span<const uint8_t> data) {
// Process data
}).on_disconnected([](std::optional<std::string_view> reason) {
std::cout << "Disconnected" << std::endl;
}).on_error([](std::error_code ec) {
std::cerr << "Error: " << ec.message() << std::endl;
});
client->set_observer(adapter);When you only need some events, extend null_connection_observer:
class receive_only_observer : public interfaces::null_connection_observer
{
public:
void on_receive(std::span<const uint8_t> data) override
{
// Only handle received data; all other events are no-ops
}
};Servers use a separate callback model for managing multiple client sessions:
// Connection events
server->set_connection_callback([](std::shared_ptr<i_session> session) {
std::cout << "New client: " << session->id() << std::endl;
// session->send() to send data to this specific client
});
// Disconnection events
server->set_disconnection_callback([](std::string_view session_id) {
std::cout << "Client left: " << session_id << std::endl;
});
// Data received from any client
server->set_receive_callback([](std::string_view session_id,
const std::vector<uint8_t>& data) {
// session_id identifies which client sent the data
});
// Error on any session
server->set_error_callback([](std::string_view session_id,
std::error_code error) {
std::cerr << "Error on " << session_id << ": "
<< error.message() << std::endl;
});All protocol clients follow the same lifecycle:
create_client() start() send()/receive stop()
─────────────► [Created] ────► [Connected] ────► [Stopped]
│ │ ▲
│ └── on_error ──────┘
└─── (invalid config) ──► Result error
- Create:
facade.create_client(config)- validates config, returnsResult<shared_ptr> - Start:
client->start(host, port)- connects to server - Communicate:
client->send()/ observeron_receive() - Stop:
client->stop()- graceful disconnection
create_server() start(port) accept/receive stop()
─────────────► [Created] ────► [Listening] ────► [Stopped]
│ │ ▲
│ └── on_error ──────┘
└─── (invalid config) ──► Result error
- Create:
facade.create_server(config)- validates config, returnsResult<shared_ptr> - Start:
server->start(port)- begins listening - Serve: Accept connections, handle data via callbacks
- Stop:
server->stop()- closes all connections gracefully
Protocols that support TLS follow a consistent pattern:
| Protocol | TLS Support | Configuration |
|---|---|---|
| TCP | Optional (use_ssl) |
cert_path, key_path, ca_cert_path |
| HTTP | Optional (use_ssl) |
Via client_config.use_ssl |
| WebSocket | Not in facade | Use direct API for WSS |
| QUIC | Always on (TLS 1.3) | cert_path, key_path (server required) |
| UDP | Not supported | Use TCP/QUIC for encrypted transport |
All facades and the objects they create are thread-safe:
- Facade methods (
create_client,create_server) can be called concurrently - Client/server
start(),stop(),send()are thread-safe - Callbacks and observer methods may be invoked from I/O threads
- Multiple
send()operations are serialized internally
The facade layer covers common use cases. Consider using the direct API when you need:
| Feature | Facade | Direct API |
|---|---|---|
| Basic client/server | Yes | Yes |
| SSL/TLS | Yes (TCP, QUIC) | Yes |
| Connection pooling | Yes (TCP) | Yes |
| Multi-stream (QUIC) | No | Yes |
| Stream multiplexing | No | Yes |
| Custom template policies | No | Yes |
| Advanced QUIC features | No | Yes |
| WebSocket text frames | No | Yes |
| Custom protocol tags | No | Yes |
| Facade | Plain Client | SSL Client | Plain Server | SSL Server |
|---|---|---|---|---|
tcp_facade |
messaging_client |
secure_messaging_client |
messaging_server |
secure_messaging_server |
udp_facade |
messaging_udp_client |
— | messaging_udp_server |
— |
http_facade |
http_client |
— | http_server |
— |
websocket_facade |
messaging_ws_client |
— | messaging_ws_server |
— |
quic_facade |
messaging_quic_client |
(always TLS) | messaging_quic_server |
(always TLS) |
Before (Direct API):
// Complex template instantiation
auto client = std::make_shared<messaging_client>(
/* ... template parameters, protocol tags, TLS policy ... */
);
// Manual setup...After (Facade):
tcp_facade facade;
auto result = facade.create_client({
.host = "127.0.0.1",
.port = 8080,
.use_ssl = true
});
if (result.is_err()) { /* handle error */ }
auto client = result.value();
// Ready to use immediatelyIf your codebase uses legacy callbacks, the callback_adapter enables gradual migration:
// Legacy callback style
client->set_receive_callback([](const std::vector<uint8_t>& data) { /* ... */ });
// New observer style (recommended)
auto adapter = std::make_shared<callback_adapter>();
adapter->on_receive([](std::span<const uint8_t> data) { /* ... */ });
client->set_observer(adapter);The callback_adapter wraps std::function callbacks into the connection_observer interface, allowing incremental migration without rewriting all event handlers at once.