mirror of
https://github.com/eclipse/paho.mqtt.cpp.git
synced 2025-05-09 03:11:23 +08:00
227 lines
7.3 KiB
C++
227 lines
7.3 KiB
C++
// async_subscribe.cpp
|
|
//
|
|
// This is a Paho MQTT C++ client, sample application.
|
|
//
|
|
// This application is an MQTT subscriber using the C++ asynchronous client
|
|
// interface, employing callbacks to receive messages and status updates.
|
|
//
|
|
// The sample demonstrates:
|
|
// - Connecting to an MQTT server/broker using MQTT v3.
|
|
// - Subscribing to a topic
|
|
// - Receiving messages through the callback API
|
|
// - Receiving network disconnect updates and attempting manual reconnects.
|
|
// - Using a "clean session" and manually re-subscribing to topics on
|
|
// reconnect.
|
|
//
|
|
|
|
/*******************************************************************************
|
|
* Copyright (c) 2013-2025 Frank Pagliughi <fpagliughi@mindspring.com>
|
|
*
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v2.0
|
|
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
|
*
|
|
* The Eclipse Public License is available at
|
|
* http://www.eclipse.org/legal/epl-v20.html
|
|
* and the Eclipse Distribution License is available at
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
*
|
|
* Contributors:
|
|
* Frank Pagliughi - initial implementation and documentation
|
|
*******************************************************************************/
|
|
|
|
#include <cctype>
|
|
#include <chrono>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
#include "mqtt/async_client.h"
|
|
|
|
const std::string DFLT_SERVER_URI("mqtt://localhost:1883");
|
|
const std::string CLIENT_ID("paho_cpp_async_subscribe");
|
|
|
|
const std::string TOPIC("#");
|
|
|
|
const int QOS = 1;
|
|
const int N_RETRY_ATTEMPTS = 5;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Callbacks for the success or failures of requested actions.
|
|
// This could be used to initiate further action, but here we just log the
|
|
// results to the console.
|
|
|
|
class action_listener : public virtual mqtt::iaction_listener
|
|
{
|
|
std::string name_;
|
|
|
|
void on_failure(const mqtt::token& tok) override
|
|
{
|
|
std::cout << name_ << " failure";
|
|
if (tok.get_message_id() != 0)
|
|
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
void on_success(const mqtt::token& tok) override
|
|
{
|
|
std::cout << name_ << " success";
|
|
if (tok.get_message_id() != 0)
|
|
std::cout << " for token: [" << tok.get_message_id() << "]" << std::endl;
|
|
auto top = tok.get_topics();
|
|
if (top && !top->empty())
|
|
std::cout << "\ttoken topic: '" << (*top)[0] << "', ..." << std::endl;
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
public:
|
|
action_listener(const std::string& name) : name_(name) {}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Local callback & listener class for use with the client connection.
|
|
* This is primarily intended to receive messages, but it will also monitor
|
|
* the connection to the broker. If the connection is lost, it will attempt
|
|
* to restore the connection and re-subscribe to the topic.
|
|
*/
|
|
class callback : public virtual mqtt::callback, public virtual mqtt::iaction_listener
|
|
|
|
{
|
|
// Counter for the number of connection retries
|
|
int nretry_;
|
|
// The MQTT client
|
|
mqtt::async_client& cli_;
|
|
// Options to use if we need to reconnect
|
|
mqtt::connect_options& connOpts_;
|
|
// An action listener to display the result of actions.
|
|
action_listener subListener_;
|
|
|
|
// This deomonstrates manually reconnecting to the broker by calling
|
|
// connect() again. This is a possibility for an application that keeps
|
|
// a copy of it's original connect_options, or if the app wants to
|
|
// reconnect with different options.
|
|
// Another way this can be done manually, if using the same options, is
|
|
// to just call the async_client::reconnect() method.
|
|
void reconnect()
|
|
{
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(2500));
|
|
try {
|
|
cli_.connect(connOpts_, nullptr, *this);
|
|
}
|
|
catch (const mqtt::exception& exc) {
|
|
std::cerr << "Error: " << exc.what() << std::endl;
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
// Re-connection failure
|
|
void on_failure(const mqtt::token& tok) override
|
|
{
|
|
std::cout << "Connection attempt failed" << std::endl;
|
|
if (++nretry_ > N_RETRY_ATTEMPTS)
|
|
exit(1);
|
|
reconnect();
|
|
}
|
|
|
|
// (Re)connection success
|
|
// Either this or connected() can be used for callbacks.
|
|
void on_success(const mqtt::token& tok) override {}
|
|
|
|
// (Re)connection success
|
|
void connected(const std::string& cause) override
|
|
{
|
|
std::cout << "\nConnection success" << std::endl;
|
|
std::cout << "\nSubscribing to topic '" << TOPIC << "'\n"
|
|
<< "\tfor client " << CLIENT_ID << " using QoS" << QOS << "\n"
|
|
<< "\nPress Q<Enter> to quit\n"
|
|
<< std::endl;
|
|
|
|
cli_.subscribe(TOPIC, QOS, nullptr, subListener_);
|
|
}
|
|
|
|
// Callback for when the connection is lost.
|
|
// This will initiate the attempt to manually reconnect.
|
|
void connection_lost(const std::string& cause) override
|
|
{
|
|
std::cout << "\nConnection lost" << std::endl;
|
|
if (!cause.empty())
|
|
std::cout << "\tcause: " << cause << std::endl;
|
|
|
|
std::cout << "Reconnecting..." << std::endl;
|
|
nretry_ = 0;
|
|
reconnect();
|
|
}
|
|
|
|
// Callback for when a message arrives.
|
|
void message_arrived(mqtt::const_message_ptr msg) override
|
|
{
|
|
std::cout << "Message arrived" << std::endl;
|
|
std::cout << "\ttopic: '" << msg->get_topic() << "'" << std::endl;
|
|
std::cout << "\tpayload: '" << msg->to_string() << "'\n" << std::endl;
|
|
}
|
|
|
|
void delivery_complete(mqtt::delivery_token_ptr token) override {}
|
|
|
|
public:
|
|
callback(mqtt::async_client& cli, mqtt::connect_options& connOpts)
|
|
: nretry_(0), cli_(cli), connOpts_(connOpts), subListener_("Subscription")
|
|
{
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
// A subscriber often wants the server to remember its messages when its
|
|
// disconnected. In that case, it needs a unique ClientID and a
|
|
// non-clean session.
|
|
|
|
auto serverURI = (argc > 1) ? std::string{argv[1]} : DFLT_SERVER_URI;
|
|
|
|
mqtt::async_client cli(serverURI, CLIENT_ID);
|
|
|
|
mqtt::connect_options connOpts;
|
|
connOpts.set_clean_session(false);
|
|
|
|
// Install the callback(s) before connecting.
|
|
callback cb(cli, connOpts);
|
|
cli.set_callback(cb);
|
|
|
|
// Start the connection.
|
|
// When completed, the callback will subscribe to topic.
|
|
|
|
try {
|
|
std::cout << "Connecting to the MQTT server '" << serverURI << "'..." << std::flush;
|
|
cli.connect(connOpts, nullptr, cb);
|
|
}
|
|
catch (const mqtt::exception& exc) {
|
|
std::cerr << "\nERROR: Unable to connect to MQTT server: '" << serverURI << "'" << exc
|
|
<< std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Just block till user tells us to quit.
|
|
|
|
while (std::tolower(std::cin.get()) != 'q');
|
|
|
|
// Disconnect
|
|
|
|
try {
|
|
std::cout << "\nDisconnecting from the MQTT server..." << std::flush;
|
|
cli.disconnect()->wait();
|
|
std::cout << "OK" << std::endl;
|
|
}
|
|
catch (const mqtt::exception& exc) {
|
|
std::cerr << exc << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|