T04 (mTLS) had grown a "WebSocketClient" subsection describing wss:// client certificates, and c12/t02 were getting similar WebSocketClient asides for timeouts and CA paths. The Cookbook's own index already separates WebSocket into its own category (W01-W04) from TLS/Security (T01-T05) and Client (C01-C19), so burying WebSocketClient specifics inside those pages fought the site's structure. Move that content into two new recipes under the WebSocket category instead: - W05: wss:// TLS setup (set_ca_cert_path CA directory parity, PemMemory client certificate) - W06: WebSocketClient's three timeouts, including the recently added chrono overloads T04, T02, C12, and W01 now carry a single reference link to the new pages instead of duplicated explanations, matching the site's existing cross-link convention. While rewriting T04's client-side section, noticed it documented SSLClient's file-path constructor but not its PemMemory one, even though the server-side section covered both forms for SSLServer. Added the missing PemMemory example so both sides are symmetric.
2.7 KiB
title, order, status
| title | order | status |
|---|---|---|
| W01. Implement a WebSocket Echo Server and Client | 51 | draft |
WebSocket is a protocol for two-way messaging between client and server. cpp-httplib provides APIs for both sides. Let's start with the simplest example: an echo server.
Server: echo server
#include <httplib.h>
int main() {
httplib::Server svr;
svr.WebSocket("/echo", [](const httplib::Request &req, httplib::ws::WebSocket &ws) {
std::string msg;
while (ws.is_open()) {
auto result = ws.read(msg);
if (result == httplib::ws::ReadResult::Fail) {
break;
}
ws.send(msg); // echo back what we received
}
});
svr.listen("0.0.0.0", 8080);
}
Register a WebSocket handler with svr.WebSocket(). By the time the handler runs, the WebSocket handshake is already complete. Inside the loop, just ws.read() and ws.send() to get a working echo.
The read() return value is a ReadResult enum:
ReadResult::Text: received a text messageReadResult::Binary: received a binary messageReadResult::Fail: error, or connection closed
Client: talk to the echo server
#include <httplib.h>
int main() {
httplib::ws::WebSocketClient cli("ws://localhost:8080/echo");
if (!cli.connect()) {
std::cerr << "failed to connect" << std::endl;
return 1;
}
cli.send("Hello, WebSocket!");
std::string msg;
if (cli.read(msg) != httplib::ws::ReadResult::Fail) {
std::cout << "received: " << msg << std::endl;
}
cli.close();
}
Use a ws:// (plain) or wss:// (TLS) URL. Call connect() to do the handshake, then send() and read() work the same as on the server side.
Text vs. binary
send() has two overloads that let you choose the frame type.
ws.send("Hello"); // text frame
ws.send(binary_data, binary_data_size); // binary frame
The std::string overload sends as text; the const char* + size overload sends as binary. A bit subtle, but once you know it, it's intuitive. See W04. Send and receive binary frames for details.
Thread pool implications
A WebSocket handler holds its worker thread for the entire life of the connection — one connection per thread. For many concurrent clients, configure a dynamic thread pool.
svr.new_task_queue = [] {
return new httplib::ThreadPool(8, 128);
};
See S21. Configure the thread pool.
Note: To run WebSocket over HTTPS, use
httplib::SSLServerinstead ofhttplib::Server— the sameWebSocket()handler just works. On the client side, use awss://URL. For CA and client certificate configuration, see W05. Configure TLS for wss:// Connections.