Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
Tutorial: TCP Client and Server

Introduction

This tutorial walks through building TCP client and server applications using Network System's facade API. By the end you will know how to:

  • Create a TCP client and connect to a remote endpoint
  • Stand up a TCP server and accept connections
  • Track sessions, send and receive arbitrary byte payloads
  • Implement an echo pattern for round-trip messaging
  • Handle connection, disconnection, and error callbacks safely

All examples target the tcp_facade class. The facade hides protocol tags and template parameters, returning the unified interfaces i_protocol_client and i_protocol_server.

Core Concepts

Concept Description
Facade Type-erased entry point that returns interface pointers
Session Server-side handle for a single connected client (i_session)
Callback User-supplied function invoked on connect, receive, error, etc.
Result Result<T> from common_system represents success or error code

The facade API uses byte vectors (std::vector<uint8_t>) as the canonical payload format. Text messages are simply byte vectors initialised from a string range.

Tutorial 1: Connecting a Client

The simplest TCP client connects to a server, sends a payload, and prints incoming data through a receive callback.

#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
using namespace kcenon::network;
int main() {
auto client = tcp.create_client({
.host = "127.0.0.1",
.port = 9000,
.client_id = "tutorial-client",
});
client->set_connected_callback([] {
std::cout << "[client] connected\n";
});
client->set_receive_callback([](const std::vector<uint8_t>& data) {
std::string msg(data.begin(), data.end());
std::cout << "[client] received: " << msg << '\n';
});
client->set_error_callback([](std::error_code ec) {
std::cerr << "[client] error: " << ec.message() << '\n';
});
if (auto r = client->start("127.0.0.1", 9000); r.is_err()) {
std::cerr << "connect failed: " << r.error().message << '\n';
return 1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::string msg = "hello, server";
client->send(std::vector<uint8_t>(msg.begin(), msg.end()));
std::this_thread::sleep_for(std::chrono::milliseconds(500));
client->stop();
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.

Notes:

  • start(host, port) initiates the asynchronous connect; the call returns immediately and connection state is reported through callbacks.
  • is_connected() can be polled if you need a blocking wait, but production code should react to the connected callback.
  • send() accepts an rvalue byte vector for zero-copy ownership transfer.

Tutorial 2: Accepting Connections

A server tracks incoming sessions in a thread-safe map and responds to each client. The facade publishes three callbacks for the server lifecycle: connection, disconnection, and receive.

#include <atomic>
#include <chrono>
#include <iostream>
#include <map>
#include <mutex>
#include <thread>
using namespace kcenon::network;
int main() {
auto server = tcp.create_server({
.port = 9000,
.server_id = "tutorial-server",
});
std::mutex sessions_mutex;
std::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 << "[server] connect: " << session->id() << '\n';
});
server->set_disconnection_callback(
[&](std::string_view session_id) {
std::lock_guard lock(sessions_mutex);
sessions.erase(std::string(session_id));
std::cout << "[server] disconnect: " << session_id << '\n';
});
server->set_receive_callback(
[&](std::string_view session_id, const std::vector<uint8_t>& data) {
std::cout << "[server] " << session_id << " bytes=" << data.size() << '\n';
});
if (auto r = server->start(9000); r.is_err()) {
std::cerr << "start failed: " << r.error().message << '\n';
return 1;
}
std::cout << "[server] listening on 9000, Ctrl+C to stop\n";
std::this_thread::sleep_for(std::chrono::seconds(60));
server->stop();
return 0;
}
Session interface representing an active client-server connection.
@ server
Server-side handling of a request.

Important details about session storage:

  • The connection callback hands you a std::shared_ptr<i_session>. Holding the shared pointer is what keeps the session alive; if you discard it the framework still owns its internal copy, but you cannot send to it later.
  • Always guard the session map with a mutex. Callbacks may fire from any of the IO threads.
  • The disconnection callback receives only the session id. Erase the entry immediately to release the session shared pointer.

Tutorial 3: Building an Echo Server

The echo pattern is the canonical "hello world" of network programming. It exercises the full receive-then-send round trip in a single callback. Combine the previous server skeleton with a send-back step:

server->set_receive_callback(
[&](std::string_view session_id, const std::vector<uint8_t>& data) {
std::shared_ptr<interfaces::i_session> session;
{
std::lock_guard lock(sessions_mutex);
if (auto it = sessions.find(std::string(session_id));
it != sessions.end()) {
session = it->second;
}
}
if (!session) {
return;
}
// Echo the original payload back to the sender.
if (auto r = session->send(std::vector<uint8_t>(data));
r.is_err()) {
std::cerr << "[server] echo failed: "
<< r.error().message << '\n';
}
});

Pair this server with the client from Tutorial 1 and you should observe the client receive the same bytes it sent. For a complete runnable program see tcp_echo_server.cpp and tcp_client.cpp under examples/.

Session Lifetime and Cleanup

The Network System's session model has two important rules:

  1. The server keeps an internal reference until disconnect; user code should release sessions in the disconnection callback to avoid leaks.
  2. Calls on a closed session return Result<> errors rather than throwing. Always check is_err() before assuming a send succeeded.

Next Steps