在Qt中实现mqtt客户端

文章目录

1.前言

目前我使用的Qt版本为Qt5.15.2,虽然有一个和qt结合得很好的mqtt库:qmqtt

但是由于其不再维护,可能有一些新标准啥的没及时跟上,后续可能会逐渐落伍甚至无法和市场主流的服务器通讯,这里还是选了一个还在继续维护的开源项目:https://github.com/eclipse-paho/paho.mqtt.c

2.mqtt服务端

在正式在qt中创建mqtt客户端之前,我们先搞定mqtt服务器,有这个服务器我们才好调试。

经过一番搜索后,发现现在主流的一般是用这两个服务程序:mosquitto、emqx。mosquitto好多都需要用命令行来操作,比较麻烦;emqx有个网页界面,操作起来方便一点,那就选emqx了。

2.1.下载软件

到其官网下载:
https://www.emqx.com/zh/try?tab=self-managed

下载个edge就够了。免费版最多只能建立10个连接,但是也够用了。

这个软件是绿色版,免安装的,解压就可以用。

下载后,解压到某个文件夹下:

2.2.启动软件

在启动之前,先修改一下nanomq.conf这个文件的内容,否则其ssl加密的服务(也就是wss、mqtts)不会起来。

listeners.ssl下的enable值从false改为true

然后再打开cmd,cd到你把emqx-edge解压到的文件夹,然后执行

bash 复制代码
nanomq.exe start

正常的话,你应该可以在命令行中看到五个服务被启动了:

服务 端口 简介
mqtt 1883 MQTT 代理(Broker)
ws 8083 MQTT 代理(Broker)
mqtts 8883 MQTT 代理(Broker)
wss 8086 MQTT 代理(Broker)
http 8081 提供了网页功能,用户通过访问此链接,就可以对服务器进行监控

2.3.监控

然后我们可以直接在网页浏览器中输入:http://127.0.0.1:8081,访问服务器配置页面

用户名、密码在其配置页面中,

登录完成:

这个页面的功能可以自行摸索或者阅读官方文档,这里不再赘述。

至此,服务器我们已经搭建起来了。

3.mqtt客户端

目前我们是在windows下使用,可以直接使用官方为我们编译好的库。假如是想集成到嵌入式系统内,需要自行拿源码进行编译。

然后修改一下我们工程的pro文件:

头文件和库文件路径根据你的实际路径来写

bash 复制代码
# 在Windows下要添加这边,不然会与paho.mqtt.c有冲突,无法顺利编译
LIBS += -lws2_32 -liphlpapi
DEFINES += WIN32_LEAN_AND_MEAN

# 服务器的创建可以使用emqx edge  https://www.emqx.com/zh/downloads-and-install/emqx-edge?os=Windows
# 使用的库为 https://github.com/eclipse-paho/paho.mqtt.c
INCLUDEPATH += $$PWD/../eclipse-paho-mqtt-c/include
LIBS += -L$$PWD/../eclipse-paho-mqtt-c/lib \
-lpaho-mqtt3cs -lpaho-mqtt3a -lpaho-mqtt3as -lpaho-mqtt3c

然后就可以写代码测试一下了

主要假如使用ssl方式时,需要指定证书,证书就用服务器提供的证书etc/certs/cacert.pem即可

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "MQTTClient.h"

// #define ADDRESS     "ws://127.0.0.1:8083"
#define ADDRESS     "wss://127.0.0.1:8086"
// #define ADDRESS     "mqtt://127.0.0.1:1883"
// #define ADDRESS     "mqtts://127.0.0.1:8883"
#define CLIENTID    "NanoMQ_MjI3Nz"
#define TOPIC       "MY_TOPIC"
#define PAYLOAD     "Hello World!"
#define QOS         1
#define TIMEOUT     10000L

volatile MQTTClient_deliveryToken deliveredtoken;

void delivered(void *context, MQTTClient_deliveryToken dt)
{
    qDebug("Message with token value %d delivery confirmed\n", dt);
    deliveredtoken = dt;
}

int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    qDebug("Message arrived\n");
    qDebug("     topic: %s\n", topicName);
    qDebug("   message: %.*s\n", message->payloadlen, (char*)message->payload);
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

void connlost(void *context, char *cause)
{
    qDebug("\nConnection lost\n");
    if (cause)
        qDebug("     cause: %s\n", cause);
}

