×

Search anything:

Basics of C++ Boost.ASIO

C++

Binary Tree book by OpenGenus

Open-Source Internship opportunity by OpenGenus for programmers. Apply now.

Boost.Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.

Note : To compile the code snippets provided , you need to setup the boost library first using the instructions provided here.

Table of contents:

  1. Introduction
  2. Installing Boost
  3. Basic Boost.Asio Anatomy
  4. Callback Functions or Completion Handlers
  5. Binding Arguments to a Handler
  6. Conclusion

1. Introduction

Many programs engage with the external environment through various means, whether it involves interacting with files, networks, serial cables, or the console. In scenarios like networking, individual input/output (I/O) operations can become time-consuming, presenting distinct challenges in application development. Boost.Asio emerges as a solution, offering tools to handle prolonged operations without necessitating the adoption of concurrency models reliant on threads and explicit locking.

Primarily designed for C++ programmers engaged in systems programming, where access to operating system functionalities like networking is crucial, the Boost.Asio library targets specific objectives:

  1. Portability: Ensuring compatibility with a variety of commonly used operating systems and maintaining consistent behavior across them.

  2. Scalability: Facilitating the creation of network applications capable of scaling to handle thousands of concurrent connections. The library's implementation on each operating system should prioritize mechanisms promoting scalability.

  3. Efficiency: Supporting techniques such as scatter-gather I/O and enabling programs to minimize data copying. The library draws inspiration from established APIs like BSD sockets, widely implemented and understood across literature, and strives to align with existing practices in other programming languages that adopt similar networking API interfaces.

  4. Ease of Use: Embracing a toolkit approach over a framework, the library seeks to lower the entry barrier for new users. Users should invest minimal upfront time in learning a few basic rules and guidelines. Subsequently, understanding specific functions in use becomes the primary requirement.

  5. Basis for Further Abstraction: Allowing the development of additional libraries that offer higher levels of abstraction, such as implementations for commonly used protocols like HTTP.

While initially focused on networking, Boost.Asio has expanded its concepts of asynchronous I/O to encompass other operating system resources like serial ports and file descriptors.

2. Installing Boost Library & Compiling Programs

