mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-08 12:23:47 +00:00
199d7eemade read() return the new ReadResult::Timeout for every read timeout and leave the connection open. The compile-time server default (CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND, 300s) is always in effect, so a handler written as `while (ws.read(msg))`, the form the README's Quick Start uses, no longer ended when a peer went quiet: Timeout is non-zero, so the loop ran its body again with the previous message still in `msg`, and the worker the backstop is meant to reclaim was never released. Nothing caught it because every test of the new result set a timeout explicitly and checked the result by value, and the heartbeat tests keep the connection alive with pings. The two timeouts mean different things. One the caller sets through set_read_timeout() is a request for control back, and is reported as Timeout on a still-open connection. The compile-time default is a backstop against a peer that has gone quiet, and elapsing it is now a failure again: read() returns Fail and closes the connection, as it did before199d7ee. WebSocket tracks whether set_read_timeout() was called, and WebSocketClient carries the same flag over to the WebSocket it creates on connect(). Tests use the heartbeat binary, which compiles both defaults down to 3s: a `while (ws.read(msg))` server handler runs its body once and exits when the client falls silent, and a client that never set a timeout gets Fail with the connection closed. The README and cookbook now say which timeout produces Timeout. Claude-Session: https://claude.ai/code/session_01EF5uZ1X2kaHhqJ8VgfjVaQ
90 lines
3.0 KiB
Markdown
90 lines
3.0 KiB
Markdown
---
|
|
title: "W01. Implement a WebSocket Echo Server and Client"
|
|
order: 52
|
|
status: "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
|
|
|
|
```cpp
|
|
#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 message
|
|
- `ReadResult::Binary`: received a binary message
|
|
- `ReadResult::Fail`: error, or connection closed
|
|
- `ReadResult::Timeout`: a read timeout you set with `set_read_timeout()` elapsed with nothing received; the connection is still open. The compile-time default timeout closes the connection and is reported as `Fail` instead — see [W06. Set Timeouts](../w06-websocket-timeouts)
|
|
|
|
## Client: talk to the echo server
|
|
|
|
```cpp
|
|
#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.
|
|
|
|
```cpp
|
|
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](../w04-websocket-binary) 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.
|
|
|
|
```cpp
|
|
svr.new_task_queue = [] {
|
|
return new httplib::ThreadPool(8, 128);
|
|
};
|
|
```
|
|
|
|
See [S21. Configure the thread pool](../s21-thread-pool).
|
|
|
|
> **Note:** To run WebSocket over HTTPS, use `httplib::SSLServer` instead of `httplib::Server` — the same `WebSocket()` handler just works. On the client side, use a `wss://` URL. For CA and client certificate configuration, see [W05. Configure TLS for wss:// Connections](../w05-websocket-tls).
|