int mqttClientTest()
{
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    int rc;

    const char* uri =  ADDRESS;
    qDebug("Using server at %s\n", uri);

    if ((rc = MQTTClient_create(&client, uri, CLIENTID,
                                MQTTCLIENT_PERSISTENCE_NONE, NULL)) != MQTTCLIENT_SUCCESS)
    {
        qDebug("Failed to create client, return code %d\n", rc);
        rc = EXIT_FAILURE;
        goto exit;
    }

    if ((rc = MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered)) != MQTTCLIENT_SUCCESS)
    {
        qDebug("Failed to set callbacks, return code %d\n", rc);
        rc = EXIT_FAILURE;
        goto destroy_exit;
    }

    // 1. 定义SSL选项
    MQTTClient_SSLOptions sslOpt = MQTTClient_SSLOptions_initializer;

    // 2. 根据你的证书来源设置路径
    // 场景一:使用从服务商下载或自己导出的CA证书(单向认证)
    sslOpt.trustStore = "C:/Users/Administrator/Desktop/jsEngine/emqx-edge-1.2.0-windows-x86_64/etc/certs/cacert.pem"; // 注意Windows路径使用双反斜杠
    sslOpt.enableServerCertAuth = 1;

    // 场景二:服务器要求双向认证(还需要客户端证书和私钥)
    // sslOpt.trustStore = "C:\\your_project\\certs\\ca.pem";
    // sslOpt.keyStore = "C:\\your_project\\certs\\client.pem"; // 你的客户端证书
    // sslOpt.privateKey = "C:\\your_project\\certs\\client.key"; // 你的客户端私钥
    // 如果私钥有密码,还需要设置:
    // sslOpt.privateKeyPassword = "your_key_password";

    // 3. 配置结构体版本和ID(必须)
    sslOpt.struct_version = 4; // 使用支持ALPN的版本,对WSS连接很重要
    strncpy(sslOpt.struct_id, "MQTS", 4);

    conn_opts.ssl = &sslOpt;

    // conn_opts.username = "123";
    // conn_opts.password = "123";
    conn_opts.keepAliveInterval = 60;
    conn_opts.cleansession = 1;
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        qDebug("Failed to connect, return code %d\n", rc);
        rc = EXIT_FAILURE;
        goto destroy_exit;
    }

    qDebug("Subscribing to topic %s\nfor client %s using QoS%d\n\n"
           "Press Q<Enter> to quit\n\n", TOPIC, CLIENTID, QOS);
    if ((rc = MQTTClient_subscribe(client, TOPIC, QOS)) != MQTTCLIENT_SUCCESS)
    {
        qDebug("Failed to subscribe, return code %d\n", rc);
        rc = EXIT_FAILURE;
    }
    else
    {
        int ch;
        do
        {
            ch = getchar();
        } while (ch!='Q' && ch != 'q');

        if ((rc = MQTTClient_unsubscribe(client, TOPIC)) != MQTTCLIENT_SUCCESS)
        {
            qDebug("Failed to unsubscribe, return code %d\n", rc);
            rc = EXIT_FAILURE;
        }
    }

    if ((rc = MQTTClient_disconnect(client, 10000)) != MQTTCLIENT_SUCCESS)
    {
        qDebug("Failed to disconnect, return code %d\n", rc);
        rc = EXIT_FAILURE;
    }
destroy_exit:
    MQTTClient_destroy(&client);
exit:
    return rc;
}

运行后,发布一个信息,qt这边可以顺利收到。完美。

4.mqtt的一些知识点

4.1.ClientID

有时我们发现在A软件上对mqtt服务器建立连接后,再在另外一个软件上对服务器建立同样的连接,先前的连接会被挤掉。这时候看看是不是使用了同样的ClientID。只要使用不同的ClientID,就不会被挤掉。

4.2.Retain

假如我们发布了一个Retain信息,那么客户端在连接上之后,服务器会自动发一次最新发布的信息给客户端;假如这时想取消,只需再发一次Recain空信息即可(payload为空)。


参考
【Windows下搭建MQTT服务器mosquitto】

相关推荐
踏过山河,踏过海2 分钟前
【用ui文件做个简单工具的开发,为什么修改完ui后,程序重新编译运行后,GUI界面还是不变呢?】
qt·ui
云栖梦泽18 分钟前
易语言开发从入门到精通:补充篇·网络编程进阶+实用爬虫开发·API集成·代理IP配置·异步请求·防封禁优化
开发语言
java1234_小锋32 分钟前
Java高频面试题:SpringBoot为什么要禁止循环依赖?
java·开发语言·面试
铅笔侠_小龙虾37 分钟前
Flutter Demo
开发语言·javascript·flutter
2501_944525541 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 账户详情页面
android·java·开发语言·前端·javascript·flutter
福大大架构师每日一题1 小时前
ComfyUI v0.11.1正式发布:新增开发者专属节点支持、API节点强化、Python 3.14兼容性更新等全方位优化!
开发语言·python
wangdaoyin20101 小时前
若依vue2前后端分离集成flowable
开发语言·前端·javascript
向阳开的夏天2 小时前
麒麟V10源码编译QT5.6.3 (x86 & arm64)
开发语言·qt
Evand J2 小时前
【MATLAB例程】TOA和TDOA混合定位,适用于二维平面的高精度定位。锚点数量、位置、测量噪声可自行调节
开发语言·matlab·定位·tdoa
念越2 小时前
数据结构:栈堆
java·开发语言·数据结构