一、mqtt介绍
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
MQTT有三种角色的存在:
Broker代理:很多人理解为中间件,当然可以这样子认为。他就是一个中间件。用于处理信息并发送到相应的订阅者。
发布者:用于发布信息到代理上面。注意:发布者也可以是订阅者。
订阅者:就是用于接受信息的客户端。
MQTT使用TCP/IP提供网络连接,主流的MQTT是基于TCP连接进行数据推送的,但是同样有基于UDP的版本,叫做MQTT-SN
三种消息发布服务质量:
“至多一次”,消息发布完全依赖底层TCP/IP网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。这一种方式主要普通APP的推送,倘若你的智能设备在消息推送时未联网,推送过去没收到,再次联网也就收不到了。
“至少一次”,确保消息到达,但消息重复可能会发生。
“只有一次”,确保消息到达一次。在一些要求比较严格的计费系统中,可以使用此级别。在计费系统中,消息重复或丢失会导致不正确的结果。这种最高质量的消息发布服务还可以用于即时通讯类的APP的推送,确保用户收到且只会收到一次
MQTT协议中的方法:
Connect:等待与服务器建立连接
Disconnect:等待MQTT客户端完成所作的工作,并于服务器断开TCP/IP会话
Subscribe:等待完成订阅
UnSubscribe:等待服务器取消客户端的一个活多个和topics订阅
Publish:MQTT客户端发送消息请求,发送完成后返回应用程序线程
优点:
低开销、低带宽占用,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。
二、在windows上安装mqtt服务器:
1.mosquitto介绍
Mosquitto是一款实现了消息推送协议MQTT 3.1的开源消息代理软件,提供轻量级的、支持可订阅/可发布的消息推送模式,是设备与设备之间的短消息通信变得简单,广泛应用于低功耗传感器、手机(app消息推送是场景之一)、嵌入式电脑、微型控制器等移动设备。
2.下载mosquitto
下载地址:https://mosquitto.org/download/
3.安装mosquitto
4.配置文件修改
4.1 mosquitto.conf文件修改
4.2 以管理员身份打开Windows PowerShell, 进行用户密码配置
4.3 重启mosquitto broker服务
4.4 测试服务器
三、编译qtmqtt和创建mqtt客户端工程
Qt在5.10版本开始支持mqtt,但是也没有集成到安装包里面,需要自己下载编译
1.先下载安装Perl (解决编译qtmqtt报perl错误)
下载地址:https://www.perl.org/get.html
ActivePerl_x64_5.24.1.2402.exe
2.下载安装qtmqtt
下载地址:https://github.com/emqx/qmqtt
打开工程文件,选择src执行qmake,然后点击构建即可生成相应的库文件
3.创建myMqttDemo客户端工程
我的demo工程下载地址:https://download.csdn.net/download/linyibin_123/87280629
4.拷贝链接库与头文件到demo工程指定位置
4.1 拷贝头文件:
4.2 拷贝库文件:
4.3 demo工程引用库文件:
四、mqtt客户端demo工程代码
mqttClient.h
#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H
#include <QObject>
#include "qmqtt.h"
class mqttClient : public QObject
{
Q_OBJECT
public:
mqttClient();
static mqttClient& getInstance();
void init(QString sIp, quint16 nPort, QString sClientId, QString sUser, QString sPwd);
private:
QMQTT::Client *m_pClient;
signals:
void sig_result_connect();
void sig_result_disconnect();
void sig_result_dataReceived(QMQTT::Message message);
public slots:
void slot_connect();
void slot_disconnect();
void slot_publish(QString sTopic, QString sPayload);
void slot_subscribe(QString sTopic);
void slot_unSubscribe(QString sTopic);
void on_dataReceived(QMQTT::Message message);
};
#endif // MQTTCLIENT_H
mqttClient.cpp
#include "mqttclient.h"
mqttClient::mqttClient()
{
m_pClient = nullptr;
}
mqttClient &mqttClient::getInstance()
{
static mqttClient s_obj;
return s_obj;
}
void mqttClient::init(QString sIp, quint16 nPort, QString sClientId, QString sUser, QString sPwd)
{
if(m_pClient)
{
delete m_pClient;
m_pClient = nullptr;
}
m_pClient = new QMQTT::Client(QHostAddress(sIp), nPort);
if(m_pClient)
{
qDebug() << "m_pClient Create Success.";
m_pClient->setClientId(sClientId);
m_pClient->setUsername(sUser);
m_pClient->setPassword(sPwd.toLatin1());
m_pClient->setKeepAlive(60);
connect(m_pClient, &QMQTT::Client::connected, this, &mqttClient::sig_result_connect);
connect(m_pClient, &QMQTT::Client::disconnected, this, &mqttClient::sig_result_disconnect);
connect(m_pClient, SIGNAL(received(QMQTT::Message)), this, SLOT(on_dataReceived(QMQTT::Message)));
}
}
void mqttClient::slot_connect()
{
qDebug() << "connect 000";
if(m_pClient)
{
if(!m_pClient->isConnectedToHost())
{
qDebug() << "connect 111";
m_pClient->connectToHost();
}
}
}
void mqttClient::slot_disconnect()
{
qDebug() << "disconnect";
if(m_pClient->isConnectedToHost())
{
m_pClient->disconnectFromHost();
}
}
void mqttClient::slot_publish(QString sTopic, QString sPayload)
{
if(sTopic.isEmpty() || sPayload.isEmpty())
{
qDebug() << "sTopic or sPayload is empty";
return;
}
QMQTT::Message message(136, sTopic, sPayload.toUtf8());
m_pClient->publish(message);
}
void mqttClient::slot_subscribe(QString sTopic)
{
if(sTopic.isEmpty())
{
qDebug() << "sTopic is empty";
return;
}
m_pClient->subscribe(sTopic);
}
void mqttClient::slot_unSubscribe(QString sTopic)
{
if(sTopic.isEmpty())
{
qDebug() << "sTopic is empty";
return;
}
m_pClient->unsubscribe(sTopic);
}
void mqttClient::on_dataReceived(QMQTT::Message message)
{
qDebug() << "on_dataReceived";
emit sig_result_dataReceived(message);
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "mqttclient.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void sig_btn_connect();
void sig_btn_disconnect();
void sig_btn_publish(QString sTopic, QString sPayload);
void sig_btn_subscribe(QString sTopic);
void sig_btn_unSubscribe(QString sTopic);
private slots:
void on_pushButton_Connect_clicked();
void on_pushButton_Publish_clicked();
void on_pushButton_Subscribe_clicked();
void on_pushButton_UnScribe_clicked();
void slot_result_connect();
void slot_result_disconnect();
void slot_result_dataReceived(QMQTT::Message message);
void on_pushButton_DisConnect_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(this, SIGNAL(sig_btn_connect()),
&mqttClient::getInstance(), SLOT(slot_connect()));
connect(this, SIGNAL(sig_btn_disconnect()),
&mqttClient::getInstance(), SLOT(slot_disconnect()));
connect(this, SIGNAL(sig_btn_publish(QString, QString)),
&mqttClient::getInstance(), SLOT(slot_publish(QString, QString)));
connect(this, SIGNAL(sig_btn_subscribe(QString)),
&mqttClient::getInstance(), SLOT(slot_subscribe(QString)));
connect(this, SIGNAL(sig_btn_unSubscribe(QString)),
&mqttClient::getInstance(), SLOT(slot_unSubscribe(QString)));
connect(&mqttClient::getInstance(), SIGNAL(sig_result_connect()),
this, SLOT(slot_result_connect()));
connect(&mqttClient::getInstance(), SIGNAL(sig_result_disconnect()),
this, SLOT(slot_result_disconnect()));
connect(&mqttClient::getInstance(), SIGNAL(sig_result_dataReceived(QMQTT::Message)),
this, SLOT(slot_result_dataReceived(QMQTT::Message)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_pushButton_Connect_clicked()
{
QString sIp = ui->lineEdit_IP->text().trimmed();
quint16 nPort = (quint16)ui->lineEdit_Port->text().toInt();
QString sClientId = ui->lineEdit_ClientId->text().trimmed();
QString sUser = ui->lineEdit_User->text().trimmed();
QString sPwd = ui->lineEdit_Password->text().trimmed();
mqttClient::getInstance().init(sIp, nPort, sClientId, sUser, sPwd);
emit sig_btn_connect();
}
void MainWindow::on_pushButton_Publish_clicked()
{
QString sTopic = ui->lineEdit_Topic->text().trimmed();
QString sPayload = ui->lineEdit_Message->text().trimmed();
emit sig_btn_publish(sTopic, sPayload);
}
void MainWindow::on_pushButton_Subscribe_clicked()
{
QString sTopic = ui->lineEdit_Topic_2->text().trimmed();
emit sig_btn_subscribe(sTopic);
}
void MainWindow::on_pushButton_UnScribe_clicked()
{
QString sTopic = ui->lineEdit_Topic_2->text().trimmed();
emit sig_btn_unSubscribe(sTopic);
}
void MainWindow::slot_result_connect()
{
qDebug() << "result:Connected";
ui->label_Status->setText("Connected");
}
void MainWindow::slot_result_disconnect()
{
qDebug() << "result:Unconnected";
ui->label_Status->setText("Unconnected");
}
void MainWindow::slot_result_dataReceived(QMQTT::Message message)
{
QString sMes = QString(message.id()) + " " + QString(message.qos()) + QString(message.topic()) + message.payload() + "\n";
ui->textEdit_Log->append(sMes);
}
void MainWindow::on_pushButton_DisConnect_clicked()
{
emit sig_btn_disconnect();
}
五、客户端与服务器连接调试结果
开启Windows PowerShell,执行服务和订阅发布操作,开启程序连接服务器,并执行订阅和发布操作,双方都可以收到对方发布的消息。