To install the boost library , we have to following setps :

  1. Download Boost: Visit the Boost website (https://www.boost.org/) and download the latest version of the Boost library.

  2. Extract the Archive: Once downloaded, extract the contents of the downloaded archive to a directory of your choice.

  3. Build Boost:Open a terminal or command prompt and navigate to the directory where you extracted Boost.

    • Run the bootstrap script:
      ./bootstrap.sh
      
    • This will generate the necessary configuration files.
    • Then, run the following command to build Boost:
      ./b2
      
    • Depending on your system, you might need to use sudo or administrator privileges.
  4. Install Boost (Optional): If you want to install Boost system-wide, run:

    sudo ./b2 install
    

    Otherwise, you can use Boost from the directory where you built it.

Compiling and Running Programs with Boost

Assuming you have a C++ program that utilizes Boost libraries, here are the steps to compile and run it:

  1. Include Boost Headers:In your C++ source files, include the necessary Boost headers using #include directive. For example:

    #include <boost/algorithm/string.hpp>
    
  2. Compile:Compile your C++ program with the Boost libraries. Use -I to specify the Boost include directory and -L to specify the Boost library directory. For example:

    g++ -o program_name source_file.cpp -I/path/to/boost -L/path/to/boost/lib -lboost_library_name
    
  3. Run:Execute your compiled program as usual:

    ./program_name
    

Replace program_name, source_file.cpp, /path/to/boost, and boost_library_name with your actual program name, source file, Boost library path, and the specific Boost library you're using respectively.

Example

Let's say you have a C++ program example.cpp that uses Boost's string algorithms:

#include <iostream>
#include <boost/algorithm/string.hpp>

int main() {
    std::string s = "hello boost library";
    boost::algorithm::to_upper(s);
    std::cout << s << std::endl;
    return 0;
}

You would compile and run it as follows:

g++ -o example example.cpp -I/path/to/boost -L/path/to/boost/lib -lboost_system
./example

Ensure you replace /path/to/boost with the actual path where Boost is installed on your system and boost_library_name with the appropriate Boost library you're using, such as boost_system for this example.

3. Basic Boost.Asio Anatomy

Boost.Asio may be used to perform both synchronous and asynchronous operations on I/O objects such as sockets. Before using Boost.Asio it may be useful to get a conceptual picture of the various parts of Boost.Asio, your program, and how they work together.

As an introductory example, let's consider what happens when you perform a connect operation on a socket. We shall start by examining synchronous operations.

sync_op

Your program will have at least one io_service object. The io_service represents your program's link to the operating system's I/O services.

boost::asio::io_service io_service;

To perform I/O operations your program will need an I/O object such as a TCP socket:

boost::asio::ip::tcp::socket socket(io_service);

Synchronous Connection

When a synchronous connect operation is performed, the following sequence of events occurs:

  1. Your program initiates the connect operation by calling the I/O object :
socket.connect(server_endpoint);
  1. The I/O object forwards the request to the io_service.

  2. The io_service calls on the operating system to perform the connect operation.

  3. The operating system returns the result of the operation to the io_service.

  4. The io_service translates any error resulting from the operation into an object of type boost::system::error_code. An error_code may be compared with specific values, or tested as a boolean (where a false result means that no error occurred). The result is then forwarded back up to the I/O object.

  5. The I/O object throws an exception of type boost::system::system_error if the operation failed. If the code to initiate the operation had instead been written as:

boost::system::error_code ec;
socket.connect(server_endpoint, ec);

then the error_code variable ec would be set to the result of the operation, and no exception would be thrown.

Asynchronous Connection

When an asynchronous operation is used, a different sequence of events occurs.

async_op1

  1. Your program initiates the connect operation by calling the I/O object:
socket.async_connect(server_endpoint, your_completion_handler);

where your_completion_handler is a function or function object with the signature:

void your_completion_handler(const boost::system::error_code& ec);

The exact signature required depends on the asynchronous operation being performed. The reference documentation indicates the appropriate form for each operation.

  1. The I/O object forwards the request to the io_service.

  2. The io_service signals to the operating system that it should start an asynchronous connect.

Time passes.......

(In the synchronous case this wait would have been contained entirely within the duration of the connect operation.)

  1. The operating system indicates that the connect operation has completed by placing the result on a queue, ready to be picked up by the io_service.

  2. Your program must make a call to io_service::run() (or to one of the similar io_service member functions) in order for the result to be retrieved. A call to io_service::run() blocks while there are unfinished asynchronous operations, so you would typically call it as soon as you have started your first asynchronous operation.

  3. While inside the call to io_service::run(), the io_service dequeues the result of the operation, translates it into an error_code, and then passes it to your completion handler.

This is a simplified picture of how Boost.Asio operates.

async_op2

You will want to delve further into the documentation if your needs are more advanced, such as extending Boost.Asio to perform other types of asynchronous operations.

4. Callback Functions or Completion Handlers

The callback functions are used in the asynchronous operations. Whenever the async operation finishes it calls a callback function. After any kind of async operation , the callback function are must to be passed. Asynchronous operations in Boost.Asio are often initiated using the async_ functions, such as async_read, async_write, or async_connect. These functions take a handler (callback function) as a parameter, and this handler is called when the operation completes or encounters an error.

Here's a brief example to illustrate the concept of a callback function in Boost.Asio:

#include <iostream>
#include <boost/asio.hpp>

// callback handler
void timerCallback(const boost::system::error_code& /*error*/) {
    std::cout << "Timer callback called!" << std::endl;
}

int main() {

    // create an io_service
    boost::asio::io_service io_service;

    // Create a deadline timer 
    boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(3));

    // Set the timer to call the callback function after 3 seconds
    timer.async_wait(timerCallback);
    
    // Print function while async is working
    std::cout<<"Main function work\n";

    // Run the IO context to start the asynchronous operations
    io_service.run();

    return 0;
}

Output :

Main function work
(wait for async operation and callback function to finish )
Timer callback called!

The example in shows the use of callback handler. In main function we have created an io_service and then creates a deadline_timer which is like a countdown. When the countdown will finish it will be considered as completion of an operation.The timer calls a async_wait function which takes in a function as argument.

Here's how the process will occur go on :

  1. The main function creates and io_service object and creates a deadline timer which will call async wait.
  2. The async wait will wait for the deadline timer to finish its count asynchronously.
  3. The async_wait return immediately in the main function after making the deadline timer run asynchronously.
  4. It will print the "Main function work" as the deadline timer is running in background.
  5. The io_service.run function is a function which waits for callback functions to finish their work. In our case the function will print "Timer callback called!" . To be more clear as the deadline timer expires it will call the callback function or completion handler.
  6. The run function will join the callback functions back in main thread. If the callback function have not finished it's work or the deadline timer has not expired yet then the run will wait to the async operation to finish.

