A forma mais rápida de usar MQTT em C++ é o cliente Eclipse Paho C++ (paho.mqtt.cpp): crie um mqtt::async_client, conecte com um connect_options_builder e depois chame publish() e subscribe() — cada chamada retorna um token no qual você pode fazer wait(). Se a sua aplicação já roda sobre Boost.Asio, o async_mqtt é uma alternativa header-only que se encaixa nesse modelo de forma nativa.
Este tutorial monta um publisher e um subscriber com Paho C++, adiciona TLS, reconexões e MQTT 5, e termina mostrando como testar tudo contra um broker real.
Escolhendo uma biblioteca
| Biblioteca | Modelo | Protocolo | Bom para |
|---|---|---|---|
Paho C++ (paho.mqtt.cpp) | Wrapper C++ fino sobre a biblioteca assíncrona Paho C; tokens, callbacks ou fila de consumo | MQTT 3.1, 3.1.1, 5; TCP, TLS, WebSocket | A maioria das aplicações, gateways Linux, ferramentas desktop |
| async_mqtt | Header-only, completion tokens do Boost.Asio (callbacks, futures, corrotinas C++20) | MQTT 3.1.1 e 5; TCP, TLS, WebSocket | Servidores baseados em Asio, muitas conexões, quando você também quer peças para um broker/endpoint |
Paho C (paho.mqtt.c) | API em C puro | MQTT 3.1.1 e 5 | Projetos em C, ou quando você não pode usar C++ — veja MQTT em C com Paho |
Para microcontroladores, nenhuma das duas é a escolha usual; bibliotecas C embarcadas como coreMQTT ou o cliente do ESP-IDF são mais leves. O restante deste guia mira hosts Linux, macOS e Windows. Para uma visão mais ampla das bibliotecas cliente por linguagem, veja as melhores bibliotecas MQTT.
Instalar o Paho C++
O Paho C++ precisa da biblioteca Paho C (compilada com SSL se você quiser TLS). Três caminhos comuns:
# 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 installUm CMakeLists.txt mínimo vincula o target importado, que já traz a biblioteca C e o OpenSSL para você:
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)Se você linkar manualmente e vir undefined reference to MQTTAsync_create, está faltando a biblioteca C: linke paho-mqtt3as (a variante assíncrona com SSL) depois de paho-mqttpp3.
Publicar uma mensagem
Toda chamada assíncrona retorna um token. Chamar ->wait() a transforma em uma chamada bloqueante, que é exatamente o que você quer em uma ferramenta pequena:
#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;
}
}Com QoS 1, o wait() no token de publicação só retorna depois que o broker confirmou a mensagem. Com QoS 0, retorna assim que o pacote é escrito. Veja os níveis de QoS do MQTT para saber quando cada um vale a pena.
Assinar com a fila de consumo
O subscriber mais simples evita callbacks por completo: start_consuming() coloca as mensagens recebidas em uma fila thread-safe que você lê no seu próprio loop.
#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;
}
}Rode o subscriber e depois o publisher em um segundo terminal. Você também pode publicar em testmqtt/cpp/hello pelo cliente MQTT online e ver a linha aparecer no seu terminal.
Callbacks e reconexão automática
Serviços de longa duração geralmente preferem callbacks e precisam sobreviver a quedas de rede. O Paho C++ tem setters com lambdas para cada evento, e automatic_reconnect() cuida do 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));
}Três regras mantêm o código com callbacks saudável:
- Nunca bloqueie em um callback. Chamar
->wait()dentro deconnectedoumessage_arrivedespera na mesma thread que deveria completar o token. Dispare a chamada e siga em frente, ou envie o trabalho para a sua própria fila. - Refaça as assinaturas ao conectar. O connected handler roda após cada (re)conexão bem-sucedida, então as assinaturas voltam mesmo que o broker tenha perdido a sessão.
- Use um client ID estável e único com
clean_session(false). Dois processos com o mesmo ID derrubam um ao outro em loop. Gere um com o gerador de client ID e leia as boas práticas de reconexão para orientações sobre back-off.
Sessões persistentes exigem um broker que as armazene: o endpoint compartilhado broker.connectmq.com não guarda sessões offline, então teste clean_session(false) contra o seu próprio broker privado. No endpoint compartilhado, é a nova assinatura no connected handler que mantém o serviço funcionando.
TLS e autenticação
Para qualquer coisa além de um teste rápido, conecte via TLS na porta 8883 com credenciais. Troque o esquema da URI para ssl:// e adicione 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();
}O caminho do bundle de CAs varia conforme o sistema (/etc/pki/tls/certs/ca-bundle.crt na família RHEL; no macOS, exporte um ou aponte para o bundle do OpenSSL do Homebrew). Se a conexão falhar antes do CONNACK, verifique a cadeia de certificados a partir da mesma máquina com os passos de testar uma conexão MQTT com TLS.
MQTT 5 com Paho C++
O MQTT 5 precisa ser ativado explicitamente. Crie o cliente com opções de MQTT 5 e use o builder v5, que também aceita propriedades:
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();Com MQTT 5 o broker retorna reason codes em vez dos antigos return codes de um byte, então uma falha diz muito mais (por exemplo 0x87 Not authorized ou 0x97 Quota exceeded). Consulte qualquer código na ferramenta de reason codes MQTT. As diferenças entre as versões estão resumidas em MQTT 5 vs 3.1.1.
async_mqtt: a opção nativa do Asio
O async_mqtt é uma biblioteca header-only em C++17 construída sobre Boost.Asio, do autor do antigo mqtt_cpp. Cada operação recebe um completion token do Asio, então a mesma chamada pode usar um callback, um std::future ou co_await em uma corrotina C++20. Ele suporta MQTT 3.1.1 e 5 sobre TCP, TLS e WebSocket, e também fornece as peças para um broker ou endpoint de servidor.
Escolha-o quando:
- o seu programa já roda um
io_context(um servidor baseado em Asio, um gateway com muitos sockets), - você quer corrotinas em vez de cadeias de callbacks,
- você prefere uma dependência header-only a uma biblioteca C compilada.
A API mudou entre versões major, então comece pelos exemplos do repositório que correspondem à versão que você instalar, e não por snippets antigos. Conceitualmente o fluxo é o mesmo do Paho: abrir o transporte, enviar CONNECT e esperar o CONNACK, enviar SUBSCRIBE e então iterar recebendo pacotes PUBLISH.
Erros comuns
| Mensagem | Causa provável | Correção |
|---|---|---|
MQTT error [-1]: TCP/TLS connect failure | Host ou porta errados, firewall ou falha no handshake TLS | Confira se o esquema da URI corresponde à porta (tcp:// 1883, ssl:// 8883) e teste a porta a partir da mesma máquina |
A conexão falha na hora com uma URI ssl:// | Paho C compilado sem suporte a SSL | Recompile com -DPAHO_WITH_SSL=ON ou use um pacote compilado com OpenSSL |
| Connection lost a cada poucos segundos | Outro cliente usa o mesmo client ID | Dê a cada processo um ID único |
undefined reference to MQTTAsync_* | Biblioteca C não linkada | Linke PahoMqttCpp::paho-mqttpp3 via CMake ou adicione paho-mqtt3as |
CONNACK recusado (rc 4/5, ou 0x86/0x87 no MQTT 5) | Credenciais erradas ou sem autorização | Verifique usuário, senha e ACLs |
Teste de ponta a ponta
Antes de culpar o seu código, prove que o lado do broker funciona com um cliente confiável. Inicie um subscriber no terminal e rode o seu publisher:
mosquitto_sub -h broker.connectmq.com -p 1883 -t 'testmqtt/cpp/#' -vDepois inverta: rode o seu subscriber C++ e envie uma mensagem pela linha de comando:
mosquitto_pub -h broker.connectmq.com -p 1883 -t 'testmqtt/cpp/commands/led' -m 'on' -q 1Para exercitar as reconexões, desative a interface de rede por mais tempo que o intervalo de keep-alive e confirme que o connection-lost handler dispara, que o cliente reconecta e que as mensagens voltam a fluir. Se você precisa de código inicial em outra linguagem para o outro lado do teste, o gerador de código MQTT cria para você.
Veja os topics de C++ no cliente online
O Paho C++ entrega um cliente MQTT confiável com muito pouco código: um builder para as opções, tokens para o fluxo de controle e callbacks ou uma fila de consumo para as mensagens recebidas. Mantenha os callbacks sem bloqueio, ative a reconexão automática com client IDs estáveis, use TLS desde o início e recorra ao async_mqtt quando o seu programa já for uma aplicação Asio.
Perguntas frequentes
Qual biblioteca MQTT devo usar em C++?
Para a maioria das aplicações, o Eclipse Paho C++ (paho.mqtt.cpp): é maduro, suporta MQTT 3.1.1 e 5, TLS e WebSockets, e encapsula a popular biblioteca Paho C. O async_mqtt é uma boa escolha se o seu código já é baseado em Boost.Asio e você quer uma biblioteca header-only com suporte a corrotinas.
Por que meu programa com Paho C++ trava dentro de um callback?
Provavelmente você chamou ->wait() em um token dentro de um callback como message_arrived ou connected. Os callbacks rodam na thread da biblioteca, então bloquear ali é esperar por si mesmo. Dispare a operação sem esperar, ou passe o trabalho para outra thread ou para a fila de consumo.
O Paho C++ reconecta automaticamente?
Sim, se você ativar automatic_reconnect() no connect_options_builder. Combine com uma sessão persistente (clean_session(false) e um client ID fixo) ou refaça as assinaturas no connected handler; caso contrário, você reconecta sem nenhuma assinatura.


