😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:2024-05-13 18:06:13
本文未经允许,不得转发!!!
目录
- 🎄一、概述
- 🎄二、paho.mqtt.cpp 库介绍
- 🎄三、paho.mqtt.cpp 库下载、交叉编译
- ✨3.1 openssl 的下载、交叉编译
- ✨3.2 paho.mqtt.c 交叉编译
- ✨3.3 paho.mqtt.cpp 交叉编译
- 🎄四、paho.mqtt.cpp库写一个MQTT客户端
- ✨4.1 订阅——async_subscribe.cpp
- ✨4.2 发布——async_publish.cpp
- 🎄五、总结
🎄一、概述
前面文章 【MQTT】mosquitto 的 “下载、交叉编译、使用” 详细教程,手把手搭建一个MQTT Broker ,介绍了怎样搭建一个MQTT Broker(代理)。MQTT Broker(代理)的搭建很重要,一般很少自己开发一个MQTT Broker,而是搭建好一个开源的MQTT Broker(代理),然后其他的设备或机器都作为MQTT客户端,让MQTT Broker来转发消息。
关于MQTT协议的,作为编程人员更多的是开发MQTT的客户端,本文介绍的paho.mqtt.cpp
就是开发MQTT客户端常用的开源库之一。paho.mqtt.cpp
是 Eclipse Paho
项目的一个开源库。
Eclipse Paho
项目提供了以各种编程语言实现的MQTT和MQTT-SN的开源库(主要是客户端)。下面是这个项目的MQTT客户端开源库,想要了解更多Eclipse Paho
项目的资料,可以到其官网:https://eclipse.dev/paho/
🎄二、paho.mqtt.cpp 库介绍
之前介绍的 paho.mqtt.c
库可以在C语言编程中使用,而 paho.mqtt.cpp
库是在C++编程中使用的,它可以使标准C++应用程序(C++11及更高版本)能够连接到 MQTT Broker(代理)、发布消息、订阅主题以及从MQTT Broker(代理)接收消息。
paho.mqtt.cpp
库有如下功能:
- Support for MQTT v3.1, v3.1.1, and v5.
- Network Transports:
- Standard TCP
- Secure sockets with SSL/TLS
- WebSockets
- Secure and insecure
- Proxy support
- Message persistence
- User configurable
- Built-in File persistence
- User-defined key/value persistence easy to implement
- Automatic Reconnect
- Offline Buffering
- High Availability
- Blocking and non-blocking APIs
- Modern C++ interface (C++11 and beyond)
最后,这个库需要依赖于 paho.mqtt.c
,所以编译这个库之前需要先编译 paho.mqtt.c
库。
🎄三、paho.mqtt.cpp 库下载、交叉编译
paho.mqtt.cpp 库需要两个依赖库:openssl、paho.mqtt.c,下面依次介绍怎么编译 openssl、paho.mqtt.c、paho.mqtt.cpp。
✨3.1 openssl 的下载、交叉编译
本文下载的是 openssl-OpenSSL_1_1_1g.tar.gz
,
下载地址:https://codeload.github.com/openssl/openssl/tar.gz/refs/tags/OpenSSL_1_1_1g
为什么使用这么旧的版本,因为这个我之前编译过,而且使用没问题。
编译步骤:
-
1、解压缩
tar zxf openssl-OpenSSL_1_1_1g.tar.gz
-
2、进入目录,并配置输出目录和交叉编译器, (linux-generic32表示是32位操作系统,个别文章加了这个选项就不用去掉 -m64,我这里行不通)
cd openssl-OpenSSL_1_1_1g/ ./config no-asm shared no-async --prefix=`pwd`/ssl_result --cross-compile-prefix=aarch64-mix210-linux-
-
3、执行下面命令,删除Makefile文件的
-m64
,sed -i 's/-m64//' Makefile
执行后,可以避免出现这个编译错误:
aarch64-mix210-linux-gcc: error: unrecognized command line option '-m64'
-
4、编译、安装
make && make install
成功编译后,在openssl-OpenSSL_1_1_1g/
目录会生成一个ssl_result
目录,可以看到里面生成的库:
✨3.2 paho.mqtt.c 交叉编译
本文下载的是paho.mqtt.c-1.3.13.tar.gz
,下载地址:https://github.com/eclipse/paho.mqtt.c/archive/refs/tags/v1.3.13.tar.gz
编译步骤:
-
1、解压缩,创建要安装目录
paho.mqtt.c_result
tar zxf paho.mqtt.c-1.3.13.tar.gz mkdir paho.mqtt.c_result/bin -p mkdir paho.mqtt.c_result/include -p mkdir paho.mqtt.c_result/lib -p mkdir paho.mqtt.c_result/share/man/man1 -p
-
2、进入目录,交叉编译
cd paho.mqtt.c-1.3.13/ make CC=aarch64-mix210-linux-gcc CFLAGS:="-I `pwd`/../ssl_result/include" LDFLAGS:="-L `pwd`/../ssl_result/lib"
CFLAGS:=“-I `pwd`/…/ssl_result/include”:指定前面编译的 openssl 的头文件;
LDFLAGS:=“-L `pwd`/…/ssl_result/lib”:指定前面编译的 openssl 的库文件路径; -
3、make install,安装编译结果
make install prefix=`pwd`/../paho.mqtt.c_result
prefix=`pwd`/…/paho.mqtt.c_result :指定安装目录路径;
编译完成后,会生成 目录,内容如下:
✨3.3 paho.mqtt.cpp 交叉编译
本文下载的是paho.mqtt.cpp-1.3.2.tar.gz
,下载地址:https://github.com/eclipse/paho.mqtt.cpp/archive/refs/tags/v1.3.2.tar.gz
如果你下载的版本跟我的一样或者更新,可以尝试使用下面的脚本进行编译。
编译步骤:
- 1、保存下面脚本为
paho.mqtt.cpp_install
,#! /bin/sh # 指定编译结果目录 RESULT_DIR=$(pwd)/result_dir RESULT_SSL=${RESULT_DIR}/ssl_result RESULT_MQTT_C=${RESULT_DIR}/paho.mqtt.c_result RESULT_MQTT_CPP=${RESULT_DIR}/paho.mqtt.cpp_result # 指定交叉编译工具 CROSSS_COMPILE_TOOL=aarch64-mix210-linux- # 1、解压缩 tar zxf paho.mqtt.cpp-1.3.2.tar.gz # 2、进入目录, cd paho.mqtt.cpp-1.3.2/ # 3、执行 cmake mkdir build_arm cd build_arm # 编译不需要 openssl 的库 cmake .. -DCMAKE_CXX_COMPILER=${CROSSS_COMPILE_TOOL}g++ \ -DCMAKE_INSTALL_PREFIX=${RESULT_MQTT_CPP} \ -DPAHO_MQTT_C_LIBRARIES=${RESULT_MQTT_C}/lib/libpaho-mqtt3a.so \ -DPAHO_MQTT_C_INCLUDE_DIRS=${RESULT_MQTT_C}/include \ -DPAHO_WITH_SSL=OFF \ -DCMAKE_CXX_FLAGS="-std=gnu++11 -mcpu=cortex-a53" # 编译带有 openssl 的库 #cmake .. -DCMAKE_CXX_COMPILER=${CROSSS_COMPILE_TOOL}g++ \ -DCMAKE_INSTALL_PREFIX=${RESULT_MQTT_CPP} \ -DPAHO_MQTT_C_LIBRARIES=${RESULT_MQTT_C}/lib/libpaho-mqtt3a.so \ -DPAHO_MQTT_C_INCLUDE_DIRS=${RESULT_MQTT_C}/include \ -DOPENSSL_SSL_LIBRARY=${RESULT_SSL}/lib/libssl.so \ -DOPENSSL_INCLUDE_DIR=${RESULT_SSL}/include \ -DOPENSSL_CRYPTO_LIBRARY=${RESULT_SSL}/lib/libcrypto.so \ -DCMAKE_CXX_FLAGS="-std=gnu++11 -mcpu=cortex-a53" # 4、编译 make && make install
- 2、将下载的源码包
paho.mqtt.cpp-1.3.2.tar.gz
和 上面保存的脚本paho.mqtt.cpp_install
放到同一目录,并且将前面编译好的openssl库、paho.mqtt.c库放在脚本指定的结果目录,我是放到 result_dir 目录,编译目录如下。
- 3、执行
./paho.mqtt.cpp_install.sh
编译,编译完成后,在result_dir目录下生成一个名为paho.mqtt.cpp_result的目录,内容如下:
🎄四、paho.mqtt.cpp库写一个MQTT客户端
下面提供两个使用了 paho.mqtt.cpp库(libpaho-mqttpp3) MQTT客户端的例子。
运行下面代码前,要先搭建一个MQTT Broker,参考上篇文章:【MQTT】mosquitto 的 “下载、交叉编译、使用” 详细教程,手把手搭建一个MQTT Broker
✨4.1 订阅——async_subscribe.cpp
这是使用了 libpaho-mqttpp3.so 进行订阅消息的源码,源码路径在源码的这个路径:paho.mqtt.cpp-1.3.2/src/samples/async_subscribe.cpp
,只更改了服务器地址。完整代码如下:
// 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.
// - 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-2023 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 <iostream>
#include <cstdlib>
#include <string>
#include <cstring>
#include <cctype>
#include <thread>
#include <chrono>
#include "mqtt/async_client.h"
const std::string SERVER_ADDRESS("192.168.3.227:1883");
const std::string CLIENT_ID("paho_cpp_async_subcribe");
const std::string TOPIC("hello");
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.
mqtt::async_client cli(SERVER_ADDRESS, 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..." << std::flush;
cli.connect(connOpts, nullptr, cb);
}
catch (const mqtt::exception& exc) {
std::cerr << "\nERROR: Unable to connect to MQTT server: '"
<< SERVER_ADDRESS << "'" << 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;
}
交叉编译:
aarch64-mix210-linux-g++ async_subscribe.cpp -I result_dir/paho.mqtt.cpp_result/include/ -I result_dir/paho.mqtt.c_result/include/ -L result_dir/paho.mqtt.cpp_result/lib/ -L result_dir/paho.mqtt.c_result/lib/ -l paho-mqttpp3 -l paho-mqtt3a
在嵌入式板子运行:
✨4.2 发布——async_publish.cpp
这是使用了 libpaho-mqttpp3.so 进行发布消息的源码,源码路径在源码的这个路径:paho.mqtt.cpp-1.3.2/src/samples/async_publish.cpp
,只更改了服务器地址。完整代码如下:
// async_publish.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// It's an example of how to send messages as an MQTT publisher using the
// C++ asynchronous client interface.
//
// The sample demonstrates:
// - Connecting to an MQTT server/broker
// - Publishing messages
// - Default file persistence
// - Last will and testament
// - Using asynchronous tokens
// - Implementing callbacks and action listeners
//
/*******************************************************************************
* Copyright (c) 2013-2023 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 <iostream>
#include <cstdlib>
#include <string>
#include <thread>
#include <atomic>
#include <chrono>
#include <cstring>
#include "mqtt/async_client.h"
using namespace std;
const string DFLT_SERVER_ADDRESS { "192.168.3.227:1883" };
const string CLIENT_ID { "paho_cpp_async_publish" };
const string PERSIST_DIR { "./persist" };
const string TOPIC { "hello" };
const char* PAYLOAD1 = "Hello World!";
const char* PAYLOAD2 = "Hi there!";
const char* PAYLOAD3 = "Is anyone listening?";
const char* PAYLOAD4 = "Someone is always listening.";
const char* LWT_PAYLOAD = "Last will and testament.";
const int QOS = 1;
const auto TIMEOUT = std::chrono::seconds(10);
/
/**
* A callback class for use with the main MQTT client.
*/
class callback : public virtual mqtt::callback
{
public:
void connection_lost(const string& cause) override {
cout << "\nConnection lost" << endl;
if (!cause.empty())
cout << "\tcause: " << cause << endl;
}
void delivery_complete(mqtt::delivery_token_ptr tok) override {
cout << "\tDelivery complete for token: "
<< (tok ? tok->get_message_id() : -1) << endl;
}
};
/
/**
* A base action listener.
*/
class action_listener : public virtual mqtt::iaction_listener
{
protected:
void on_failure(const mqtt::token& tok) override {
cout << "\tListener failure for token: "
<< tok.get_message_id() << endl;
}
void on_success(const mqtt::token& tok) override {
cout << "\tListener success for token: "
<< tok.get_message_id() << endl;
}
};
/
/**
* A derived action listener for publish events.
*/
class delivery_action_listener : public action_listener
{
atomic<bool> done_;
void on_failure(const mqtt::token& tok) override {
action_listener::on_failure(tok);
done_ = true;
}
void on_success(const mqtt::token& tok) override {
action_listener::on_success(tok);
done_ = true;
}
public:
delivery_action_listener() : done_(false) {}
bool is_done() const { return done_; }
};
/
int main(int argc, char* argv[])
{
// A client that just publishes normally doesn't need a persistent
// session or Client ID unless it's using persistence, then the local
// library requires an ID to identify the persistence files.
string address = (argc > 1) ? string(argv[1]) : DFLT_SERVER_ADDRESS,
clientID = (argc > 2) ? string(argv[2]) : CLIENT_ID;
cout << "Initializing for server '" << address << "'..." << endl;
mqtt::async_client client(address, clientID, PERSIST_DIR);
callback cb;
client.set_callback(cb);
auto connOpts = mqtt::connect_options_builder()
.clean_session()
.will(mqtt::message(TOPIC, LWT_PAYLOAD, strlen(LWT_PAYLOAD), QOS, false))
.finalize();
cout << " ...OK" << endl;
try {
cout << "\nConnecting..." << endl;
mqtt::token_ptr conntok = client.connect(connOpts);
cout << "Waiting for the connection..." << endl;
conntok->wait();
cout << " ...OK" << endl;
// First use a message pointer.
cout << "\nSending message..." << endl;
mqtt::message_ptr pubmsg = mqtt::make_message(TOPIC, PAYLOAD1);
pubmsg->set_qos(QOS);
client.publish(pubmsg)->wait_for(TIMEOUT);
cout << " ...OK" << endl;
// Now try with itemized publish.
cout << "\nSending next message..." << endl;
mqtt::delivery_token_ptr pubtok;
pubtok = client.publish(TOPIC, PAYLOAD2, strlen(PAYLOAD2), QOS, false);
cout << " ...with token: " << pubtok->get_message_id() << endl;
cout << " ...for message with " << pubtok->get_message()->get_payload().size()
<< " bytes" << endl;
pubtok->wait_for(TIMEOUT);
cout << " ...OK" << endl;
// Now try with a listener
cout << "\nSending next message..." << endl;
action_listener listener;
pubmsg = mqtt::make_message(TOPIC, PAYLOAD3);
pubtok = client.publish(pubmsg, nullptr, listener);
pubtok->wait();
cout << " ...OK" << endl;
// Finally try with a listener, but no token
cout << "\nSending final message..." << endl;
delivery_action_listener deliveryListener;
pubmsg = mqtt::make_message(TOPIC, PAYLOAD4);
client.publish(pubmsg, nullptr, deliveryListener);
while (!deliveryListener.is_done()) {
this_thread::sleep_for(std::chrono::milliseconds(100));
}
cout << "OK" << endl;
// Double check that there are no pending tokens
auto toks = client.get_pending_delivery_tokens();
if (!toks.empty())
cout << "Error: There are pending delivery tokens!" << endl;
// Disconnect
cout << "\nDisconnecting..." << endl;
client.disconnect()->wait();
cout << " ...OK" << endl;
}
catch (const mqtt::exception& exc) {
cerr << exc.what() << endl;
return 1;
}
return 0;
}
交叉编译:
aarch64-mix210-linux-g++ async_publish.cpp -I result_dir/paho.mqtt.cpp_result/include/ -I result_dir/paho.mqtt.c_result/include/ -L result_dir/paho.mqtt.cpp_result/lib/ -L result_dir/paho.mqtt.c_result/lib/ -l paho-mqttpp3 -l paho-mqtt3a -o async_publish
在嵌入式板子的运行结果:
🎄五、总结
👉本文详细介绍了 paho.mqtt.cpp 库的“下载、交叉编译”、最后给出了怎么写MQTT客户端例子源码的例子
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