Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
Tutorial: Choosing the Right Protocol

Introduction

Network System ships facades for several transport protocols. Picking the wrong one early in a project leads to expensive rewrites later, so this tutorial walks through the trade-offs and shows how each facade is used.

You will learn:

  • When to reach for TCP, UDP, or WebSocket
  • How the facade API stays consistent across protocols
  • How to write code that targets the unified i_protocol_client interface so you can swap transports later

Decision Matrix

Concern TCP UDP WebSocket
Reliability Guaranteed Best-effort Guaranteed (over TCP)
Ordering In-order Unordered In-order
Connection-oriented Yes No Yes (with handshake)
Framing Stream of bytes Datagrams Discrete messages
Browser-friendly No No Yes
NAT/firewall Generally allowed Frequently blocked Allowed (HTTP port)
Overhead per message Low Lowest ~6 bytes header
Typical use cases RPC, file transfer Telemetry, gaming, voice Browser realtime, chat

Rules of thumb:

  • Choose TCP when you need reliable byte streams and full control over framing. It is the safest default for new server-to-server protocols.
  • Choose UDP when latency matters more than reliability, when you can tolerate (or recover from) lost or reordered packets, or when you need multicast.
  • Choose WebSocket when one peer is a web browser, when you need to traverse HTTP proxies, or when the framework's automatic message framing saves you from writing your own length-prefix protocol.

Facade Quick Reference

Each protocol has a facade in the kcenon::network::facade namespace:

Facade Header Purpose
tcp_facade kcenon/network/facade/tcp_facade.h Reliable byte-stream client/server with optional TLS
udp_facade kcenon/network/facade/udp_facade.h Datagram client/server
websocket_facade kcenon/network/facade/websocket_facade.h Framed full-duplex messaging over an HTTP upgrade
http_facade kcenon/network/facade/http_facade.h HTTP/2 client/server
quic_facade kcenon/network/facade/quic_facade.h QUIC transport

All facades follow the same shape:

facade_type fac;
auto client = fac.create_client({ /* config */ });
auto server = fac.create_server({ /* config */ });

The returned objects implement kcenon::network::interfaces::i_protocol_client and kcenon::network::interfaces::i_protocol_server, so once a connection is established the rest of your code is protocol-agnostic.

Example 1: TCP Reliable Stream

Use TCP for binary protocols where ordering and reliability are mandatory.

using namespace kcenon::network;
int main() {
auto client = tcp.create_client({
.host = "127.0.0.1",
.port = 9000,
.client_id = "rpc-client",
});
client->set_receive_callback([](const std::vector<uint8_t>& data) {
// Length-prefixed framing is up to the application.
});
client->start("127.0.0.1", 9000);
std::vector<uint8_t> request{0x01, 0x02, 0x03};
client->send(std::move(request));
return 0;
}
Simplified facade for creating TCP clients and servers.
Definition tcp_facade.h:95
int main()
Main namespace for all Network System components.
Simplified facade for creating TCP clients and servers.

Example 2: UDP Datagrams

Use UDP for telemetry, low-latency control loops, and any traffic that can tolerate loss. UDP datagrams are independent and unordered.

using namespace kcenon::network;
int main() {
auto client = udp.create_client({
.host = "127.0.0.1",
.port = 5555,
.client_id = "telemetry-emitter",
});
client->set_receive_callback([](const std::vector<uint8_t>& data) {
// Drop or accept stragglers as appropriate for the workload.
});
client->start("127.0.0.1", 5555);
std::vector<uint8_t> sample = {/* serialized telemetry sample */};
client->send(std::move(sample));
return 0;
}
Simplified facade for creating UDP clients and servers.
Definition udp_facade.h:72
Simplified facade for creating UDP clients and servers.

When designing UDP protocols you must handle:

  • Packet loss (retransmit at the application layer if necessary)
  • Reordering (include sequence numbers in the payload)
  • Path MTU (keep payloads under ~1200 bytes for IPv6 safety)

Example 3: WebSocket for Browsers

Use WebSocket when browsers are part of the topology or when an HTTP path makes routing through proxies easier.

using namespace kcenon::network;
int main() {
auto client = ws.create_client({
.client_id = "browser-bridge",
.ping_interval = std::chrono::seconds(20),
});
client->set_receive_callback([](const std::vector<uint8_t>& data) {
// Each callback delivers exactly one message.
});
client->start("127.0.0.1", 9001);
std::string hello = "hello world";
client->send(std::vector<uint8_t>(hello.begin(), hello.end()));
return 0;
}
Simplified facade for creating WebSocket clients and servers.
auto create_client(const client_config &config) const -> Result< std::shared_ptr< interfaces::i_protocol_client > >
Creates a WebSocket client with the specified configuration.
Simplified facade for creating WebSocket clients and servers.

Swapping Protocols Without Rewriting

Because all facades return the same i_protocol_client interface, you can isolate the choice of transport in a single factory function:

std::shared_ptr<interfaces::i_protocol_client>
make_client(std::string_view kind) {
if (kind == "tcp") {
return tcp.create_client({.host = "127.0.0.1", .port = 9000});
}
if (kind == "udp") {
return udp.create_client({.host = "127.0.0.1", .port = 5555});
}
return ws.create_client({.client_id = "default"});
}

Application code that calls client->send(...) and registers callbacks remains unchanged across transports.

Next Steps