实现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 += quickCONFIG += c++17SOURCES += \main.cppRESOURCES += qml.qrcINCLUDEPATH += $$PWD/ mqtt-c/include \$$PWD/mqtt-c/templates# 链接静态库
LIBS += -L$$PWD/mqtt-c/lib -lmqttcDebugBuild {DESTDIR = $${OUT_PWD}/debug
} else {DESTDIR = $${OUT_PWD}/release
}win32 {message("Building for MQTT-C Windows")# MQTT文件夹名MQTT_PATH = mqtt-cDESTDIR_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/89730327https://download.csdn.net/download/qq_38159549/89730327
Demo基于MQTT-C examples下simple_publisher.c与 simple_subscriber.c例子修改。