Search⌘ K
AI Features

A Concurrent Server Using Boost.Asio

Explore how to implement a single-threaded TCP server using Boost.Asio with coroutines in C++. Understand asynchronous programming with co_await and handle multiple clients concurrently without multiple OS threads. Gain insights on writing efficient, linear asynchronous code and the benefits of coroutine-based concurrency over traditional thread models.

This section will demonstrate how to write concurrent programs with multiple threads of execution but only use a single OS thread. We are about to implement a rudimentary concurrent single-threaded TCP server that can handle multiple clients. There are no networking capabilities in the C++ standard library but fortunately, Boost.Asio provides us with a platform-agnostic interface for handling socket communication.

Instead of wrapping the callback-based Boost.Asio API, we will demonstrate how to use the boost::asio::awaitable class to show a more realistic example of how asynchronous application programming using coroutines can look. The class template boost::asio::awaitable corresponds to the Task template we created earlier; it’s used as a return type for coroutines that represent asynchronous computations.

Implementing the server

The server is very simple; once a client connects, it starts updating a numeric counter and writes back the value whenever it is updated. This time we will follow the code from top to bottom, starting with the main() function:

C++
#include <boost/asio.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/use_awaitable.hpp>
using namespace std::chrono;
namespace asio = boost::asio;
using boost::asio::ip::tcp;
asio::awaitable<void> listen(tcp::endpoint endpoint) {
auto acceptor = tcp::acceptor{co_await asio::this_coro::executor};
co_await acceptor.async_listen(endpoint, asio::use_awaitable);
}
int main() {
auto server = [] {
auto endpoint = tcp::endpoint{tcp::v4(), 37259};
co_await listen(endpoint);
};
auto ctx = asio::io_context{};
boost::asio::co_spawn(ctx, server, asio::detached);
ctx.run();
}

The mandatory io_context runs the event processing loop. It’s possible to invoke run() from multiple threads as well if we want our server ...