实现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/89730327https://download.csdn.net/download/qq_38159549/89730327
Demo基于MQTT-C examples下simple_publisher.c与 simple_subscriber.c例子修改。