MQTT客户端程序的编写
一、前言??
?在前面《MQTT服務器的搭建》一文中,我們介紹了EMQX。打開其幫助文檔,我們可以看到:
很開心,作為一個主流的Qt軟件開發者,看到了熟悉的基于Qt框架的MQTT客戶端。你可以直接下載下來,按照說明文檔編譯、運行和測試。當然,也可以看下面的介紹,咱們自己寫一個簡單的MQTT程序,實現發布和訂閱消息即可。
二、QtMQTT?項目
1、庫文件下載、編譯和鏈接
Qt開發MQTT程序有兩種方式,一個是Qt官方提供的基于MQTT的封裝,一個是第三方(EMQ)開發的用于Qt調用MQTT的接口。我們只介紹第一種,基于Qt官方提供的封裝來使用MQTT。
Qt官方雖然在2017年就已經提供了對MQTT的封裝,但是并沒有正式加入到Qt的標準庫里面,所以需要自己下載源碼進行編譯。
Qt官方介紹文檔地址:https://doc.qt.io/QtMQTT/qtmqtt-index.html
Qt官方在github上提供了源代碼,地址:https://github.com/qt/qtmqtt
編譯項目,可以得到我們需要的Qt5Mqtt.dll 及Qt5Mqtt.lib 庫文件。
在Qt中,我們只需要在.pro文件中添加該庫文件的鏈接路徑即可。
其次,請記得將你的QtMqtt項目文件夾拷貝到 你指定的編譯器下 include文件夾中(如果已經存在請忽略)。如下圖
?2、客戶端項目程序編寫
在自己編寫之前,我們先看一下
/****************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtMqtt module. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ******************************************************************************/#ifndef QTMQTTCLIENT_H #define QTMQTTCLIENT_H#include <QtMqtt/qmqttglobal.h> #include <QtMqtt/qmqttauthenticationproperties.h> #include <QtMqtt/qmqttconnectionproperties.h> #include <QtMqtt/qmqttpublishproperties.h> #include <QtMqtt/qmqttsubscription.h> #include <QtMqtt/qmqttsubscriptionproperties.h> #include <QtMqtt/qmqtttopicfilter.h>#include <QtCore/QIODevice> #include <QtCore/QObject> #include <QtCore/QSharedPointer> #include <QtNetwork/QTcpSocket>QT_BEGIN_NAMESPACEclass QMqttClientPrivate;class /*Q_MQTT_EXPORT*/ QMqttClient : public QObject { public:enum TransportType {IODevice = 0,AbstractSocket,SecureSocket};enum ClientState {Disconnected = 0,Connecting,Connected};enum ClientError {// Protocol statesNoError = 0,InvalidProtocolVersion = 1,IdRejected = 2,ServerUnavailable = 3,BadUsernameOrPassword = 4,NotAuthorized = 5,// Qt statesTransportInvalid = 256,ProtocolViolation,UnknownError,Mqtt5SpecificError};enum ProtocolVersion {MQTT_3_1 = 3,MQTT_3_1_1 = 4,MQTT_5_0 = 5};private:Q_OBJECTQ_ENUMS(ClientState)Q_ENUMS(ClientError)Q_PROPERTY(QString clientId READ clientId WRITE setClientId NOTIFY clientIdChanged)Q_PROPERTY(QString hostname READ hostname WRITE setHostname NOTIFY hostnameChanged)Q_PROPERTY(quint16 port READ port WRITE setPort NOTIFY portChanged)Q_PROPERTY(quint16 keepAlive READ keepAlive WRITE setKeepAlive NOTIFY keepAliveChanged)Q_PROPERTY(ProtocolVersion protocolVersion READ protocolVersion WRITE setProtocolVersion NOTIFY protocolVersionChanged)Q_PROPERTY(ClientState state READ state WRITE setState NOTIFY stateChanged)Q_PROPERTY(ClientError error READ error WRITE setError NOTIFY errorChanged)Q_PROPERTY(QString username READ username WRITE setUsername NOTIFY usernameChanged)Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged)Q_PROPERTY(bool cleanSession READ cleanSession WRITE setCleanSession NOTIFY cleanSessionChanged)Q_PROPERTY(QString willTopic READ willTopic WRITE setWillTopic NOTIFY willTopicChanged)Q_PROPERTY(QByteArray willMessage READ willMessage WRITE setWillMessage NOTIFY willMessageChanged)Q_PROPERTY(quint8 willQoS READ willQoS WRITE setWillQoS NOTIFY willQoSChanged)Q_PROPERTY(bool willRetain READ willRetain WRITE setWillRetain NOTIFY willRetainChanged) public:explicit QMqttClient(QObject *parent = nullptr);void setTransport(QIODevice *device, TransportType transport);QIODevice *transport() const;QMqttSubscription *subscribe(const QMqttTopicFilter &topic, quint8 qos = 0);QMqttSubscription *subscribe(const QMqttTopicFilter &topic,const QMqttSubscriptionProperties &properties, quint8 qos = 0);void unsubscribe(const QMqttTopicFilter &topic);void unsubscribe(const QMqttTopicFilter &topic, const QMqttUnsubscriptionProperties &properties);Q_INVOKABLE qint32 publish(const QMqttTopicName &topic, const QByteArray &message = QByteArray(),quint8 qos = 0, bool retain = false);Q_INVOKABLE qint32 publish(const QMqttTopicName &topic, const QMqttPublishProperties &properties,const QByteArray &message = QByteArray(),quint8 qos = 0,bool retain = false);bool requestPing();QString hostname() const;quint16 port() const;QString clientId() const;quint16 keepAlive() const;ProtocolVersion protocolVersion() const;Q_INVOKABLE void connectToHost(); #ifndef QT_NO_SSLQ_INVOKABLE void connectToHostEncrypted(const QString &sslPeerName = QString()); #endifQ_INVOKABLE void disconnectFromHost();ClientState state() const;ClientError error() const;QString username() const;QString password() const;bool cleanSession() const;QString willTopic() const;quint8 willQoS() const;QByteArray willMessage() const;bool willRetain() const;void setConnectionProperties(const QMqttConnectionProperties &prop);QMqttConnectionProperties connectionProperties() const;void setLastWillProperties(const QMqttLastWillProperties &prop);QMqttLastWillProperties lastWillProperties() const;QMqttServerConnectionProperties serverConnectionProperties() const;void authenticate(const QMqttAuthenticationProperties &prop); Q_SIGNALS:void connected();void disconnected();void messageReceived(const QByteArray &message, const QMqttTopicName &topic = QMqttTopicName());void messageStatusChanged(qint32 id, QMqtt::MessageStatus s, const QMqttMessageStatusProperties &properties);void messageSent(qint32 id);void pingResponseReceived();void brokerSessionRestored();void hostnameChanged(QString hostname);void portChanged(quint16 port);void clientIdChanged(QString clientId);void keepAliveChanged(quint16 keepAlive);void protocolVersionChanged(ProtocolVersion protocolVersion);void stateChanged(ClientState state);void errorChanged(ClientError error);void usernameChanged(QString username);void passwordChanged(QString password);void cleanSessionChanged(bool cleanSession);void willTopicChanged(QString willTopic);void willQoSChanged(quint8 willQoS);void willMessageChanged(QByteArray willMessage);void willRetainChanged(bool willRetain);void authenticationRequested(const QMqttAuthenticationProperties &p);void authenticationFinished(const QMqttAuthenticationProperties &p); public Q_SLOTS:void setHostname(const QString &hostname);void setPort(quint16 port);void setClientId(const QString &clientId);void setKeepAlive(quint16 keepAlive);void setProtocolVersion(ProtocolVersion protocolVersion);void setState(ClientState state);void setError(ClientError error);void setUsername(const QString &username);void setPassword(const QString &password);void setCleanSession(bool cleanSession);void setWillTopic(const QString &willTopic);void setWillQoS(quint8 willQoS);void setWillMessage(const QByteArray &willMessage);void setWillRetain(bool willRetain);private:void connectToHost(bool encrypted, const QString &sslPeerName);Q_DISABLE_COPY(QMqttClient)Q_DECLARE_PRIVATE(QMqttClient) };Q_DECLARE_TYPEINFO(QMqttClient::TransportType, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(QMqttClient::ClientState, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(QMqttClient::ClientError, Q_PRIMITIVE_TYPE);QT_END_NAMESPACEQ_DECLARE_METATYPE(QMqttClient::TransportType) Q_DECLARE_METATYPE(QMqttClient::ClientState) Q_DECLARE_METATYPE(QMqttClient::ClientError)#endif // QTMQTTCLIENT_H重點關注QMqttClient中的信號函數和對應的槽函數,這將是我們使用QMqttClient實例化一個mqtt客戶端對象后操作的重點。
SLOTS:
void setHostname(const QString &hostname);? //用于設置即將連接的Mqtt服務器。
void setPort(quint16 port); // 設置端口號(與Mqtt服務器一致)
void setUsername(const QString &username); //? ?設置用戶名
 void setPassword(const QString &password);? // 設置密碼
