Acceptors
The acceptor class listens for incoming TCP connections and accepts them
into socket objects. It’s the foundation for building TCP servers.
| Code snippets assume: |
#include <boost/corosio/acceptor.hpp>
#include <boost/corosio/socket.hpp>
#include <boost/corosio/endpoint.hpp>
namespace corosio = boost::corosio;
Overview
An acceptor binds to a local endpoint and waits for clients to connect:
corosio::acceptor acc(ioc);
acc.listen(corosio::endpoint(8080)); // Listen on port 8080
corosio::socket peer(ioc);
auto [ec] = co_await acc.accept(peer);
if (!ec)
{
// peer is now a connected socket
}
Construction
Acceptors are constructed from an execution context or executor:
// From io_context
corosio::acceptor acc1(ioc);
// From executor
auto ex = ioc.get_executor();
corosio::acceptor acc2(ex);
The acceptor doesn’t own system resources until listen() is called.
Listening
listen()
The listen() method creates a socket, binds to an endpoint, and begins
listening for connections:
acc.listen(corosio::endpoint(8080));
This performs three operations:
-
Creates an IPv4 TCP socket
-
Binds to the specified endpoint
-
Marks the socket as passive (listening)
Throws std::system_error on failure.
Parameters
void listen(endpoint ep, int backlog = 128);
The backlog parameter specifies the maximum queue length for pending
connections. When the queue is full, new connection attempts receive
ECONNREFUSED. The default of 128 works for most applications.
Accepting Connections
accept()
The accept() operation waits for and accepts an incoming connection:
corosio::socket peer(ioc);
auto [ec] = co_await acc.accept(peer);
On success, peer is initialized with the new connection. Any existing
connection on peer is closed first.
The operation is asynchronous—your coroutine suspends until a connection arrives or an error occurs.
Cancellation
Move Semantics
Acceptors are move-only:
corosio::acceptor acc1(ioc);
corosio::acceptor acc2 = std::move(acc1); // OK
corosio::acceptor acc3 = acc2; // Error: deleted copy constructor
Move assignment closes any existing acceptor:
acc1 = std::move(acc2); // Closes acc1's socket if open, then moves acc2
| Source and destination must share the same execution context. |
Thread Safety
| Operation | Thread Safety |
|---|---|
Distinct acceptors |
Safe from different threads |
Same acceptor |
NOT safe for concurrent operations |
Don’t start multiple accept() operations concurrently on the same acceptor.
Example: Accept Loop
A typical server accept loop:
capy::task<void> accept_loop(
corosio::io_context& ioc,
corosio::acceptor& acc)
{
for (;;)
{
corosio::socket peer(ioc);
auto [ec] = co_await acc.accept(peer);
if (ec)
{
if (ec == make_error_code(system::errc::operation_canceled))
break; // Shutdown requested
std::cerr << "Accept error: " << ec.message() << "\n";
continue; // Try again
}
// Spawn a coroutine to handle this connection
capy::run_async(ioc.get_executor())(
handle_connection(std::move(peer)));
}
}
Key points:
-
Create a fresh socket for each accept
-
Move the socket into the handler coroutine
-
Continue accepting after non-fatal errors
-
Check for cancellation to support graceful shutdown
Example: Graceful Shutdown
Coordinate shutdown with signal handling:
capy::task<void> run_server(corosio::io_context& ioc)
{
corosio::acceptor acc(ioc);
acc.listen(corosio::endpoint(8080));
corosio::signal_set signals(ioc, SIGINT, SIGTERM);
// Spawn accept loop
capy::run_async(ioc.get_executor())(accept_loop(ioc, acc));
// Wait for shutdown signal
auto [ec, signum] = co_await signals.async_wait();
if (!ec)
{
std::cout << "Received signal " << signum << ", shutting down\n";
acc.cancel(); // Stop accepting
// Existing connections continue until complete
}
}
Relationship to tcp_server
For production servers, consider using tcp_server which provides:
-
Worker pool management
-
Connection limiting
-
Multi-port support
-
Automatic coroutine lifecycle
The acceptor class is the lower-level primitive that tcp_server builds
upon.
Next Steps
-
Sockets — Using accepted connections
-
TCP Server — Higher-level server framework
-
Echo Server Tutorial — Complete example