S18. Control Startup Order with listen_after_bind
Normally svr.listen("0.0.0.0", 8080) handles bind and listen in one shot. When you need to do something between the two, split them into two calls.
Separate bind and listen
httplib::Server svr; svr.Get("/", [](const auto &, auto &res) { res.set_content("ok", "text/plain"); }); if (!svr.bind_to_port("0.0.0.0", 8080)) { std::cerr << "bind failed" << std::endl; return 1; } // bind is done here. accept hasn't started yet. drop_privileges(); signal_ready_to_parent_process(); svr.listen_after_bind(); // start the accept loop
httplib::Server svr; svr.Get("/", [](const auto &, auto &res) { res.set_content("ok", "text/plain"); }); if (!svr.bind_to_port("0.0.0.0", 8080)) { std::cerr << "bind failed" << std::endl; return 1; } // bind is done here. accept hasn't started yet. drop_privileges(); signal_ready_to_parent_process(); svr.listen_after_bind(); // start the accept loop
bind_to_port() reserves the port; listen_after_bind() actually starts accepting. Splitting them gives you a window between the two steps.
Common use cases
Privilege drop: Binding to a port under 1024 requires root. Bind as root, drop to a normal user, and all subsequent request handling runs with reduced privileges.
svr.bind_to_port("0.0.0.0", 80); drop_privileges(); svr.listen_after_bind();
svr.bind_to_port("0.0.0.0", 80); drop_privileges(); svr.listen_after_bind();
Startup notification: Tell the parent process or systemd "I'm ready" before starting to accept connections.
Test synchronization: In tests, you can reliably catch "the moment the server is bound" and start the client after that.
Check the return values
bind_to_port() returns false on failure — typically when the port is already taken. Always check it.
if (!svr.bind_to_port("0.0.0.0", 8080)) { std::cerr << "port already in use" << std::endl; return 1; }
if (!svr.bind_to_port("0.0.0.0", 8080)) { std::cerr << "port already in use" << std::endl; return 1; }
listen_after_bind() blocks until the server stops and returns true on a clean shutdown.
Note: To auto-pick a free port, see S17. Bind to any available port. Under the hood, that's just
bind_to_any_port()+listen_after_bind().