From f15992c7ed0c8eb31cfcac2aadff33790f1b265d Mon Sep 17 00:00:00 2001 From: yhirose Date: Mon, 14 Sep 2026 12:27:52 -0400 Subject: [PATCH] Update documentation --- README.md | 19 ++++++++++++ .../en/cookbook/s18-listen-after-bind.md | 29 +++++++++++++++++-- .../ja/cookbook/s18-listen-after-bind.md | 29 +++++++++++++++++-- 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f5feb0f..4aff3ab 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,25 @@ int port = svr.bind_to_any_port("0.0.0.0"); svr.listen_after_bind(); ``` +### Port sharing and exclusive binding + +By default, the server socket enables address/port reuse: `SO_REUSEPORT` where it is available (Linux, macOS), and `SO_REUSEADDR` otherwise (Windows). A restarted server can bind again immediately, but binding to a port that another server is already listening on also succeeds, and connections are distributed between them. + +If you want `listen()` to fail when the port is already in use, replace the default socket options with `set_socket_options`: + +```cpp +svr.set_socket_options([](socket_t sock) { +#ifdef _WIN32 + httplib::set_socket_opt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1); +#else + httplib::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); +#endif +}); +``` + +> [!NOTE] +> Setting only `SO_REUSEADDR` is not enough on Windows. There, `SO_REUSEADDR` allows two sockets that both set it to bind to the same port, so use `SO_EXCLUSIVEADDRUSE` instead. + ### Static File Server ```cpp diff --git a/docs-src/pages/en/cookbook/s18-listen-after-bind.md b/docs-src/pages/en/cookbook/s18-listen-after-bind.md index 0e959b7..17ff9fe 100644 --- a/docs-src/pages/en/cookbook/s18-listen-after-bind.md +++ b/docs-src/pages/en/cookbook/s18-listen-after-bind.md @@ -43,15 +43,40 @@ svr.listen_after_bind(); ## Check the return values -`bind_to_port()` returns `false` on failure — typically when the port is already taken. Always check it. +`bind_to_port()` returns `false` on failure, for example when you don't have permission to bind to the port. Always check it. ```cpp if (!svr.bind_to_port("0.0.0.0", 8080)) { - std::cerr << "port already in use" << std::endl; + std::cerr << "bind failed" << std::endl; return 1; } ``` `listen_after_bind()` blocks until the server stops and returns `true` on a clean shutdown. +## Detect a port that's already in use + +With the default settings, you can actually bind to a port another server is already using. That's because cpp-httplib sets `SO_REUSEPORT` (Linux, macOS) or `SO_REUSEADDR` (Windows) on the server socket. A restarted server can bind again right away. The flip side is that a second server on the same port starts without an error, and connections get split between the two. + +To make `bind_to_port()` fail on a port in use, replace the socket options with `set_socket_options()`. + +```cpp +svr.set_socket_options([](socket_t sock) { +#ifdef _WIN32 + httplib::set_socket_opt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1); +#else + httplib::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); +#endif +}); + +if (!svr.bind_to_port("0.0.0.0", 8080)) { + std::cerr << "port already in use" << std::endl; + return 1; +} +``` + +`set_socket_options()` replaces the defaults entirely. Setting `SO_REUSEADDR` on Linux and macOS keeps the "restarted server can bind again right away" behavior. + +> **Note:** `SO_REUSEADDR` alone isn't enough on Windows. Two sockets that both set it can bind to the same port, so use `SO_EXCLUSIVEADDRUSE` instead. + > **Note:** To auto-pick a free port, see [S17. Bind to any available port](../s17-bind-any-port). Under the hood, that's just `bind_to_any_port()` + `listen_after_bind()`. diff --git a/docs-src/pages/ja/cookbook/s18-listen-after-bind.md b/docs-src/pages/ja/cookbook/s18-listen-after-bind.md index 357eac6..f63b188 100644 --- a/docs-src/pages/ja/cookbook/s18-listen-after-bind.md +++ b/docs-src/pages/ja/cookbook/s18-listen-after-bind.md @@ -43,15 +43,40 @@ svr.listen_after_bind(); ## 戻り値のチェック -`bind_to_port()`は失敗すると`false`を返します。ポートが既に使われている場合などです。必ずチェックしてください。 +`bind_to_port()`は失敗すると`false`を返します。ポートにbindする権限が無い場合などです。必ずチェックしてください。 ```cpp if (!svr.bind_to_port("0.0.0.0", 8080)) { - std::cerr << "port already in use" << std::endl; + std::cerr << "bind failed" << std::endl; return 1; } ``` `listen_after_bind()`はサーバーが停止するまでブロックし、正常終了なら`true`を返します。 +## 使用中のポートを検出する + +実は、デフォルトの設定では、ほかのサーバーが使っているポートにもbindできてしまいます。cpp-httplibがサーバーソケットに`SO_REUSEPORT`(Linux、macOS)か`SO_REUSEADDR`(Windows)を設定しているからです。再起動したサーバーはすぐにbindし直せます。その代わり、同じポートで2つ目のサーバーを起動してもエラーにならず、接続が両方に振り分けられます。 + +使用中のポートで`bind_to_port()`を失敗させたいときは、`set_socket_options()`でソケットオプションを差し替えてください。 + +```cpp +svr.set_socket_options([](socket_t sock) { +#ifdef _WIN32 + httplib::set_socket_opt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1); +#else + httplib::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1); +#endif +}); + +if (!svr.bind_to_port("0.0.0.0", 8080)) { + std::cerr << "port already in use" << std::endl; + return 1; +} +``` + +`set_socket_options()`はデフォルトの設定を丸ごと置き換えます。Linux、macOSで`SO_REUSEADDR`を設定しているのは、再起動したサーバーがすぐにbindし直せるようにするためです。 + +> **Note:** Windowsでは`SO_REUSEADDR`だけでは足りません。お互いに`SO_REUSEADDR`を設定したソケット同士は、同じポートにbindできてしまいます。`SO_EXCLUSIVEADDRUSE`を使ってください。 + > **Note:** 空いているポートを自動で選びたいときは[S17. ポートを動的に割り当てる](../s17-bind-any-port)を参照してください。こちらも内部では`bind_to_any_port()` + `listen_after_bind()`の組み合わせです。