mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-19 11:19:36 +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
4.2 KiB
Markdown
95 lines
4.2 KiB
Markdown
---
|
||
title: "W01. WebSocketエコーサーバー/クライアントを実装する"
|
||
order: 52
|
||
status: "draft"
|
||
---
|
||
|
||
WebSocketは、クライアントとサーバーの間で**双方向**にメッセージをやり取りするためのプロトコルです。cpp-httplibはサーバーとクライアントの両方のAPIを提供しています。まずは一番シンプルなエコーサーバーから見てみましょう。
|
||
|
||
## サーバー: エコーサーバー
|
||
|
||
```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;
|
||
}
|
||
// 受け取った内容を同じフレームタイプでそのまま返す
|
||
if (result == httplib::ws::ReadResult::Binary) {
|
||
ws.send(msg.data(), msg.size());
|
||
} else {
|
||
ws.send(msg);
|
||
}
|
||
}
|
||
});
|
||
|
||
svr.listen("0.0.0.0", 8080);
|
||
}
|
||
```
|
||
|
||
`svr.WebSocket()`でWebSocket用のハンドラを登録します。ハンドラが呼ばれた時点で、すでにWebSocketのハンドシェイクは完了しています。ループの中で`ws.read()`して`ws.send()`するだけで、エコー動作が完成します。バイナリメッセージは`data()`と`size()`を渡すオーバーロードで返すので、バイナリのまま届きます(詳しくは後述)。
|
||
|
||
`read()`の返り値は`ReadResult`列挙値で、次の4種類です。
|
||
|
||
- `ReadResult::Text`: テキストメッセージを受信
|
||
- `ReadResult::Binary`: バイナリメッセージを受信
|
||
- `ReadResult::Fail`: エラー、または接続が閉じた
|
||
- `ReadResult::Timeout`: `set_read_timeout()`で自分が設定した読み取りタイムアウトが、何も受信しないまま経過した。接続は開いたまま。コンパイル時のデフォルトのタイムアウトは接続を閉じ、`Fail`として返る([W06. タイムアウトを設定する](../w06-websocket-timeouts)を参照)
|
||
|
||
## クライアント: エコーを叩く
|
||
|
||
```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();
|
||
}
|
||
```
|
||
|
||
URLには`ws://`(平文)または`wss://`(TLS)を指定します。`connect()`でハンドシェイクを行い、あとは`send()`と`read()`でサーバーと同じAPIでやり取りできます。
|
||
|
||
## テキストとバイナリの送り分け
|
||
|
||
`send()`には2つのオーバーロードがあり、テキストとバイナリで使い分けられます。
|
||
|
||
```cpp
|
||
ws.send("Hello"); // テキストフレーム
|
||
ws.send(binary_data, binary_data_size); // バイナリフレーム
|
||
```
|
||
|
||
`std::string`を受け取るオーバーロードはテキスト、`const char*`とサイズを受け取るオーバーロードはバイナリとして送られます。詳しくは[W04. バイナリフレームを送受信する](../w04-websocket-binary)を参照してください。
|
||
|
||
## スレッドとの関係
|
||
|
||
WebSocket接続はハンドラが終わるまで生き続けるので、1接続につきワーカースレッドを1つ占有します。デフォルトのスレッドプールも負荷に応じてベースの4倍まで増えますが、同時接続数が多い場合は、その最大スレッド数を引き上げましょう。
|
||
|
||
```cpp
|
||
svr.new_task_queue = [] {
|
||
return new httplib::ThreadPool(8, 128);
|
||
};
|
||
```
|
||
|
||
詳細は[S21. マルチスレッド数を設定する](../s21-thread-pool)を参照してください。
|
||
|
||
> **Note:** HTTPSサーバーの上でWebSocketを動かしたいときは、`httplib::Server`の代わりに`httplib::SSLServer`を使えば、同じ`WebSocket()`ハンドラがそのまま動きます。クライアント側は`wss://`スキームを指定するだけです。CA証明書やクライアント証明書の設定は[W05. wss接続でTLSを設定する](../w05-websocket-tls)を参照してください。
|