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.
4.3 KiB
title, order, status
| title | order | status |
|---|---|---|
| E04. SSEをクライアントで受信する | 51 | draft |
cpp-httplibにはsse::SSEClientという専用のクラスが用意されています。自動再接続、イベント名別のハンドラ、Last-Event-IDの管理まで面倒を見てくれるので、SSEを受信するときはこれを使うのが一番ラクです。
基本の使い方
#include <httplib.h>
httplib::Client cli("http://localhost:8080");
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << "data: " << msg.data << std::endl;
});
sse.start(); // ブロッキング
Clientと接続先パスを渡してSSEClientを作り、on_message()でコールバックを登録します。start()を呼ぶとイベントループが走り、接続が切れると自動で再接続を試みます。
イベント名で分岐する
サーバーがevent:を付けてイベントを送ってくる場合は、on_event()で名前ごとにハンドラを登録できます。
sse.on_event("message", [](const auto &msg) {
std::cout << "chat: " << msg.data << std::endl;
});
sse.on_event("join", [](const auto &msg) {
std::cout << msg.data << " joined" << std::endl;
});
sse.on_event("leave", [](const auto &msg) {
std::cout << msg.data << " left" << std::endl;
});
on_message()は、on_event()で登録していない名前のイベントをまとめて受け取る汎用ハンドラです。上の例のようにon_event("message", ...)を登録した場合、messageイベントはon_message()ではなくそちらに届きます。
接続イベントとエラーハンドリング
sse.on_open([] {
std::cout << "connected" << std::endl;
});
sse.on_error([](httplib::Error err) {
std::cerr << "error: " << httplib::to_string(err) << std::endl;
});
接続確立時やエラー発生時にもフックを挟めます。エラーハンドラが呼ばれても、SSEClientは内部で再接続を試みます。ただし、サーバーが204、403、404のいずれかを返したときや、set_max_reconnect_attempts()で決めた回数を使い切ったときは再接続しません。
また、1つのイベントや、まだ改行が来ていない行がCPPHTTPLIB_SSE_MAX_EVENT_LENGTH(デフォルトは16MB、0なら無制限)を超えたときも止まります。このときエラーハンドラにはError::ExceedMaxPayloadSizeが渡されます。イベントストリームには終わりがないので、クライアントのset_payload_max_length()はストリーム全体には適用されません。
非同期で動かす
メインスレッドを塞ぎたくない場合はstart_async()を使います。
sse.start_async();
// メインスレッドは別の仕事を続ける
do_other_work();
// 終わったら止める
sse.stop();
start_async()は裏でスレッドを立ち上げてイベントループを回します。stop()でクリーンに止められます。
再接続の設定
再接続間隔や最大試行回数を調整できます。
sse.set_reconnect_interval(5000); // 5秒
sse.set_max_reconnect_attempts(10); // 10回まで(0=無制限)
サーバー側でretry:フィールドを送っていると、そちらが優先されます。
Last-Event-IDの自動管理
SSEClientは受信したイベントのidを内部で保持していて、再接続時にLast-Event-IDヘッダーとして送ってくれます。この挙動はサーバー側でid:付きイベントを送っていれば自動で有効になります。
空のid:を受け取ると、保持しているIDはクリアされます。ヘッダーの値として送れないid:(NULなどの制御文字を含むものや、前後に空白があるもの)は無視され、それまでのIDがそのまま残ります。
std::cout << "last id: " << sse.last_event_id() << std::endl;
現在のIDはlast_event_id()で参照できます。
Note: SSEClientの
start()はブロッキングなので、単発のツールならそのまま使えますが、GUIアプリやサーバーに組み込むときはstart_async()+stop()の組み合わせが基本です。
サーバー側の実装はE01. SSEサーバーを実装するを参照してください。