C++ Boost Asio Introduction Tutorial

By , last updated December 16, 2019

In the C++ Boost.ASIO library, ASIO stands for asynchronous input/output. This library allows asynchronous processing of data. Asynchronous means that a program doesn’t have to wait for completion of an operation to start a new one. It can execute more than one operations concurrently.

Boost.ASIO comes in two flavors. One with Boost, and one non-Boost. Boost.ASIO is the ASIO library, but within the Boost namespace. It is possible to use only ASIO by downloading it directly from the author, or cloning the code from Github.

The Boost.ASIO library equips developers with basic building blocks for c++ network programming, concurrency and other kinds of I/O operations.

To better understand the concept of asynchronous, consider a real life analogy. Let’s say you ask your daughter to make a cup of coffee for you while you are working. After some time your daughter serve you a cup of coffee. This is an asynchronous operation, as you needed a coffee and you get it without leaving your work.

Boost.Asio is best choice if you are looking to make a portable networking code in C++.

Some key features of Boost.Asio library include:

  • Give programmers the ability to write cross-platform networking code, while working in Microsoft Windows, Linux, QNX, etc. It means developers do not need to learn each platform, but can use ASIO to implement networking for any supported platform.
  • Support both IPv4 and IPv6
  • Support timers (delayed operations)
  • Support both TCP & UDP
  • Provide std::iostream compatible interfaces
  • Support SSL (Secure Sockets Layer) connections
  • Support asynchronous operations

Although Boost.ASIO can be used to process any kind of data asynchronously, it has found its main usage in network programming. The reason for this is that Boost.ASIO supported network functions long before additional I/O objects were added. Network functions are a perfect use for asynchronous operations because the transmission of data over a network may take a long time, which means acknowledgments and errors may not be available as fast as the functions that send or receive data can execute.

I/O Services and I/O Objects

Boost.Asio uses the concept of I/O objects and I/O services for asynchronous processing of data.

I/O services abstract the operating system interfaces that process data asynchronously. Whereas I/O objects initiate asynchronous operations.

These two concepts are needed for a clear boundary to separate tasks cleanly: I/O services look towards the operating system API, and I/O objects look towards tasks that are required by programmer.

I/O service object manages I/O services also and as a user you normally don’t interact with I/O services. An I/O service object is like a registry where I/O services are listed. Every I/O object knows its I/O service and gets access to its I/O service through the I/O service object.

Every program based on Boost.Asio uses an object of type boost::asio::io_service. This can also be made a global variable.

Starting an io_service

In these examples, I’m using the standalone ASIO version. The only difference between standalone and Boost.ASIO is that the Boost version uses some Boost facilities. In usage, there are minimal differences, if any.

In it’s simplest form, the io_service can be instantiated and started like this:

asio::io_service _io_service;
_io_service.run();

This simple example creates an io_service, and runs it. The io_service will run to completion, and return execution once it runs out of work. In this case, it’s immediately.

Give the service work

To actually make the service work, we have to give it work. As before, when calling .run() on an io_service, it will block until there is no more work left.

Work is given to the io_service by posting work to it, using the .post() method.

asio::io_service _io_service;

// Work lambda
auto io_work = []() -> void
{
    std::cout << "Doing some work within ASIOn";
};

_io_service.post(io_work);
_io_service.run();

In this example, work is the lambda io_work. In this context, everything is run synchronously on the same thread.

Neverending work

// Make io_service
asio::io_service _io_service;

// Make io_service::work
asio::io_service::work _work(_io_service);

// Runs until work is reset
_io_service.run();

This example will lock itself indefinitely, because io_service::work will keep the io_service busy.

Self-resetting work

// Make io_service
asio::io_service _io_service;

// Make io_service::work
using work_ptr = std::unique_ptr<asio::io_service::work>;
work_ptr _work(new work_ptr::element_type(_io_service));

// Self resetting work
auto io_work = [](work_ptr & work)
{
	using namespace std::literals::chrono_literals;
	std::this_thread::sleep_for(6000ms);

	work.reset();
};

// Post work
_io_service.post(std::bind(io_work, std::ref(_work)));

// Runs until work is reset
_io_service.run();

Scaling out with threads

// Make io_service
asio::io_service _io_service;

// Make io_service::work
using work_ptr = std::unique_ptr<asio::io_service::work>;
work_ptr _work(new work_ptr::element_type(_io_service));

// Start and make threads available to the io_service
constexpr int num_threads = 8;

std::array<std::thread, num_threads> threads;;

for (auto i = 0; i < num_threads; ++i)
{
	threads[i] = std::thread([&_io_service]() {
		_io_service.run();
	});
}

// Make the threads work
auto print_thread_id = []()
{
	std::cout << "Current thread id: " << std::this_thread::get_id() << "n";
};

for (auto i = 0; i < num_threads * 2; ++i)
{
	_io_service.post(print_thread_id);
}

// Reset work
_work.reset();

// Wait for completion
for (auto & thread : threads)
{
	thread.join();
}

/*
Current thread id: Current thread id: 9604
Current thread id: 9604
Current thread id: 8696
7880
Current thread id: 6208
Current thread id: 9604
Current thread id: 8312
Current thread id: 5604
Current thread id: 8576
Current thread id: 1712
Current thread id: 8696
Current thread id: 7880
Current thread id: 6208
Current thread id: 8312
Current thread id: 9604
Current thread id: 5604
*/

epoll: Function not implemented

If you’re using boost::asio and you’re getting this error, compile your project with BOOST_ASIO_DISABLE_EPOLL defined.

Senior Software Engineer developing all kinds of stuff.