实现mqtt订阅与发布话题,与mqtt服务器进行数据通信
编译环境:Qt5.15.2 + vs2019
需要mqttc库:mqttc.lib, mqttc.dll(根据MQTT-C源码编译出来的库,参考cmake编译MQTT-C源码-CSDN博客)
一、Qt pro文件编写
在Demo中创建mqtt-c文件夹,将mqttc库放在指定文件夹下,MQTT-C头文件包括mqtt_pal.h与mqtt.h。

QT += quick
CONFIG += c++17
SOURCES += \
        main.cpp
RESOURCES += qml.qrc
INCLUDEPATH += $$PWD/ mqtt-c/include \
                $$PWD/mqtt-c/templates
# 链接静态库
LIBS += -L$$PWD/mqtt-c/lib -lmqttc
DebugBuild {
    DESTDIR  = $${OUT_PWD}/debug
} else {
    DESTDIR  = $${OUT_PWD}/release
}
win32 {
    message("Building for MQTT-C Windows")
    # MQTT文件夹名
    MQTT_PATH = mqtt-c
    DESTDIR_WIN = $$replace(DESTDIR, "/", "\\")
    message($$DESTDIR_WIN)
    # 拷贝动态库
    MQTTC_DLL = \
        $$PWD\\$$MQTT_PATH\\bin\\mqttc.dll
    # 拷贝动态库到exe可执行文件同级文件夹下
    QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$MQTTC_DLL\" \"$$DESTDIR_WIN\"
}
# Additional import path used to resolve QML modules in Qt Creator's code model
QML_IMPORT_PATH =
# Additional import path used to resolve QML modules just for Qt Quick Designer
QML_DESIGNER_IMPORT_PATH =
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
 
二、实现Mqtt发布、订阅话题功能,与mqtt服务器数据通信。
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <iostream>
#include "posix_sockets.h"
#include "mqtt.h"  // 包含MQTT库的头文件
const char* DEFAULT_HOST = "47.120.xx.xxx"; // 替换成mqtt服务器地址
const char* DEFAULT_PORT = "1883";
const char* TOPIC = "testtopic/MAV";
void publish_callback(void** unused, struct mqtt_response_publish *published) {
    // 收到发布消息时的回调
    char* topic_name = (char*)malloc(published->topic_name_size + 1);
    memcpy(topic_name, published->topic_name, published->topic_name_size);
    topic_name[published->topic_name_size] = '\0';
    // 修复收到的多余字符
    char* message = (char*)malloc(published->application_message_size + 1);
    memcpy(message, published->application_message, published->application_message_size);
    message[published->application_message_size] = '\0';  // 添加结束符
    
std::cout << "Received message on topic: " << std::string((const char*)published->topic_name, published->topic_name_size)
              << ", message: " << message << std::endl;
}
DWORD WINAPI client_refresher(LPVOID client) {
    while (1) {
        mqtt_sync((struct mqtt_client*)client);
        Sleep(100);
    }
    return 0;
}
int main(int argc, char *argv[]) {
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
        qDebug() << "WSAStartup failed.";
        return -1;
    }
    //SOCKET sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    int sockfd = open_nb_socket(DEFAULT_HOST, DEFAULT_PORT);
    if (sockfd == INVALID_SOCKET) {
        qDebug() << "Socket creation failed.";
        WSACleanup();
        return -1;
    }
    struct mqtt_client client;
    uint8_t sendbuf[2048];
    uint8_t recvbuf[1024];
    mqtt_init(&client, sockfd, sendbuf, sizeof(sendbuf), recvbuf, sizeof(recvbuf), publish_callback);
    /* Create an anonymous session */
    const char* client_id = NULL;
    /* Ensure we have a clean session */
    uint8_t connect_flags = MQTT_CONNECT_CLEAN_SESSION;
    /* Send connection request to the broker. */
    mqtt_connect(&client, client_id, NULL, NULL, 0, NULL, NULL, connect_flags, 400);
    /* check that we don't have any errors */
    if (client.error != MQTT_OK) {
        qDebug() << "error:" << mqtt_error_str(client.error);
        //fprintf(stderr, "error: %s\n", mqtt_error_str(client.error));
    }
    // 订阅
    mqtt_subscribe(&client, TOPIC, 0);
    HANDLE refresh_thread = CreateThread(NULL, 0, client_refresher, &client, 0, NULL);
    if (refresh_thread == NULL) {
        qDebug() << "Failed to start client daemon thread.";
        mqtt_disconnect(&client);
        closesocket(sockfd);
        WSACleanup();
        return -1;
    }
    // 发布
    const char* _mockData = "CusData";
    mqtt_publish(&client, TOPIC, _mockData, strlen(_mockData), MQTT_PUBLISH_QOS_0);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty()) {
        qDebug() << "Failed to load QML.";
        return -1;
    }
    int result = app.exec();
    WaitForSingleObject(refresh_thread, INFINITE);
    CloseHandle(refresh_thread);
    mqtt_disconnect(&client);
    closesocket(sockfd);
    WSACleanup();
    return result;
}
 
三、编译报错
1、“close”: 找不到标识符报错
..\Qt-MQTT-C-Demo\mqtt-c\templates\posix_sockets.h(46): error C3861: “close”: 找不到标识符
 原因是posix_sockets.h源码只适配了Linux,需要做Windows端的适配。

2、无法解析的外部符号 __imp_closesocket、无法解析的外部符号 __imp_socket 报错
原因:__imp_closesocket 等无法解析的外部符号,都是 Windows 网络 API(Winsock 库)中的函数。在 Windows 上,使用网络相关的功能时,通常需要链接 ws2_32.lib 库。
问题是由于在链接阶段没有包含 ws2_32.lib,这是 Winsock 库的静态链接库。
解决方案:确保项目正确地链接了 ws2_32.lib
 
在mcin.cpp使用到了 mqtt地方加上 #pragma comment(lib,"ws2_32.lib")
四、完整的MQTT发布与订阅Demo代码https://download.csdn.net/download/qq_38159549/89730327
https://download.csdn.net/download/qq_38159549/89730327
Demo基于MQTT-C examples下simple_publisher.c与 simple_subscriber.c例子修改。







![[mysql]最基本的SELECT...FROM结构](https://i-blog.csdnimg.cn/direct/fbf9af7f460a475f867c63f82de2c48e.png)