?void connectToHost(bool encrypted, const QString &sslPeerName); // 連接到服務器,如果需要SSL加密,可以對應設置
SIGNALS:?
void connected();? // 當客戶端與服務器連接上,此信號將被觸發
 void disconnected(); // 當客戶端與服務器斷開連接,此信號將被觸發
 void messageReceived(const QByteArray &message, const QMqttTopicName &topic = QMqttTopicName()); // 當有消息被接受到,此信號被觸發。參數包含消息內容和對應的topic。顯然,我們訂閱一個主題后,異步接收消息就全靠這個信號了。
 void pingResponseReceived();? // MQTT客戶端與服務器之間會定時發送ping包,類似于心跳檢測客戶端與服務器之間的通信是否連接正常。當服務器端有ping包的確認消息返回,此消息被觸發。
// 下面還有其他的一些狀態改變觸發的信號,不做詳細解釋
void hostnameChanged(QString hostname);
 void portChanged(quint16 port);
 void keepAliveChanged(quint16 keepAlive);
 void protocolVersionChanged(ProtocolVersion protocolVersion);
 void stateChanged(ClientState state);
 void errorChanged(ClientError error);
 void usernameChanged(QString username);
 void passwordChanged(QString password);
 void cleanSessionChanged(bool cleanSession);
