Examples#
The snippets below cover the most common client configurations. For a complete executable, see examples/basic_client/main.cpp in the project repository.
Connect and receive events#
Create namespace sockets before connecting so their handlers are ready when the server accepts the namespace:
#include <iostream>
#include <sioxx/sioxx.hpp>
sioxx::client client;
auto chat = client.socket("/chat");
chat->on_connect([] {
std::cout << "chat connected\n";
});
chat->on("message", [](const std::string& event, sioxx::message data) {
std::cout << event << ": " << data.dump() << '\n';
});
client.connect("wss://example.com");
Callbacks run on the connection’s background thread. Dispatch back to your application thread before modifying thread-affine state such as a UI.
Use an existing Asio event loop#
Pass a caller-owned boost::asio::io_context to make both WebSocket and
HTTP polling networking run on an application’s existing event loop:
#include <boost/asio/io_context.hpp>
#include <sioxx/sioxx.hpp>
boost::asio::io_context io_context;
sioxx::client_options options;
sioxx::client client(io_context, options);
client.connect("wss://example.com");
io_context.run();
The context must outlive the client. The application is responsible for running it; sioxx does not run or stop a supplied context. Transport callbacks run through the supplied context and are serialized by an internal strand.
Emit an event and receive an acknowledgement#
Payloads are nlohmann::json values. Pass a callback as the third argument
when the server should acknowledge the event. Events emitted before the
namespace connects are buffered and sent in order after connection:
chat->emit("hello", sioxx::json{{"name", "Ada"}});
chat->emit(
"sum",
sioxx::json::array({1, 2, 3}),
[](sioxx::message reply) {
std::cout << "server replied: " << reply.dump() << '\n';
});
Send and receive binary attachments#
Create binary values with sioxx::binary_message() and place them at any
depth in an event payload. The default JSON parser automatically emits the
Socket.IO placeholder header followed by the binary attachment frames; the
application does not need to construct BINARY_EVENT packets itself:
auto image = sioxx::binary_message(
std::vector<std::uint8_t>{0x89, 0x50, 0x4e, 0x47});
chat->emit(
"upload",
sioxx::json{{"name", "avatar.png"}, {"bytes", image}},
[](sioxx::message reply) {
std::cout << "upload reply: " << reply.dump() << '\n';
});
Listener payloads remain arrays of Socket.IO arguments. Check
is_binary() before reading a value with get_binary():
chat->on("download", [](const std::string&, sioxx::message arguments) {
if (!arguments.is_array() || arguments.empty()) return;
const auto& file = arguments.at(0);
if (!file.is_object() || !file.contains("bytes")) return;
const auto& value = file.at("bytes");
if (!value.is_binary()) return;
const auto& bytes = value.get_binary();
std::cout << "received " << bytes.size() << " bytes\n";
});
Binary values also work in acknowledgements. For example, an acknowledgement-aware listener can return a binary preview:
chat->on(
"request_preview",
[](const std::string&, sioxx::message,
sioxx::socket::ack_callback acknowledge) {
if (!acknowledge) return;
acknowledge(sioxx::json::array({sioxx::binary_message(
std::vector<std::uint8_t>{0x01, 0x02, 0x03})}));
});
The same API works over WebSocket and HTTP long-polling, with nested or multiple attachments, and with either built-in parser.
Select the wire parser#
The default JSON parser interoperates with a standard Socket.IO server and
supports binary attachments as shown above. The server and client parser must
always match. Select MessagePack when the server uses
socket.io-msgpack-parser or when carrying binary-heavy payloads in a single
MessagePack frame:
sioxx::client_options options;
options.parser = sioxx::parser_kind::msgpack;
sioxx::client client(options);
client.connect("wss://example.com");
Use HTTP long-polling#
WebSocket is preferred and sioxx falls back to polling when the initial WebSocket connection fails. Polling can also be forced for restrictive networks or transport-specific testing:
sioxx::client_options options;
options.force_http_polling = true;
sioxx::client client(options);
client.connect("https://example.com");
Configure TLS and request headers#
Add authentication or application headers to the handshake with
extra_headers. Keep certificate verification enabled in production:
sioxx::client_options options;
options.extra_headers = {
{"Authorization", "Bearer example-token"},
{"X-Client-Version", "1.0"},
};
// Development with a self-signed certificate only:
// options.verify_tls = false;
Connect through an HTTP proxy#
Set an HTTP proxy for WebSocket and polling connections. Optional Basic credentials authenticate with the proxy and are not forwarded to the Socket.IO server:
sioxx::client_options options;
options.proxy = sioxx::proxy_options{
"http://proxy.example.com:8080", "user", "password"};
sioxx::client client(options);
client.connect("wss://example.com");
Only HTTP proxy endpoints are supported. Secure origin connections are tunnelled through the proxy before TLS is established with the origin.
Authenticate a namespace and customize the endpoint#
Pass authentication data when creating a namespace socket. The payload is
sent with each namespace CONNECT, including reconnects. Update it before
calling connect() again when a token is refreshed:
sioxx::client_options options;
options.engineio_path = "/realtime/";
options.query = {
{"client", "desktop"},
{"version", "2"},
};
sioxx::client client(options);
auto private_socket = client.socket(
"/private", sioxx::json{{"token", "example-token"}});
client.connect("wss://example.com");
// Later, after authentication expires:
private_socket->set_auth(
sioxx::json{{"token", "refreshed-token"}});
private_socket->disconnect();
private_socket->connect();
engineio_path defaults to /socket.io/. Query keys and values are
percent-encoded; the reserved Engine.IO keys EIO, transport, and
sid cannot be supplied by the application.
Configure reconnection#
Reconnects use capped exponential back-off with jitter:
using namespace std::chrono_literals;
sioxx::client_options options;
options.reconnect_attempts = 5;
options.reconnect_delay = 1s;
options.reconnect_delay_max = 30s;
options.reconnect_randomization_factor = 0.5;
client.set_reconnect_listener([](unsigned attempt, unsigned delay_ms) {
// A retry was scheduled. attempt is zero-based.
});
client.set_reconnecting_listener([] {
// The back-off elapsed and the retry is starting.
});
Use client.opened() to query whether the Engine.IO handshake is currently
open. client.get_sessionid() returns the handshake session ID, or an empty
string before a session exists.
Run the bundled client and server#
The repository contains a C++ client and a matching Node.js Socket.IO server. Build the client from the repository root:
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
$ cmake --build build --parallel
Start the server in another terminal:
$ cd examples/basic_client/server
$ pnpm install
$ pnpm start
Then run the C++ client:
$ ./build/sioxx_basic_client ws://localhost:3000
The example also supports msgpack, cbor, and polling modes. See
the test-server guide for all
matching commands.
Build the Qt chat example#
An optional Qt Widgets chat client demonstrates a typical GUI integration, including namespace authentication, reconnects, event acknowledgements, catch-all listeners, transport and parser selection, and binary attachments with either JSON or MessagePack. Most importantly, it queues sioxx’s background-thread callbacks onto Qt’s GUI thread before touching widgets.
With Qt 6 or Qt 5 installed, enable the examples and build the target. CMake skips this target without error when Qt Widgets is unavailable:
$ cmake -S . -B build-qt -DSIOXX_BUILD_EXAMPLES=ON \
-DSIOXX_BUILD_TESTS=OFF
$ cmake --build build-qt --target sioxx_qt_chat --parallel
$ pnpm --dir examples/qt_chat install --frozen-lockfile
$ pnpm --dir examples/qt_chat start
$ ./build-qt/sioxx_qt_chat
See the Qt chat README for parser and binary-sharing instructions.