Skip to content

Async.MQTT5: A New C++20 MQTT Client Implementation

by Ivica Siladić
10 min read
HiveMQ welcomes this guest post from Ivica Siladić at Mireo. His team developed a modern C++ MQTT client that works out of the box with HiveMQ Cloud and we believe is a very exciting library as you set up an MQTT implementation. 

Async.MQTT5 is a modern C++20 client implementation of the MQTT 5.0 protocol. The client is constructed on Boost Asio, fully adhering to the Asio asynchronous model, which includes comprehensive support for all forms of asynchronous completion tokens, per-operation cancellation, and custom allocators. Async.MQTT5 is a header-only library with no third-party dependencies besides the Boost library.

The library constitutes a complete implementation of the MQTT 5.0 protocol standard, offering full support for publishing or receiving messages with QoS 0, 1, and 2. 

It also provides the option to integrate extended challenge/response-style authentication. Async.MQTT5 can be used with any underlying ordered network transport, typically TCP, SSL/TLS over TCP, or WebSockets (secure and unsecured).

Async.MQTT5 is BSD-3 licensed and may be freely used without restrictions in commercial or non-commercial products. It is our intention to include Async.MQTT5 into the Boost C++ library, providing a professional, industrial-grade C++ implementation of the client-side of the MQTT 5.0 protocol to developers.

Async.MQTT5 seamlessly integrates with HiveMQ Cloud, allowing you to experiment without the immediate need to set up a broker using the Serverless Free Plan.

Below is a simple yet complete example of a client publishing a 'Hello World!' message with QoS 0 to your HiveMQ MQTT broker:

int main() {
   namespace asio = boost::asio;
   asio::io_context ioc;


   using stream_type = asio::ssl::stream<asio::ip::tcp::socket>;
   asio::ssl::context tls_context(asio::ssl::context::tls_client);


   using client_type = async_mqtt5::mqtt_client<stream_type, asio::ssl::context>;
   client_type c(ioc, "", std::move(tls_context));


   c.credentials("<your client id>", "<client-username>", "<client-pwd>")
       .brokers("<your-hivemq-cluster-url>", 8883)
       .run();


   c.async_publish<async_mqtt5::qos_e::at_most_once>(
       "test", "hello world!",
       async_mqtt5::retain_e::no, async_mqtt5::publish_props {},
       [&c](async_mqtt5::error_code ec) {
           std::cout << ec.message() << std::endl;
           c.cancel(); // close the client
       }
   );


   ioc.run();
}

Library Design and Features

According to the formal specification, the "MQTT protocol is lightweight, open, simple, and designed to be easy to implement." However, correctly integrating the client side of the protocol into a robust, 24/7 IoT device that correctly deals with continuous connectivity losses is not a straightforward task. Existing MQTT client C++ libraries tend to leave the complex disconnect/backoff/reconnect behavior to developers, which often results in long development times, very difficult testing, and hard-to-get reliability. For that reason, the primary goal of Async.MQTT5 was to "do the hard part" once and for all and let the developers focus on messages they need to send, without worrying about what should happen when the underlying network connectivity gets lost.

Async.MQTT5 aims to provide developers with a simple and clear C++ interface for sending and receiving messages using the MQTT 5.0 protocol. The library hides many MQTT implementation details that typically require deep MQTT protocol knowledge but are generally irrelevant to application developers.

The following is a comprehensive list of Async.MQTT5 library features:

  1. Full implementation of MQTT 5.0 specification

  2. User-focused simplicity: Providing an interface that is as simple as possible without compromising functionality.

  3. Complete TCP, TLS/SSL, and WebSocket support

  4. Support for QoS 0, QoS 1, and QoS 2

  5. Extended authentication: Async.MQTT5 defines an interface for your own custom authenticators to perform Enhanced Authentication.

  6. Automatic reconnect: Automatically attempt to re-establish a connection when connection gets dropped. Re-transmit messages in the correct order after reconnection.

  7. Minimal memory footprint: Ensuring optimal performance in resource-constrained environments typical for IoT devices.

  8. Prioritized efficiency: Utilizing network and memory resources as efficiently as possible.

  9. Fully Boost Asio compliant: The interfaces and implementation strategies are built upon the foundations of Boost Asio. Boost Asio and Boost Beast users will have no issues understanding and integrating Async.MQTT5. Furthermore, Async.MQTT5 integrates well with any other library within the Boost Asio ecosystem.

  10. Completion Token: All asynchronous functions support all forms of Completion Tokens, allowing for versatile usage with callbacks, coroutines, futures, and more.

  11. Per-Operation Cancellation: All asynchronous operations support individual, targeted cancellation as per Asio's Per-Operation Cancellation.

  12. Custom allocators: Support for custom allocators allows extra flexibility and control over the memory resources. Async.MQTT5 will use allocators associated with handlers from asynchronous functions to create instances of objects needed in the library implementation.

  13. High availability: Async.MQTT5 supports listing multiple brokers within the same cluster to which the client can connect. In the event of a connection failure with one broker, the client switches to the next in the list.

Some Design and Implementation Notes

MQTT is a full-duplex protocol, and both the client and broker may send messages through the communication link at any time. From the programming interface point of view, getting notifications about received messages (or some other communication event) is typically implemented through a set of registered callback functions. This exact point is the main reason why it is generally hard to implement a stable and reliable IoT application that needs to send or receive MQTT messages. With out-of-the-blue callbacks, the application flow becomes highly non-procedural; it requires tedious synchronization between threads, and, in the end, becomes very hard to debug.

On the other hand, network protocols are inherently asynchronous, and MQTT is no exception. Asynchronous programming naturally involves the "callbacks" concept, and it is, therefore, quite challenging to programmatically communicate over the network in a procedural fashion. C++ coroutines are designed to convert the asynchronous callback model into procedural steps, which are then easy to develop and debug. Async.MQTT5 is designed precisely with this in mind, and for that reason, all library interfaces strictly adhere to Boost Asio's asynchronous model.

Code readability was also one of the main design goals of Async.MQTT5. For example, tedious protocol message parsing/composing is implemented in a declarative manner (partially using Boost Spirit), and Asio composed operations are implemented using a novel, clear, and extremely efficient "chaining functional operators" approach, and so on.

How to Get Async.MQTT5

You can get Async.MQTT5 from https://github.com/mireo/async-mqtt5. The library is open-sourced with a permissive BSD-3 license, and you may use it freely in any kind of project. The full API documentation and examples are available at https://spacetime.mireo.com/async-mqtt5/.

Ivica Siladić

Ivica Siladić is one of the founders and the CTO at Mireo, a Croatian-based company specializing in embedded GPS navigation systems and GPS/GSM vehicle tracking. For the last 15 years, he’s been managing and directing the development of all vital Mireo products, gathering experience in embedded systems, digital maps and GPS navigation systems, fleet tracking, and Big Data analysis.

  • Contact Ivica Siladić via e-mail
HiveMQ logo
Review HiveMQ on G2