服务端:
下载安装好EMQX后再bin目录下用控制台输入EMQX start
打开网址localhost:18083
客户端:
https://projects.eclipse.org/projects/iot.paho/downloads
上述网址下载好对应的C++版本的库文件
记得设置Setting下的Build下的Cmake中的Toolchain和Generator
在CMAKELIST文件夹下加上
link_directories(lib)这是能找到来自外部的库
include_directories(include)是能找到include文件夹下的头文件
target_link_libraries(MQTT paho-mqtt3a.dll)是将动态库链接到项目
完整代码
#include "MQTTClient.h" #include <iostream> //#define MQTTCLIENT_SUCCESS 0 //#define MQTTVERSION_DEFAULT 0 //using namespace std; // //struct Options //{ // char* connection; /**< connection to system under test. */ // char** haconnections; // int hacount; // int verbose; // int test_no; // int MQTTVersion; // int iterations; //} options = //{ // "http://127.0.0.1/:1883", // NULL, // 0, // 0, // 0, // MQTTVERSION_DEFAULT, // 100, //}; #include <stdio.h> #include <string.h> #include "MQTTAsync.h" #include "MQTTClient.h" #define ADDRESS "tcp://192.168.1.25:1883" #define CLIENTID "ExampleClientPub" #define TOPIC "mqtt" #define QOS 1 using namespace std; static MQTTClient client=NULL; volatile MQTTClient_deliveryToken deliveredtoken; void delivered(void *context ,MQTTClient_deliveryToken dt) { printf("Message with token value %d delivery confirmed\n",dt); deliveredtoken=dt; } int msgarrvd(void *context,char *topicname,int topiclen,MQTTClient_message *message) { int i; char* payloadptr; printf("Message arrived \n"); printf("topic:%s\n",topicname); printf("message:"); payloadptr= static_cast<char *>(message->payload); for(i=0;i<message->payloadlen;i++) { putchar(*payloadptr++); } putchar('\n'); MQTTClient_freeMessage(&message); MQTTClient_free(topicname); return 1; } void connlost(void *context, char *cause) { printf("\nConnection lost\n"); printf(" cause: %s\n", cause); } int mqtt_client_publish(char *top, int qos, char *msg, int len) { MQTTClient_deliveryToken token; MQTTClient_message pubmsg = MQTTClient_message_initializer; if (client == NULL) return -1; pubmsg.payload = msg; pubmsg.payloadlen = len; pubmsg.qos = qos; pubmsg.retained = 0; deliveredtoken = 0; MQTTClient_publishMessage(client, top, &pubmsg, &token); printf("Waiting for publication of %s\n" "on topic %s for client\n", msg, top); while (deliveredtoken != token); return 0; } int mqtt_client_subscribe(char *top, int qos) { if (client == NULL) return -1; MQTTClient_subscribe(client, top, qos); return 0; } int mqtt_client_open() { int rc; MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; MQTTClient_create(&client, ADDRESS, CLIENTID, MQTTCLIENT_PERSISTENCE_NONE, NULL); MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered); conn_opts.keepAliveInterval = 20; conn_opts.cleansession = 1; if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS) { printf("Failed to connect, return code %d\n", rc); return -1; } printf("Success to connect!\n"); return 0; } int main(int argc, char* argv[]) { char str[256] ; mqtt_client_open();//开启一个客户端 mqtt_client_subscribe(TOPIC, QOS);//添加一个订阅 while(1) { cin.getline(str,256);//一直发送消息 if(strcmp(str,"q")==0) { break; }else { mqtt_client_publish(TOPIC, QOS, str, strlen(str));//发布一个消息 } } // printf("退出成功"); MQTTClient_disconnect(client, 10000);//断开连接 MQTTClient_destroy(&client);//清理缓存 return 0; }
编译的时候记得要加入动态链接库选项,在Run下面的configuration里面的Environment Variables加入库的目录如PATH=D:\project_wang\MQTT\lib
两个数据结构
MQTTClient_connectOptions定义
规定所有客户端的性质
typedef struct { char struct_id[4]; //结构体的识别序列,必须为MQTC int struct_version; //结构体版本 /** 在0,1,2,3,4,5中取值: 0-表示没有SSL选项且没有serverURIs; 1-表示没有serverURIs; 2-表示没有MQTTVersion 3-表示没有返回值; 4-表示没有二进制密码选项 */ int keepAliveInterval; /** 在这段时间内没有数据相关的消息时,客户端发送一个非常小的MQTT“ping”消息,服务器将会确认这个消息 */ int cleansession; /** 当cleansession为true时,会话状态信息在连接和断开连接时被丢弃。 将cleansession设置为false将保留会话状态信息 */ int reliable; /* 将该值设置为true意味着必须完成发布的消息(已收到确认),才能发送另一个消息 */ MQTTClient_willOptions* will; /* 如果程序不使用最后的意愿和遗嘱功能,请将此指针设置为NULL。 */ const char* username;//用户名 const char* password;//密码 int connectTimeout;//允许尝试连接的过时时间 int retryInterval;//尝试重连的时间 MQTTClient_SSLOptions* ssl; /* 如果程序不使用最后的ssl,请将此指针设置为NULL。 */ int serverURIcount; char* const* serverURIs; /* 连接服务器的url,以protocol:// host:port为格式 */ int MQTTVersion; /* MQTT的版本,MQTTVERSION_3_1(3),MQTTVERSION_3_1_1 (4) */ struct { const char* serverURI; int MQTTVersion; int sessionPresent; } returned; struct { int len; const void* data; } binarypwd; } MQTTClient_connectOptions;
MQTTClient_message
typedef struct { char struct_id[4];//结构体的识别序列,必须为MQTM int struct_version;//结构体的版本,必须为0 int payloadlen;//MQTT信息的长度 void* payload;//指向消息负载的指针 int qos;//服务质量 int retained;//保留标志 int dup;dup//标志指示这个消息是否是重复的。 只有在收到QoS1消息时才有意义。 如果为true,则客户端应用程序应采取适当的措施来处理重复的消息。 int msgid;//消息标识符通常保留供MQTT客户端和服务器内部使用。 } MQTTClient_message;
操作函数
DLLExport int MQTTClient_create( MQTTClient * handle, const char * serverURI, const char * clientId, int persistence_type, void * persistence_context )
作用: 该函数创建了一个用于连接到特定服务器,使用特定持久存储的MQTT客户端。 参数: handle 指向MQTT客户端句柄的指针。句柄被成功从函数中返回的客户端引用所填充 serverURI 以空结尾的字符串,其指定客户端将连接到的服务器。其格式为protocol://host:port。现在的(protocol)协议必须是tcp或ssl,而host可以指定为IP地址或域名。例如, 要使用默认 MQTT 端口连接到本地计算机上运行的服务器, 请指定为 tcp://localhost:1883。 clientId 客户端标识符(clientId)是一个以空结尾的 UTF-8 编码字符串,客户端连接到服务器时将它传递过去。 persistence_type 客户端所使用的持久类型。MQTTCLIENT_PERSISTENCE_NONE-使用内存持久化。如果客户端运行的设备或系统出故障或关闭, 则任何正在运行的消息的当前状态都将丢失, 甚至在 QoS1 和 QoS2 中也可能无法传递某些消息; MQTTCLIENT_PERSISTENCE_DEFAULT-使用默认的持久化机制(文件系统)。正在运行消息的状态被保存在持久存储中,以便在意外出现时对消息的丢失提供一些保护; MQTTCLIENT_PERSISTENCE_USER-使用程序指定的持久化实现。使用这种类型,应用程序可对持久化机制进行控制,应用程序必须实现MQTTClient_persistence 接口。 persistence_context 如果应用程序使用的是MQTTCLIENT_PERSISTENCE_NONE持久化,该参数不使用,而且值应该设置为NULL。对于MQTTCLIENT_PERSISTENCE_DEFAULT持久化,应该设置持久化目录的位置(如果设置为NULL,则使用工作目录作为持久化目录)。使用MQTTCLIENT_PERSISTENCE_USER持久化,则将此参数指向有效的MQTTClient_persistence结构。
DLLExport int MQTTClient_setCallbacks ( MQTTClient handle, void * context, MQTTClient_connectionLost * cl, MQTTClient_messageArrived * ma, MQTTClient_deliveryComplete * dc )
作用: 该函数为特定的客户端创建回调函数。如果您的客户端应用程序不使用特定的回调函数,请将相关参数设置为NULL。 调用MQTTClient_setCallbacks()使客户端进入多线程模式。 任何必要的消息确认和状态通信都在后台处理,而不需要客户端应用程序的任何干预。 注意:在调用该函数时,MQTT客户端必须断开连接。(即先要调用该函数在连接客户端)。 参数: handle 指向MQTT客户端句柄的指针。句柄被成功从函数中返回的客户端引用所填充 context 指向任何应用程序特定上下文的指针。 上下文指针被传递给每个回调函数,以提供对回调中的上下文信息的访问。 cl 指向MQTTClient_connectionLost()回调函数的指针。 如果您的应用程序不处理断开连接,您可以将其设置为NULL。 ma 指向MQTTClient_messageArrived()回调函数的指针。 当您调用MQTTClient_setCallbacks()时,必须指定此回调函数。 dc 指向MQTTClient_deliveryComplete()回调函数的指针。 如果您的应用程序同步发布,或者您不想检查是否成功发送,则可以将其设置为NULL。
DLLExport int MQTTClient_connect ( MQTTClient handle, MQTTClient_connectOptions * options )
作用: 此函数尝试使用指定的选项将先前创建的客户端连接到MQTT服务器。 参数: handle 指向MQTT客户端句柄的指针。句柄被成功从函数中返回的客户端引用所填充 options 指向有效的MQTTClient_connectOptions结构的指针。 返回值: 0 连接成功 1 拒绝连接:不可接受的协议版本。 2 拒绝连接:标识符被拒绝。 3 拒绝连接:服务器不可用。 4 拒绝连接:用户名或密码错误。 5 拒绝连接:未经授权。 6 保留给未来用。
DLLExport int MQTTClient_subscribe ( MQTTClient handle, const char * topic, int qos )
作用: 此功能尝试将客户订阅到单个主题,该主题可能包含通配符。 此函数还指定服务质量。 参数: handle 指向MQTT客户端句柄的指针。句柄被成功从函数中返回的客户端引用所填充 topic 订阅的主题,可使用通配符。 qos 订阅的请求服务质量
DLLExport int MQTTClient_publishMessage ( MQTTClient handle, const char * topicName, MQTTClient_message * msg, MQTTClient_deliveryToken * dt )
作用: 此功能尝试将客户订阅到单个主题,该主题可能包含通配符。 此函数还指定服务质量。 参数: handle 指向MQTT客户端句柄的指针。句柄被成功从函数中返回的客户端引用所填充 topicName 与信息相关的主题。 msg 指向有效的 MQTTClient_message 结构的指针, 其中包含要发布消息的有效负载和属性 dt 指向MQTTClient_deliveryToken的指针。当函数成功返回时,dt会被赋值为代表消息的token。如果程序中没有使用传递token,将其设置为NULL。
DLLExport int MQTTClient_waitForCompletion ( MQTTClient handle, MQTTClient_deliveryToken dt, unsigned long timeout )
作用: 客户端应用程序调用此函数来将主线程的执行与消息的完成发布同步。 被调用时,MQTTClient_waitForCompletion()阻塞执行,直到消息成功传递或已超过指定的时间。 参数: handle 指向MQTT客户端句柄的指针。句柄被成功从函数中返回的客户端引用所填充 dt 代表消息的MQTTClient_deliveryToken用来检测是否成功传递。传递token由发布函数MQTTClient_publish () 和 MQTTClient_publishMessage ()所产生。 timeout 等待的最大毫秒数。 返回值 : 消息成功传递则返回MQTTCLIENT_SUCCESS(0) ,如果时间已过期或检测token时出问题,则返回错误码。