Le moyen le plus rapide d'utiliser MQTT en C++ est le client Eclipse Paho C++ (paho.mqtt.cpp) : créez un mqtt::async_client, connectez-vous avec un connect_options_builder, puis appelez publish() et subscribe() — chaque appel renvoie un token sur lequel vous pouvez faire wait(). Si votre application tourne déjà sur Boost.Asio, async_mqtt est une alternative header-only qui s'intègre nativement à ce modèle.
Ce tutoriel construit un publisher et un subscriber avec Paho C++, ajoute TLS, les reconnexions et MQTT 5, puis montre comment tout tester contre un vrai broker.
Choisir une bibliothèque
| Bibliothèque | Modèle | Protocole | Idéale pour |
|---|---|---|---|
Paho C++ (paho.mqtt.cpp) | Fine surcouche C++ de la bibliothèque asynchrone Paho C ; tokens, callbacks ou file de consommation | MQTT 3.1, 3.1.1, 5 ; TCP, TLS, WebSocket | La plupart des applications, passerelles Linux, outils desktop |
| async_mqtt | Header-only, completion tokens Boost.Asio (callbacks, futures, coroutines C++20) | MQTT 3.1.1 et 5 ; TCP, TLS, WebSocket | Serveurs basés sur Asio, nombreuses connexions, quand vous voulez aussi des briques pour un broker/endpoint |
Paho C (paho.mqtt.c) | API en C pur | MQTT 3.1.1 et 5 | Projets en C, ou quand le C++ n'est pas possible — voir MQTT en C avec Paho |
Pour les microcontrôleurs, aucune des deux n'est le choix habituel ; des bibliothèques C embarquées comme coreMQTT ou le client d'ESP-IDF sont plus légères. La suite de ce guide vise les hôtes Linux, macOS et Windows. Pour un panorama plus large des bibliothèques clientes par langage, consultez les meilleures bibliothèques MQTT.
Installer Paho C++
Paho C++ a besoin de la bibliothèque Paho C (compilée avec SSL si vous voulez TLS). Trois approches courantes :
# Debian / Ubuntu (distribution packages can lag behind the latest release)
sudo apt install libpaho-mqtt-dev libpaho-mqttpp-dev
# vcpkg
vcpkg install paho-mqttpp3
# From source, building the bundled Paho C library with TLS
git clone https://github.com/eclipse/paho.mqtt.cpp
cd paho.mqtt.cpp && git submodule update --init
cmake -Bbuild -DPAHO_WITH_MQTT_C=ON -DPAHO_WITH_SSL=ON
sudo cmake --build build --target installUn CMakeLists.txt minimal lie la cible importée, qui embarque pour vous la bibliothèque C et OpenSSL :
cmake_minimum_required(VERSION 3.16)
project(mqtt_demo CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(PahoMqttCpp REQUIRED)
add_executable(publish publish.cpp)
target_link_libraries(publish PRIVATE PahoMqttCpp::paho-mqttpp3)
add_executable(subscribe subscribe.cpp)
target_link_libraries(subscribe PRIVATE PahoMqttCpp::paho-mqttpp3)Si vous faites l'édition de liens à la main et voyez undefined reference to MQTTAsync_create, il manque la bibliothèque C : liez paho-mqtt3as (la variante asynchrone avec SSL) après paho-mqttpp3.
Publier un message
Chaque appel asynchrone renvoie un token. Appeler ->wait() le transforme en appel bloquant, ce qui est exactement ce qu'il vous faut dans un petit outil :
#include <chrono>
#include <iostream>
#include <mqtt/async_client.h>
int main() {
const std::string server = "tcp://broker.connectmq.com:1883";
mqtt::async_client client(server, "testmqtt-cpp-pub");
auto opts = mqtt::connect_options_builder()
.clean_session()
.keep_alive_interval(std::chrono::seconds(30))
.finalize();
try {
client.connect(opts)->wait();
auto msg = mqtt::make_message("testmqtt/cpp/hello", "Hello from C++", 1, false);
client.publish(msg)->wait(); // returns after PUBACK for QoS 1
std::cout << "published" << std::endl;
client.disconnect()->wait();
} catch (const mqtt::exception& e) {
std::cerr << e.what() << std::endl;
return 1;
}
}En QoS 1, le wait() sur le token de publication ne rend la main qu'une fois le message acquitté par le broker. En QoS 0, il revient dès que le paquet est écrit. Consultez les niveaux de QoS MQTT pour savoir quand chacun en vaut la peine.
S'abonner avec la file de consommation
Le subscriber le plus simple se passe complètement de callbacks : start_consuming() place les messages entrants dans une file thread-safe que vous lisez dans votre propre boucle.
#include <iostream>
#include <mqtt/async_client.h>
int main() {
mqtt::async_client client("tcp://broker.connectmq.com:1883", "testmqtt-cpp-sub");
auto opts = mqtt::connect_options_builder()
.clean_session()
.finalize();
try {
client.start_consuming(); // before connect, so nothing is missed
client.connect(opts)->wait();
client.subscribe("testmqtt/cpp/#", 1)->wait();
std::cout << "waiting for messages..." << std::endl;
while (true) {
auto msg = client.consume_message(); // blocks
if (!msg) break; // nullptr: connection lost
std::cout << msg->get_topic() << " -> " << msg->to_string() << std::endl;
}
} catch (const mqtt::exception& e) {
std::cerr << e.what() << std::endl;
return 1;
}
}Lancez le subscriber, puis le publisher dans un second terminal. Vous pouvez aussi publier sur testmqtt/cpp/hello depuis le client MQTT en ligne et voir la ligne apparaître dans votre terminal.
Callbacks et reconnexion automatique
Les services de longue durée préfèrent généralement les callbacks et doivent survivre aux coupures réseau. Paho C++ propose des setters à base de lambdas pour chaque événement, et automatic_reconnect() gère le back-off :
#include <chrono>
#include <iostream>
#include <thread>
#include <mqtt/async_client.h>
int main() {
mqtt::async_client client("tcp://broker.connectmq.com:1883", "testmqtt-cpp-service");
client.set_connected_handler([&client](const std::string&) {
std::cout << "connected, subscribing" << std::endl;
client.subscribe("testmqtt/cpp/commands/#", 1); // no wait() in a callback!
});
client.set_connection_lost_handler([](const std::string& cause) {
std::cerr << "connection lost: " << cause << std::endl;
});
client.set_message_callback([](mqtt::const_message_ptr msg) {
std::cout << msg->get_topic() << " " << msg->get_payload_str() << std::endl;
});
auto opts = mqtt::connect_options_builder()
.clean_session(false)
.keep_alive_interval(std::chrono::seconds(30))
.automatic_reconnect(std::chrono::seconds(1), std::chrono::seconds(30))
.finalize();
client.connect(opts)->wait();
while (true) std::this_thread::sleep_for(std::chrono::seconds(60));
}Trois règles gardent le code à base de callbacks en bonne santé :
- Ne bloquez jamais dans un callback. Appeler
->wait()dansconnectedoumessage_arrivedattend sur le thread même qui doit compléter le token. Lancez l'appel et continuez, ou transférez le travail vers votre propre file. - Réabonnez-vous à la connexion. Le connected handler s'exécute après chaque (re)connexion réussie, donc les abonnements reviennent même si le broker a perdu la session.
- Utilisez un client ID stable et unique avec
clean_session(false). Deux processus avec le même ID s'éjectent mutuellement en boucle. Générez-en un avec le générateur de client ID et lisez les bonnes pratiques de reconnexion pour le back-off.
Les sessions persistantes exigent un broker qui les conserve : l'endpoint partagé broker.connectmq.com ne garde aucune session hors ligne, testez donc clean_session(false) contre votre propre broker privé. Sur l'endpoint partagé, c'est le réabonnement dans le connected handler qui maintient le service en marche.
TLS et authentification
Pour tout ce qui dépasse un test rapide, connectez-vous en TLS sur le port 8883 avec des identifiants. Passez le schéma de l'URI à ssl:// et ajoutez des ssl_options :
#include <cstdlib>
#include <mqtt/async_client.h>
static std::string env(const char* name) {
const char* v = std::getenv(name);
return v ? v : "";
}
int main() {
mqtt::async_client client("ssl://broker.connectmq.com:8883", "testmqtt-cpp-tls");
auto ssl = mqtt::ssl_options_builder()
.trust_store("/etc/ssl/certs/ca-certificates.crt") // system CA bundle
.enable_server_cert_auth(true)
.finalize();
auto opts = mqtt::connect_options_builder()
.user_name(env("MQTT_USER"))
.password(env("MQTT_PASS"))
.ssl(std::move(ssl))
.finalize();
client.connect(opts)->wait();
client.publish("testmqtt/cpp/secure", "over TLS", 1, false)->wait();
client.disconnect()->wait();
}Le chemin du bundle de CA dépend du système (/etc/pki/tls/certs/ca-bundle.crt sur la famille RHEL ; sur macOS, exportez-en un ou pointez vers le bundle de l'OpenSSL de Homebrew). Si la connexion échoue avant le CONNACK, vérifiez la chaîne de certificats depuis la même machine en suivant tester une connexion MQTT TLS.
MQTT 5 avec Paho C++
MQTT 5 doit être activé explicitement. Créez le client avec les options MQTT 5 et utilisez le builder v5, qui accepte aussi des propriétés :
mqtt::async_client client("tcp://broker.connectmq.com:1883", "testmqtt-cpp-v5",
mqtt::create_options(MQTTVERSION_5));
auto opts = mqtt::connect_options_builder::v5()
.clean_start(false)
.properties({{mqtt::property::SESSION_EXPIRY_INTERVAL, 3600}})
.finalize();
client.connect(opts)->wait();Avec MQTT 5, le broker renvoie des reason codes au lieu des anciens return codes sur un octet, donc un échec vous en dit beaucoup plus (par exemple 0x87 Not authorized ou 0x97 Quota exceeded). Retrouvez n'importe quel code dans l'outil des reason codes MQTT. Les différences entre versions sont résumées dans MQTT 5 vs 3.1.1.
async_mqtt : l'option native Asio
async_mqtt est une bibliothèque header-only en C++17 bâtie sur Boost.Asio, par l'auteur de l'ancien mqtt_cpp. Chaque opération prend un completion token Asio, si bien que le même appel peut utiliser un callback, un std::future ou co_await dans une coroutine C++20. Elle prend en charge MQTT 3.1.1 et 5 sur TCP, TLS et WebSocket, et fournit aussi les briques d'un broker ou d'un endpoint serveur.
Choisissez-la quand :
- votre programme exécute déjà un
io_context(un serveur basé sur Asio, une passerelle avec de nombreux sockets), - vous voulez des coroutines plutôt que des chaînes de callbacks,
- vous préférez une dépendance header-only à une bibliothèque C compilée.
Son API a évolué entre versions majeures : partez des exemples du dépôt correspondant à la version que vous installez plutôt que d'anciens extraits. Sur le principe, le flux est le même qu'avec Paho : ouvrir le transport, envoyer CONNECT et attendre le CONNACK, envoyer SUBSCRIBE, puis boucler sur la réception des paquets PUBLISH.
Erreurs courantes
| Message | Cause probable | Correctif |
|---|---|---|
MQTT error [-1]: TCP/TLS connect failure | Hôte ou port erroné, pare-feu ou échec du handshake TLS | Vérifiez que le schéma de l'URI correspond au port (tcp:// 1883, ssl:// 8883), puis testez le port depuis la même machine |
La connexion échoue immédiatement avec une URI ssl:// | Paho C compilé sans prise en charge SSL | Recompilez avec -DPAHO_WITH_SSL=ON ou utilisez un paquet compilé avec OpenSSL |
| Connection lost toutes les quelques secondes | Un autre client utilise le même client ID | Donnez un ID unique à chaque processus |
undefined reference to MQTTAsync_* | Bibliothèque C non liée | Liez PahoMqttCpp::paho-mqttpp3 via CMake, ou ajoutez paho-mqtt3as |
CONNACK refusé (rc 4/5, ou 0x86/0x87 en MQTT 5) | Identifiants erronés ou non autorisé | Vérifiez nom d'utilisateur, mot de passe et ACL |
Tester de bout en bout
Avant d'accuser votre code, prouvez que le côté broker fonctionne avec un client éprouvé. Lancez un subscriber dans le terminal et exécutez votre publisher :
mosquitto_sub -h broker.connectmq.com -p 1883 -t 'testmqtt/cpp/#' -vPuis inversez : lancez votre subscriber C++ et envoyez un message depuis la ligne de commande :
mosquitto_pub -h broker.connectmq.com -p 1883 -t 'testmqtt/cpp/commands/led' -m 'on' -q 1Pour éprouver les reconnexions, désactivez votre interface réseau plus longtemps que l'intervalle de keep-alive et vérifiez que le connection-lost handler se déclenche, que le client se reconnecte et que les messages circulent à nouveau. S'il vous faut du code de départ dans un autre langage pour l'autre côté du test, le générateur de code MQTT le produit pour vous.
Observer les topics C++ dans le client en ligne
Paho C++ vous donne un client MQTT fiable avec très peu de code : un builder pour les options, des tokens pour le flux de contrôle et des callbacks ou une file de consommation pour les messages entrants. Gardez les callbacks non bloquants, activez la reconnexion automatique avec des client IDs stables, utilisez TLS dès le départ et tournez-vous vers async_mqtt lorsque votre programme est déjà une application Asio.
Questions fréquentes
Quelle bibliothèque MQTT utiliser en C++ ?
Pour la plupart des applications, Eclipse Paho C++ (paho.mqtt.cpp) : elle est mature, prend en charge MQTT 3.1.1 et 5, TLS et WebSockets, et encapsule la très répandue bibliothèque Paho C. async_mqtt est un bon choix si votre code repose déjà sur Boost.Asio et que vous voulez une bibliothèque header-only avec prise en charge des coroutines.
Pourquoi mon programme Paho C++ se bloque-t-il dans un callback ?
Vous avez probablement appelé ->wait() sur un token dans un callback comme message_arrived ou connected. Les callbacks s'exécutent sur le thread de la bibliothèque : bloquer à cet endroit revient à s'attendre soi-même. Lancez l'opération sans attendre, ou confiez le travail à un autre thread ou à la file de consommation.
Paho C++ se reconnecte-t-il automatiquement ?
Oui, si vous activez automatic_reconnect() dans connect_options_builder. Combinez-le avec une session persistante (clean_session(false) et un client ID fixe) ou réabonnez-vous dans le connected handler ; sinon vous vous reconnectez sans aucun abonnement.


