mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-20 11:29:38 +00:00
Bring the READMEs and the docs pages (en and ja) in line with the current header: - Fix the client sample that does not compile, the Transfer-Encoding statement for default headers, the verifier callback comment, the thread pool spawn rule, the sanitize_filename description, and the inverted Accept-Encoding sentence. - Describe recent behaviour: TLS errors behind Read/Write failures, Host validation, Connection: close for a body without length, content_reader rules, refused CONNECT, WebSocketClient proxies, CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT, the SSE event size limit, and the payload limit of the Stream API. - Complete the API references of WebSocket, WebSocketClient and the stream:: helpers, and the SSE reconnect rules. - Fix cookbook pages: keychain loading on every backend, received Close status, a multipart sample that trusted the client's file name, an echo sample that sent binary as text, SNI, pre-request handler scope, stop() with WebSocket handlers, and the thread pool notes. - Restore the mutex in the LLM app's Chapter 5 code.
95 lines
3.3 KiB
Markdown
95 lines
3.3 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;
|
||
}
|
||
// echo back what we received, in the same frame type
|
||
if (result == httplib::ws::ReadResult::Binary) {
|
||
ws.send(msg.data(), msg.size());
|
||
} else {
|
||
ws.send(msg);
|
||
}
|
||
}
|
||
});
|
||
|
||
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. A binary message goes back through the `data()` + `size()` overload so it stays binary (more on that below).
|
||
|
||
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. The default pool already grows under load, up to 4× its base thread count. For many concurrent clients, raise that maximum.
|
||
|
||
```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).
|