If you've ever tried to use
What changed vs. the original
socket.io-client-cpp | sioxx | |
|---|---|---|
| JSON | RapidJSON | nlohmann/json |
| WebSocket | websocketpp | Boost.Beast ( boost::asio + boost::beast::websocket) |
| Wire protocol | JSON only | JSON or MessagePack, selectable per client |
| Transport | WebSocket | WebSocket first, HTTP long-polling fallback |
| Reconnection | basic retry | capped exponential backoff with jitter |
| Build | vendored deps, older CMake | modern CMake + CPM.cmake, find_package, full install(EXPORT ...) |
| Tests | Catch2 tests | GoogleTest suite (parsers, transports, URL parsing, reconnection, socket bookkeeping, engine.io framing) |
The public API keeps the same shape you'd expect from socket.io — client, socket("/namespace"), on(event, handler), emit(event, data), ack callbacks — but almost everything under the hood is new.
The JSON value problem, solved by not solving it
The original library has a whole sio::message class hierarchy — int_message, string_message, object_message, array_message, binary_message — basically reimplementing a JSON value type from scratch. But nlohmann::json is a JSON value type, and it already handles null/bool/int/double/string/array/object and raw binary via json::binary_t. So sioxx::message is just:
namespace sioxx {
using json = nlohmann::json;
using message = json; // that's it
using message_list = json; // always a JSON array
}
That one decision deletes an entire subsystem. Building event args is just building a json array:
sock->emit("hello", sioxx::json{"world"});
sock->emit("ping_ack", sioxx::json::array({1, 2, 3}), [](sioxx::message reply) {
std::cout << "ack: " << reply.dump() << "\n";
});
Two wire protocols behind one interface
socket.io actually has two parsers in the wild: the default text protocol, and
SOCIAL SHARE CARD GENERATOR