Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/agent-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
Boost.Corosio specific instructions
---

* Research:

* Introduction
- Requirements: Familiarity with Boost.Capy and coroutines

* First section is an introduction to TCP/IP networking
- https://archive.org/stream/TCPIPIllustratedVol.1TheProtocols1stEdition/TCP-IP%20Illustrated_djvu.txt
- TCP/IP only (no UDP)

* Second section is an introduction to concurrent programming
- https://start-concurrent.github.io/full/index.html
- C++20 Coroutines
- Strands: Synchronization without mutexes
4 changes: 4 additions & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
** xref:tutorials/http-client.adoc[HTTP Client]
** xref:tutorials/dns-lookup.adoc[DNS Lookup]
* Guide
** xref:guide/tcp-networking.adoc[TCP/IP Networking]
** xref:guide/concurrent-programming.adoc[Concurrent Programming]
** xref:guide/io-context.adoc[I/O Context]
** xref:guide/sockets.adoc[Sockets]
** xref:guide/acceptor.adoc[Acceptors]
** xref:guide/endpoints.adoc[Endpoints]
** xref:guide/composed-operations.adoc[Composed Operations]
** xref:guide/timers.adoc[Timers]
** xref:guide/signals.adoc[Signal Handling]
** xref:guide/resolver.adoc[Name Resolution]
** xref:guide/tcp-server.adoc[TCP Server]
** xref:guide/tls.adoc[TLS Encryption]
** xref:guide/error-handling.adoc[Error Handling]
** xref:guide/buffers.adoc[Buffer Sequences]
Expand Down
319 changes: 319 additions & 0 deletions doc/modules/ROOT/pages/guide/acceptor.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/corosio
//

= Acceptors

The `acceptor` class listens for incoming TCP connections and accepts them
into socket objects. It's the foundation for building TCP servers.

NOTE: Code snippets assume:
[source,cpp]
----
#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:

[source,cpp]
----
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:

[source,cpp]
----
// 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:

[source,cpp]
----
acc.listen(corosio::endpoint(8080));
----

This performs three operations:

1. Creates an IPv4 TCP socket
2. Binds to the specified endpoint
3. Marks the socket as passive (listening)

Throws `std::system_error` on failure.

=== Parameters

[source,cpp]
----
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.

=== Binding to All Interfaces

To accept connections on any network interface:

[source,cpp]
----
// Port only - binds to 0.0.0.0 (all IPv4 interfaces)
acc.listen(corosio::endpoint(8080));
----

=== Binding to a Specific Interface

To accept connections only on a specific interface:

[source,cpp]
----
// Localhost only
acc.listen(corosio::endpoint(
boost::urls::ipv4_address::loopback(), 8080));
----

== Accepting Connections

=== accept()

The `accept()` operation waits for and accepts an incoming connection:

[source,cpp]
----
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.

=== Errors

Common accept errors:

[cols="1,2"]
|===
| Error | Meaning

| `operation_canceled`
| Cancelled via `cancel()` or stop token

| `bad_file_descriptor`
| Acceptor not listening

| Resource errors
| System limit reached (file descriptors, memory)
|===

=== Preconditions

* The acceptor must be listening (`is_open() == true`)
* The peer socket must be associated with the same execution context

== Cancellation

=== cancel()

Cancel pending accept operations:

[source,cpp]
----
acc.cancel();
----

All outstanding `accept()` operations complete with `operation_canceled`.

=== Stop Token Cancellation

Accept operations support `std::stop_token` through the affine awaitable
protocol:

[source,cpp]
----
// Inside a cancellable task:
auto [ec] = co_await acc.accept(peer);
if (ec == make_error_code(system::errc::operation_canceled))
std::cout << "Accept cancelled\n";
----

== Closing

=== close()

Release acceptor resources:

[source,cpp]
----
acc.close();
----

Pending accept operations complete with `operation_canceled`.

=== is_open()

Check if the acceptor is listening:

[source,cpp]
----
if (acc.is_open())
// Ready to accept
----

== Move Semantics

Acceptors are move-only:

[source,cpp]
----
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:

[source,cpp]
----
acc1 = std::move(acc2); // Closes acc1's socket if open, then moves acc2
----

IMPORTANT: Source and destination must share the same execution context.

== Thread Safety

[cols="1,2"]
|===
| 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:

[source,cpp]
----
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:

[source,cpp]
----
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 xref:tcp-server.adoc[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

* xref:sockets.adoc[Sockets] — Using accepted connections
* xref:tcp-server.adoc[TCP Server] — Higher-level server framework
* xref:../tutorials/echo-server.adoc[Echo Server Tutorial] — Complete example
Loading
Loading