5. Binding Arguments to a Handler

Binding arguments to a handler in Boost.Asio is a way to pass additional information to the handler function when it is invoked. This can be useful in scenarios where you need to provide extra context or parameters to the handler. The boost::bind function is commonly used for this purpose.

Here's a simple example demonstrating how to bind arguments to a handler using boost::bind:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

using boost::asio::ip::tcp;

void handler(const boost::system::error_code& error, int arg1, double arg2) {
    if (!error) {
        std::cout << "Handler called with arguments: " << arg1 << ", " << arg2 << std::endl;
    } else {
        std::cerr << "Error in handler: " << error.message() << std::endl;
    }
}

int main() {
    boost::asio::io_service io_service;

    // Simulating some arguments
    int arg1 = 42;
    double arg2 = 3.14;

    boost::asio::deadline_timer timer(io_service , boost::posix_time::seconds(3));

    // Bind arguments to the handler
    auto bound =  boost::bind(handler, boost::asio::placeholders::error, arg1, arg2);

    // calling async wait
    timer.async_wait(bound);

    // Run the io_service to execute handlers
    io_service.run();

    return 0;
}

Output :

Handler called with arguments: 42, 3.14

In this example:

  1. We have a handler function that takes three parameters: a boost::system::error_code, an integer arg1, and a double arg2.

  2. We use boost::bind to create a new function object (bound_handler) by binding the handler function with specific values for its arguments. The placeholders (boost::asio::placeholders::error) are used to indicate that the error code parameter will be supplied later.

  3. We simulate some arguments (arg1 and arg2), and then we use post to post the bound_handler to the io_service for execution.

  4. The io_service.run() call is used to execute the posted handlers.

This is a basic example, but it demonstrates the concept of binding arguments to a handler. In a real-world scenario, you might use this technique when working with asynchronous operations and need to pass additional context or parameters to the completion handler.

6. Conclusion

In conclusion, Boost.Asio is a powerful and versatile C++ library designed for network and low-level I/O programming. It provides a consistent asynchronous model, allowing developers to perform I/O operations efficiently without relying on explicit thread management. The library is cross-platform, ensuring portability across different operating systems.

Here's a summary of key points covered:

  1. Introduction: Boost.Asio addresses challenges in systems programming, offering a modern C++ approach to handle asynchronous I/O operations efficiently. It prioritizes portability, scalability, efficiency, ease of use, and serves as a basis for building higher-level abstractions.

  2. Basic Boost.Asio Anatomy: The core of Boost.Asio involves the io_service object, representing the program's link to the operating system's I/O services. I/O operations are performed on objects like sockets. Synchronous and asynchronous operations have distinct sequences of events, with asynchronous operations involving callback functions or completion handlers.

  3. Callback Functions or Completion Handlers: Asynchronous operations in Boost.Asio are often initiated using async_ functions, and they involve callback functions or completion handlers. These handlers are called when an asynchronous operation completes or encounters an error. The example illustrated the use of a timer callback function in an asynchronous context.

  4. Binding Arguments to a Handler: Binding arguments to a handler is a technique used to pass additional information to the handler function when it's invoked. The boost::bind function allows developers to create function objects with specific argument values. This is useful when extra context or parameters need to be provided to the completion handler in asynchronous operations.

Boost.Asio's flexibility, combined with its adherence to modern C++ principles, makes it a valuable tool for developers working on network applications or any scenario involving asynchronous I/O. Asynchronous programming in Boost.Asio enhances the efficiency and responsiveness of applications, and the library's design principles contribute to a smooth learning curve for developers.

Key Takeaways (Basics of Boost.Asio)

  • Boost.Asio is a powerful C++ library for network and low-level I/O programming, providing a consistent asynchronous model.
  • Boost.Asio aims for portability, scalability, and efficiency in handling I/O operations without explicit thread management.
  • The library's core involves the `io_service` object, representing the program's link to the operating system's I/O services.
  • Synchronous and asynchronous operations have distinct sequences of events, with asynchronous operations involving callback functions or completion handlers.
  • Callback functions in Boost.Asio are essential for handling asynchronous operation completion or errors.
  • Binding arguments to a handler using `boost::bind` allows passing additional information to the handler function during invocation.
  • Boost.Asio's flexibility, adherence to modern C++ principles, and efficient asynchronous programming make it valuable for network applications.
Basics of C++ Boost.ASIO
Share this