Any program performs I/O operations in various forms like a file, a network, serial port or simple console operation. Particularly in case of networking, sometimes individual I/O operation takes long time to complete. This poses its own kind of challenges to application development in managing CPU cycles and longer execution time etc. One option that comes to mind is to make a program multi-threaded and give the long running I/O operation to a thread. However, there is another solution to the same problem and that is to perform I/O operations asynchronously.
There are few things to notice about asynchronous I/O over thread
- With Asynchronous code it is known where the control will shift from one task to another; hence race conditions are harder to arrive. In case of multi-threaded program the code needs to be thread safe to control race conditions. The I/O part of threaded code is comparatively easy but managing the shared state of critical section between threads (using locks/queues/etc) without race conditions is what makes it challenging. Using an asynchronous model means you have less activities going on at the same time so races can be easily avoided.
- With asynchronous approach, whole code shares the same process stack and it is kept small by continuous stack unwinding between tasks. Each thread will consume at least one memory page of stack, plus some unknown amount of memory for other data structures related to that thread’s state.
Boost.Asio (Asynchronous Input/ Output) provides rich set of APIs to handle long running jobs, without spawning multiple threads and providing synchronization mechanisms between them.
In a typical asynchronous network application, if data is sent over the Internet, it is important to know whether it has been sent successfully or not. With the synchronous way, the return value of a function would be evaluated. However, that would require the main program to wait until all data bytes has been sent and either an acknowledgement or an error code is available. Using Boost.Asio, the process is divided into two steps:
- Start the data transmission as an asynchronous task and return immediately.
- Once the transmission has finished either successful or with an error, notify the application about the result to its registered callback accordingly.
The key difference is that the application does not need to block/wait until the transmission has finished but can execute other operations in the meantime.
Below are few more features of Boost.Asio
- Boost Asio is compatible with most of the existing platforms like Windows, flavors of UNIX, QNX etc. It enables programmers to write cross-platform networking code.
- It supports widely used networking protocols like TCP, UDP and standards like IPv4 and IPv6.
- It supports asynchronous operations over serial port.
- Its std::iostream compatible interface makes its usage familiar to C++ developers.
- support for SSL connections
- support for delayed operations (deadline timers)
Boost::Asio has come to rescue in one of our C++ based project where we wanted to develop a
- Centralized controller to manage different types of I/O operations in any C++ based system.
- TCP socket and serial port I/O operations should be supported and it should be scalable enough to accommodate more I/O types like UDP communication in future.
- The Controller should be highly versatile to support N number of different I/O operations.
- It should be reliable to control data losses and integrity while sending and receiving.
- It should address frequent reconnects happening due to network glitches between clients and servers.
- All I/O operations should be completed in a non blocking way for better performance and user experience.
To achieve the solution, we programmed a centralized I/O Hub using boost Asio with following approach –
- Any Server socket is of no use to outside word till at least one client is connected to it so that it becomes readable or writable. Hence, numbers of Server Ports is abstracted, can stay within I/O Hub without being exposed outside.
- Provide an XML based configuration to I/O Hub for each system, specifying Role of a Port (i.e TCP Server, TCP Client, Serial Port etc.) and other required parameters like baud rate, parity, flow control etc. in case of serial Port and IP Address, port, keepAlive interval etc. for TCP Ports.
- Develop a common Port interface class for all users, and inherit TCPClientPort and SerialPort classes from the same interface. C++ virtual polymorphism mechanism has helped to hide the implementation details about each Port.
- To support non blocking I/O operation, we used asynchronous model over thread concurrency due to reasons explained at the beginning of the article. We used Boost.Asio to implement one common I/O service among all operations and implemented own set of methods for synchronous as well as asynchronous send and receive variation. The variations covers send & receive for exactly specified number of bytes, till particular delimiter is received or just keep on streaming till timeout occurs etc.
- Each variation expects one call back from the user system where it can be notified with success or error status when a particular send and receive operation gets finished.
- Every user need to register minimum two call backs to I/O Hub, One when its port is ready and one when its port is gone or disconnected , irrespective of type of port. These are the callbacks where any system will get notified about its ports available or not available status. These callbacks have one common parameter of port interface for the caller systems. This mechanism has made the port creation and disconnection process asynchronous. The user system can continue with its work, whenever Hub is ready with the port based on provided configuration, it will notify the system so that send / receive operations based processing can be started or stopped.
- The I/O Hub is also capable of keeping track of all running servers and live clients connected to each server, hence any system can query about its connections.
- Whenever any client to a server gets disconnected, before notifying the system, Hub waits asynchronously for some pre-configured interval and if the client comes back in stipulated duration the same socket is retained and send / receive continues uninterrupted.
As a result a highly scalable, versatile and non blocking, centralized I/O Hub has been developed by taking Boost.Asio as a base technology for data communication and with the help of few C++ features.
Boost Asio supports both synchronous and asynchronous operations. It uses Proactor design pattern for implementing asynchronous and Reactor pattern to support synchronous operations respectively.
Below are the main building blocks of Boost Asio.
The library also provides various Macros to control the behavior of Boost Asio.
Boost.Asio is available in boost distributions in version 1.35 and higher. You can also download it as a separate package from SourceForge link provided below
Hetal is a Technical Lead at eInfochips, working with PES (Product Engineering Services) division. She has delivered multiple C, C++, UNIX based projects in domains like telecommunications, retail, datacenter automation and embedded software. Hetal holds a degree in Computer Engineering from Ganpat University.