3. Walk-through

The server API is declared by one header, with the examples' support header alongside it:

#include <vws/server.h>

#include "vwsx.h"

Each worker thread is given its own context by a constructor and destructor. Here the context is just a label, to show the lifecycle:

static void* worker_ctor(void* data)
{
    (void)data;
    return vws.strdup("worker-ctx");
}

static void worker_dtor(void* ctx)
{
    vws.free(ctx);
}

The constructor's return value becomes the ctx passed to the data callback on that thread, and the destructor frees it when the thread exits. vws.strdup and vws.free are the library's allocator-aware helpers. A real server would use this context for per-thread resources such as a database handle.

The processing callback echoes the inbound bytes. It receives a vws_svr_data — a heap-allocated blob carrying the received bytes, the originating connection id, and a back pointer to the server — and the worker context (unused here):

static void echo(vws_svr_data* req, void* ctx)
{
    (void)ctx;

    char* data = (char*)vws.malloc(req->size);
    memcpy(data, req->data, req->size);

    vws_svr_data* reply = vws_svr_data_own(req->server, req->cid,
                                           (ucstr)data, req->size);

    vws_svr_data_free(req);
    vws_tcp_svr_send(reply);
}

The handler copies the request payload into a fresh buffer and wraps it in a response with vws_svr_data_own(), which takes ownership of that buffer and binds the reply to the same connection id. The original request is released with vws_svr_data_free(), and the reply is queued for the networking thread with vws_tcp_svr_send().

The server is created and wired up in main:

vws_tcp_svr* server = vws_tcp_svr_new(10, 0, 0);

if (server == NULL)
{
    vwsx_die("vws_tcp_svr_new() failed");
}

server->on_data_in  = echo;
server->worker_ctor = worker_ctor;
server->worker_dtor = worker_dtor;

vws_tcp_svr_new() takes three arguments: the number of worker threads, the listen backlog, and the request/response queue size; passing zero for either of the latter two selects its default (128 and 1024 respectively). The echo handler is installed on on_data_in and the worker hooks on worker_ctor and worker_dtor.

The server is then set up to stop cleanly, run, and be freed:

vwsx_serve_stop_on_signal(server);

vws_tcp_svr_run(server, HOST, PORT);

vws_tcp_svr_free(server);

vwsx_serve_stop_on_signal installs SIGINT/SIGTERM handlers that stop the server; because this is the base server, it is passed directly, with no cast. vws_tcp_svr_run() then blocks, serving connections until a signal triggers vws_tcp_svr_stop(), after which vws_tcp_svr_free() releases the server.