??
?
記得將mqtt文件夾拷貝到項目文件目錄下,如下圖:
好了,代碼開干:
// mqttclientwindow.h#ifndef MQTTCLIENTWINDOW_H #define MQTTCLIENTWINDOW_H#include <QWidget> #include "mqtt/qmqttclient.h"namespace Ui { class MqttClientWindow; }class MqttClientWindow : public QWidget {Q_OBJECTpublic:explicit MqttClientWindow(QWidget *parent = nullptr);~MqttClientWindow(); public slots:void on_pub_pushButton_clicked();void on_connect_pushButton_clicked();void on_sub_pushButton_clicked();void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic = QMqttTopicName());void onPingResponseReceived();void onConnected();void onDisconnected(); private:Ui::MqttClientWindow *ui;QMqttClient* client; };#endif // MQTTCLIENTWINDOW_H // mqttclientwindow.cpp#include "mqttclientwindow.h" #include "ui_mqttclientwindow.h" #include <QDebug> #include <QDateTime> #include <QHostAddress>MqttClientWindow::MqttClientWindow(QWidget *parent) :QWidget(parent),ui(new Ui::MqttClientWindow) {ui->setupUi(this);client = new QMqttClient;QObject::connect(client,SIGNAL(connected()),this,SLOT(onConnected()));QObject::connect(client,SIGNAL(disconnected()),this,SLOT(onDisconnected()));QObject::connect(client, SIGNAL(messageReceived(const QByteArray&, const QMqttTopicName&)),this, SLOT(onMessageReceived(const QByteArray &, const QMqttTopicName &)));QObject::connect(client,SIGNAL(pingResponseReceived()), this, SLOT(onPingResponseReceived())); }MqttClientWindow::~MqttClientWindow() {if(client->state() == QMqttClient::Connected){client->disconnectFromHost();}delete ui; }void MqttClientWindow::on_pub_pushButton_clicked() {if(client->publish(ui->pub_topic_lineEdit->text(),ui->pub_message_lineEdit->text().toUtf8()) == -1){qDebug() <<" Could not publish message";} }void MqttClientWindow::on_connect_pushButton_clicked() {//未連接服務器則連接if (client->state() == QMqttClient::Disconnected) {ui-> connect_pushButton->setText(tr("Disconnect"));client->setHostname("127.0.0.1");client->setPort(ui->port_lineEdit->text().trimmed().toUInt());client->setUsername("James");client->setPassword("james");ui->host_lineEdit->setEnabled(false);ui->port_lineEdit->setEnabled(false);client->connectToHost();} else {//斷開連接ui->connect_pushButton->setText(tr("Connect"));ui->host_lineEdit->setEnabled(true);ui->port_lineEdit->setEnabled(true);client->disconnectFromHost();} }void MqttClientWindow::on_sub_pushButton_clicked() {client->subscribe(ui->sub_lineEdit->text()); }void MqttClientWindow::onMessageReceived(const QByteArray &message, const QMqttTopicName &topic) {const QString content = QDateTime::currentDateTime().toString()+ QLatin1String(" Received Topic: ")+ topic.name()+ QLatin1String(" Message: ")+ message+ QLatin1Char('\n');ui->sub_textEdit->append(content); }void MqttClientWindow::onPingResponseReceived() {const QString content = QDateTime::currentDateTime().toString()+ QLatin1String(" PingResponse")+ QLatin1Char('\n');ui->sub_textEdit->append(content); }void MqttClientWindow::onConnected() {ui->sub_textEdit->append("has connected to server");}void MqttClientWindow::onDisconnected() {ui->sub_textEdit->append("has disConnected to server"); }?
// main.cpp#include "mqttclientwindow.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);MqttClientWindow w;w.show();return a.exec(); }?
總結
以上是生活随笔為你收集整理的MQTT客户端程序的编写的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: chrome书签搜索
 - 下一篇: 推荐好用的Linux远程连接工具