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
87 lines
4.6 KiB
Markdown
87 lines
4.6 KiB
Markdown
---
|
||
title: "W06. タイムアウトを設定する"
|
||
order: 57
|
||
status: "draft"
|
||
---
|
||
|
||
`ws::WebSocketClient`には`Client`と同じ3種類のタイムアウトがあり、意味も同じです。
|
||
|
||
| 種類 | API | デフォルト |
|
||
| --- | --- | --- |
|
||
| 接続タイムアウト | `set_connection_timeout` | 300秒 |
|
||
| 読み取りタイムアウト | `set_read_timeout` | なし。無期限に待つ(`CPPHTTPLIB_WEBSOCKET_CLIENT_READ_TIMEOUT_SECOND`) |
|
||
| 書き込みタイムアウト | `set_write_timeout` | 5秒 |
|
||
|
||
## 基本の使い方
|
||
|
||
```cpp
|
||
httplib::ws::WebSocketClient ws("ws://localhost:8080/ws");
|
||
|
||
ws.set_connection_timeout(5, 0); // 5秒
|
||
ws.set_read_timeout(30, 0); // 30秒
|
||
ws.set_write_timeout(10, 0); // 10秒
|
||
|
||
if (ws.connect()) {
|
||
ws.send("hello");
|
||
}
|
||
```
|
||
|
||
接続タイムアウトと書き込みタイムアウトは`connect()`を呼ぶ前に設定してください。読み取りタイムアウトはいつでも変更でき、接続済みの状態で設定した場合は次の`read()`から効きます。
|
||
|
||
## `std::chrono`で指定する
|
||
|
||
`Client`と同じく、`std::chrono`の期間を直接渡すオーバーロードもあります。
|
||
|
||
```cpp
|
||
using namespace std::chrono_literals;
|
||
|
||
ws.set_connection_timeout(5s);
|
||
ws.set_read_timeout(30s);
|
||
ws.set_write_timeout(10s);
|
||
```
|
||
|
||
## 読み取りタイムアウトの意味
|
||
|
||
`set_read_timeout()`は「1回の`read()`呼び出し」に対するタイムアウトです。メッセージが届かないまま指定時間が経過すると`read()`は`ReadResult::Timeout`を返します。このとき**接続は開いたまま**で、1バイトも読み進めていないので、そのまま送信して読み直せます。接続が失われたことを意味する`ReadResult::Fail`とはここが違います。
|
||
|
||
1本の接続を1つのスレッドで双方向に扱えるのはこのためです。
|
||
|
||
```cpp
|
||
using namespace std::chrono_literals;
|
||
|
||
ws.set_read_timeout(100ms);
|
||
std::string msg;
|
||
while (ws.is_open()) {
|
||
auto r = ws.read(msg);
|
||
if (r == httplib::ws::Timeout) {
|
||
flush_outgoing(ws); // 何も届いていない。溜まっている分を送る
|
||
continue;
|
||
}
|
||
if (r == httplib::ws::Fail) { break; }
|
||
handle(msg);
|
||
}
|
||
```
|
||
|
||
読み取りタイムアウトを設定しないと`read()`はメッセージが届くまで戻らないので、接続を持っているスレッドは送信に手が回りません。
|
||
|
||
`Timeout`について2点あります。
|
||
|
||
- `msg`は書き換えられません。値も0以外なので、読み取りタイムアウトを設定した状態で`while (ws.read(msg))`と書くと、**前回のメッセージ**が`msg`に残ったままループが回り続けます。
|
||
- 報告されるのはメッセージの境界だけです。分割されたメッセージの途中でタイムアウトした場合、そのメッセージは再開できないので`read()`は`Fail`を返します。
|
||
|
||
通知の待受のように長時間メッセージが来ないことが正常な接続では、読み取りタイムアウトを設定しないままにするか、`Timeout`を「まだ何も来ていない」印として扱ってループを続けてください。
|
||
|
||
## サーバ側
|
||
|
||
ハンドラが受け取る`ws::WebSocket`にも`set_read_timeout()`があります。ハンドラが`read()`で止まったままにならないので、上と同じ書き方で複数の接続の間をメッセージが中継できます。
|
||
|
||
サーバ側のデフォルトは「無期限」ではなく300秒(`CPPHTTPLIB_WEBSOCKET_SERVER_READ_TIMEOUT_SECOND`)です。WebSocketのハンドラは接続が続く限りワーカーを1つ占有するので、無言になったピアからワーカーを回収する保険として働きます。
|
||
|
||
この保険はハンドラが求めたタイムアウトではないので、`Timeout`としては返りません。経過すると`read()`は`Fail`を返して接続を閉じるため、`while (ws.read(msg))`と書いたハンドラは従来どおりそこで終わります。`Timeout`が返るのは、ハンドラ自身が`set_read_timeout()`で設定したタイムアウトだけです。
|
||
|
||
> Ping/Pongによる無応答ピア検出は別の仕組みです。詳しくは[W02. ハートビートを設定する](../w02-websocket-ping)を参照してください。
|
||
|
||
## `Client`との違い
|
||
|
||
`Client`のタイムアウト設定については[C12. タイムアウトを設定する](../c12-timeouts)を参照してください。挙動とAPIはほぼ同じですが、`WebSocketClient`には`set_max_timeout()`に相当するリクエスト全体のタイムアウトはありません。接続を確立したあとは、`read()`のループを回し続ける限り接続が維持されます。
|