Ryan-CW 3 سال پیش
والد
کامیت
cb4ee9c6b0

+ 4 - 50
.gitignore

@@ -1,52 +1,6 @@
 # Prerequisites
-*.d
+# SConscript
 
-# Object files
-*.o
-*.ko
-*.obj
-*.elf
-
-# Linker output
-*.ilk
-*.map
-*.exp
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Libraries
-*.lib
-*.a
-*.la
-*.lo
-
-# Shared objects (inc. Windows DLLs)
-*.dll
-*.so
-*.so.*
-*.dylib
-
-# Executables
-*.exe
-*.out
-*.app
-*.i*86
-*.x86_64
-*.hex
-
-# Debug files
-*.dSYM/
-*.su
-*.idb
-*.pdb
-
-# Kernel Module Compile Results
-*.mod*
-*.cmd
-.tmp_versions/
-modules.order
-Module.symvers
-Mkfile.old
-dkms.conf
+# 平台移植层
+platform/linux/*
+platform/FreeRTOS/*

+ 92 - 1
README.md

@@ -1,2 +1,93 @@
 # RyanMqtt
-RyanMqtt实现了MQTT3.1.1协议的客户端。此库针对资源受限的嵌入式设备进行了优化。
+
+### 1、介绍
+
+RyanMqtt 实现了 MQTT3.1.1 协议的客户端。此库针对资源受限的嵌入式设备进行了优化。
+
+初衷:在使用[RT-Thread](https://github.com/RT-Thread/rt-thread)时,没有非常合适的 mqtt 客户端。项目中 mqtt 又是非常核心的功能。随即参考 MQTT3.1.1 标准和项目需求设计的 mqtt 客户端,它拥有以下特点。
+
+- 严格遵循 MQTT3.1.1 协议标准
+- 应该是非常稳定的 QOS2 / QOS1 消息实现。用户可控的消息丢弃,避免 QOS2 / QOS1 消息无限重发消耗的内存空间
+- 丰富的、可配置的事件回调函数和多功能参数配置,满足实际项目的绝大部分需求
+- 支持多客户端
+- 完整的 MQTT3.1.1 通配符支持
+- 可选择的内部心跳保活、掉线重连、遗嘱消息等
+- 跨平台,只需实现少量的平台接口即可
+- 更高的并发能力,无等待的连续 200 条 QOS2 消息稳定发送和接收(也取决于硬件收发能力)
+- 没有内置 TLS 支持,用户可以在接口层实现 TLS(作者对 TLS 并不熟悉、项目中也暂未使用到)
+- 不支持裸机平台,裸机想要稳定的 MQTT3.1.1 实现可以参考[coreMQTT](https://github.com/FreeRTOS/coreMQTT)
+
+### 2、设计
+
+RyanMqtt 设计时参考了[mqttclient](https://github.com/jiejieTop/mqttclient)、[esp-mqtt](https://github.com/espressif/esp-mqtt)、[coreMQTT](https://github.com/FreeRTOS/coreMQTT)。
+
+文案待补充
+
+### 3、平台接口
+
+*RyanMqtt库希望应用程序为以下接口提供实现:*
+
+#### system接口
+
+*RyanMqtt需要RTOS支持,必须实现如下接口才可以保证mqtt客户端的正常运行*
+
+| 函数名称              | 函数简介            |
+| --------------------- | ------------------- |
+| platformMemoryMalloc  | 申请内存            |
+| platformMemoryFree    | 释放已申请内存      |
+| platformDelay         | 毫秒延时            |
+| platformThreadInit    | 初始化线程          |
+| platformThreadStart   | 开启线程            |
+| platformThreadStop    | 挂起线程            |
+| platformThreadDestroy | 销毁线程            |
+| platformMutexInit     | 初始化互斥锁        |
+| platformMutexLock     | 获取互斥锁          |
+| platformMutexUnLock   | 释放互斥锁          |
+| platformMutexDestroy  | 销毁互斥锁          |
+| platformCriticalEnter | 进入临界区 / 关中断 |
+| platformCriticalExit  | 退出临界区 / 开中断 |
+
+#### network接口
+
+*RyanMqtt依赖于底层传输接口 API,必须实现该接口 API 才能在网络上发送和接收数据包*
+
+*MQTT 协议要求基础传输层能够提供有序的、可靠的、双向传输(从客户端到服务端 和从服务端到客户端)的字节流*
+
+| 函数名称                 | 函数简介           |
+| ------------------------ | ------------------ |
+| platformNetworkConnect   | 连接mqtt服务器     |
+| platformNetworkRecvAsync | 非阻塞接收数据     |
+| platformNetworkSendAsync | 非阻塞发送数据     |
+| platformNetworkClose     | 断开mqtt服务器连接 |
+
+#### time接口
+
+*RyanMqtt依靠函数生成毫秒时间戳,用于计算持续时间和超时,内部已经做了数值溢出处理*
+
+| 函数名称         | 函数简介           |
+| ---------------- | ------------------ |
+| platformUptimeMs | 自启动以来ms时间戳 |
+
+
+
+### 4、示例
+
+RT-Thread 平台
+
+- 接口示例请参考platform/rtthread文件夹
+- RyanMqtt使用示例请参考 example 文件夹
+- 需要使能 SAL 或者 LWIP,示例使用 socket 实现数据收发。
+- 需要MSH组件,示例默认挂载到MSH组件
+
+其余平台暂无示例
+
+### 5、依赖
+
+RT-Thread 内置 ulog 组件,方便的使用 ulog api 来管理 RyanMqtt 打印信息
+
+如果没有使能 ulog 或者非 RT-Thread 平台,用户需要手动修改 RyanMqttLog.h 文件调整打印等级。
+
+### 6、声明
+
+- 请勿将此库QOS消息等级用于支付等可能造成重大损失的场景,如需使用请自行深度评估后使用,作者不对使用此库造成的任何经济损失负责。(尽管此库QOS2消息等级经过很多测试,但是异步组件由于诸多因素例如波动非常大的网络甚至无法建立稳定的tcp连接、mqtt服务端的策略配置等,无法做到绝对的实时性,需要用户手动做到数据的最终一致性。)
+

+ 25 - 0
SConscript

@@ -0,0 +1,25 @@
+from building import *
+Import('RTT_ROOT')
+
+# get current directory
+cwd = GetCurrentDir()
+
+# The set of source files associated with this SConscript file.
+src = Glob('common/*.c')
+src += Glob('pahoMqtt/*.c')
+src += Glob('mqttclient/*.c')
+src += Glob('platform/rtthread/*.c')
+
+path = [cwd + '/common']
+path += [cwd + '/pahoMqtt']
+path += [cwd + '/mqttclient']
+path += [cwd + '/platform/rtthread']
+
+if GetDepend(['PKG_USING_RYANMQTT_EXAMPLE']):
+    src += Glob('example/*.c')
+    path += [cwd + '/example']
+
+group = DefineGroup('RyanMqtt', src, depend=[
+                    'PKG_USING_RYANMQTT'], CPPPATH=path)
+
+Return('group')

+ 129 - 0
common/RyanList.c

@@ -0,0 +1,129 @@
+
+
+#include "RyanList.h"
+
+/**
+ * @brief 在prev和next之前插入节点
+ *
+ * @param node
+ * @param prev
+ * @param next
+ */
+static void _RyanListAdd(RyanList_t *node, RyanList_t *prev, RyanList_t *next)
+{
+    next->prev = node;
+    node->next = next;
+    node->prev = prev;
+    prev->next = node;
+}
+
+/**
+ * @brief 删除prev和next之间的节点
+ *
+ * @param prev
+ * @param next
+ */
+static void _RyanListDel(RyanList_t *prev, RyanList_t *next)
+{
+    prev->next = next;
+    next->prev = prev;
+}
+
+/**
+ * @brief 删除自己
+ *
+ * @param entry
+ */
+static void _RyanListDel_entry(RyanList_t *entry)
+{
+    _RyanListDel(entry->prev, entry->next);
+}
+
+/**
+ * @brief 初始链表
+ *
+ * @param list
+ */
+void RyanMqttListInit(RyanList_t *list)
+{
+    list->next = list;
+    list->prev = list;
+}
+
+/**
+ * @brief 链表头插
+ *
+ * @param node
+ * @param list
+ */
+void RyanListAdd(RyanList_t *node, RyanList_t *list)
+{
+    _RyanListAdd(node, list, list->next);
+}
+
+/**
+ * @brief 链表尾插
+ *
+ * @param node
+ * @param list
+ */
+void RyanListAddTail(RyanList_t *node, RyanList_t *list)
+{
+    _RyanListAdd(node, list->prev, list);
+}
+
+/**
+ * @brief 删除自己
+ *
+ * @param entry
+ */
+void RyanListDel(RyanList_t *entry)
+{
+    _RyanListDel_entry(entry);
+}
+
+/**
+ * @brief 删除自己
+ *
+ * @param entry
+ */
+void RyanListDelInit(RyanList_t *entry)
+{
+    _RyanListDel_entry(entry);
+    RyanMqttListInit(entry);
+}
+
+/**
+ * @brief 将节点移到链表头部
+ *
+ * @param node
+ * @param list
+ */
+void RyanListMove(RyanList_t *node, RyanList_t *list)
+{
+    _RyanListDel_entry(node);
+    RyanListAdd(node, list);
+}
+
+/**
+ * @brief 将节点移到链表尾部
+ *
+ * @param node
+ * @param list
+ */
+void RyanListMoveTail(RyanList_t *node, RyanList_t *list)
+{
+    _RyanListDel_entry(node);
+    RyanListAddTail(node, list);
+}
+
+/**
+ * @brief 链表是否为空
+ *
+ * @param list
+ * @return int
+ */
+int RyanListIsEmpty(RyanList_t *list)
+{
+    return list->next == list;
+}

+ 69 - 0
common/RyanList.h

@@ -0,0 +1,69 @@
+
+#ifndef __RyanList__
+#define __RyanList__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    typedef struct RyanListNode
+    {
+        struct RyanListNode *next;
+        struct RyanListNode *prev;
+    } RyanList_t;
+
+#define RyanOffsetOf(type, member) ((size_t) & (((type *)0)->member))
+
+#define RyanContainerOf(ptr, type, member) \
+    ((type *)((unsigned char *)(ptr)-RyanOffsetOf(type, member)))
+
+// 通过链表获取节点首地址
+#define RyanListEntry(list, type, member) \
+    RyanContainerOf(list, type, member)
+
+// 从链表指针ptr的下一指针中获得包含该链表的结构体指针
+#define RyanListFirstEntry(list, type, member) \
+    RyanListEntry((list)->next, type, member)
+
+// 从链表指针ptr的上一指针中获得包含该链表的结构体指针
+#define RyanListPrevEntry(list, type, member) \
+    RyanListEntry((list)->prev, type, member)
+
+// 遍历链表正序
+#define RyanListForEach(curr, list) \
+    for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next)
+
+// 遍历链表反序
+#define RyanListForEachPrev(curr, list) \
+    for ((curr) = (list)->prev; (curr) != (list); (curr) = (curr)->prev)
+
+// 安全遍历链表正序
+#define RyanListForEachSafe(curr, next, list)          \
+    for ((curr) = (list)->next, (next) = (curr)->next; \
+         (curr) != (list);                             \
+         (curr) = (next), (next) = (curr)->next)
+
+// 安全遍历链表反序
+#define RyanListForEachPrevSafe(curr, next, list)      \
+    for ((curr) = (list)->prev, (next) = (curr)->prev; \
+         (curr) != (list);                             \
+         (curr) = (next), (next) = (curr)->prev)
+
+    void RyanMqttListInit(RyanList_t *list);
+
+    void RyanListAdd(RyanList_t *node, RyanList_t *list);
+    void RyanListAddTail(RyanList_t *node, RyanList_t *list);
+
+    void RyanListDel(RyanList_t *entry);
+    void RyanListDelInit(RyanList_t *entry);
+
+    void RyanListMove(RyanList_t *node, RyanList_t *list);
+    void RyanListMoveTail(RyanList_t *node, RyanList_t *list);
+    int RyanListIsEmpty(RyanList_t *list);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 48 - 0
common/RyanMqttLog.h

@@ -0,0 +1,48 @@
+
+
+#ifndef __RyanMqttLog__
+#define __RyanMqttLog__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <rtthread.h>
+#define RyanMqttTag ("RyanMqtt")
+
+#ifdef RT_USING_ULOG
+#include "ulog.h"
+#else
+
+#define ulog_d(TAG, ...)            \
+    {                               \
+        printf("%s", TAG);          \
+        printf(fmt, ##__VA_ARGS__); \
+        printf("\r\n");             \
+    }
+#define ulog_i(TAG, ...)            \
+    {                               \
+        printf("%s", TAG);          \
+        printf(fmt, ##__VA_ARGS__); \
+        printf("\r\n");             \
+    }
+#define ulog_w(TAG, ...)            \
+    {                               \
+        printf("%s", TAG);          \
+        printf(fmt, ##__VA_ARGS__); \
+        printf("\r\n");             \
+    }
+#define ulog_e(TAG, ...)            \
+    {                               \
+        printf("%s", TAG);          \
+        printf(fmt, ##__VA_ARGS__); \
+        printf("\r\n");             \
+    }
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

BIN
docs/MQTT-3.1.1-CN.pdf


+ 641 - 0
example/RyanMqtt.c

@@ -0,0 +1,641 @@
+#include "rtconfig.h"
+#ifdef PKG_USING_RYANMQTT_EXAMPLE
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <board.h>
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <rtdbg.h>
+#include "ulog.h"
+
+#include "RyanMqttClient.h"
+
+// ccm内存
+// 存放未初始化
+#define ccmBss __attribute__((section(".ccmbss")))
+
+#define delay(ms) rt_thread_mdelay(ms)
+
+static const char *TAG = "mqtt";
+
+RyanMqttClient_t *client = NULL;
+
+ccmBss static char mqttRecvBuffer[2048];
+ccmBss static char mqttSendBuffer[2048];
+
+// 具体数值计算可以查看事件回调函数
+static uint32_t mqttTest[10] = {0};
+#define dataEventCount (0)      // 接收到几次数据
+#define PublishedEventCount (1) // 发布成功的次数
+
+/**
+ * @brief mqtt事件回调处理函数
+ * 事件的详细定义可以查看枚举定义
+ *
+ * @param pclient
+ * @param event
+ * @param eventData
+ */
+void mqttEventHandle(void *pclient, RyanMqttEventId_e event, const void const *eventData)
+{
+    RyanMqttClient_t *client = (RyanMqttClient_t *)pclient;
+
+    switch (event)
+    {
+    case RyanMqttEventError:
+        break;
+
+    case RyanMqttEventConnected: // 不管有没有使能clearSession,都非常推荐在连接成功回调函数中订阅主题
+        ulog_i(TAG, "mqtt连接成功回调");
+        RyanMqttSubscribe(client, "sub/#", QOS0);
+        break;
+
+    case RyanMqttEventDisconnected:
+        ulog_w(TAG, "mqtt断开连接回调 %d", *(int32_t *)eventData);
+        break;
+
+    case RyanMqttEventSubscribed:
+    {
+        RyanMqttMsgHandler_t *msgHandler = (RyanMqttMsgHandler_t *)eventData;
+        ulog_w(TAG, "mqtt订阅成功回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
+        break;
+    }
+
+    case RyanMqttEventSubscribedFaile:
+    {
+        RyanMqttMsgHandler_t *msgHandler = (RyanMqttMsgHandler_t *)eventData;
+        ulog_w(TAG, "mqtt订阅失败回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
+        break;
+    }
+
+    case RyanMqttEventUnSubscribed:
+    {
+        RyanMqttMsgHandler_t *msgHandler = (RyanMqttMsgHandler_t *)eventData;
+        ulog_w(TAG, "mqtt取消订阅成功回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
+        break;
+    }
+
+    case RyanMqttEventUnSubscribedFaile:
+    {
+        RyanMqttMsgHandler_t *msgHandler = (RyanMqttMsgHandler_t *)eventData;
+        ulog_w(TAG, "mqtt取消订阅失败回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
+        break;
+    }
+
+    case RyanMqttEventPublished:
+    {
+        RyanMqttMsgHandler_t *msgHandler = ((RyanMqttAckHandler_t *)eventData)->msgHandler;
+        ulog_w(TAG, "qos1 / qos2发送成功事件回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
+        mqttTest[PublishedEventCount]++;
+        break;
+    }
+
+    case RyanMqttEventData:
+    {
+        RyanMqttMsgData_t *msgData = (RyanMqttMsgData_t *)eventData;
+        ulog_i(TAG, " umqtt topic recv callback! topic: %s, packetId: %d, payload len: %d \r\n \\
+                    data: %.*s",
+               msgData->topic, msgData->packetId, msgData->payloadLen,
+               msgData->payloadLen, msgData->payload);
+
+        mqttTest[dataEventCount]++;
+        break;
+    }
+
+    case RyanMqttEventRepeatPublishPacket: // qos2 / qos1重发事件回调
+    {
+        RyanMqttAckHandler_t *ackHandler = (RyanMqttAckHandler_t *)eventData;
+        ulog_w(TAG, "%s:%d %s()... packetType: %d, packetId: %d, topic: %s, qos: %d",
+               __FILE__, __LINE__, __FUNCTION__,
+               ackHandler->packetType, ackHandler->packetId, ackHandler->msgHandler->topic, ackHandler->msgHandler->qos);
+
+        printfArrStr(ackHandler->packet, ackHandler->packetLen, "data: ");
+
+        break;
+    }
+
+    case RyanMqttEventReconnectBefore:
+        // 如果每次connect都需要修改连接信息,这里是最好的选择。 否则需要注意资源互斥
+        ulog_i(TAG, "重连前事件回调");
+
+        RyanMqttClientConfig_t mqttConfig = {
+            .clientId = "RyanMqttTest", // 这里只修改了客户端名字
+            .userName = "test",
+            .password = "test",
+            .host = "39.164.131.143",
+            .port = "1883",
+            .taskName = "mqttThread",
+            .taskPrio = 16,
+            .taskStack = 4096,
+            .recvBufferSize = sizeof(mqttRecvBuffer),
+            .sendBufferSize = sizeof(mqttSendBuffer),
+            .recvBuffer = mqttRecvBuffer,
+            .sendBuffer = mqttSendBuffer,
+            .recvBufferStaticFlag = RyanTrue,
+            .sendBufferStaticFlag = RyanTrue,
+            .mqttVersion = 4,
+            .ackHandlerRepeatCountWarning = 6,
+            .ackHandlerCountWarning = 20,
+            .autoReconnectFlag = RyanTrue,
+            .cleanSessionFlag = 0,
+            .reconnectTimeout = 3000,
+            .recvTimeout = 11000,
+            .sendTimeout = 2000,
+            .ackTimeout = 10000,
+            .keepaliveTimeoutS = 60,
+            .mqttEventHandle = mqttEventHandle,
+            .userData = NULL};
+
+        RyanMqttSetConfig(client, &mqttConfig);
+        break;
+
+    case RyanMqttEventAckCountWarning: // qos2 / qos1的ack链表超过警戒值,不进行释放会一直重发,占用额外内存
+    {
+        // 根据实际情况清除ack, 这里等待每个ack重发次数到达警戒值后清除。
+        // 在资源有限的单片机中也不应频繁发送qos2 / qos1消息
+        uint16_t ackHandlerCount = *(uint16_t *)eventData;
+        ulog_i(TAG, "ack记数值超过警戒值回调: %d", ackHandlerCount);
+        break;
+    }
+
+    case RyanMqttEventAckRepeatCountWarning: // 重发次数到达警戒值事件
+    {
+        // 这里选择直接丢弃该消息
+        RyanMqttAckHandler_t *ackHandler = (RyanMqttAckHandler_t *)eventData;
+        ulog_i(TAG, "ack重发次数超过警戒值回调");
+        ulog_w(TAG, "%s:%d %s()... packetType: %d, packetId: %d, topic: %s, qos: %d",
+               __FILE__, __LINE__, __FUNCTION__,
+               ackHandler->packetType, ackHandler->packetId, ackHandler->msgHandler->topic, ackHandler->msgHandler->qos);
+
+        RyanMqttDiscardAckHandler(client, ackHandler->packetType, ackHandler->packetId);
+
+        break;
+    }
+
+    case RyanMqttEventAckHandlerdiscard:
+    {
+        RyanMqttAckHandler_t *ackHandler = (RyanMqttAckHandler_t *)eventData;
+        ulog_i(TAG, "ack丢弃回调: packetType: %d, packetId: %d, topic: %s, qos: %d",
+               ackHandler->packetType, ackHandler->packetId, ackHandler->msgHandler->topic, ackHandler->msgHandler->qos);
+        break;
+    }
+
+    case RyanMqttEventDestoryBefore:
+        ulog_i(TAG, "销毁mqtt客户端前回调");
+        break;
+
+    default:
+        break;
+    }
+}
+
+void mqttConnectFun()
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttClientConfig_t mqttConfig = {
+        .clientId = "RyanMqttTessdfwrt",
+        .userName = "test",
+        .password = "test",
+        .host = "39.164.131.143",
+        .port = "1883",
+        .taskName = "mqttThread",
+        .taskPrio = 16,
+        .taskStack = 4096,
+        .recvBufferSize = sizeof(mqttRecvBuffer),
+        .sendBufferSize = sizeof(mqttSendBuffer),
+        .recvBuffer = mqttRecvBuffer,
+        .sendBuffer = mqttSendBuffer,
+        .recvBufferStaticFlag = RyanTrue,
+        .sendBufferStaticFlag = RyanTrue,
+        .mqttVersion = 4,
+        .ackHandlerRepeatCountWarning = 6,
+        .ackHandlerCountWarning = 20,
+        .autoReconnectFlag = RyanTrue,
+        .cleanSessionFlag = 0,
+        .reconnectTimeout = 3000,
+        .recvTimeout = 11000,
+        .sendTimeout = 2000,
+        .ackTimeout = 10000,
+        .keepaliveTimeoutS = 60,
+        .mqttEventHandle = mqttEventHandle,
+        .userData = NULL};
+
+    // 初始化mqtt客户端
+    result = RyanMqttInit(&client);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // 注册需要的事件回调
+    result = RyanMqttRegisterEventId(client, RyanMqttEventAnyId);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // 设置mqtt客户端config
+    result = RyanMqttSetConfig(client, &mqttConfig);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // 设置遗嘱消息
+    result = RyanMqttSetLwt(client, "pub/test", "this is will", strlen("this is will"), QOS0, 0);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // 启动mqtt客户端线程
+    result = RyanMqttStart(client);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+}
+
+/**
+ * @brief mqtt msh命令
+ *
+ */
+struct RyanMqttCmdDes
+{
+    const char *cmd;
+    const char *explain;
+    int (*fun)(int argc, char *argv[]);
+};
+
+static int MqttHelp(int argc, char *argv[]);
+
+/**
+ * @brief 获取mqtt状态
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttState(int argc, char *argv[])
+{
+    char *str = NULL;
+    RyanMqttState_e clientState = RyanMqttGetState(client);
+    switch (clientState)
+    {
+    case mqttInvalidState:
+        str = "无效状态";
+        break;
+
+    case mqttInitState:
+        str = "初始化状态";
+        break;
+
+    case mqttStartState:
+        str = "mqtt开始状态";
+        break;
+
+    case mqttConnectState:
+        str = "连接状态";
+        break;
+
+    case mqttDisconnectState:
+        str = "断开连接状态";
+        break;
+
+    case mqttReconnectState:
+        str = "重新连接状态";
+        break;
+
+    default:
+        RyanMqttCheck(NULL, RyanMqttFailedError);
+        break;
+    }
+
+    ulog_i(TAG, "client state: %s", clientState);
+
+    return 0;
+}
+
+/**
+ * @brief mqtt 连接服务器
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttConnect(int argc, char *argv[])
+{
+
+    if (mqttConnectState == RyanMqttGetState(client))
+    {
+        ulog_w(TAG, "mqtt客户端没有连接");
+        return 0;
+    }
+    mqttConnectFun();
+    return 0;
+}
+
+/**
+ * @brief mqtt 重连函数,注意事项请看函数简介
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttReconnect(int argc, char *argv[])
+{
+    RyanMqttReconnect(client);
+    return 0;
+}
+
+/**
+ * @brief mqtt销毁客户端
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttDestroy(int argc, char *argv[])
+{
+    RyanMqttDestroy(client);
+    return 0;
+}
+
+/**
+ * @brief 断开mqtt客户端连接
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttDisconnect(int argc, char *argv[])
+{
+    if (mqttConnectState != RyanMqttGetState(client))
+    {
+        ulog_w(TAG, "mqtt客户端没有连接");
+        return 0;
+    }
+    RyanMqttDisconnect(client, RyanTrue);
+    return 0;
+}
+
+/**
+ * @brief mqtt发布消息
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int Mqttpublish(int argc, char *argv[])
+{
+    if (argc < 7)
+    {
+        ulog_i(TAG, "请输入 topic、 qos、 payload内容、 发送条数、 间隔时间(可以为0) ");
+        return 0;
+    }
+
+    if (mqttConnectState != RyanMqttGetState(client))
+    {
+        ulog_w(TAG, "mqtt客户端没有连接");
+        return 0;
+    }
+
+    char *topic = argv[2];
+    RyanMqttQos_e qos = atoi(argv[3]);
+    char *payload = argv[4];
+    uint16_t count = atoi(argv[5]);
+    uint16_t delayTime = atoi(argv[6]);
+
+    uint16_t pubCount = 0;
+    ulog_i(TAG, "qos: %d, count: %d, delayTime: %d, payload: %s", qos, count, delayTime, payload);
+
+    for (uint16_t i = 0; i < count; i++)
+    {
+        if (RyanMqttSuccessError == RyanMqttPublish(client, topic, payload, strlen(payload), qos, 0))
+            pubCount++;
+        delay(delayTime);
+    }
+
+    delay(3000);
+    ulog_e(TAG, "pubCount: %d", pubCount);
+    return 0;
+}
+
+/**
+ * @brief mqtt订阅主题,支持通配符
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int Mqttsubscribe(int argc, char *argv[])
+{
+    if (argc < 4)
+    {
+        ulog_i(TAG, "请输入 topic、 qos ");
+        return 0;
+    }
+
+    if (mqttConnectState != RyanMqttGetState(client))
+    {
+        ulog_w(TAG, "mqtt客户端没有连接");
+        return 0;
+    }
+
+    RyanMqttSubscribe(client, argv[2], atoi(argv[3]));
+    return 0;
+}
+
+/**
+ * @brief mqtt取消订阅主题
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttUnSubscribe(int argc, char *argv[])
+{
+    if (argc < 3)
+    {
+        ulog_i(TAG, "请输入 取消订阅主题");
+        return 0;
+    }
+
+    if (mqttConnectState != RyanMqttGetState(client))
+    {
+        ulog_w(TAG, "mqtt客户端没有连接");
+        return 0;
+    }
+
+    RyanMqttUnSubscribe(client, argv[2]);
+    return 0;
+}
+
+/**
+ * @brief mqtt获取已订阅主题
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttListSubscribe(int argc, char *argv[])
+{
+    if (mqttConnectState != RyanMqttGetState(client))
+    {
+        ulog_w(TAG, "mqtt客户端没有连接");
+        return 0;
+    }
+
+    RyanMqttMsgHandler_t msgHandles[10] = {0};
+    int32_t subscribeNum = 0;
+    int32_t result = RyanMqttSuccessError;
+
+    result = RyanMqttGetSubscribe(client, msgHandles, sizeof(msgHandles) / sizeof(msgHandles[0]), &subscribeNum);
+
+    if (result == RyanMqttNoRescourceError)
+        ulog_w(TAG, "订阅主题数超过10个,已截断");
+    ulog_i(TAG, "mqtt客户端已订阅的主题数: %d", subscribeNum);
+
+    for (int32_t i = 0; i < subscribeNum; i++)
+        ulog_i(TAG, "订阅主题: %d, topic: %s, QOS: %d", i, msgHandles[i].topic, msgHandles[i].qos);
+
+    return 0;
+}
+
+/**
+ * @brief 打印ack链表
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttListAck(int argc, char *argv[])
+{
+    RyanList_t *curr = NULL,
+               *next = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+
+    if (RyanListIsEmpty(&client->ackHandlerList))
+    {
+        ulog_i(TAG, "ack链表为空");
+        return 0;
+    }
+
+    // 遍历链表
+    RyanListForEachSafe(curr, next, &client->ackHandlerList)
+    {
+        // 获取此节点的结构体
+        ackHandler = RyanListEntry(curr, RyanMqttAckHandler_t, list);
+
+        // 发送qos1 / qos2消息服务器ack响应超时。需要重新发送它们。
+        ulog_w(TAG, " type: %d, packetId is %d ", ackHandler->packetType, ackHandler->packetId);
+        if (NULL != ackHandler->msgHandler)
+            ulog_w(TAG, "topic: %s, qos: %d", ackHandler->msgHandler->topic, ackHandler->msgHandler->qos);
+    }
+    return 0;
+}
+
+/**
+ * @brief 打印msg链表
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int MqttListMsg(int argc, char *argv[])
+{
+    RyanList_t *curr = NULL,
+               *next = NULL;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+
+    if (RyanListIsEmpty(&client->msgHandlerList))
+    {
+        ulog_i(TAG, "msg链表为空");
+        return 0;
+    }
+
+    RyanListForEachSafe(curr, next, &client->msgHandlerList)
+    {
+        msgHandler = RyanListEntry(curr, RyanMqttMsgHandler_t, list);
+        ulog_w(TAG, "topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
+    }
+    return 0;
+}
+
+/**
+ * @brief 打印接收到的数据计数
+ *
+ * @param argc
+ * @param argv
+ * @return int
+ */
+static int Mqttdata(int argc, char *argv[])
+{
+    // mqtt data
+    if (argc == 3)
+    {
+        uint32_t num = atoi(argv[2]);
+        if (num < sizeof(mqttTest) / sizeof(mqttTest[0]) - 1)
+            mqttTest[num] = 0;
+        else
+            ulog_e(TAG, "数组越界");
+    }
+
+    ulog_i(TAG, "dataEventCount: %d, publishCount:%u",
+           mqttTest[dataEventCount], mqttTest[PublishedEventCount]);
+
+    return 0;
+}
+
+static const struct RyanMqttCmdDes cmdTab[] =
+    {
+        {"help", "打印帮助信息", MqttHelp},
+        {"state", "打印mqtt客户端状态", MqttState},
+        {"connect", "mqtt客户端连接服务器", MqttConnect},
+        {"disc", "mqtt客户端断开连接", MqttDisconnect},
+        {"reconnect", "mqtt断开连接时,重新连接mqtt服务器", MqttReconnect},
+        {"destory", "mqtt销毁客户端", MqttDestroy},
+        {"pub", "mqtt发布消息", Mqttpublish},
+        {"sub", "mqtt订阅主题", Mqttsubscribe},
+        {"unsub", "mqtt取消订阅主题", MqttUnSubscribe},
+        {"listsub", "mqtt获取已订阅主题", MqttListSubscribe},
+        {"listack", "打印ack链表", MqttListAck},
+        {"listmsg", "打印msg链表", MqttListMsg},
+        {"data", "打印测试信息,用户自定义的", Mqttdata},
+};
+
+static int MqttHelp(int argc, char *argv[])
+{
+
+    for (uint8_t i = 0; i < sizeof(cmdTab) / sizeof(cmdTab[0]); i++)
+        rt_kprintf("mqtt %-16s %s\r\n", cmdTab[i].cmd, cmdTab[i].explain);
+
+    return 0;
+}
+
+static int RyanMqttMsh(int argc, char *argv[])
+{
+    int32_t i = 0,
+            result = 0;
+    const struct RyanMqttCmdDes *runCmd = NULL;
+
+    if (argc == 1)
+    {
+        MqttHelp(argc, argv);
+        return 0;
+    }
+
+    for (i = 0; i < sizeof(cmdTab) / sizeof(cmdTab[0]); i++)
+    {
+        if (rt_strcmp(cmdTab[i].cmd, argv[1]) == 0)
+        {
+            runCmd = &cmdTab[i];
+            break;
+        }
+    }
+
+    if (runCmd == NULL)
+    {
+        MqttHelp(argc, argv);
+        return 0;
+    }
+
+    if (runCmd->fun != NULL)
+        result = runCmd->fun(argc, argv);
+
+    return 0;
+}
+
+#if defined(RT_USING_MSH)
+MSH_CMD_EXPORT_ALIAS(RyanMqttMsh, mqtt, RyanMqtt command);
+#endif
+
+#endif

+ 710 - 0
mqttclient/RyanMqttClient.c

@@ -0,0 +1,710 @@
+
+
+#include "RyanMqttPublic.h"
+#include "RyanMqttUtile.h"
+#include "RyanMqttThread.h"
+
+/**
+ * @brief 获取报文标识符,报文标识符不可为0
+ *
+ * @param client
+ * @return uint16_t
+ */
+static uint16_t RyanMqttGetNextPacketId(RyanMqttClient_t *client)
+{
+    RyanMqttAssert(NULL != client);
+    client->packetId = (client->packetId >= RyanMqttMaxPacketId || client->packetId < 1) ? 1 : client->packetId + 1;
+    return client->packetId;
+}
+
+static RyanMqttError_e setConfigValue(char **dest, char const *const rest)
+{
+
+    if (NULL == dest || NULL == rest)
+        return RyanMqttNoRescourceError;
+
+    if (NULL != *dest)
+        platformMemoryFree(*dest);
+
+    RyanMqttStringCopy(dest, rest, strlen(rest));
+    if (NULL == *dest)
+        return RyanMqttFailedError;
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief mqtt初始化
+ *
+ * @param clientConfig
+ * @param pClient mqtt客户端指针
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttInit(RyanMqttClient_t **pClient)
+{
+
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttClient_t *client = NULL;
+    RyanMqttCheck(NULL != pClient, RyanMqttParamInvalidError);
+
+    client = (RyanMqttClient_t *)platformMemoryMalloc(sizeof(RyanMqttClient_t));
+    RyanMqttCheck(NULL != client, RyanMqttNotEnoughMemError);
+    memset(client, 0, sizeof(RyanMqttClient_t));
+
+    // 网络接口初始化
+    client->network = (platformNetwork_t *)platformMemoryMalloc(sizeof(platformNetwork_t));
+    RyanMqttCheckCode(NULL != client->network, RyanMqttNotEnoughMemError, RyanMqttDestroy(client));
+    memset(client->network, 0, sizeof(platformNetwork_t));
+
+    client->config = (RyanMqttClientConfig_t *)platformMemoryMalloc(sizeof(RyanMqttClientConfig_t));
+    RyanMqttCheckCode(NULL != client->config, RyanMqttNotEnoughMemError, RyanMqttDestroy(client));
+    memset(client->config, 0, sizeof(RyanMqttClientConfig_t));
+
+    client->mqttThread = platformMemoryMalloc(sizeof(platformThread_t));
+    RyanMqttCheckCode(NULL != client->mqttThread, RyanMqttNotEnoughMemError, RyanMqttDestroy(client));
+    memset(client->mqttThread, 0, sizeof(platformThread_t));
+
+    client->sendBufLock = platformMemoryMalloc(sizeof(platformMutex_t));
+    RyanMqttCheckCode(NULL != client->sendBufLock, RyanMqttNotEnoughMemError, RyanMqttDestroy(client));
+    memset(client->sendBufLock, 0, sizeof(platformMutex_t));
+
+    client->packetId = 1; // 控制报文必须包含一个非零的 16 位报文标识符
+    client->clientState = 0;
+    client->eventFlag = 0;
+    client->keepaliveTimeoutCount = 0;
+    client->ackHandlerCount = 0;
+    client->lwtFlag = RyanFalse;
+    client->lwtOptions = NULL;
+
+    platformMutexInit(client->config->userData, client->sendBufLock); // 初始化发送缓冲区互斥锁
+
+    RyanMqttListInit(&client->msgHandlerList);
+    RyanMqttListInit(&client->ackHandlerList);
+    platformTimerInit(&client->keepaliveTimer);
+
+    RyanMqttSetClientState(client, mqttInitState);
+
+    *pClient = client;
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 销毁mqtt客户端
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttDestroy(RyanMqttClient_t *client)
+{
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+
+    RyanMqttEventMachine(client, RyanMqttEventDestoryBefore, (void *)NULL);
+
+    // 先清除掉线程
+    if (NULL != client->mqttThread)
+    {
+        platformThreadDestroy(client->config->userData, client->mqttThread);
+        platformMemoryFree(client->mqttThread);
+        client->mqttThread = NULL;
+    }
+
+    // 清除网络组件
+    if (NULL != client->network)
+    {
+        platformNetworkClose(client->config->userData, client->network);
+        platformMemoryFree(client->network);
+        client->network = NULL;
+    }
+
+    // 清除互斥锁
+    if (NULL != client->sendBufLock)
+    {
+        platformMutexDestroy(client->config->userData, client->sendBufLock);
+        platformMemoryFree(client->sendBufLock);
+        client->sendBufLock = NULL;
+    }
+
+    // 清除config信息
+    if (NULL != client->config)
+    {
+        if (RyanTrue != client->config->recvBufferStaticFlag && NULL != client->config->recvBuffer)
+            platformMemoryFree(client->config->recvBuffer);
+
+        if (RyanTrue != client->config->sendBufferStaticFlag && NULL != client->config->sendBuffer)
+            platformMemoryFree(client->config->sendBuffer);
+
+        if (NULL != client->config->clientId)
+            platformMemoryFree(client->config->clientId);
+
+        if (NULL != client->config->host)
+            platformMemoryFree(client->config->host);
+
+        if (NULL != client->config->port)
+            platformMemoryFree(client->config->port);
+
+        if (NULL != client->config->userName)
+            platformMemoryFree(client->config->userName);
+
+        if (NULL != client->config->password)
+            platformMemoryFree(client->config->password);
+
+        if (NULL != client->config->taskName)
+            platformMemoryFree(client->config->taskName);
+
+        if (NULL != client->config)
+            platformMemoryFree(client->config);
+    }
+
+    // 清除遗嘱相关配置
+    if (RyanTrue == client->lwtFlag && NULL != client->lwtOptions)
+    {
+        if (NULL != client->lwtOptions->topic)
+            platformMemoryFree(client->lwtOptions->topic);
+
+        platformMemoryFree(client->lwtOptions);
+    }
+
+    // 清除session  ack链表和msg链表
+    RyanMqttCleanSession(client);
+
+    platformMemoryFree(client);
+    client = NULL;
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 启动mqtt客户端
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttStart(RyanMqttClient_t *client)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(mqttInitState == client->clientState, RyanMqttFailedError);
+
+    RyanMqttSetClientState(client, mqttStartState);
+    // 连接成功,需要初始化 MQTT 线程
+    result = platformThreadInit(client->config->userData,
+                                client->mqttThread,
+                                client->config->taskName,
+                                RyanMqttThread,
+                                client,
+                                client->config->taskStack,
+                                client->config->taskPrio);
+    RyanMqttCheckCode(NULL == result, RyanMqttNotEnoughMemError,
+                      RyanMqttSetClientState(client, mqttInitState));
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 断开mqtt服务器连接
+ *
+ * @param client
+ * @param sendDiscFlag RyanTrue表示发送断开连接报文,RyanFalse表示不发送断开连接报文
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttDisconnect(RyanMqttClient_t *client, RyanBool_t sendDiscFlag)
+{
+
+    int32_t connectState = RyanMqttConnectAccepted;
+    int32_t packetLen = 0;
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(mqttConnectState == RyanMqttGetClientState(client), RyanMqttNotConnectError);
+
+    if (RyanTrue == sendDiscFlag)
+    {
+        platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+        // 序列化断开连接数据包并发送
+        packetLen = MQTTSerialize_disconnect(client->config->sendBuffer, client->config->sendBufferSize);
+        if (packetLen > 0)
+            RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+        platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+    }
+
+    connectState = RyanMqttConnectUserDisconnected;
+    RyanMqttEventMachine(client, RyanMqttEventDisconnected, (void *)&connectState);
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 手动重连mqtt客户端
+ * ! 仅在未使能自动连接时,客户端断开连接时用户手动调用
+ * ! 否则可能会造成内存泄漏
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttReconnect(RyanMqttClient_t *client)
+{
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != client->mqttThread, RyanMqttParamInvalidError);
+    RyanMqttCheck(mqttDisconnectState != RyanMqttGetClientState(client), RyanMqttConnectError);
+
+    RyanMqttEventMachine(client, RyanMqttEventReconnectBefore, NULL);
+    platformThreadStart(client->config->userData, client->mqttThread);
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 订阅主题
+ *
+ * @param client
+ * @param topic
+ * @param qos 服务端可以授予比订阅者要求的低一些的QoS等级,可在订阅成功回调函数中查看服务端给定的qos等级
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttSubscribe(RyanMqttClient_t *client, char *topic, RyanMqttQos_e qos)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    int32_t packetLen = 0;
+    uint16_t packetId = 0;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    MQTTString topicName = MQTTString_initializer;
+    topicName.cstring = (char *)topic;
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != topic, RyanMqttParamInvalidError);
+    RyanMqttCheck(QOS0 <= qos && QOS2 >= qos, RyanMqttParamInvalidError);
+    RyanMqttCheck(mqttConnectState == RyanMqttGetClientState(client), RyanMqttNotConnectError);
+
+    platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+    packetId = RyanMqttGetNextPacketId(client);
+
+    packetLen = MQTTSerialize_subscribe(client->config->sendBuffer, client->config->sendBufferSize, 0, packetId, 1, &topicName, &qos);
+    RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError,
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    result = RyanMqttMsgHandlerCreate(topic, strlen(topic), qos, &msgHandler);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    result = RyanMqttAckHandlerCreate(client, SUBACK, packetId, packetLen, msgHandler, &ackHandler);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                      platformMemoryFree(msgHandler);
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+    platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+    // 确定节点是否已存在,存在就删除
+    result = RyanMqttAckListNodeFind(client, SUBACK, packetId, &ackHandler);
+    if (RyanMqttSuccessError == result)
+        RyanMqttAckHandlerDestroy(client, ackHandler);
+
+    result = RyanMqttAckListAdd(client, ackHandler);
+
+    result = RyanMqttSendPacket(client, ackHandler->packet, ackHandler->packetLen);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttAckHandlerDestroy(client, ackHandler));
+
+    return result;
+}
+
+/**
+ * @brief 取消订阅指定主题
+ *
+ * @param client
+ * @param topic
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttUnSubscribe(RyanMqttClient_t *client, char *topic)
+{
+    int32_t packetLen = 0;
+    RyanMqttError_e result = RyanMqttFailedError;
+    uint16_t packetId;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    MQTTString topicName = MQTTString_initializer;
+    topicName.cstring = topic;
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != topic, RyanMqttParamInvalidError);
+    RyanMqttCheck(mqttConnectState == RyanMqttGetClientState(client), RyanMqttNotConnectError);
+
+    // 查找当前主题是否已经订阅,没有订阅就取消发送
+    result = RyanMqttMsgHandlerFind(client, topicName.cstring, strlen(topicName.cstring), RyanFalse, &msgHandler);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+    packetId = RyanMqttGetNextPacketId(client);
+
+    packetLen = MQTTSerialize_unsubscribe(client->config->sendBuffer, client->config->sendBufferSize, 0, packetId, 1, &topicName);
+    RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError,
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    result = RyanMqttAckHandlerCreate(client, UNSUBACK, packetId, packetLen, msgHandler, &ackHandler);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+    platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+    // 确定节点是否已存在,存在就删除
+    result = RyanMqttAckListNodeFind(client, UNSUBACK, packetId, &ackHandler);
+    if (RyanMqttSuccessError == result)
+        RyanMqttAckHandlerDestroy(client, ackHandler);
+
+    result = RyanMqttAckListAdd(client, ackHandler);
+
+    result = RyanMqttSendPacket(client, ackHandler->packet, ackHandler->packetLen);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttAckHandlerDestroy(client, ackHandler));
+
+    return result;
+}
+
+/**
+ * @brief 客户端向服务端发送消息
+ *
+ * @param client
+ * @param topic
+ * @param payload
+ * @param payloadLen
+ * @param QOS
+ * @param retain
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttPublish(RyanMqttClient_t *client, char *topic, char *payload, uint32_t payloadLen, RyanMqttQos_e qos, RyanBool_t retain)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    int32_t packetLen = 0;
+    int32_t packetId = 0;
+    MQTTString topicName = MQTTString_initializer;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    topicName.cstring = (char *)topic;
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != topic, RyanMqttParamInvalidError);
+    RyanMqttCheck(RyanMqttMaxPayloadLen >= payloadLen, RyanMqttParamInvalidError);
+    RyanMqttCheck(QOS0 <= qos && QOS2 >= qos, RyanMqttParamInvalidError);
+    RyanMqttCheck(RyanTrue == retain || RyanFalse == retain, RyanMqttParamInvalidError);
+    RyanMqttCheck(mqttConnectState == RyanMqttGetClientState(client), RyanMqttNotConnectError);
+
+    if (payloadLen > 0 && NULL == payload) // 报文支持有效载荷长度为0
+        return RyanMqttParamInvalidError;
+
+    if (QOS0 == qos)
+    {
+        platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+        packetLen = MQTTSerialize_publish(client->config->sendBuffer, client->config->sendBufferSize, 0, qos, retain, packetId,
+                                          topicName, payload, payloadLen);
+        RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError,
+                          platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+        result = RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+        platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+        return result;
+    }
+
+    // qos1 / qos2需要收到预期响应ack,否则数据将被重新发送
+    platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+    packetId = RyanMqttGetNextPacketId(client);
+
+    packetLen = MQTTSerialize_publish(client->config->sendBuffer, client->config->sendBufferSize, 0, qos, retain, packetId,
+                                      topicName, payload, payloadLen);
+    RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError,
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    result = RyanMqttMsgHandlerCreate(topic, strlen(topic), qos, &msgHandler);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    result = RyanMqttAckHandlerCreate(client, (QOS1 == qos) ? PUBACK : PUBREC, packetId, packetLen, msgHandler, &ackHandler);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                      platformMemoryFree(msgHandler);
+                      platformMutexUnLock(client->config->userData, client->sendBufLock));
+    platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+    // 确定节点是否已存在,存在就删除,理论上不会存在
+    result = RyanMqttAckListNodeFind(client, (QOS1 == qos) ? PUBACK : PUBREC, packetId, &ackHandler);
+    if (RyanMqttSuccessError == result)
+        RyanMqttAckHandlerDestroy(client, ackHandler);
+
+    result = RyanMqttAckListAdd(client, ackHandler);
+
+    result = RyanMqttSendPacket(client, ackHandler->packet, ackHandler->packetLen);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                      RyanMqttAckHandlerDestroy(client, ackHandler));
+
+    // 提前设置重发标志位
+    RyanMqttSetPublishDup(&ackHandler->packet[0], 1);
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 获取mqtt客户端状态
+ *
+ * @param client
+ * @return RyanMqttState_e
+ */
+RyanMqttState_e RyanMqttGetState(RyanMqttClient_t *client)
+{
+
+    if (NULL == client)
+        return mqttInvalidState;
+
+    return RyanMqttGetClientState(client);
+}
+
+/**
+ * @brief 获取已订阅主题
+ *
+ * @param client
+ * @param msgHandles 存放已订阅主题的空间
+ * @param msgHandleSize  存放已订阅主题的空间大小个数
+ * @param subscribeNum 函数内部返回已订阅主题的个数
+ * @return RyanMqttState_e
+ */
+RyanMqttState_e RyanMqttGetSubscribe(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgHandles, int32_t msgHandleSize, int32_t *subscribeNum)
+{
+
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanList_t *curr = NULL,
+               *next = NULL;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != msgHandles, RyanMqttParamInvalidError);
+    RyanMqttCheck(0 < msgHandleSize, RyanMqttParamInvalidError);
+
+    *subscribeNum = 0;
+
+    if (RyanListIsEmpty(&client->msgHandlerList))
+        return RyanMqttSuccessError;
+
+    RyanListForEachSafe(curr, next, &client->msgHandlerList)
+    {
+        msgHandler = RyanListEntry(curr, RyanMqttMsgHandler_t, list);
+
+        msgHandles[*subscribeNum].topic = msgHandler->topic;
+        msgHandles[*subscribeNum].qos = msgHandler->qos;
+
+        (*subscribeNum)++;
+
+        if (subscribeNum >= msgHandleSize)
+            return RyanMqttNoRescourceError;
+    }
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 获取mqtt config
+ * 使用完毕后,需要用户释放pclientConfig指针内容
+ *
+ * @param client
+ * @param pclientConfig
+ * @return RyanMqttError_e
+ */
+/* RyanMqttError_e RyanMqttGetConfig(RyanMqttClient_t *client, RyanMqttClientConfig_t **pclientConfig)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttClientConfig_t *clientConfig = NULL;
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != pclientConfig, RyanMqttParamInvalidError);
+
+    RyanMqttCheck(NULL != client->config, RyanMqttNoRescourceError);
+
+    clientConfig = (RyanMqttClientConfig_t *)platformMemoryMalloc(sizeof(RyanMqttClientConfig_t));
+    RyanMqttCheck(NULL != clientConfig, RyanMqttNotEnoughMemError);
+
+    memcpy(clientConfig, client->config, sizeof(RyanMqttClientConfig_t));
+
+    result = setConfigValue(&clientConfig->clientId, client->config->clientId);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    result = setConfigValue(&clientConfig->userName, client->config->userName);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    result = setConfigValue(&clientConfig->password, client->config->password);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    result = setConfigValue(&clientConfig->host, client->config->host);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    result = setConfigValue(&clientConfig->port, client->config->port);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    result = setConfigValue(&clientConfig->taskName, client->config->taskName);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    *pclientConfig = clientConfig;
+
+    return RyanMqttSuccessError;
+}
+ */
+
+/**
+ * @brief 设置mqtt config 这是很危险的操作,需要考虑mqtt thread线程和用户线程的资源互斥
+ * 推荐在 RyanMqttStart函数前 / 非用户手动触发的事件回调函数中 / mqtt thread处于挂起状态时调用
+ * mqtt thread处于阻塞状态时调用此函数也是很危险的行为,因为无法确定此函数的执行时间,调用此函数的任务运行时间片有多少
+ * 总之就是要保证mqtt线程和用户线程的资源互斥
+ * 项目中用户也不应该频繁调用此函数
+ *
+ * ! 此函数如果返回RyanMqttFailedError,需要立即停止mqtt客户端相关操作.因为操作失败此函数会调用RyanMqttDestroy()销毁客户端
+ *
+ * @param client
+ * @param clientConfig
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttSetConfig(RyanMqttClient_t *client, RyanMqttClientConfig_t *clientConfig)
+{
+
+    RyanMqttError_e result = RyanMqttSuccessError;
+
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != clientConfig->clientId, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != clientConfig->host, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != clientConfig->port, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != clientConfig->userName, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != clientConfig->password, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != clientConfig->taskName, RyanMqttParamInvalidError);
+    RyanMqttCheck(2 < clientConfig->recvBufferSize && (RyanMqttMaxPayloadLen + 5) >= clientConfig->recvBufferSize, RyanMqttParamInvalidError);
+    RyanMqttCheck(2 < clientConfig->sendBufferSize && (RyanMqttMaxPayloadLen + 5) >= clientConfig->sendBufferSize, RyanMqttParamInvalidError);
+
+    RyanMqttCheckCode(NULL != client->config, RyanMqttParamInvalidError, goto exit);
+
+    result = setConfigValue(&client->config->clientId, clientConfig->clientId);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, goto exit);
+
+    result = setConfigValue(&client->config->userName, clientConfig->userName);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, goto exit);
+
+    result = setConfigValue(&client->config->password, clientConfig->password);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, goto exit);
+
+    result = setConfigValue(&client->config->host, clientConfig->host);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, goto exit);
+
+    result = setConfigValue(&client->config->port, clientConfig->port);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, goto exit);
+
+    result = setConfigValue(&client->config->taskName, clientConfig->taskName);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, goto exit);
+
+    client->config->taskPrio = clientConfig->taskPrio;
+    client->config->taskStack = clientConfig->taskStack;
+    client->config->mqttVersion = clientConfig->mqttVersion;
+    client->config->ackHandlerRepeatCountWarning = clientConfig->ackHandlerRepeatCountWarning;
+    client->config->ackHandlerCountWarning = clientConfig->ackHandlerCountWarning;
+    client->config->autoReconnectFlag = clientConfig->autoReconnectFlag;
+    client->config->cleanSessionFlag = clientConfig->cleanSessionFlag;
+    client->config->reconnectTimeout = clientConfig->reconnectTimeout;
+    client->config->recvTimeout = clientConfig->recvTimeout;
+    client->config->sendTimeout = clientConfig->sendTimeout;
+    client->config->ackTimeout = clientConfig->ackTimeout;
+    client->config->keepaliveTimeoutS = clientConfig->keepaliveTimeoutS;
+    client->config->mqttEventHandle = clientConfig->mqttEventHandle;
+    client->config->userData - clientConfig->userData;
+
+    client->config->recvBufferSize = clientConfig->recvBufferSize;
+    client->config->sendBufferSize = clientConfig->sendBufferSize;
+    client->config->recvBufferStaticFlag = clientConfig->recvBufferStaticFlag;
+    client->config->sendBufferStaticFlag = clientConfig->sendBufferStaticFlag;
+    client->config->recvBuffer = clientConfig->recvBuffer;
+    client->config->sendBuffer = clientConfig->sendBuffer;
+
+    if (RyanTrue != client->config->recvBufferStaticFlag)
+    {
+        client->config->recvBuffer = (char *)platformMemoryMalloc(client->config->recvBufferSize);
+        RyanMqttCheckCode(NULL != client->config->recvBuffer, RyanMqttFailedError, goto exit);
+    }
+
+    if (RyanTrue != client->config->sendBufferStaticFlag)
+    {
+        client->config->sendBuffer = (char *)platformMemoryMalloc(client->config->sendBufferSize);
+        RyanMqttCheckCode(NULL != client->config->sendBuffer, RyanMqttFailedError, goto exit);
+    }
+    return RyanMqttSuccessError;
+
+exit:
+    RyanMqttDestroy(client);
+    return RyanMqttFailedError;
+}
+
+/**
+ * @brief 设置遗嘱的配置信息
+ * 此函数必须在发送connect报文前调用,因为遗嘱消息包含在connect报文中
+ * 例如 RyanMqttStart前 / RyanMqttEventReconnectBefore事件中
+ *
+ * @param client
+ * @param topicName
+ * @param qos
+ * @param retain
+ * @param payload
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttSetLwt(RyanMqttClient_t *client, char *topicName, char *payload, uint32_t payloadLen, RyanMqttQos_e qos, RyanBool_t retain)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL != topicName, RyanMqttParamInvalidError);
+    RyanMqttCheck(RyanMqttMaxPayloadLen >= payloadLen, RyanMqttParamInvalidError);
+    RyanMqttCheck(QOS0 <= qos && QOS2 >= qos, RyanMqttParamInvalidError);
+    RyanMqttCheck(RyanTrue == retain || RyanFalse == retain, RyanMqttParamInvalidError);
+    RyanMqttCheck(NULL == client->lwtOptions, RyanMqttFailedError);
+
+    if (payloadLen > 0 && NULL == payload) // 报文支持有效载荷长度为0
+        return RyanMqttParamInvalidError;
+
+    client->lwtOptions = (lwtOptions_t *)platformMemoryMalloc(sizeof(lwtOptions_t) + payloadLen);
+    RyanMqttCheck(NULL != client->lwtOptions, RyanMqttNotEnoughMemError);
+    memset(client->lwtOptions, 0, sizeof(lwtOptions_t) + payloadLen);
+
+    client->lwtFlag = RyanTrue;
+    client->lwtOptions->qos = qos;
+    client->lwtOptions->retain = retain;
+    client->lwtOptions->payloadLen = payloadLen;
+    client->lwtOptions->payload = (char *)client->lwtOptions + sizeof(lwtOptions_t);
+    memcpy(client->lwtOptions->payload, payload, payloadLen);
+
+    result = RyanMqttStringCopy(&client->lwtOptions->topic, topicName, strlen(topicName));
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                      platformMemoryFree(client->lwtOptions));
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 丢弃指定ack
+ *
+ * @param client
+ * @param packetId
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttDiscardAckHandler(RyanMqttClient_t *client, enum msgTypes packetType, uint16_t packetId)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    RyanMqttCheck(CONNECT <= packetType && DISCONNECT >= packetType, RyanMqttParamInvalidError);
+    RyanMqttCheck(0 < packetId, RyanMqttParamInvalidError);
+
+    // 删除pubrel记录
+    result = RyanMqttAckListNodeFind(client, packetType, packetId, &ackHandler);
+    RyanMqttCheck(RyanMqttSuccessError == result, RyanMqttNoRescourceError);
+
+    RyanMqttEventMachine(client, RyanMqttEventAckHandlerdiscard, (void *)ackHandler); // 回调函数
+
+    RyanMqttAckHandlerDestroy(client, ackHandler);
+    return RyanMqttSuccessError;
+}
+
+RyanMqttError_e RyanMqttRegisterEventId(RyanMqttClient_t *client, RyanMqttEventId_e eventId)
+{
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    client->eventFlag |= eventId;
+    return RyanMqttSuccessError;
+}
+
+RyanMqttError_e RyanMqttCancelEventId(RyanMqttClient_t *client, RyanMqttEventId_e eventId)
+{
+    RyanMqttCheck(NULL != client, RyanMqttParamInvalidError);
+    client->eventFlag &= ~eventId;
+    return RyanMqttSuccessError;
+}

+ 126 - 0
mqttclient/RyanMqttClient.h

@@ -0,0 +1,126 @@
+
+
+#ifndef __mqttClient__
+#define __mqttClient__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "RyanMqttPublic.h"
+
+    // 接收到订阅消息回调函数类型,eventData用户不要进行修改否则mqtt客户端可能崩溃
+    typedef void (*RyanMqttEventHandle)(void *client, RyanMqttEventId_e event, const void const *eventData);
+
+    typedef struct
+    {
+        uint8_t retained;    // retained 标志位
+        uint8_t dup;         // 重发标志
+        uint16_t packetId;   // packetId 系统生成
+        RyanMqttQos_e qos;   // QOS等级
+        uint32_t payloadLen; // 数据长度
+        char *topic;         // 主题信息
+        char *payload;       // 数据内容
+    } RyanMqttMsgData_t;
+
+    typedef struct
+    {
+        RyanMqttQos_e qos; // qos等级
+        RyanList_t list;   // 链表节点,用户勿动
+        char *topic;       // 主题
+    } RyanMqttMsgHandler_t;
+
+    typedef struct
+    {
+        uint16_t repeatCount;             // 当前ack超时重发次数
+        uint16_t packetId;                // 报文标识符 系统生成,用户勿动
+        uint32_t packetLen;               // 报文长度
+        enum msgTypes packetType;         // 期望接收到的ack报文类型
+        RyanList_t list;                  // 链表节点,用户勿动
+        platformTimer_t timer;            // ack超时定时器,用户勿动
+        RyanMqttMsgHandler_t *msgHandler; // msg信息
+        char *packet;                     // 没有收到期望ack,重新发送的原始报文
+    } RyanMqttAckHandler_t;
+
+    typedef struct
+    {
+
+        uint8_t retain;      // 遗嘱保留标志位
+        uint32_t payloadLen; // 消息长度
+        RyanMqttQos_e qos;   // 遗嘱qos等级
+        char *topic;         // 遗嘱主题
+        char *payload;       // 遗嘱消息
+    } lwtOptions_t;
+
+    typedef struct
+    {
+        char *clientId;                        // 客户端ID
+        char *userName;                        // 用户名
+        char *password;                        // 密码
+        char *host;                            // mqtt服务器地址
+        char *port;                            // mqtt服务器端口
+        char *taskName;                        // 线程名字
+        char *recvBuffer;                      // mqtt接收缓冲区
+        char *sendBuffer;                      // mqtt发送缓冲区
+        uint8_t recvBufferStaticFlag : 1;      // 接收缓冲区是静态空间,不是动态申请
+        uint8_t sendBufferStaticFlag : 1;      // 发送缓冲区是静态空间,不是动态申请
+        uint8_t autoReconnectFlag : 1;         // 自动重连标志位
+        uint8_t cleanSessionFlag : 1;          // 清除会话标志位
+        uint8_t mqttVersion : 4;               // mqtt版本 3.1.1是4, 3.1是3
+        uint16_t ackHandlerRepeatCountWarning; // ack重发超过这个数值后触发事件回调,根据实际硬件选择。典型值为 * ackTimeout ~= 300秒
+        uint16_t taskPrio;                     // mqtt线程优先级
+        uint16_t taskStack;                    // 线程栈大小
+        uint16_t recvTimeout;                  // mqtt等待接收命令超时时间, 根据实际硬件选择。推荐 > ackTimeout && <= (keepaliveTimeoutS / 2)
+        uint16_t sendTimeout;                  // mqtt发送给命令超时时间, 根据实际硬件选择。
+        uint16_t ackTimeout;                   // mqtack等待命令超时时间, 典型值为5 - 30
+        uint16_t keepaliveTimeoutS;            // mqtt心跳时间间隔秒
+        uint16_t ackHandlerCountWarning;       // 等待ack的警告数 每次添加ack,ack总数大于或等于该值将触发事件回调,根据实际硬件选择。典型值是32
+        uint32_t recvBufferSize;               // mqtt接收缓冲区大小
+        uint32_t sendBufferSize;               // mqtt发送缓冲区大小
+        uint16_t reconnectTimeout;             // mqtt重连间隔时间
+        RyanMqttEventHandle mqttEventHandle;   // mqtt事件回调函数
+        void *userData;                        // 用户自定义数据,用户需要保证指针指向内容的持久性
+    } RyanMqttClientConfig_t;
+
+    typedef struct
+    {
+        uint8_t lwtFlag : 1;               // 遗嘱标志位
+        uint8_t keepaliveTimeoutCount : 7; // 心跳超时计数器
+        uint16_t ackHandlerCount;          // 等待ack的记录个数
+        uint16_t packetId;                 // mqtt报文标识符,控制报文必须包含一个非零的 16 位报文标识符
+        uint32_t eventFlag;                // 事件标志位
+        RyanMqttState_e clientState;       // mqtt客户端的状态
+        RyanList_t msgHandlerList;         // 维护消息处理列表,这是mqtt协议必须实现的内容,所有来自服务器的publish报文都会被处理(前提是订阅了对应的消息,或者设置了拦截器)
+        RyanList_t ackHandlerList;         // 维护ack链表
+        platformTimer_t keepaliveTimer;    // 保活定时器
+        platformNetwork_t *network;        // 网络组件
+        RyanMqttClientConfig_t *config;    // mqtt config
+        platformThread_t *mqttThread;      // mqtt线程
+        platformMutex_t *sendBufLock;      // 写缓冲区锁
+        lwtOptions_t *lwtOptions;          // 遗嘱相关配置
+    } RyanMqttClient_t;
+
+    extern RyanMqttError_e RyanMqttInit(RyanMqttClient_t **pClient);
+    extern RyanMqttError_e RyanMqttDestroy(RyanMqttClient_t *client);
+    extern RyanMqttError_e RyanMqttStart(RyanMqttClient_t *client);
+    extern RyanMqttError_e RyanMqttDisconnect(RyanMqttClient_t *client, RyanBool_t sendDiscFlag);
+    extern RyanMqttError_e RyanMqttReconnect(RyanMqttClient_t *client);
+    extern RyanMqttError_e RyanMqttSubscribe(RyanMqttClient_t *client, char *topic, RyanMqttQos_e qos);
+    extern RyanMqttError_e RyanMqttUnSubscribe(RyanMqttClient_t *client, char *topic);
+    extern RyanMqttError_e RyanMqttPublish(RyanMqttClient_t *client, char *topic, char *payload, uint32_t payloadLen, RyanMqttQos_e qos, RyanBool_t retain);
+
+    extern RyanMqttState_e RyanMqttGetState(RyanMqttClient_t *client);
+    extern RyanMqttState_e RyanMqttGetSubscribe(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgHandles, int32_t msgHandleSize, int32_t *subscribeNum);
+    extern RyanMqttError_e RyanMqttSetConfig(RyanMqttClient_t *client, RyanMqttClientConfig_t *clientConfig);
+    extern RyanMqttError_e RyanMqttSetLwt(RyanMqttClient_t *client, char *topicName, char *payload, uint32_t payloadLen, RyanMqttQos_e qos, RyanBool_t retain);
+
+    extern RyanMqttError_e RyanMqttDiscardAckHandler(RyanMqttClient_t *client, enum msgTypes packetType, uint16_t packetId);
+    extern RyanMqttError_e RyanMqttRegisterEventId(RyanMqttClient_t *client, RyanMqttEventId_e eventId);
+    extern RyanMqttError_e RyanMqttCancelEventId(RyanMqttClient_t *client, RyanMqttEventId_e eventId);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 166 - 0
mqttclient/RyanMqttPublic.h

@@ -0,0 +1,166 @@
+
+
+#ifndef __mqttClientStatic__
+#define __mqttClientStatic__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#define RyanMqttMaxPacketId (0xFFFFU - 1U)
+#define RyanMqttMaxPayloadLen (268435455UL)
+#define RyanMqttVersion ("0.0.1")
+
+#define RyanMqttCheck(EX, ErrorCode) RyanMqttCheckCode(EX, ErrorCode, NULL)
+
+#define RyanMqttCheckCode(EX, ErrorCode, code)                          \
+    if (!(EX))                                                          \
+    {                                                                   \
+        code;                                                           \
+        ulog_d(RyanMqttTag, "%s:%d ErrorCode: %d, strError: %s",        \
+               __FILE__, __LINE__, ErrorCode, RyanStrError(ErrorCode)); \
+        return ErrorCode;                                               \
+    }
+
+    typedef enum
+    {
+        RyanBit31 = 0x80000000,
+        RyanBit30 = 0x40000000,
+        RyanBit29 = 0x20000000,
+        RyanBit28 = 0x10000000,
+        RyanBit27 = 0x08000000,
+        RyanBit26 = 0x04000000,
+        RyanBit25 = 0x02000000,
+        RyanBit24 = 0x01000000,
+        RyanBit23 = 0x00800000,
+        RyanBit22 = 0x00400000,
+        RyanBit21 = 0x00200000,
+        RyanBit20 = 0x00100000,
+        RyanBit19 = 0x00080000,
+        RyanBit18 = 0x00040000,
+        RyanBit17 = 0x00020000,
+        RyanBit16 = 0x00010000,
+        RyanBit15 = 0x00008000,
+        RyanBit14 = 0x00004000,
+        RyanBit13 = 0x00002000,
+        RyanBit12 = 0x00001000,
+        RyanBit11 = 0x00000800,
+        RyanBit10 = 0x00000400,
+        RyanBit9 = 0x00000200,
+        RyanBit8 = 0x00000100,
+        RyanBit7 = 0x00000080,
+        RyanBit6 = 0x00000040,
+        RyanBit5 = 0x00000020,
+        RyanBit4 = 0x00000010,
+        RyanBit3 = 0x00000008,
+        RyanBit2 = 0x00000004,
+        RyanBit1 = 0x00000002,
+        RyanBit0 = 0x00000001,
+
+    } RyanBit_e;
+
+    typedef enum
+    {
+        RyanFalse = 0,
+        RyanTrue = 1
+    } RyanBool_t;
+
+    typedef enum
+    {
+        QOS0 = 0x00,
+        QOS1 = 0x01,
+        QOS2 = 0x02,
+        subFail = 0x80
+    } RyanMqttQos_e;
+
+    typedef enum
+    {
+        mqttInvalidState = -1, // 无效状态
+        mqttInitState = 0,     // 初始化状态
+        mqttStartState,        // 开始状态
+        mqttConnectState,      // 连接状态
+        mqttDisconnectState,   // 断开连接状态
+        mqttReconnectState,    // 重新连接状态
+    } RyanMqttState_e;
+
+    typedef enum
+    {
+        RyanMqttEventError = RyanBit0,                 // 保留事件
+        RyanMqttEventConnected = RyanBit1,             // 连接成功 正数为RyanMqttConnectStatus_e*,负数为RyanMqttError_e*
+        RyanMqttEventDisconnected = RyanBit2,          // 可能由用户触发,断开连接 正数为RyanMqttConnectStatus_e*,负数为RyanMqttError_e*
+        RyanMqttEventSubscribed = RyanBit3,            // 订阅成功事件,服务端可以授予比订阅者要求的低的QoS等级。 RyanMqttMsgHandler_t*
+        RyanMqttEventSubscribedFaile = RyanBit4,       // 订阅失败事件,超时 / 服务器返回订阅失败 RyanMqttMsgHandler_t*
+        RyanMqttEventUnSubscribed = RyanBit5,          // 取消订阅事件 RyanMqttMsgHandler_t*
+        RyanMqttEventUnSubscribedFaile = RyanBit6,     // 取消订阅失败事件,超时 RyanMqttMsgHandler_t*
+        RyanMqttEventPublished = RyanBit7,             // qos1 / qos2发送成功事件。发送没有失败,只会重发或者用户手动丢弃。RyanMqttAckHandler_t*
+        RyanMqttEventRepeatPublishPacket = RyanBit8,   // qos1 / qos2数据(或者ack)重发回调函数 RyanMqttAckHandler_t*
+        RyanMqttEventAckRepeatCountWarning = RyanBit9, // ack重发次数超过警戒值 RyanMqttAckHandler_t*
+        RyanMqttEventAckCountWarning = RyanBit10,      // ack记数值超过警戒值 uint16_t* ackHandlerCount; // 等待ack的记录个数
+        RyanMqttEventAckHandlerdiscard = RyanBit11,    /*!< 用户触发,ack句柄丢弃事件,由用户手动调用RyanMqttDestroyAckHandler函数触发
+                                                        * 可能是发送qos1 / qos2消息丢弃 ack丢弃,也可能是publish报文的ack丢弃 RyanMqttAckHandler_t*
+                                                        */
+        RyanMqttEventReconnectBefore = RyanBit12,      // 重连前事件,用户可以在此时更改connect信息 NULL
+        RyanMqttEventDestoryBefore = RyanBit13,        // 用户触发,销毁客户端前回调 NULL
+        RyanMqttEventData = RyanBit14,                 // 接收到订阅主题数据事件,支持通配符识别,返回的主题信息是报文主题 RyanMqttMsgData_t*
+        RyanMqttEventAnyId = UINT32_MAX,
+    } RyanMqttEventId_e;
+
+    // 定义枚举类型
+    typedef enum
+    {
+        RyanMqttParamInvalidError = -0x100, // 参数无效
+        RyanMqttRecvPacketTimeOutError,     // 读取数据超时
+        RyanMqttSendPacketTimeOutError,     // 发送数据超时
+        RyanSocketFailedError,              // 套接字 FD 失败
+        RyanMqttSocketConnectFailError,     // MQTT socket连接失败
+        RyanMqttSendPacketError,            // MQTT 发送数据包错误
+        RyanMqttSerializePacketError,       // 序列化报文失败
+        RyanMqttDeserializePacketError,     // 解析报文失败
+        RyanMqttNoRescourceError,           // 没有资源
+        RyanMqttHaveRescourceError,         // 资源已存在
+        RyanMqttNotConnectError,            // MQTT 没有连接
+        RyanMqttConnectError,               // MQTT 已连接
+        RyanMqttRecvBufToShortError,        // MQTT 缓冲区太短
+        RyanMqttSendBufToShortError,        // MQTT 缓冲区太短
+        RyanMqttNotEnoughMemError,          // MQTT 内存不足
+        RyanMqttFailedError,                // 失败
+        RyanMqttSuccessError = 0x0000       // 成功
+    } RyanMqttError_e;
+
+    typedef enum
+    {
+        // mqtt标准定义
+        RyanMqttConnectAccepted = 0,               // 连接已被服务端接受
+        RyanMqttConnectRefusedProtocolVersion = 1, // 服务端不支持客户端请求的 MQTT 协议级别
+        RyanMqttConnectRefusedIdentifier = 2,      // 不合格的客户端标识符
+        RyanMqttConnectRefusedServer = 3,          // 服务端不可用
+        RyanMqttConnectRefusedUsernamePass = 4,    // 无效的用户名或密码
+        RyanMqttConnectRefusedNotAuthorized = 5,   // 连接已拒绝,未授权
+
+        // mqtt非标准定义
+        RyanMqttConnectClientInvalid = 200, // 客户端处于无效状态
+        RyanMqttConnectNetWorkFail,         // 网络错误
+        RyanMqttConnectDisconnected,        // mqtt客户端断开连接
+        RyanMqttKeepaliveTimeout,           // 心跳超时断开连接
+        RyanMqttConnectUserDisconnected,    // 用户手动断开连接
+        RyanMqttConnectTimeout              // 超时断开
+    } RyanMqttConnectStatus_e;
+
+#include "MQTTPacket.h"
+#include "RyanMqttLog.h"
+#include "platformNetwork.h"
+#include "platformTimer.h"
+#include "platformSystem.h"
+#include "RyanList.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 721 - 0
mqttclient/RyanMqttThread.c

@@ -0,0 +1,721 @@
+#include "RyanMqttPublic.h"
+#include "RyanMqttUtile.h"
+#include "RyanMqttThread.h"
+
+/**
+ * @brief mqtt心跳保活
+ *
+ * @param client
+ * @return int32_t
+ */
+RyanMqttError_e RyanMqttKeepalive(RyanMqttClient_t *client)
+{
+    int32_t connectState = RyanMqttConnectAccepted;
+    int32_t packetLen = 0;
+    RyanMqttAssert(NULL != client);
+
+    // 如果没有连接则不需要心跳保活
+    RyanMqttCheck(mqttConnectState == RyanMqttGetClientState(client), RyanMqttNotConnectError);
+
+    // 服务器在心跳时间的1.5倍内没有收到消息则会断开连接
+    // 在心跳的一半发送keepalive
+    if (platformTimerRemain(&client->keepaliveTimer) != 0)
+        return RyanMqttSuccessError;
+
+    // 心跳超时,断开连接
+    connectState = RyanMqttKeepaliveTimeout;
+    RyanMqttCheckCode(2 > client->keepaliveTimeoutCount, RyanMqttKeepaliveTimeout,
+                      RyanMqttEventMachine(client, RyanMqttEventDisconnected, (void *)&connectState));
+
+    platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+    packetLen = MQTTSerialize_pingreq(client->config->sendBuffer, client->config->sendBufferSize);
+    if (packetLen > 0)
+        RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+    platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+    client->keepaliveTimeoutCount++;
+    platformTimerCutdown(&client->keepaliveTimer, client->config->keepaliveTimeoutS * 1000 / 2); // 启动心跳定时器
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief
+ *
+ * @param client
+ * @return int32_t
+ */
+static RyanMqttError_e RyanMqttGetPayloadLen(RyanMqttClient_t *client, uint32_t *payloadLen)
+{
+
+    RyanMqttError_e result = RyanMqttSuccessError;
+    uint8_t len = 0;
+    uint8_t encodedByte = 0;
+    int32_t multiplier = 1;
+    RyanMqttAssert(NULL != client);
+    // RyanMqttAssert(NULL != payloadLen); payloadLen == 0时会误判
+
+    do
+    {
+        RyanMqttCheck(++len < 4, RyanMqttFailedError);
+
+        result = RyanMqttRecvPacket(client, &encodedByte, 1);
+        RyanMqttCheck(RyanMqttSuccessError == result, RyanMqttFailedError);
+
+        *payloadLen += (encodedByte & 127) * multiplier; // 根据 MQTT 协议解码数据长度
+        multiplier *= 128;
+
+    } while ((encodedByte & 128) != 0);
+
+    RyanMqttCheck(*payloadLen <= RyanMqttMaxPayloadLen, RyanMqttFailedError);
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief qos1或者qos2接收消息成功
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttPubackAndPubcompPacketHandler(RyanMqttClient_t *client)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    uint8_t dup = 0;
+    uint8_t packetType = 0;
+    uint16_t packetId = 0;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttAssert(NULL != client);
+
+    result = MQTTDeserialize_ack(&packetType, &dup, &packetId, client->config->recvBuffer, client->config->recvBufferSize);
+    RyanMqttCheck(1 == result, RyanMqttDeserializePacketError);
+
+    // 可能会多次收到puback / pubcomp,仅在首次收到时触发发布成功回调函数
+    result = RyanMqttAckListNodeFind(client, packetType, packetId, &ackHandler);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, ulog_i(RyanMqttTag, "packetType: %d, packetId: %d", packetType, packetId));
+
+    RyanMqttEventMachine(client, RyanMqttEventPublished, (void *)ackHandler); // 回调函数
+
+    RyanMqttAckHandlerDestroy(client, ackHandler); // 销毁ackHandler
+    return result;
+}
+
+/**
+ * @brief 发布释放处理函数
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttPubrelPacketHandler(RyanMqttClient_t *client)
+{
+    RyanMqttError_e result = RyanMqttFailedError;
+    uint8_t dup = 0;
+    uint8_t packetType = 0;
+    uint16_t packetId = 0;
+    int32_t packetLen = 0;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttAssert(NULL != client);
+
+    result = MQTTDeserialize_ack(&packetType, &dup, &packetId, client->config->recvBuffer, client->config->recvBufferSize);
+    RyanMqttCheck(1 == result, RyanMqttDeserializePacketError);
+
+    // 制作确认数据包并发送
+    platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+    packetLen = MQTTSerialize_ack(client->config->sendBuffer, client->config->sendBufferSize, PUBCOMP, 0, packetId);
+    RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError, platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    // 每次收到PUBREL都返回消息
+    result = RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, platformMutexUnLock(client->config->userData, client->sendBufLock));
+    platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+    // 删除pubrel记录
+    result = RyanMqttAckListNodeFind(client, PUBREL, packetId, &ackHandler);
+    if (RyanMqttSuccessError == result)
+        RyanMqttAckHandlerDestroy(client, ackHandler);
+}
+
+/**
+ * @brief 发布收到处理函数
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttPubrecPacketHandler(RyanMqttClient_t *client)
+{
+    RyanMqttError_e result = RyanMqttFailedError;
+    uint8_t dup = 0;
+    RyanBool_t fastFlag = RyanFalse;
+    uint8_t packetType = 0;
+    uint16_t packetId = 0;
+    int32_t packetLen = 0;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttAckHandler_t *ackHandlerPubrec = NULL;
+    RyanMqttAssert(NULL != client);
+
+    result = MQTTDeserialize_ack(&packetType, &dup, &packetId, client->config->recvBuffer, client->config->recvBufferSize);
+    RyanMqttCheck(1 == result, RyanMqttDeserializePacketError);
+
+    // 只在首次收到pubrec, 并pubcomp不存在于ack链表时,才创建pubcmp到ack链表,再删除pubrec记录
+    result = RyanMqttAckListNodeFind(client, PUBREC, packetId, &ackHandlerPubrec);
+    if (RyanMqttSuccessError == result)
+    {
+        // 查找ack链表是否存在pubcomp报文,不存在表示首次接收到
+        result = RyanMqttAckListNodeFind(client, PUBCOMP, packetId, &ackHandler);
+        if (RyanMqttSuccessError != result)
+            fastFlag = RyanTrue;
+
+        // 出现pubrec和pubcomp同时存在的情况,清除pubrec理论上不会出现
+        else
+            RyanMqttAckHandlerDestroy(client, ackHandlerPubrec);
+    }
+
+    // 制作确认数据包并发送
+    platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+    // 序列化发布释放报文
+    packetLen = MQTTSerialize_ack(client->config->sendBuffer, client->config->sendBufferSize, PUBREL, 0, packetId);
+    RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError, platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    // 每次收到PUBREC都返回ack
+    result = RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    // 只在首次收到pubrec, 并pubcomp不存在于ack链表时,才创建pubcmp到ack链表,再删除pubrec记录
+    if (RyanTrue == fastFlag)
+    {
+        result = RyanMqttMsgHandlerCreate(ackHandlerPubrec->msgHandler->topic,
+                                          strlen(ackHandlerPubrec->msgHandler->topic),
+                                          ackHandlerPubrec->msgHandler->qos, &msgHandler);
+        RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                          platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+        // 创建一个 ACK 处理程序节点
+        result = RyanMqttAckHandlerCreate(client, PUBCOMP, packetId, packetLen, msgHandler, &ackHandler);
+        RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                          platformMemoryFree(msgHandler);
+                          platformMutexUnLock(client->config->userData, client->sendBufLock));
+    }
+    platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+    // 只在首次收到pubrec, 并pubcomp不存在于ack链表时,才创建pubcmp到ack链表,再删除pubrec记录
+    if (RyanTrue == fastFlag)
+    {
+        result = RyanMqttAckListAdd(client, ackHandler);
+        // 保证等待PUBCOMP记录成功后再清除PUBREC记录
+        RyanMqttAckHandlerDestroy(client, ackHandlerPubrec);
+    }
+
+    return result;
+}
+
+/**
+ * @brief 收到服务器发布消息处理函数
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttPublishPacketHandler(RyanMqttClient_t *client)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    int32_t packetLen = 0;
+    RyanBool_t deliverMsgFlag = RyanFalse;
+    MQTTString topicName = MQTTString_initializer;
+    RyanMqttMsgData_t msgData = {0};
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttAssert(NULL != client);
+
+    result = MQTTDeserialize_publish(&msgData.dup, &msgData.qos, &msgData.retained, &msgData.packetId, &topicName,
+                                     &msgData.payload, (int *)&msgData.payloadLen, client->config->recvBuffer, client->config->recvBufferSize);
+    RyanMqttCheck(1 == result, RyanMqttDeserializePacketError);
+
+    // 查看订阅列表是否包含此消息主题,进行通配符匹配。不包含就直接退出在一定程度上可以防止恶意攻击
+    result = RyanMqttMsgHandlerFind(client, topicName.lenstring.data, topicName.lenstring.len, RyanTrue, &msgHandler);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    switch (msgData.qos)
+    {
+    case QOS0:
+        deliverMsgFlag = RyanTrue;
+        break;
+
+    case QOS1:
+        platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+        packetLen = MQTTSerialize_ack(client->config->sendBuffer, client->config->sendBufferSize, PUBACK, 0, msgData.packetId);
+        RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError,
+                          platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+        result = RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+        RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                          platformMutexUnLock(client->config->userData, client->sendBufLock));
+        platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+        deliverMsgFlag = RyanTrue;
+
+        break;
+
+    case QOS2:
+    {
+        RyanBool_t fastFlag = RyanFalse;
+        // 收到publish就期望收到PUBREL,如果PUBREL报文已经存在说明不是首次收到publish不进行qos2消息处理
+        result = RyanMqttAckListNodeFind(client, PUBREL, msgData.packetId, &ackHandler);
+        if (RyanMqttSuccessError != result)
+            fastFlag = RyanTrue;
+
+        platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+        packetLen = MQTTSerialize_ack(client->config->sendBuffer, client->config->sendBufferSize, PUBREC, 0, msgData.packetId);
+        RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError,
+                          platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+        result = RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+        RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                          platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+        if (RyanTrue == fastFlag)
+        {
+            result = RyanMqttMsgHandlerCreate(topicName.lenstring.data, topicName.lenstring.len, msgData.qos, &msgHandler);
+            RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                              platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+            result = RyanMqttAckHandlerCreate(client, PUBREL, msgData.packetId, packetLen, msgHandler, &ackHandler);
+            RyanMqttCheckCode(RyanMqttSuccessError == result, result,
+                              platformMemoryFree(msgHandler);
+                              platformMutexUnLock(client->config->userData, client->sendBufLock));
+        }
+        platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+
+        if (RyanTrue == fastFlag)
+        {
+            result = RyanMqttAckListAdd(client, ackHandler);
+            deliverMsgFlag = RyanTrue;
+        }
+    }
+
+    break;
+
+    default:
+        break;
+    }
+
+    if (RyanTrue == deliverMsgFlag)
+    {
+        // 复制主题名字
+        result = RyanMqttStringCopy(&msgData.topic, topicName.lenstring.data, topicName.lenstring.len);
+        RyanMqttCheck(RyanMqttSuccessError == result, result);
+        RyanMqttEventMachine(client, RyanMqttEventData, (void *)&msgData);
+        platformMemoryFree(msgData.topic);
+    }
+
+    return result;
+}
+
+/**
+ * @brief 订阅确认处理函数
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttSubackHandler(RyanMqttClient_t *client)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    int32_t count = 0;
+    int32_t grantedQoS = 0;
+    uint16_t packetId = 0;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttAssert(NULL != client);
+
+    result = MQTTDeserialize_suback(&packetId, 1, (int *)&count, (int *)&grantedQoS, client->config->recvBuffer, client->config->recvBufferSize);
+    RyanMqttCheck(1 == result, RyanMqttDeserializePacketError);
+
+    // ack链表不存在当前订阅确认节点就直接退出
+    result = RyanMqttAckListNodeFind(client, SUBACK, packetId, &ackHandler);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // 订阅失败
+    if (subFail == grantedQoS)
+    {
+        // mqtt事件回调
+        RyanMqttEventMachine(client, RyanMqttEventSubscribedFaile, (void *)ackHandler->msgHandler);
+        RyanMqttAckHandlerDestroy(client, ackHandler); // 销毁ackHandler
+        return RyanMqttSuccessError;
+    }
+
+    // 订阅成功
+    // 查找是否有同名订阅,如果有就销毁之前的
+    result = RyanMqttMsgHandlerFind(client, ackHandler->msgHandler->topic, strlen(ackHandler->msgHandler->topic), RyanFalse, &msgHandler);
+    if (RyanMqttSuccessError == result)
+        RyanMqttMsgHandlerDestory(msgHandler);
+
+    // 服务端可以授予比订阅者要求的低一些的 QoS 等级。
+    result = RyanMqttMsgHandlerCreate(ackHandler->msgHandler->topic,
+                                      strlen(ackHandler->msgHandler->topic),
+                                      grantedQoS, &msgHandler);
+    RyanMqttCheck(RyanMqttSuccessError == result, result); // 这里创建失败了不触发回调,等待ack超时触发失败回调函数
+
+    RyanMqttMsgHandlerAdd(client, msgHandler);                                 // 将msg信息添加到订阅链表上
+    RyanMqttEventMachine(client, RyanMqttEventSubscribed, (void *)msgHandler); // mqtt回调函数
+    RyanMqttAckHandlerDestroy(client, ackHandler);                             // 销毁ackHandler
+
+    return result;
+}
+
+/**
+ * @brief 取消订阅确认处理函数
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttUnSubackHandler(RyanMqttClient_t *client)
+{
+    RyanMqttError_e result = RyanMqttFailedError;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    uint16_t packetId = 0;
+    RyanMqttAssert(NULL != client);
+
+    result = MQTTDeserialize_unsuback(&packetId, client->config->recvBuffer, client->config->recvBufferSize);
+    RyanMqttCheck(1 == result, RyanMqttDeserializePacketError);
+
+    // ack链表不存在当前取消订阅确认节点就直接退出
+    result = RyanMqttAckListNodeFind(client, UNSUBACK, packetId, &ackHandler);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // mqtt事件回调
+    RyanMqttEventMachine(client, RyanMqttEventUnSubscribed, (void *)ackHandler->msgHandler);
+
+    RyanMqttAckHandlerDestroy(client, ackHandler); // 销毁ackHandler
+
+    return result;
+}
+
+/**
+ * @brief mqtt数据包处理函数
+ *
+ * @param client
+ * @param packetType
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttReadPacketHandler(RyanMqttClient_t *client, uint8_t *packetType)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    int32_t fixedHeaderLen = 1;
+    uint32_t payloadLen = 0;
+    MQTTHeader header = {0};
+    RyanMqttAssert(NULL != client);
+    // RyanMqttAssert(NULL != packetType); packetType == 0时会误判
+
+    // 1.读取标头字节。 其中包含数据包类型
+    result = RyanMqttRecvPacket(client, client->config->recvBuffer, fixedHeaderLen);
+    if (RyanMqttRecvPacketTimeOutError == result)
+        return RyanMqttRecvPacketTimeOutError;
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // 填充联合体标头信息
+    header.byte = client->config->recvBuffer[0];
+    // ulog_i(RyanMqttTag, "packetType: %d", header.bits.type);
+    RyanMqttCheck(CONNECT <= header.bits.type && DISCONNECT >= header.bits.type, result);
+
+    // 2.读取mqtt报文剩余长度。 这本身是可变的
+    result = RyanMqttGetPayloadLen(client, &payloadLen);
+    RyanMqttCheck(RyanMqttSuccessError == result, result);
+
+    // 将剩余长度编码成mqtt报文,并放入接收缓冲区,如果消息长度超过缓冲区长度则抛弃此次数据
+    fixedHeaderLen += MQTTPacket_encode(client->config->recvBuffer + fixedHeaderLen, payloadLen);
+    RyanMqttCheckCode((fixedHeaderLen + payloadLen) <= client->config->recvBufferSize, RyanMqttRecvBufToShortError,
+                      RyanMqttRecvPacket(client, client->config->recvBuffer, payloadLen));
+
+    // 3.读取mqtt载荷数据并放到读取缓冲区
+    if (payloadLen > 0)
+    {
+        result = RyanMqttRecvPacket(client, client->config->recvBuffer + fixedHeaderLen, payloadLen);
+        RyanMqttCheck(RyanMqttSuccessError == result, result);
+    }
+
+    // 控制报文类型
+    switch (header.bits.type)
+    {
+
+    case CONNACK: // 连接报文确认
+        break;
+
+    case PUBACK:  // QoS 1消息发布收到确认
+    case PUBCOMP: // 发布完成(qos2 第三步)
+        result = RyanMqttPubackAndPubcompPacketHandler(client);
+        break;
+
+    case PUBREC: // 发布收到(qos2 第一步)
+        result = RyanMqttPubrecPacketHandler(client);
+        break;
+
+    case PUBREL: // 发布释放(qos2 第二步)
+        result = RyanMqttPubrelPacketHandler(client);
+        break;
+
+    case SUBACK: // 订阅确认
+        result = RyanMqttSubackHandler(client);
+        break;
+
+    case UNSUBACK: // 取消订阅确认
+        result = RyanMqttUnSubackHandler(client);
+        break;
+
+    case PUBLISH: // 接收到订阅消息
+        result = RyanMqttPublishPacketHandler(client);
+        break;
+
+    case PINGRESP: // 心跳响应
+        client->keepaliveTimeoutCount = 0;
+        break;
+
+    default:
+        break;
+    }
+
+    *packetType = header.bits.type;
+
+    return result;
+}
+
+/**
+ * @brief 遍历ack链表,进行相应的处理
+ *
+ * @param client
+ * @param WaitFlag
+ *      WaitFlag : RyanFalse 表示不需要等待超时立即处理这些数据包。通常在重新连接后立即进行处理
+ *      WaitFlag : RyanTrue 表示需要等待超时再处理这些消息,一般是稳定连接下的超时处理
+ */
+void RyanMqttAckListScan(RyanMqttClient_t *client, RyanBool_t WaitFlag)
+{
+    RyanList_t *curr = NULL,
+               *next = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttAssert(NULL != client);
+
+    // 如果链表为空或者mqtt没有连接就退出
+    if ((RyanListIsEmpty(&client->ackHandlerList)) || (mqttConnectState != RyanMqttGetClientState(client)))
+        return;
+
+    RyanListForEachSafe(curr, next, &client->ackHandlerList)
+    {
+        // 获取此节点的结构体
+        ackHandler = RyanListEntry(curr, RyanMqttAckHandler_t, list);
+
+        // ack响应没有超时就不进行处理
+        if (0 != platformTimerRemain(&ackHandler->timer) && RyanTrue == WaitFlag)
+            continue;
+
+        switch (ackHandler->packetType)
+        {
+        // 发送qos1 / qos2消息, 服务器ack响应超时。需要重新发送它们。
+        case PUBACK:
+        case PUBREC:
+        case PUBREL:
+        case PUBCOMP:
+        {
+
+            if (mqttConnectState != RyanMqttGetClientState(client))
+                continue;
+
+            printfArrStr(ackHandler->packet, ackHandler->packetLen, "重发数据:");
+            // 重发数据事件回调
+            RyanMqttEventMachine(client, RyanMqttEventRepeatPublishPacket, (void *)ackHandler);
+
+            RyanMqttSendPacket(client, ackHandler->packet, ackHandler->packetLen); // 重新发送数据
+
+            // 重置ack超时时间
+            platformTimerCutdown(&ackHandler->timer, client->config->ackTimeout);
+            ackHandler->repeatCount++;
+
+            // 重发次数超过警告值回调
+            if (ackHandler->repeatCount >= client->config->ackHandlerRepeatCountWarning)
+                RyanMqttEventMachine(client, RyanMqttEventAckRepeatCountWarning, (void *)ackHandler);
+
+            break;
+        }
+
+        // 订阅 / 取消订阅超时就认为失败
+        case SUBACK:
+        case UNSUBACK:
+        {
+            RyanMqttEventMachine(client, (SUBACK == ackHandler->packetType) ? RyanMqttEventSubscribedFaile : RyanMqttEventUnSubscribedFaile,
+                                 (void *)ackHandler->msgHandler);
+            // 清除句柄
+            RyanMqttAckHandlerDestroy(client, ackHandler);
+            break;
+        }
+
+        default:
+            RyanMqttAssert(NULL); // 不应该为别的值
+            break;
+        }
+    }
+}
+
+/**
+ * @brief mqtt连接函数
+ *
+ * @param client
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttConnect(RyanMqttClient_t *client)
+{
+
+    RyanMqttError_e result = RyanMqttSuccessError;
+    uint8_t sessionPresent = 0; // 会话存在标志
+    uint8_t packetType = 0;     // 接收到的报文类型
+    int32_t packetLen = 0;
+    int32_t connackRc = 0;
+    MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != client->network);
+    RyanMqttAssert(NULL != client->config);
+
+    RyanMqttCheck(mqttConnectState != RyanMqttGetClientState(client), RyanMqttConnectAccepted);
+
+    // 连接标志位
+    connectData.clientID.cstring = client->config->clientId;
+    connectData.username.cstring = client->config->userName;
+    connectData.password.cstring = client->config->password;
+    connectData.keepAliveInterval = client->config->keepaliveTimeoutS;
+    connectData.cleansession = client->config->cleanSessionFlag;
+    connectData.MQTTVersion = client->config->mqttVersion;
+
+    if (RyanTrue == client->lwtFlag)
+    {
+        connectData.willFlag = 1;
+        connectData.will.qos = client->lwtOptions->qos;
+        connectData.will.retained = client->lwtOptions->retain;
+        connectData.will.message.lenstring.data = client->lwtOptions->payload;
+        connectData.will.message.lenstring.len = client->lwtOptions->payloadLen;
+        connectData.will.topicName.cstring = client->lwtOptions->topic;
+    }
+
+    // 调用底层的连接函数连接上服务器
+    result = platformNetworkConnect(client->config->userData, client->network, client->config->host, client->config->port);
+    RyanMqttCheck(RyanMqttSuccessError == result, RyanMqttConnectNetWorkFail);
+
+    platformMutexLock(client->config->userData, client->sendBufLock); // 获取互斥锁
+    // 序列化mqtt的CONNECT报文
+    packetLen = MQTTSerialize_connect(client->config->sendBuffer, client->config->sendBufferSize, &connectData);
+    RyanMqttCheckCode(packetLen > 0, RyanMqttSerializePacketError, platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    // 发送序列化mqtt的CONNECT报文
+    result = RyanMqttSendPacket(client, client->config->sendBuffer, packetLen);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    // 等待报文
+    // mqtt规范 服务端接收到connect报文后,服务端发送给客户端的第一个报文必须是 CONNACK
+    result = RyanMqttReadPacketHandler(client, &packetType);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, result, platformMutexUnLock(client->config->userData, client->sendBufLock));
+    RyanMqttCheckCode(CONNACK == packetType, RyanMqttConnectDisconnected, platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    // 解析CONNACK报文
+    result = MQTTDeserialize_connack(&sessionPresent, &connackRc, client->config->recvBuffer, client->config->recvBufferSize);
+    RyanMqttCheckCode(1 == result, RyanMqttDeserializePacketError, platformMutexUnLock(client->config->userData, client->sendBufLock));
+
+    platformMutexUnLock(client->config->userData, client->sendBufLock); // 释放互斥锁
+    // ulog_w(TAG, "result: %d, packetLen: %d, packetType: %d connackRc: %d", result, packetLen, packetType, connackRc);
+
+    return connackRc;
+}
+
+/**
+ * @brief mqtt事件处理函数
+ *
+ * @param client
+ * @param eventId
+ * @param eventData
+ */
+void RyanMqttEventMachine(RyanMqttClient_t *client, RyanMqttEventId_e eventId, void *eventData)
+{
+
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != client->network);
+    RyanMqttAssert(NULL != client->config);
+
+    switch (eventId)
+    {
+    case RyanMqttEventConnected:                                                                       // 连接成功
+        client->keepaliveTimeoutCount = 0;                                                             // 重置心跳超时计数器
+        platformTimerCutdown(&client->keepaliveTimer, (client->config->keepaliveTimeoutS * 1000 / 2)); // 启动心跳定时器
+        RyanMqttAckListScan(client, RyanFalse);                                                        // 扫描确认列表,销毁已超时的确认处理程序或重新发送它们
+        RyanMqttSetClientState(client, mqttConnectState);
+        break;
+
+    case RyanMqttEventDisconnected: // 断开连接事件
+        platformNetworkClose(client->config->userData, client->network);
+
+        if (RyanTrue == client->config->cleanSessionFlag)
+            RyanMqttCleanSession(client);
+        RyanMqttSetClientState(client, mqttDisconnectState); // 将客户端状态设置为断开连接
+        break;
+
+    case RyanMqttEventReconnectBefore: // 接收到数据事件
+        RyanMqttSetClientState(client, mqttReconnectState);
+        break;
+    default:
+        break;
+    }
+
+    if (client->config->mqttEventHandle == NULL)
+        return;
+
+    if (client->eventFlag & eventId)
+        client->config->mqttEventHandle(client, eventId, eventData);
+}
+
+/**
+ * @brief mqtt运行线程
+ *
+ * @param argument
+ */
+void RyanMqttThread(void *argument)
+{
+    int32_t result = 0;
+    RyanMqttClient_t *client = (RyanMqttClient_t *)argument;
+    RyanMqttAssert(NULL != client);          // RyanMqttStart前没有调用RyanMqttInit
+    RyanMqttAssert(NULL != client->network); // RyanMqttStart前没有调用RyanMqttInit
+    RyanMqttAssert(NULL != client->config);  // RyanMqttStart前没有调用RyanMqttSetConfig
+
+    while (1)
+    {
+        switch (client->clientState)
+        {
+
+        case mqttStartState: // 开始状态状态
+            ulog_w(RyanMqttTag, "初始化状态,开始连接");
+            result = RyanMqttConnect(client);
+            RyanMqttEventMachine(client, RyanMqttConnectAccepted == result ? RyanMqttEventConnected : RyanMqttEventDisconnected,
+                                 (void *)&result);
+            break;
+
+        case mqttConnectState: // 连接状态
+            // ulog_w(RyanMqttTag, "连接状态");
+            result = RyanMqttReadPacketHandler(client, NULL);
+            RyanMqttAckListScan(client, 1);
+            RyanMqttKeepalive(client);
+            break;
+
+        case mqttDisconnectState: // 断开连接状态
+            ulog_w(RyanMqttTag, "断开连接状态");
+            if (RyanTrue != client->config->autoReconnectFlag) // 没有使能自动连接就休眠线程
+                platformThreadStop(client->config->userData, client->mqttThread);
+
+            ulog_w(RyanMqttTag, "触发自动连接,%dms后开始连接\r\n", client->config->reconnectTimeout);
+            platformDelay(client->config->reconnectTimeout);
+            RyanMqttEventMachine(client, RyanMqttEventReconnectBefore, NULL); // 给上层触发重新连接前事件
+
+            break;
+
+        case mqttReconnectState:
+            result = RyanMqttConnect(client);
+            RyanMqttEventMachine(client, RyanMqttConnectAccepted == result ? RyanMqttEventConnected : RyanMqttEventDisconnected,
+                                 (void *)&result);
+            break;
+
+        default:
+            RyanMqttAssert(NULL);
+            break;
+        }
+    }
+}

+ 19 - 0
mqttclient/RyanMqttThread.h

@@ -0,0 +1,19 @@
+
+#ifndef __mqttClientTask__
+#define __mqttClientTask__
+
+#include "RyanMqttClient.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern void RyanMqttThread(void *argument);
+extern void RyanMqttEventMachine(RyanMqttClient_t *client, RyanMqttEventId_e eventId, void *eventData);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 710 - 0
mqttclient/RyanMqttUtile.c

@@ -0,0 +1,710 @@
+#include "RyanMqttPublic.h"
+#include "RyanMqttUtile.h"
+#include "RyanMqttThread.h"
+
+void printfArrStr(char *buf, uint32_t len, char *userData)
+{
+    rt_kprintf("%s", userData);
+    for (uint32_t i = 0; i < len; i++)
+        rt_kprintf("%x", buf[i]);
+
+    rt_kprintf("\r\n");
+}
+
+/**
+ * @brief 清理session
+ *
+ * @param client
+ */
+void RyanMqttCleanSession(RyanMqttClient_t *client)
+{
+    RyanList_t *curr = NULL,
+               *next = NULL;
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAssert(NULL != client);
+
+    // 释放所有ackHandler_list内存
+    if (0 == RyanListIsEmpty(&client->ackHandlerList))
+    {
+        RyanListForEachSafe(curr, next, &client->ackHandlerList)
+        {
+            ackHandler = RyanListEntry(curr, RyanMqttAckHandler_t, list);
+            RyanMqttAckHandlerDestroy(client, ackHandler);
+        }
+        RyanListDelInit(&client->ackHandlerList);
+    }
+
+    // 释放所有msg_handler_list内存
+    if (0 == RyanListIsEmpty(&client->msgHandlerList))
+    {
+        RyanListForEachSafe(curr, next, &client->msgHandlerList)
+        {
+            msgHandler = RyanListEntry(curr, RyanMqttMsgHandler_t, list);
+            RyanMqttMsgHandlerDestory(msgHandler);
+        }
+        RyanListDelInit(&client->msgHandlerList);
+    }
+
+    client->ackHandlerCount = 0;
+}
+
+/**
+ * @brief 字符串拷贝,需要手动释放内存
+ *
+ * @param dest
+ * @param rest
+ * @param strLen
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttStringCopy(char **dest, char *rest, uint32_t strLen)
+{
+    char *str2 = NULL;
+    RyanMqttAssert(NULL != dest);
+    RyanMqttAssert(NULL != rest);
+    RyanMqttCheck(0 != strLen, RyanMqttFailedError);
+
+    str2 = (char *)platformMemoryMalloc(strLen + 1);
+    if (NULL == str2)
+        return RyanMqttNotEnoughMemError;
+
+    memcpy(str2, rest, strLen);
+    str2[strLen] = '\0';
+
+    *dest = str2;
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 设置重发标志位
+ *
+ * @param headerBuf
+ * @param dup
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttSetPublishDup(char *headerBuf, uint8_t dup)
+{
+
+    MQTTHeader header = {0};
+    RyanMqttAssert(NULL != headerBuf);
+
+    header.byte = *headerBuf;
+    RyanMqttCheck(PUBLISH == header.bits.type, RyanMqttFailedError);
+
+    header.bits.dup = dup;
+    *headerBuf = header.byte;
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief mqtt读取报文
+ *
+ * @param client
+ * @param buf
+ * @param length
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttRecvPacket(RyanMqttClient_t *client, char *recvBuf, int32_t recvLen)
+{
+
+    int32_t connectState = RyanMqttConnectAccepted;
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != client->network);
+    RyanMqttAssert(NULL != client->config);
+    RyanMqttAssert(NULL != recvBuf);
+
+    RyanMqttCheck(0 != recvLen, RyanMqttSuccessError);
+
+    result = platformNetworkRecvAsync(client->config->userData, client->network, recvBuf, recvLen, client->config->recvTimeout);
+
+    switch (result)
+    {
+
+    case RyanMqttRecvPacketTimeOutError:
+    case RyanMqttSuccessError:
+        return result;
+
+    case RyanSocketFailedError:
+    default:
+        connectState = RyanSocketFailedError;
+        RyanMqttEventMachine(client, RyanMqttEventDisconnected, &connectState);
+        return RyanSocketFailedError;
+    }
+}
+
+/**
+ * @brief mqtt发送报文
+ *
+ * @param client
+ * @param buf
+ * @param length
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttSendPacket(RyanMqttClient_t *client, char *sendBuf, int32_t sendLen)
+{
+    int32_t connectState = RyanMqttConnectAccepted;
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != client->network);
+    RyanMqttAssert(NULL != client->config);
+    RyanMqttAssert(NULL != sendBuf);
+
+    RyanMqttCheck(0 != sendLen, RyanMqttSuccessError);
+
+    result = platformNetworkSendAsync(client->config->userData, client->network, sendBuf, sendLen, client->config->sendTimeout);
+    switch (result)
+    {
+    case RyanMqttSuccessError:
+        return result;
+
+    case RyanMqttSendPacketTimeOutError:
+    case RyanSocketFailedError:
+    default:
+        connectState = RyanSocketFailedError;
+        RyanMqttEventMachine(client, RyanMqttEventDisconnected, &connectState);
+        return RyanSocketFailedError;
+    }
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 设置mqtt客户端状态
+ *
+ * @param client
+ * @param state
+ */
+void RyanMqttSetClientState(RyanMqttClient_t *client, RyanMqttState_e state)
+{
+    RyanMqttAssert(NULL != client);
+
+    platformCriticalEnter();
+    client->clientState = state;
+    platformCriticalExit();
+}
+
+/**
+ * @brief 获取mqtt客户端状态
+ *
+ * @param client
+ * @return RyanMqttState_e
+ */
+RyanMqttState_e RyanMqttGetClientState(RyanMqttClient_t *client)
+{
+    RyanMqttAssert(NULL != client);
+    // 原子操作不必互斥
+    return client->clientState;
+}
+
+/**
+ * @brief 根据 MQTT 3.1.1 协议规范确定传递的主题过滤器和主题名称是否匹配的实用程序函数,
+ *          应仅在strcmp / strncmp不相等时再进行通配符匹配
+ *
+ * @param topic 要检查的主题名称
+ * @param topicLength 主题名称的长度。
+ * @param topicFilter 要检查的主题过滤器。
+ * @param topicFilterLength 要检查的主题过滤器长度
+ * @return RyanBool_t
+ */
+RyanBool_t RyanMqttMatchTopic(const char *topic,
+                              const uint16_t topicLength,
+                              const char *topicFilter,
+                              const uint16_t topicFilterLength)
+{
+
+    RyanBool_t topicFilterStartsWithWildcard = RyanFalse,
+               matchFound = RyanFalse,
+               shouldStopMatching = RyanFalse;
+    uint16_t topicIndex = 0,
+             topicFilterIndex = 0;
+
+    RyanMqttAssert((NULL != topic) && (topicLength != 0u));
+    RyanMqttAssert((NULL != topicFilter) && (topicFilterLength != 0u));
+
+    // 确定主题过滤器是否以通配符开头。
+    topicFilterStartsWithWildcard = (topicFilter[0] == '+') ||
+                                    (topicFilter[0] == '#');
+
+    // 不能将 $ 字符开头的主题名匹配通配符 (#或+) 开头的主题过滤器
+    if ((topic[0] == '$') && (topicFilterStartsWithWildcard == RyanTrue))
+        return RyanFalse;
+
+    // 匹配主题名称和主题过滤器,允许使用通配符。
+    while ((topicIndex < topicLength) && (topicFilterIndex < topicFilterLength))
+    {
+        // 检查主题名称中的字符是否与主题筛选器字符串中的对应字符匹配。
+        if (topic[topicIndex] == topicFilter[topicFilterIndex])
+        {
+            // 当主题名称已被消耗但主题过滤器中还有剩余字符需要匹配时,此功能处理以下两种情况:
+            // -当主题过滤器以“/+”或“/#”字符结尾时,主题名称以“/”结尾。
+            // -当主题过滤器以“/#”字符结尾时,主题名称以父级别结尾。
+            if (topicIndex == (topicLength - 1U))
+            {
+
+                // 检查主题筛选器是否有2个剩余字符,并且以“/#”结尾。
+                // 此检查处理将筛选器“sport/#”与主题“sport”匹配的情况。
+                // 原因是“#”通配符表示主题名称中的父级和任意数量的子级。
+                if ((topicFilterLength >= 3U) &&
+                    (topicFilterIndex == (topicFilterLength - 3U)) &&
+                    (topicFilter[topicFilterIndex + 1U] == '/') &&
+                    (topicFilter[topicFilterIndex + 2U] == '#'))
+                    matchFound = RyanTrue;
+
+                // 检查下一个字符是否为“#”或“+”,主题过滤器以“/#”或“/+”结尾。
+                // 此检查处理要匹配的情况:
+                // -主题过滤器“sport/+”与主题“sport/”。
+                // -主题过滤器“sport/#”,主题为“sport/”。
+                if ((topicFilterIndex == (topicFilterLength - 2U)) &&
+                    (topicFilter[topicFilterIndex] == '/'))
+                    // 检查最后一个字符是否为通配符
+                    matchFound = (topicFilter[topicFilterIndex + 1U] == '+') || (topicFilter[topicFilterIndex + 1U] == '#');
+            }
+        }
+        else
+        {
+            // 检查是否匹配通配符
+            RyanBool_t locationIsValidForWildcard;
+
+            // 主题过滤器中的通配符仅在起始位置或前面有“/”时有效。
+            locationIsValidForWildcard = ((topicFilterIndex == 0u) ||
+                                          (topicFilter[topicFilterIndex - 1U] == '/'));
+
+            if ((topicFilter[topicFilterIndex] == '+') && (locationIsValidForWildcard == RyanTrue))
+            {
+                RyanBool_t nextLevelExistsInTopicName = RyanFalse;
+                RyanBool_t nextLevelExistsinTopicFilter = RyanFalse;
+
+                // 将主题名称索引移动到当前级别的末尾。 当前级别的结束由下一个级别分隔符“/”之前的最后一个字符标识。
+                while (topicIndex < topicLength)
+                {
+                    // 如果我们碰到级别分隔符,则退出循环
+                    if (topic[topicIndex] == '/')
+                    {
+                        nextLevelExistsInTopicName = RyanTrue;
+                        break;
+                    }
+
+                    (topicIndex)++;
+                }
+
+                // 确定主题过滤器是否包含在由“+”通配符表示的当前级别之后的子级别。
+                if ((topicFilterIndex < (topicFilterLength - 1U)) &&
+                    (topicFilter[topicFilterIndex + 1U] == '/'))
+                    nextLevelExistsinTopicFilter = RyanTrue;
+
+                // 如果主题名称包含子级别但主题过滤器在当前级别结束,则不存在匹配项。
+                if ((nextLevelExistsInTopicName == RyanTrue) &&
+                    (nextLevelExistsinTopicFilter == RyanFalse))
+                {
+                    matchFound = RyanFalse;
+                    shouldStopMatching = RyanTrue;
+                }
+                // 如果主题名称和主题过滤器有子级别,则将过滤器索引推进到主题过滤器中的级别分隔符,以便在下一个级别进行匹配。
+                // 注意:名称索引已经指向主题名称中的级别分隔符。
+                else if (nextLevelExistsInTopicName == RyanTrue)
+                    (topicFilterIndex)++;
+
+                // 如果我们已经到达这里,循环以(*pNameIndex<topicLength)条件终止,
+                // 这意味着已经超过主题名称的末尾,因此,我们将索引缩减为主题名称中的最后一个字符。
+                else
+                    (topicIndex)--;
+            }
+
+            // “#”匹配主题名称中剩余的所有内容。它必须是主题过滤器中的最后一个字符。
+            else if ((topicFilter[topicFilterIndex] == '#') &&
+                     (topicFilterIndex == (topicFilterLength - 1U)) &&
+                     (locationIsValidForWildcard == RyanTrue))
+            {
+                // 后续字符不需要检查多级通配符。
+                matchFound = RyanTrue;
+                shouldStopMatching = RyanTrue;
+            }
+            else
+            {
+                // 除“+”或“#”以外的任何字符不匹配均表示主题名称与主题过滤器不匹配。
+                matchFound = RyanFalse;
+                shouldStopMatching = RyanTrue;
+            }
+        }
+
+        if ((matchFound == RyanTrue) || (shouldStopMatching == RyanTrue))
+            break;
+
+        // 增量索引
+        topicIndex++;
+        topicFilterIndex++;
+    }
+
+    // 如果已到达两个字符串的末尾,则它们匹配。这表示当主题过滤器在非起始位置包含 “+” 通配符时的情况。
+    // 例如,当将 “sport/+/player” 或 “sport/hockey/+” 主题过滤器与 “sport/hockey/player” 主题名称匹配时。
+    if (matchFound == RyanFalse)
+        matchFound = (topicIndex == topicLength) && (topicFilterIndex == topicFilterLength);
+
+    return matchFound;
+}
+
+/**
+ * @brief 创建msg句柄
+ *
+ * @param topic
+ * @param topicLen
+ * @param qos
+ * @param pMsgHandler
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttMsgHandlerCreate(char *topic, uint16_t topicLen, RyanMqttQos_e qos, RyanMqttMsgHandler_t **pMsgHandler)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    RyanMqttAssert(NULL != topic);
+    RyanMqttAssert(NULL != pMsgHandler);
+    RyanMqttAssert(QOS0 <= qos && QOS2 >= qos);
+
+    msgHandler = (RyanMqttMsgHandler_t *)platformMemoryMalloc(sizeof(RyanMqttMsgHandler_t));
+    RyanMqttCheck(NULL != msgHandler, RyanMqttNotEnoughMemError);
+    memset(msgHandler, 0, sizeof(RyanMqttMsgHandler_t));
+
+    // 初始化链表
+    RyanMqttListInit(&msgHandler->list);
+
+    msgHandler->qos = qos;
+    result = RyanMqttStringCopy((char **)&msgHandler->topic, (char *)topic, topicLen);
+    RyanMqttCheckCode(RyanMqttSuccessError == result, RyanMqttNotEnoughMemError, platformMemoryFree(msgHandler); msgHandler = NULL);
+
+    *pMsgHandler = msgHandler;
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 销毁msg 句柄
+ *
+ * @param msgHandler
+ */
+void RyanMqttMsgHandlerDestory(RyanMqttMsgHandler_t *msgHandler)
+{
+    RyanMqttAssert(NULL != msgHandler);
+    RyanMqttAssert(NULL != msgHandler->topic);
+
+    RyanListDel(&msgHandler->list);
+
+    platformMemoryFree(msgHandler->topic);
+    msgHandler->topic = NULL;
+
+    platformMemoryFree(msgHandler);
+}
+
+/**
+ * @brief 查找msg句柄
+ *
+ * @param client
+ * @param topic
+ * @param topicLen
+ * @param topicMatchedFlag
+ * @param pMsgHandler
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttMsgHandlerFind(RyanMqttClient_t *client, char *topic, uint16_t topicLen, RyanBool_t topicMatchedFlag, RyanMqttMsgHandler_t **pMsgHandler)
+{
+    RyanList_t *curr = NULL,
+               *next = NULL;
+    RyanMqttMsgHandler_t *msgHandler = NULL;
+    uint16_t topicFilterLen = 0;
+
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != topic && 0 != topicLen);
+    RyanMqttAssert(NULL != pMsgHandler);
+
+    if (RyanListIsEmpty(&client->msgHandlerList))
+        return RyanMqttNoRescourceError;
+
+    RyanListForEachSafe(curr, next, &client->msgHandlerList)
+    {
+        msgHandler = RyanListEntry(curr, RyanMqttMsgHandler_t, list);
+
+        // 通过 MQTT 主题判断节点是否已存在,不进行通配符匹配
+        if (NULL == msgHandler->topic)
+            continue;
+
+        // 不相等跳过
+        topicFilterLen = strlen(msgHandler->topic);
+        if (topicLen != topicFilterLen && RyanTrue != topicMatchedFlag)
+            continue;
+
+        // 主题名称不相等且没有使能通配符匹配
+        if (0 != strncmp(topic, msgHandler->topic, topicLen) && RyanTrue != topicMatchedFlag)
+            continue;
+
+        // 进行通配符匹配
+        if (RyanTrue != RyanMqttMatchTopic(topic, topicLen, msgHandler->topic, topicFilterLen))
+            continue;
+
+        *pMsgHandler = msgHandler;
+
+        return RyanMqttSuccessError;
+    }
+
+    return RyanMqttNoRescourceError;
+}
+
+/**
+ * @brief 将msg句柄存入client msg链表
+ *
+ * @param client
+ * @param msgHandler
+ * @return int32_t
+ */
+RyanMqttError_e RyanMqttMsgHandlerAdd(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgHandler)
+{
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != msgHandler);
+    RyanMqttAssert(NULL != msgHandler->topic);
+
+    platformCriticalEnter();
+    RyanListAddTail(&msgHandler->list, &client->msgHandlerList); // 将msgHandler节点添加到链表尾部
+    platformCriticalExit();
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 创建ack句柄
+ *
+ * @param client
+ * @param packetType
+ * @param packetId
+ * @param packetLen
+ * @param msgHandler
+ * @param packHandler
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttAckHandlerCreate(RyanMqttClient_t *client, enum msgTypes packetType, uint16_t packetId, uint16_t packetLen, RyanMqttMsgHandler_t *msgHandler, RyanMqttAckHandler_t **packHandler)
+{
+    RyanMqttAckHandler_t *ackHandler = NULL;
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != msgHandler);
+    RyanMqttAssert(NULL != msgHandler->topic);
+    RyanMqttAssert(NULL != packHandler);
+
+    // 给消息主题添加空格
+    ackHandler = (RyanMqttAckHandler_t *)platformMemoryMalloc(sizeof(RyanMqttAckHandler_t) + packetLen);
+    RyanMqttCheck(NULL != ackHandler, RyanMqttNotEnoughMemError);
+    memset(ackHandler, 0, sizeof(RyanMqttAckHandler_t) + packetLen);
+
+    RyanMqttListInit(&ackHandler->list);
+    platformTimerCutdown(&ackHandler->timer, client->config->ackTimeout); // 超时内没有响应将被销毁或重新发送
+
+    ackHandler->repeatCount = 0;
+    ackHandler->packetId = packetId;
+    ackHandler->packetLen = packetLen;
+    ackHandler->packetType = packetType;
+    ackHandler->msgHandler = msgHandler;
+    ackHandler->packet = (char *)ackHandler + sizeof(RyanMqttAckHandler_t);
+    memcpy(ackHandler->packet, client->config->sendBuffer, packetLen); // 将packet数据保存到ack中
+
+    *packHandler = ackHandler;
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 销毁ack句柄
+ *
+ * @param client
+ * @param ackHandler
+ */
+void RyanMqttAckHandlerDestroy(RyanMqttClient_t *client, RyanMqttAckHandler_t *ackHandler)
+{
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != ackHandler);
+    RyanMqttAssert(NULL != ackHandler->msgHandler);
+    RyanMqttAssert(NULL != ackHandler->msgHandler->topic);
+
+    if (NULL == &ackHandler->list)
+        return;
+
+    RyanListDel(&ackHandler->list);
+
+    RyanMqttMsgHandlerDestory(ackHandler->msgHandler); // 释放msgHandler
+
+    platformMemoryFree(ackHandler);
+
+    if (client->ackHandlerCount > 0)
+        client->ackHandlerCount--;
+}
+
+/**
+ * @brief 检查链表中是否存在ack句柄
+ *
+ * @param client
+ * @param packetType
+ * @param packetId
+ * @param packHandler
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttAckListNodeFind(RyanMqttClient_t *client, enum msgTypes packetType, uint16_t packetId, RyanMqttAckHandler_t **packHandler)
+{
+    RyanList_t *curr, *next;
+    RyanMqttAckHandler_t *ackHandler;
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != packHandler);
+
+    if (RyanListIsEmpty(&client->ackHandlerList))
+        return RyanMqttNoRescourceError;
+
+    RyanListForEachSafe(curr, next, &client->ackHandlerList)
+    {
+        ackHandler = RyanListEntry(curr, RyanMqttAckHandler_t, list);
+
+        // 对于 qos1 和 qos2 的 mqtt 数据包,使用数据包 ID 和类型作为唯一
+        // 标识符,用于确定节点是否已存在并避免重复。
+        if ((packetId == ackHandler->packetId) && (packetType == ackHandler->packetType))
+        {
+            *packHandler = ackHandler;
+            return RyanMqttSuccessError;
+        }
+    }
+
+    return RyanMqttNoRescourceError;
+}
+
+/**
+ * @brief 添加等待ack到链表
+ *
+ * @param client
+ * @param ackHandler
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttAckListAdd(RyanMqttClient_t *client, RyanMqttAckHandler_t *ackHandler)
+{
+    RyanMqttAssert(NULL != client);
+    RyanMqttAssert(NULL != ackHandler);
+    RyanMqttAssert(NULL != ackHandler->msgHandler);
+    RyanMqttAssert(NULL != ackHandler->msgHandler->topic);
+
+    // 将ack节点添加到链表尾部
+    platformCriticalEnter();
+    RyanListAddTail(&ackHandler->list, &client->ackHandlerList);
+    client->ackHandlerCount++;
+    platformCriticalExit();
+
+    if (client->ackHandlerCount >= client->config->ackHandlerCountWarning)
+        RyanMqttEventMachine(client, RyanMqttEventAckCountWarning, (void *)&client->ackHandlerCount);
+
+    return RyanMqttSuccessError;
+}
+
+const char *RyanStrError(RyanMqttError_e state)
+{
+    const char *str = NULL;
+
+    switch (state)
+    {
+    case RyanMqttRecvPacketTimeOutError:
+        str = "读取数据超时";
+        break;
+
+    case RyanMqttParamInvalidError:
+        str = "无效参数";
+        break;
+
+    case RyanSocketFailedError:
+        str = "套接字失败";
+        break;
+
+    case RyanMqttSendPacketError:
+        str = "数据包发送失败";
+        break;
+
+    case RyanMqttSerializePacketError:
+        str = "序列化报文失败";
+        break;
+
+    case RyanMqttDeserializePacketError:
+        str = "反序列化报文失败";
+        break;
+
+    case RyanMqttNoRescourceError:
+        str = "没有资源";
+        break;
+
+    case RyanMqttHaveRescourceError:
+        str = "资源已存在";
+        break;
+
+    case RyanMqttNotConnectError:
+        str = "mqttClient没有连接";
+        break;
+
+    case RyanMqttConnectError:
+        str = "mqttClient已经连接";
+        break;
+
+    case RyanMqttRecvBufToShortError:
+        str = "接收缓冲区不足";
+        break;
+
+    case RyanMqttSendBufToShortError:
+        str = "发送缓冲区不足";
+        break;
+
+    case RyanMqttSocketConnectFailError:
+        str = "socket连接失败";
+        break;
+
+    case RyanMqttNotEnoughMemError:
+        str = "动态内存不足";
+        break;
+
+    case RyanMqttFailedError:
+        str = "mqtt失败, 详细信息请看函数内部";
+        break;
+
+    case RyanMqttSuccessError:
+        str = "mqtt成功, 详细信息请看函数内部";
+        break;
+
+    case RyanMqttConnectRefusedProtocolVersion:
+        str = "mqtt断开连接, 服务端不支持客户端请求的 MQTT 协议级别";
+        break;
+
+    case RyanMqttConnectRefusedIdentifier:
+        str = "mqtt断开连接, 不合格的客户端标识符";
+        break;
+
+    case RyanMqttConnectRefusedServer:
+        str = "mqtt断开连接, 服务端不可用";
+        break;
+
+    case RyanMqttConnectRefusedUsernamePass:
+        str = "mqtt断开连接, 无效的用户名或密码";
+        break;
+
+    case RyanMqttConnectRefusedNotAuthorized:
+        str = "mqtt断开连接, 连接已拒绝,未授权";
+        break;
+
+    case RyanMqttConnectClientInvalid:
+        str = "mqtt断开连接, 客户端处于无效状态";
+        break;
+    case RyanMqttConnectNetWorkFail:
+        str = "mqtt断开连接, 网络错误";
+        break;
+    case RyanMqttConnectDisconnected:
+        str = "mqtt断开连接, mqtt客户端断开连接";
+        break;
+    case RyanMqttKeepaliveTimeout:
+        str = "mqtt断开连接, 心跳超时断开连接";
+        break;
+    case RyanMqttConnectUserDisconnected:
+        str = "mqtt断开连接, 用户手动断开连接";
+        break;
+    case RyanMqttConnectTimeout:
+        str = "mqtt断开连接, connect超时断开";
+        break;
+    }
+
+    return str;
+}

+ 38 - 0
mqttclient/RyanMqttUtile.h

@@ -0,0 +1,38 @@
+
+#ifndef __mqttGlobalFun__
+#define __mqttGlobalFun__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "RyanMqttClient.h"
+    extern void RyanMqttSetClientState(RyanMqttClient_t *client, RyanMqttState_e state);
+    extern RyanMqttState_e RyanMqttGetClientState(RyanMqttClient_t *client);
+    extern RyanMqttError_e RyanMqttIsConnected(RyanMqttClient_t *client);
+
+    extern RyanMqttError_e RyanMqttSendPacket(RyanMqttClient_t *client, char *buf, int32_t length);
+    extern RyanMqttError_e RyanMqttRecvPacket(RyanMqttClient_t *client, char *buf, int32_t length);
+
+    extern RyanMqttError_e RyanMqttAckHandlerCreate(RyanMqttClient_t *client, enum msgTypes packetType, uint16_t packetId, uint16_t packetLen, RyanMqttMsgHandler_t *msgHandler, RyanMqttAckHandler_t **packHandler);
+    extern void RyanMqttAckHandlerDestroy(RyanMqttClient_t *client, RyanMqttAckHandler_t *ackHandler);
+    extern RyanMqttError_e RyanMqttAckListAdd(RyanMqttClient_t *client, RyanMqttAckHandler_t *ackHandler);
+    extern RyanMqttError_e RyanMqttAckListNodeFind(RyanMqttClient_t *client, enum msgTypes packetType, uint16_t packetId, RyanMqttAckHandler_t **packHandler);
+
+    extern RyanMqttError_e RyanMqttMsgHandlerCreate(char *topic, uint16_t topicLen, RyanMqttQos_e qos, RyanMqttMsgHandler_t **pMsgHandler);
+    extern void RyanMqttMsgHandlerDestory(RyanMqttMsgHandler_t *msgHandler);
+    extern RyanMqttError_e RyanMqttMsgHandlerFind(RyanMqttClient_t *client, char *topic, uint16_t topicLen, RyanBool_t topicMatchedFlag, RyanMqttMsgHandler_t **pMsgHandler);
+    extern RyanMqttError_e RyanMqttMsgHandlerAdd(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgHandler);
+
+    extern RyanMqttError_e RyanMqttStringCopy(char **dest, char *rest, uint32_t strLen);
+    extern RyanMqttError_e RyanMqttSetPublishDup(char *headerBuf, uint8_t dup);
+
+    extern void RyanMqttCleanSession(RyanMqttClient_t *client);
+    extern const char *RyanStrError(RyanMqttError_e state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 148 - 0
pahoMqtt/MQTTConnect.h

@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Ian Craggs - add connack return code definitions 
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *    Ian Craggs - fix for issue #64, bit order in connack response
+ *******************************************************************************/
+
+#ifndef MQTTCONNECT_H_
+#define MQTTCONNECT_H_
+
+enum connack_return_codes
+{
+    MQTT_CONNECTION_ACCEPTED = 0,
+    MQTT_UNNACCEPTABLE_PROTOCOL = 1,
+    MQTT_CLIENTID_REJECTED = 2,
+    MQTT_SERVER_UNAVAILABLE = 3,
+    MQTT_BAD_USERNAME_OR_PASSWORD = 4,
+    MQTT_NOT_AUTHORIZED = 5,
+};
+
+#if !defined(DLLImport)
+  #define DLLImport
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+
+typedef union
+{
+	unsigned char all;	/**< all connect flags */
+#if defined(REVERSED)
+	struct
+	{
+		unsigned int username : 1;			/**< 3.1 user name */
+		unsigned int password : 1; 			/**< 3.1 password */
+		unsigned int willRetain : 1;		/**< will retain setting */
+		unsigned int willQoS : 2;				/**< will QoS value */
+		unsigned int will : 1;			    /**< will flag */
+		unsigned int cleansession : 1;	  /**< clean session flag */
+		unsigned int : 1;	  	          /**< unused */
+	} bits;
+#else
+	struct
+	{
+		unsigned int : 1;	     					/**< unused */
+		unsigned int cleansession : 1;	  /**< cleansession flag */
+		unsigned int will : 1;			    /**< will flag */
+		unsigned int willQoS : 2;				/**< will QoS value */
+		unsigned int willRetain : 1;		/**< will retain setting */
+		unsigned int password : 1; 			/**< 3.1 password */
+		unsigned int username : 1;			/**< 3.1 user name */
+	} bits;
+#endif
+} MQTTConnectFlags;	/**< connect flags byte */
+
+
+
+/**
+ * Defines the MQTT "Last Will and Testament" (LWT) settings for
+ * the connect packet.
+ */
+typedef struct
+{
+	/** The eyecatcher for this structure.  must be MQTW. */
+	char struct_id[4];
+	/** The version number of this structure.  Must be 0 */
+	int struct_version;
+	/** The LWT topic to which the LWT message will be published. */
+	MQTTString topicName;
+	/** The LWT payload. */
+	MQTTString message;
+	/**
+      * The retained flag for the LWT message (see MQTTAsync_message.retained).
+      */
+	unsigned char retained;
+	/**
+      * The quality of service setting for the LWT message (see
+      * MQTTAsync_message.qos and @ref qos).
+      */
+	char qos;
+} MQTTPacket_willOptions;
+
+
+#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 }
+
+
+typedef struct
+{
+	/** The eyecatcher for this structure.  must be MQTC. */
+	char struct_id[4];
+	/** The version number of this structure.  Must be 0 */
+	int struct_version;
+	/** Version of MQTT to be used.  3 = 3.1 4 = 3.1.1
+	  */
+	unsigned char MQTTVersion;
+	MQTTString clientID;
+	unsigned short keepAliveInterval;
+	unsigned char cleansession;
+	unsigned char willFlag;
+	MQTTPacket_willOptions will;
+	MQTTString username;
+	MQTTString password;
+} MQTTPacket_connectData;
+
+typedef union
+{
+	unsigned char all;	/**< all connack flags */
+#if defined(REVERSED)
+	struct
+	{
+    unsigned int reserved : 7;	  	    /**< unused */
+		unsigned int sessionpresent : 1;    /**< session present flag */
+	} bits;
+#else
+	struct
+	{
+		unsigned int sessionpresent : 1;    /**< session present flag */
+    unsigned int reserved: 7;	     			/**< unused */
+	} bits;
+#endif
+} MQTTConnackFlags;	/**< connack flags byte */
+
+#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \
+		MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} }
+
+DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options);
+DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent);
+DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen);
+
+DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen);
+DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen);
+
+#endif /* MQTTCONNECT_H_ */

+ 214 - 0
pahoMqtt/MQTTConnectClient.c

@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+/**
+  * Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
+  * @param options the options to be used to build the connect packet
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
+{
+	int len = 0;
+
+	FUNC_ENTRY;
+
+	if (options->MQTTVersion == 3)
+		len = 12; /* variable depending on MQTT or MQIsdp */
+	else if (options->MQTTVersion == 4)
+		len = 10;
+
+	len += MQTTstrlen(options->clientID)+2;
+	if (options->willFlag)
+		len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
+	if (options->username.cstring || options->username.lenstring.data)
+		len += MQTTstrlen(options->username)+2;
+	if (options->password.cstring || options->password.lenstring.data)
+		len += MQTTstrlen(options->password)+2;
+
+	FUNC_EXIT_RC(len);
+	return len;
+}
+
+
+/**
+  * Serializes the connect options into the buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param len the length in bytes of the supplied buffer
+  * @param options the options to be used to build the connect packet
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	MQTTConnectFlags flags = {0};
+	int len = 0;
+	int rc = -1;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = CONNECT;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
+
+	if (options->MQTTVersion == 4)
+	{
+		writeCString(&ptr, "MQTT");
+		writeChar(&ptr, (char) 4);
+	}
+	else
+	{
+		writeCString(&ptr, "MQIsdp");
+		writeChar(&ptr, (char) 3);
+	}
+
+	flags.all = 0;
+	flags.bits.cleansession = options->cleansession;
+	flags.bits.will = (options->willFlag) ? 1 : 0;
+	if (flags.bits.will)
+	{
+		flags.bits.willQoS = options->will.qos;
+		flags.bits.willRetain = options->will.retained;
+	}
+
+	if (options->username.cstring || options->username.lenstring.data)
+		flags.bits.username = 1;
+	if (options->password.cstring || options->password.lenstring.data)
+		flags.bits.password = 1;
+
+	writeChar(&ptr, flags.all);
+	writeInt(&ptr, options->keepAliveInterval);
+	writeMQTTString(&ptr, options->clientID);
+	if (options->willFlag)
+	{
+		writeMQTTString(&ptr, options->will.topicName);
+		writeMQTTString(&ptr, options->will.message);
+	}
+	if (flags.bits.username)
+		writeMQTTString(&ptr, options->username);
+	if (flags.bits.password)
+		writeMQTTString(&ptr, options->password);
+
+	rc = ptr - buf;
+
+	exit: FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Deserializes the supplied (wire) buffer into connack data - return code
+  * @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
+  * @param connack_rc returned integer value of the connack return code
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param len the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+	MQTTConnackFlags flags = {0};
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != CONNACK)
+		goto exit;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+	if (enddata - curdata < 2)
+		goto exit;
+
+	flags.all = readChar(&curdata);
+	*sessionPresent = flags.bits.sessionpresent;
+	*connack_rc = readChar(&curdata);
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
+  * @param packettype the message type
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
+{
+	MQTTHeader header = {0};
+	int rc = -1;
+	unsigned char *ptr = buf;
+
+	FUNC_ENTRY;
+	if (buflen < 2)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.byte = 0;
+	header.bits.type = packettype;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_disconnect(unsigned char* buf, int buflen)
+{
+	return MQTTSerialize_zero(buf, buflen, DISCONNECT);
+}
+
+
+/**
+  * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer, to avoid overruns
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_pingreq(unsigned char* buf, int buflen)
+{
+	return MQTTSerialize_zero(buf, buflen, PINGREQ);
+}

+ 148 - 0
pahoMqtt/MQTTConnectServer.c

@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+#include <string.h>
+
+#define min(a, b) ((a < b) ? a : b)
+
+
+/**
+  * Validates MQTT protocol name and version combinations
+  * @param protocol the MQTT protocol name as an MQTTString
+  * @param version the MQTT protocol version number, as in the connect packet
+  * @return correct MQTT combination?  1 is true, 0 is false
+  */
+int MQTTPacket_checkVersion(MQTTString* protocol, int version)
+{
+	int rc = 0;
+
+	if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp",
+			min(6, protocol->lenstring.len)) == 0)
+		rc = 1;
+	else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT",
+			min(4, protocol->lenstring.len)) == 0)
+		rc = 1;
+	return rc;
+}
+
+
+/**
+  * Deserializes the supplied (wire) buffer into connect data structure
+  * @param data the connect data structure to be filled out
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param len the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len)
+{
+	MQTTHeader header = {0};
+	MQTTConnectFlags flags = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = &buf[len];
+	int rc = 0;
+	MQTTString Protocol;
+	int version;
+	int mylen = 0;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != CONNECT)
+		goto exit;
+
+	curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */
+
+	if (!readMQTTLenString(&Protocol, &curdata, enddata) ||
+		enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
+		goto exit;
+
+	version = (int)readChar(&curdata); /* Protocol version */
+	/* If we don't recognize the protocol version, we don't parse the connect packet on the
+	 * basis that we don't know what the format will be.
+	 */
+	if (MQTTPacket_checkVersion(&Protocol, version))
+	{
+		flags.all = readChar(&curdata);
+		data->cleansession = flags.bits.cleansession;
+		data->keepAliveInterval = readInt(&curdata);
+		if (!readMQTTLenString(&data->clientID, &curdata, enddata))
+			goto exit;
+		data->willFlag = flags.bits.will;
+		if (flags.bits.will)
+		{
+			data->will.qos = flags.bits.willQoS;
+			data->will.retained = flags.bits.willRetain;
+			if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) ||
+				  !readMQTTLenString(&data->will.message, &curdata, enddata))
+				goto exit;
+		}
+		if (flags.bits.username)
+		{
+			if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata))
+				goto exit; /* username flag set, but no username supplied - invalid */
+			if (flags.bits.password &&
+				(enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata)))
+				goto exit; /* password flag set, but no password supplied - invalid */
+		}
+		else if (flags.bits.password)
+			goto exit; /* password flag set without username - invalid */
+		rc = 1;
+	}
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes the connack packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param connack_rc the integer connack return code to be used 
+  * @param sessionPresent the MQTT 3.1.1 sessionPresent flag
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent)
+{
+	MQTTHeader header = {0};
+	int rc = 0;
+	unsigned char *ptr = buf;
+	MQTTConnackFlags flags = {0};
+
+	FUNC_ENTRY;
+	if (buflen < 2)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.byte = 0;
+	header.bits.type = CONNACK;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
+
+	flags.all = 0;
+	flags.bits.sessionpresent = sessionPresent;
+	writeChar(&ptr, flags.all); 
+	writeChar(&ptr, connack_rc);
+
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+

+ 107 - 0
pahoMqtt/MQTTDeserializePublish.c

@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+#include <string.h>
+
+#define min(a, b) ((a < b) ? 1 : 0)
+
+/**
+  * Deserializes the supplied (wire) buffer into publish data
+  * @param dup returned integer - the MQTT dup flag
+  * @param qos returned integer - the MQTT QoS value
+  * @param retained returned integer - the MQTT retained flag
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param topicName returned MQTTString - the MQTT topic in the publish
+  * @param payload returned byte buffer - the MQTT publish payload
+  * @param payloadlen returned integer - the length of the MQTT payload
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success
+  */
+int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
+		unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen = 0;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != PUBLISH)
+		goto exit;
+	*dup = header.bits.dup;
+	*qos = header.bits.qos;
+	*retained = header.bits.retain;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+
+	if (!readMQTTLenString(topicName, &curdata, enddata) ||
+		enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */
+		goto exit;
+
+	if (*qos > 0)
+		*packetid = readInt(&curdata);
+
+	*payloadlen = enddata - curdata;
+	*payload = curdata;
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+
+/**
+  * Deserializes the supplied (wire) buffer into an ack
+  * @param packettype returned integer - the MQTT packet type
+  * @param dup returned integer - the MQTT dup flag
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	*dup = header.bits.dup;
+	*packettype = header.bits.type;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+
+	if (enddata - curdata < 2)
+		goto exit;
+	*packetid = readInt(&curdata);
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+

+ 262 - 0
pahoMqtt/MQTTFormat.c

@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+
+#include <string.h>
+
+
+const char* MQTTPacket_names[] =
+{
+	"RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
+	"PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
+	"PINGREQ", "PINGRESP", "DISCONNECT"
+};
+
+
+const char* MQTTPacket_getName(unsigned short packetid)
+{
+	return MQTTPacket_names[packetid];
+}
+
+
+int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data)
+{
+	int strindex = 0;
+
+	strindex = snprintf(strbuf, strbuflen,
+			"CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d",
+			(int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data,
+			(int)data->cleansession, data->keepAliveInterval);
+	if (data->willFlag)
+		strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
+				", will QoS %d, will retain %d, will topic %.*s, will message %.*s",
+				data->will.qos, data->will.retained,
+				data->will.topicName.lenstring.len, data->will.topicName.lenstring.data,
+				data->will.message.lenstring.len, data->will.message.lenstring.data);
+	if (data->username.lenstring.data && data->username.lenstring.len > 0)
+		strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
+				", user name %.*s", data->username.lenstring.len, data->username.lenstring.data);
+	if (data->password.lenstring.data && data->password.lenstring.len > 0)
+		strindex += snprintf(&strbuf[strindex], strbuflen - strindex,
+				", password %.*s", data->password.lenstring.len, data->password.lenstring.data);
+	return strindex;
+}
+
+
+int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent)
+{
+	int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc);
+	return strindex;
+}
+
+
+int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
+		unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen)
+{
+	int strindex = snprintf(strbuf, strbuflen,
+				"PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s",
+				dup, qos, retained, packetid,
+				(topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data,
+				payloadlen, (payloadlen < 20) ? payloadlen : 20, payload);
+	return strindex;
+}
+
+
+int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
+{
+	int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid);
+	if (dup)
+		strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup);
+	return strindex;
+}
+
+
+int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
+		MQTTString topicFilters[], int requestedQoSs[])
+{
+	return snprintf(strbuf, strbuflen,
+		"SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d",
+		dup, packetid, count,
+		topicFilters[0].lenstring.len, topicFilters[0].lenstring.data,
+		requestedQoSs[0]);
+}
+
+
+int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs)
+{
+	return snprintf(strbuf, strbuflen,
+		"SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]);
+}
+
+
+int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[])
+{
+	return snprintf(strbuf, strbuflen,
+					"UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s",
+					dup, packetid, count,
+					topicFilters[0].lenstring.len, topicFilters[0].lenstring.data);
+}
+
+
+#if defined(MQTT_CLIENT)
+char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
+{
+	int index = 0;
+	int rem_length = 0;
+	MQTTHeader header = {0};
+	int strindex = 0;
+
+	header.byte = buf[index++];
+	index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
+
+	switch (header.bits.type)
+	{
+
+	case CONNACK:
+	{
+		unsigned char sessionPresent, connack_rc;
+		if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1)
+			strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent);
+	}
+	break;
+	case PUBLISH:
+	{
+		unsigned char dup, retained, *payload;
+		unsigned short packetid;
+		int qos, payloadlen;
+		MQTTString topicName = MQTTString_initializer;
+		if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
+				&payload, &payloadlen, buf, buflen) == 1)
+			strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
+					topicName, payload, payloadlen);
+	}
+	break;
+	case PUBACK:
+	case PUBREC:
+	case PUBREL:
+	case PUBCOMP:
+	{
+		unsigned char packettype, dup;
+		unsigned short packetid;
+		if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
+			strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
+	}
+	break;
+	case SUBACK:
+	{
+		unsigned short packetid;
+		int maxcount = 1, count = 0;
+		int grantedQoSs[1];
+		if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1)
+			strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs);
+	}
+	break;
+	case UNSUBACK:
+	{
+		unsigned short packetid;
+		if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1)
+			strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid);
+	}
+	break;
+	case PINGREQ:
+	case PINGRESP:
+	case DISCONNECT:
+		strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
+		break;
+	}
+	return strbuf;
+}
+#endif
+
+#if defined(MQTT_SERVER)
+char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen)
+{
+	int index = 0;
+	int rem_length = 0;
+	MQTTHeader header = {0};
+	int strindex = 0;
+
+	header.byte = buf[index++];
+	index += MQTTPacket_decodeBuf(&buf[index], &rem_length);
+
+	switch (header.bits.type)
+	{
+	case CONNECT:
+	{
+		MQTTPacket_connectData data;
+		int rc;
+		if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1)
+			strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data);
+	}
+	break;
+	case PUBLISH:
+	{
+		unsigned char dup, retained, *payload;
+		unsigned short packetid;
+		int qos, payloadlen;
+		MQTTString topicName = MQTTString_initializer;
+		if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName,
+				&payload, &payloadlen, buf, buflen) == 1)
+			strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid,
+					topicName, payload, payloadlen);
+	}
+	break;
+	case PUBACK:
+	case PUBREC:
+	case PUBREL:
+	case PUBCOMP:
+	{
+		unsigned char packettype, dup;
+		unsigned short packetid;
+		if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1)
+			strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid);
+	}
+	break;
+	case SUBSCRIBE:
+	{
+		unsigned char dup;
+		unsigned short packetid;
+		int maxcount = 1, count = 0;
+		MQTTString topicFilters[1];
+		int requestedQoSs[1];
+		if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count,
+				topicFilters, requestedQoSs, buf, buflen) == 1)
+			strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);;
+	}
+	break;
+	case UNSUBSCRIBE:
+	{
+		unsigned char dup;
+		unsigned short packetid;
+		int maxcount = 1, count = 0;
+		MQTTString topicFilters[1];
+		if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1)
+			strindex =  MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters);
+	}
+	break;
+	case PINGREQ:
+	case PINGRESP:
+	case DISCONNECT:
+		strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]);
+		break;
+	}
+	strbuf[strbuflen] = '\0';
+	return strbuf;
+}
+#endif

+ 37 - 0
pahoMqtt/MQTTFormat.h

@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#if !defined(MQTTFORMAT_H)
+#define MQTTFORMAT_H
+
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+
+const char* MQTTPacket_getName(unsigned short packetid);
+int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data);
+int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent);
+int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained,
+		unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen);
+int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid);
+int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count,
+		MQTTString topicFilters[], int requestedQoSs[]);
+int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs);
+int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[]);
+char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
+char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen);
+
+#endif

+ 412 - 0
pahoMqtt/MQTTPacket.c

@@ -0,0 +1,412 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Sergio R. Caprile - non-blocking packet read functions for stream transport
+ *******************************************************************************/
+
+#include "StackTrace.h"
+#include "MQTTPacket.h"
+
+#include <string.h>
+
+/**
+ * Encodes the message length according to the MQTT algorithm
+ * @param buf the buffer into which the encoded data is written
+ * @param length the length to be encoded
+ * @return the number of bytes written to buffer
+ */
+int MQTTPacket_encode(unsigned char* buf, int length)
+{
+	int rc = 0;
+
+	FUNC_ENTRY;
+	do
+	{
+		char d = length % 128;
+		length /= 128;
+		/* if there are more digits to encode, set the top bit of this digit */
+		if (length > 0)
+			d |= 0x80;
+		buf[rc++] = d;
+	} while (length > 0);
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+ * Decodes the message length according to the MQTT algorithm
+ * @param getcharfn pointer to function to read the next character from the data source
+ * @param value the decoded length returned
+ * @return the number of bytes read from the socket
+ */
+int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value)
+{
+	unsigned char c;
+	int multiplier = 1;
+	int len = 0;
+#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
+
+	FUNC_ENTRY;
+	*value = 0;
+	do
+	{
+		int rc = MQTTPACKET_READ_ERROR;
+
+		if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+		{
+			rc = MQTTPACKET_READ_ERROR;	/* bad data */
+			goto exit;
+		}
+		rc = (*getcharfn)(&c, 1);
+		if (rc != 1)
+			goto exit;
+		*value += (c & 127) * multiplier;
+		multiplier *= 128;
+	} while ((c & 128) != 0);
+exit:
+	FUNC_EXIT_RC(len);
+	return len;
+}
+
+
+int MQTTPacket_len(int rem_len)
+{
+	rem_len += 1; /* header byte */
+
+	/* now remaining_length field */
+	if (rem_len < 128)
+		rem_len += 1;
+	else if (rem_len < 16384)
+		rem_len += 2;
+	else if (rem_len < 2097151)
+		rem_len += 3;
+	else
+		rem_len += 4;
+	return rem_len;
+}
+
+
+static unsigned char* bufptr;
+
+int bufchar(unsigned char* c, int count)
+{
+	int i;
+
+	for (i = 0; i < count; ++i)
+		*c = *bufptr++;
+	return count;
+}
+
+
+int MQTTPacket_decodeBuf(unsigned char* buf, int* value)
+{
+	bufptr = buf;
+	return MQTTPacket_decode(bufchar, value);
+}
+
+
+/**
+ * Calculates an integer from two bytes read from the input buffer
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the integer value calculated
+ */
+int readInt(unsigned char** pptr)
+{
+	unsigned char* ptr = *pptr;
+	int len = 256*(*ptr) + (*(ptr+1));
+	*pptr += 2;
+	return len;
+}
+
+
+/**
+ * Reads one character from the input buffer.
+ * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
+ * @return the character read
+ */
+char readChar(unsigned char** pptr)
+{
+	char c = **pptr;
+	(*pptr)++;
+	return c;
+}
+
+
+/**
+ * Writes one character to an output buffer.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param c the character to write
+ */
+void writeChar(unsigned char** pptr, char c)
+{
+	**pptr = c;
+	(*pptr)++;
+}
+
+
+/**
+ * Writes an integer as 2 bytes to an output buffer.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param anInt the integer to write
+ */
+void writeInt(unsigned char** pptr, int anInt)
+{
+	**pptr = (unsigned char)(anInt / 256);
+	(*pptr)++;
+	**pptr = (unsigned char)(anInt % 256);
+	(*pptr)++;
+}
+
+
+/**
+ * Writes a "UTF" string to an output buffer.  Converts C string to length-delimited.
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param string the C string to write
+ */
+void writeCString(unsigned char** pptr, const char* string)
+{
+	int len = strlen(string);
+	writeInt(pptr, len);
+	memcpy(*pptr, string, len);
+	*pptr += len;
+}
+
+
+int getLenStringLen(char* ptr)
+{
+	int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
+	return len;
+}
+
+
+void writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
+{
+	if (mqttstring.lenstring.len > 0)
+	{
+		writeInt(pptr, mqttstring.lenstring.len);
+		memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
+		*pptr += mqttstring.lenstring.len;
+	}
+	else if (mqttstring.cstring)
+		writeCString(pptr, mqttstring.cstring);
+	else
+		writeInt(pptr, 0);
+}
+
+
+/**
+ * @param mqttstring the MQTTString structure into which the data is to be read
+ * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
+ * @param enddata pointer to the end of the data: do not read beyond
+ * @return 1 if successful, 0 if not
+ */
+int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata)
+{
+	int rc = 0;
+
+	FUNC_ENTRY;
+	/* the first two bytes are the length of the string */
+	if (enddata - (*pptr) > 1) /* enough length to read the integer? */
+	{
+		mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */
+		if (&(*pptr)[mqttstring->lenstring.len] <= enddata)
+		{
+			mqttstring->lenstring.data = (char*)*pptr;
+			*pptr += mqttstring->lenstring.len;
+			rc = 1;
+		}
+	}
+	mqttstring->cstring = NULL;
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+ * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string
+ * @param mqttstring the string to return the length of
+ * @return the length of the string
+ */
+int MQTTstrlen(MQTTString mqttstring)
+{
+	int rc = 0;
+
+	if (mqttstring.cstring)
+		rc = strlen(mqttstring.cstring);
+	else
+		rc = mqttstring.lenstring.len;
+	return rc;
+}
+
+
+/**
+ * Compares an MQTTString to a C string
+ * @param a the MQTTString to compare
+ * @param bptr the C string to compare
+ * @return boolean - equal or not
+ */
+int MQTTPacket_equals(MQTTString* a, char* bptr)
+{
+	int alen = 0,
+		blen = 0;
+	char *aptr;
+	
+	if (a->cstring)
+	{
+		aptr = a->cstring;
+		alen = strlen(a->cstring);
+	}
+	else
+	{
+		aptr = a->lenstring.data;
+		alen = a->lenstring.len;
+	}
+	blen = strlen(bptr);
+	
+	return (alen == blen) && (strncmp(aptr, bptr, alen) == 0);
+}
+
+
+/**
+ * Helper function to read packet data from some source into a buffer
+ * @param buf the buffer into which the packet will be serialized
+ * @param buflen the length in bytes of the supplied buffer
+ * @param getfn pointer to a function which will read any number of bytes from the needed source
+ * @return integer MQTT packet type, or -1 on error
+ * @note  the whole message must fit into the caller's buffer
+ */
+int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int))
+{
+	int rc = -1;
+	MQTTHeader header = {0};
+	int len = 0;
+	int rem_len = 0;
+
+	/* 1. read the header byte.  This has the packet type in it */
+	if ((*getfn)(buf, 1) != 1)
+		goto exit;
+
+	len = 1;
+	/* 2. read the remaining length.  This is variable in itself */
+	MQTTPacket_decode(getfn, &rem_len);
+	len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */
+
+	/* 3. read the rest of the buffer using a callback to supply the rest of the data */
+	if((rem_len + len) > buflen)
+		goto exit;
+	if (rem_len && ((*getfn)(buf + len, rem_len) != rem_len))
+		goto exit;
+
+	header.byte = buf[0];
+	rc = header.bits.type;
+exit:
+	return rc;
+}
+
+/**
+ * Decodes the message length according to the MQTT algorithm, non-blocking
+ * @param trp pointer to a transport structure holding what is needed to solve getting data from it
+ * @param value the decoded length returned
+ * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error
+ */
+static int MQTTPacket_decodenb(MQTTTransport *trp)
+{
+	unsigned char c;
+	int rc = MQTTPACKET_READ_ERROR;
+
+	FUNC_ENTRY;
+	if(trp->len == 0){		/* initialize on first call */
+		trp->multiplier = 1;
+		trp->rem_len = 0;
+	}
+	do {
+		int frc;
+		if (trp->len >= MAX_NO_OF_REMAINING_LENGTH_BYTES)
+			goto exit;
+		if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1)
+			goto exit;
+		if (frc == 0){
+			rc = 0;
+			goto exit;
+		}
+		++(trp->len);
+		trp->rem_len += (c & 127) * trp->multiplier;
+		trp->multiplier *= 128;
+	} while ((c & 128) != 0);
+	rc = trp->len;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+/**
+ * Helper function to read packet data from some source into a buffer, non-blocking
+ * @param buf the buffer into which the packet will be serialized
+ * @param buflen the length in bytes of the supplied buffer
+ * @param trp pointer to a transport structure holding what is needed to solve getting data from it
+ * @return integer MQTT packet type, 0 for call again, or -1 on error
+ * @note  the whole message must fit into the caller's buffer
+ */
+int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp)
+{
+	int rc = -1, frc;
+	MQTTHeader header = {0};
+
+	switch(trp->state){
+	default:
+		trp->state = 0;
+		/*FALLTHROUGH*/
+	case 0:
+		/* read the header byte.  This has the packet type in it */
+		if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1)
+			goto exit;
+		if (frc == 0)
+			return 0;
+		trp->len = 0;
+		++trp->state;
+		/*FALLTHROUGH*/
+		/* read the remaining length.  This is variable in itself */
+	case 1:
+		if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR)
+			goto exit;
+		if(frc == 0)
+			return 0;
+		trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */
+		if((trp->rem_len + trp->len) > buflen)
+			goto exit;
+		++trp->state;
+		/*FALLTHROUGH*/
+	case 2:
+		if(trp->rem_len){
+			/* read the rest of the buffer using a callback to supply the rest of the data */
+			if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1)
+				goto exit;
+			if (frc == 0)
+				return 0;
+			trp->rem_len -= frc;
+			trp->len += frc;
+			if(trp->rem_len)
+				return 0;
+		}
+		header.byte = buf[0];
+		rc = header.bits.type;
+		break;
+	}
+
+exit:
+	trp->state = 0;
+	return rc;
+}
+

+ 133 - 0
pahoMqtt/MQTTPacket.h

@@ -0,0 +1,133 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTPACKET_H_
+#define MQTTPACKET_H_
+
+#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */
+extern "C" {
+#endif
+
+#if defined(WIN32_DLL) || defined(WIN64_DLL)
+  #define DLLImport __declspec(dllimport)
+  #define DLLExport __declspec(dllexport)
+#elif defined(LINUX_SO)
+  #define DLLImport extern
+  #define DLLExport  __attribute__ ((visibility ("default")))
+#else
+  #define DLLImport
+  #define DLLExport  
+#endif
+
+enum errors
+{
+	MQTTPACKET_BUFFER_TOO_SHORT = -2,
+	MQTTPACKET_READ_ERROR = -1,
+	MQTTPACKET_READ_COMPLETE
+};
+
+enum msgTypes
+{
+	CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
+	PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
+	PINGREQ, PINGRESP, DISCONNECT
+};
+
+/**
+ * Bitfields for the MQTT header byte.
+ */
+typedef union
+{
+	unsigned char byte;	                /**< the whole byte */
+#if defined(REVERSED)
+	struct
+	{
+		unsigned int type : 4;			/**< message type nibble */
+		unsigned int dup : 1;				/**< DUP flag bit */
+		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */
+		unsigned int retain : 1;		/**< retained flag bit */
+	} bits;
+#else
+	struct
+	{
+		unsigned int retain : 1;		/**< retained flag bit */
+		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */
+		unsigned int dup : 1;				/**< DUP flag bit */
+		unsigned int type : 4;			/**< message type nibble */
+	} bits;
+#endif
+} MQTTHeader;
+
+typedef struct
+{
+	int len;
+	char* data;
+} MQTTLenString;
+
+typedef struct
+{
+	char* cstring;
+	MQTTLenString lenstring;
+} MQTTString;
+
+#define MQTTString_initializer {NULL, {0, NULL}}
+
+int MQTTstrlen(MQTTString mqttstring);
+
+#include "MQTTConnect.h"
+#include "MQTTPublish.h"
+#include "MQTTSubscribe.h"
+#include "MQTTUnsubscribe.h"
+#include "MQTTFormat.h"
+
+DLLExport int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid);
+DLLExport int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen);
+
+int MQTTPacket_len(int rem_len);
+DLLExport int MQTTPacket_equals(MQTTString* a, char* b);
+
+DLLExport int MQTTPacket_encode(unsigned char* buf, int length);
+int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value);
+int MQTTPacket_decodeBuf(unsigned char* buf, int* value);
+
+int readInt(unsigned char** pptr);
+char readChar(unsigned char** pptr);
+void writeChar(unsigned char** pptr, char c);
+void writeInt(unsigned char** pptr, int anInt);
+int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata);
+void writeCString(unsigned char** pptr, const char* string);
+void writeMQTTString(unsigned char** pptr, MQTTString mqttstring);
+
+DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int));
+
+typedef struct {
+	int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */
+	void *sck;	/* pointer to whatever the system may use to identify the transport */
+	int multiplier;
+	int rem_len;
+	int len;
+	char state;
+}MQTTTransport;
+
+int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp);
+
+#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */
+}
+#endif
+
+
+#endif /* MQTTPACKET_H_ */

+ 38 - 0
pahoMqtt/MQTTPublish.h

@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTPUBLISH_H_
+#define MQTTPUBLISH_H_
+
+#if !defined(DLLImport)
+  #define DLLImport 
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
+		MQTTString topicName, unsigned char* payload, int payloadlen);
+
+DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName,
+		unsigned char** payload, int* payloadlen, unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid);
+DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid);
+DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid);
+
+#endif /* MQTTPUBLISH_H_ */

+ 169 - 0
pahoMqtt/MQTTSerializePublish.c

@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144
+ *******************************************************************************/
+
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+
+/**
+  * Determines the length of the MQTT publish packet that would be produced using the supplied parameters
+  * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0)
+  * @param topicName the topic name to be used in the publish  
+  * @param payloadlen the length of the payload to be sent
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen)
+{
+	int len = 0;
+
+	len += 2 + MQTTstrlen(topicName) + payloadlen;
+	if (qos > 0)
+		len += 2; /* packetid */
+	return len;
+}
+
+
+/**
+  * Serializes the supplied publish data into the supplied buffer, ready for sending
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param dup integer - the MQTT dup flag
+  * @param qos integer - the MQTT QoS value
+  * @param retained integer - the MQTT retained flag
+  * @param packetid integer - the MQTT packet identifier
+  * @param topicName MQTTString - the MQTT topic in the publish
+  * @param payload byte buffer - the MQTT publish payload
+  * @param payloadlen integer - the length of the MQTT payload
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid,
+		MQTTString topicName, unsigned char* payload, int payloadlen)
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	int rem_len = 0;
+	int rc = 0;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.bits.type = PUBLISH;
+	header.bits.dup = dup;
+	header.bits.qos = qos;
+	header.bits.retain = retained;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
+
+	writeMQTTString(&ptr, topicName);
+
+	if (qos > 0)
+		writeInt(&ptr, packetid);
+
+	memcpy(ptr, payload, payloadlen);
+	ptr += payloadlen;
+
+	rc = ptr - buf;
+
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+
+/**
+  * Serializes the ack packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param type the MQTT packet type
+  * @param dup the MQTT dup flag
+  * @param packetid the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid)
+{
+	MQTTHeader header = {0};
+	int rc = 0;
+	unsigned char *ptr = buf;
+
+	FUNC_ENTRY;
+	if (buflen < 4)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.bits.type = packettype;
+	header.bits.dup = dup;
+	header.bits.qos = (packettype == PUBREL) ? 1 : 0;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
+	writeInt(&ptr, packetid);
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes a puback packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param packetid integer - the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid)
+{
+	return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid);
+}
+
+
+/**
+  * Serializes a pubrel packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param dup integer - the MQTT dup flag
+  * @param packetid integer - the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid)
+{
+	return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid);
+}
+
+
+/**
+  * Serializes a pubrel packet into the supplied buffer.
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param packetid integer - the MQTT packet identifier
+  * @return serialized length, or error if 0
+  */
+int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid)
+{
+	return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid);
+}
+
+

+ 46 - 0
pahoMqtt/MQTTSubscribe.h

@@ -0,0 +1,46 @@
+/*
+ * @Author: jiejie
+ * @Github: https://github.com/jiejieTop
+ * @Date: 2019-12-09 20:15:32
+ * @LastEditTime: 2019-12-20 20:37:31
+ * @Description: the code belongs to jiejie, please keep the author information and source code according to the license.
+ */
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTSUBSCRIBE_H_
+#define MQTTSUBSCRIBE_H_
+
+#if !defined(DLLImport)
+  #define DLLImport 
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[], int requestedQoSs[]);
+
+DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid,
+		int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs);
+
+DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len);
+
+
+#endif /* MQTTSUBSCRIBE_H_ */

+ 137 - 0
pahoMqtt/MQTTSubscribeClient.c

@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+/**
+  * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters
+  * @param count the number of topic filter strings in topicFilters
+  * @param topicFilters the array of topic filter strings to be used in the publish
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[])
+{
+	int i;
+	int len = 2; /* packetid */
+
+	for (i = 0; i < count; ++i)
+		len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */
+	return len;
+}
+
+
+/**
+  * Serializes the supplied subscribe data into the supplied buffer, ready for sending
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied bufferr
+  * @param dup integer - the MQTT dup flag
+  * @param packetid integer - the MQTT packet identifier
+  * @param count - number of members in the topicFilters and reqQos arrays
+  * @param topicFilters - array of topic filter names
+  * @param requestedQoSs - array of requested QoS
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count,
+		MQTTString topicFilters[], int requestedQoSs[])
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	int rem_len = 0;
+	int rc = 0;
+	int i = 0;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = SUBSCRIBE;
+	header.bits.dup = dup;
+	header.bits.qos = 1;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
+
+	writeInt(&ptr, packetid);
+
+	for (i = 0; i < count; ++i)
+	{
+		writeMQTTString(&ptr, topicFilters[i]);
+		writeChar(&ptr, requestedQoSs[i]);
+	}
+
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+
+/**
+  * Deserializes the supplied (wire) buffer into suback data
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param maxcount - the maximum number of members allowed in the grantedQoSs array
+  * @param count returned integer - number of members in the grantedQoSs array
+  * @param grantedQoSs returned array of integers - the granted qualities of service
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != SUBACK)
+		goto exit;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+	if (enddata - curdata < 2)
+		goto exit;
+
+	*packetid = readInt(&curdata);
+
+	*count = 0;
+	while (curdata < enddata)
+	{
+		if (*count > maxcount)
+		{
+			rc = -1;
+			goto exit;
+		}
+		grantedQoSs[(*count)++] = readChar(&curdata);
+	}
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+

+ 112 - 0
pahoMqtt/MQTTSubscribeServer.c

@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+
+/**
+  * Deserializes the supplied (wire) buffer into subscribe data
+  * @param dup integer returned - the MQTT dup flag
+  * @param packetid integer returned - the MQTT packet identifier
+  * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
+  * @param count - number of members in the topicFilters and requestedQoSs arrays
+  * @param topicFilters - array of topic filter names
+  * @param requestedQoSs - array of requested QoS
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
+	int requestedQoSs[], unsigned char* buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = -1;
+	int mylen = 0;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != SUBSCRIBE)
+		goto exit;
+	*dup = header.bits.dup;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+
+	*packetid = readInt(&curdata);
+
+	*count = 0;
+	while (curdata < enddata)
+	{
+		if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
+			goto exit;
+		if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */
+			goto exit;
+		requestedQoSs[*count] = readChar(&curdata);
+		(*count)++;
+	}
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes the supplied suback data into the supplied buffer, ready for sending
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param packetid integer - the MQTT packet identifier
+  * @param count - number of members in the grantedQoSs array
+  * @param grantedQoSs - array of granted QoS
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs)
+{
+	MQTTHeader header = {0};
+	int rc = -1;
+	unsigned char *ptr = buf;
+	int i;
+
+	FUNC_ENTRY;
+	if (buflen < 2 + count)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.byte = 0;
+	header.bits.type = SUBACK;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */
+
+	writeInt(&ptr, packetid);
+
+	for (i = 0; i < count; ++i)
+		writeChar(&ptr, grantedQoSs[i]);
+
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+

+ 38 - 0
pahoMqtt/MQTTUnsubscribe.h

@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Xiang Rong - 442039 Add makefile to Embedded C client
+ *******************************************************************************/
+
+#ifndef MQTTUNSUBSCRIBE_H_
+#define MQTTUNSUBSCRIBE_H_
+
+#if !defined(DLLImport)
+  #define DLLImport 
+#endif
+#if !defined(DLLExport)
+  #define DLLExport
+#endif
+
+DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[]);
+
+DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[],
+		unsigned char* buf, int len);
+
+DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid);
+
+DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len);
+
+#endif /* MQTTUNSUBSCRIBE_H_ */

+ 106 - 0
pahoMqtt/MQTTUnsubscribeClient.c

@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+/**
+  * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters
+  * @param count the number of topic filter strings in topicFilters
+  * @param topicFilters the array of topic filter strings to be used in the publish
+  * @return the length of buffer needed to contain the serialized version of the packet
+  */
+int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[])
+{
+	int i;
+	int len = 2; /* packetid */
+
+	for (i = 0; i < count; ++i)
+		len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/
+	return len;
+}
+
+
+/**
+  * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @param dup integer - the MQTT dup flag
+  * @param packetid integer - the MQTT packet identifier
+  * @param count - number of members in the topicFilters array
+  * @param topicFilters - array of topic filter names
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid,
+		int count, MQTTString topicFilters[])
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = {0};
+	int rem_len = 0;
+	int rc = -1;
+	int i = 0;
+
+	FUNC_ENTRY;
+	if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = UNSUBSCRIBE;
+	header.bits.dup = dup;
+	header.bits.qos = 1;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */;
+
+	writeInt(&ptr, packetid);
+
+	for (i = 0; i < count; ++i)
+		writeMQTTString(&ptr, topicFilters[i]);
+
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Deserializes the supplied (wire) buffer into unsuback data
+  * @param packetid returned integer - the MQTT packet identifier
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return error code.  1 is success, 0 is failure
+  */
+int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen)
+{
+	unsigned char type = 0;
+	unsigned char dup = 0;
+	int rc = 0;
+
+	FUNC_ENTRY;
+	rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen);
+	if (type == UNSUBACK)
+		rc = 1;
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+

+ 102 - 0
pahoMqtt/MQTTUnsubscribeServer.c

@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#include "MQTTPacket.h"
+#include "StackTrace.h"
+
+#include <string.h>
+
+
+/**
+  * Deserializes the supplied (wire) buffer into unsubscribe data
+  * @param dup integer returned - the MQTT dup flag
+  * @param packetid integer returned - the MQTT packet identifier
+  * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays
+  * @param count - number of members in the topicFilters and requestedQoSs arrays
+  * @param topicFilters - array of topic filter names
+  * @param buf the raw buffer data, of the correct length determined by the remaining length field
+  * @param buflen the length in bytes of the data in the supplied buffer
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[],
+		unsigned char* buf, int len)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen = 0;
+
+	FUNC_ENTRY;
+	header.byte = readChar(&curdata);
+	if (header.bits.type != UNSUBSCRIBE)
+		goto exit;
+	*dup = header.bits.dup;
+
+	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+
+	*packetid = readInt(&curdata);
+
+	*count = 0;
+	while (curdata < enddata)
+	{
+		if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata))
+			goto exit;
+		(*count)++;
+	}
+
+	rc = 1;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+
+/**
+  * Serializes the supplied unsuback data into the supplied buffer, ready for sending
+  * @param buf the buffer into which the packet will be serialized
+  * @param buflen the length in bytes of the supplied buffer
+  * @param packetid integer - the MQTT packet identifier
+  * @return the length of the serialized data.  <= 0 indicates error
+  */
+int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid)
+{
+	MQTTHeader header = {0};
+	int rc = 0;
+	unsigned char *ptr = buf;
+
+	FUNC_ENTRY;
+	if (buflen < 2)
+	{
+		rc = MQTTPACKET_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.byte = 0;
+	header.bits.type = UNSUBACK;
+	writeChar(&ptr, header.byte); /* write header */
+
+	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */
+
+	writeInt(&ptr, packetid);
+
+	rc = ptr - buf;
+exit:
+	FUNC_EXIT_RC(rc);
+	return rc;
+}
+
+

+ 78 - 0
pahoMqtt/StackTrace.h

@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Ian Craggs - fix for bug #434081
+ *******************************************************************************/
+
+#ifndef STACKTRACE_H_
+#define STACKTRACE_H_
+
+#include <stdio.h>
+#define NOSTACKTRACE 1
+
+#if defined(NOSTACKTRACE)
+#define FUNC_ENTRY
+#define FUNC_ENTRY_NOLOG
+#define FUNC_ENTRY_MED
+#define FUNC_ENTRY_MAX
+#define FUNC_EXIT
+#define FUNC_EXIT_NOLOG
+#define FUNC_EXIT_MED
+#define FUNC_EXIT_MAX
+#define FUNC_EXIT_RC(x)
+#define FUNC_EXIT_MED_RC(x)
+#define FUNC_EXIT_MAX_RC(x)
+
+#else
+
+#if defined(WIN32)
+#define inline __inline
+#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM)
+#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1)
+#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM)
+#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM)
+#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM)
+#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1)
+#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM)
+#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM)
+#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM)
+#else
+#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM)
+#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1)
+#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM)
+#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM)
+#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM)
+#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1)
+#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM)
+#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM)
+#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM)
+#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM)
+
+void StackTrace_entry(const char* name, int line, int trace);
+void StackTrace_exit(const char* name, int line, void* return_value, int trace);
+
+void StackTrace_printStack(FILE* dest);
+char* StackTrace_get(unsigned long);
+
+#endif
+
+#endif
+
+
+
+
+#endif /* STACKTRACE_H_ */

+ 196 - 0
platform/rtthread/platformNetwork.c

@@ -0,0 +1,196 @@
+
+
+#include "platformNetwork.h"
+
+/**
+ * @brief 连接mqtt服务器
+ *
+ * @param userData
+ * @param platformNetwork
+ * @param host
+ * @param port
+ * @return RyanMqttError_e
+ * 成功返回RyanMqttSuccessError, 失败返回错误信息
+ */
+RyanMqttError_e platformNetworkConnect(void *userData, platformNetwork_t *platformNetwork, const char *host, const char *port)
+{
+    RyanMqttError_e result = RyanMqttSuccessError;
+    struct addrinfo *addr_list = NULL;
+    struct addrinfo hints = {
+        .ai_family = AF_UNSPEC,
+        .ai_socktype = SOCK_STREAM,
+        .ai_protocol = IPPROTO_TCP};
+
+    if (getaddrinfo(host, port, &hints, &addr_list) != 0)
+    {
+        result = RyanMqttSuccessError;
+        goto exit;
+    }
+
+    platformNetwork->socket = socket(addr_list->ai_family, addr_list->ai_socktype, addr_list->ai_protocol);
+    if (platformNetwork->socket < 0)
+    {
+        result = RyanSocketFailedError;
+        goto exit;
+    }
+
+    if (connect(platformNetwork->socket, addr_list->ai_addr, addr_list->ai_addrlen) != 0)
+    {
+        platformNetworkClose(userData, platformNetwork);
+        result = RyanMqttSocketConnectFailError;
+        goto exit;
+    }
+
+exit:
+
+    if (NULL != addr_list)
+        freeaddrinfo(addr_list);
+    return result;
+}
+
+/**
+ * @brief 非阻塞接收数据
+ *
+ * @param userData
+ * @param platformNetwork
+ * @param recvBuf
+ * @param recvLen
+ * @param timeout
+ * @return RyanMqttError_e
+ * socket错误返回 RyanSocketFailedError
+ * 接收超时或者接收数据长度不等于期待数据接受长度 RyanMqttRecvPacketTimeOutError
+ * 接收成功 RyanMqttSuccessError
+ */
+RyanMqttError_e platformNetworkRecvAsync(void *userData, platformNetwork_t *platformNetwork, char *recvBuf, int recvLen, int timeout)
+{
+
+    int32_t recvResult = 0;
+    int32_t offset = 0;
+    int32_t timeOut2 = timeout;
+    struct timeval tv = {0};
+    platformTimer_t timer = {0};
+
+    if (-1 == platformNetwork->socket)
+        return RyanSocketFailedError;
+
+    platformTimerCutdown(&timer, timeout);
+
+    while ((offset < recvLen) && (0 != timeOut2))
+    {
+
+        tv.tv_sec = timeOut2 / 1000;
+        tv.tv_usec = timeOut2 % 1000 * 1000;
+
+        if (tv.tv_sec <= 0 && tv.tv_usec <= 100)
+        {
+            tv.tv_sec = 0;
+            tv.tv_usec = 100;
+        }
+
+        setsockopt(platformNetwork->socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)); // 设置错做模式为非阻塞
+
+        recvResult = recv(platformNetwork->socket, recvBuf, recvLen - offset, 0);
+
+        if (recvResult < 0) // 小于零,表示错误,个别错误不代表socket错误
+        {
+            // 下列3种表示没问题,但需要推出发送
+            if ((errno == EAGAIN ||      // 套接字已标记为非阻塞,而接收操作被阻塞或者接收超时
+                 errno == EWOULDBLOCK || // 发送时套接字发送缓冲区已满,或接收时套接字接收缓冲区为空
+                 errno == EINTR))        // 操作被信号中断
+                break;
+
+            return RyanSocketFailedError;
+        }
+
+        offset += recvResult;
+        timeOut2 = platformTimerRemain(&timer);
+    }
+
+    if (offset != recvLen)
+        return RyanMqttRecvPacketTimeOutError;
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 非阻塞发送数据
+ *
+ * @param userData
+ * @param platformNetwork
+ * @param sendBuf
+ * @param sendLen
+ * @param timeout
+ * @return RyanMqttError_e
+ * socket错误返回 RyanSocketFailedError
+ * 接收超时或者接收数据长度不等于期待数据接受长度 RyanMqttRecvPacketTimeOutError
+ * 接收成功 RyanMqttSuccessError
+ */
+RyanMqttError_e platformNetworkSendAsync(void *userData, platformNetwork_t *platformNetwork, char *sendBuf, int sendLen, int timeout)
+{
+
+    int32_t sendResult = 0;
+    int32_t offset = 0;
+    int32_t timeOut2 = timeout;
+    struct timeval tv = {0};
+    platformTimer_t timer = {0};
+
+    if (-1 == platformNetwork->socket)
+        return RyanSocketFailedError;
+
+    platformTimerCutdown(&timer, timeout);
+
+    while ((offset < sendLen) && (0 != timeOut2))
+    {
+
+        tv.tv_sec = timeOut2 / 1000;
+        tv.tv_usec = timeOut2 % 1000 * 1000;
+
+        if (tv.tv_sec <= 0 && tv.tv_usec <= 100)
+        {
+            tv.tv_sec = 0;
+            tv.tv_usec = 100;
+        }
+
+        setsockopt(platformNetwork->socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(struct timeval)); // 设置错做模式为非阻塞
+
+        sendResult = send(platformNetwork->socket, sendBuf, sendLen - offset, 0);
+
+        if (sendResult < 0) // 小于零,表示错误,个别错误不代表socket错误
+        {
+            // 下列3种表示没问题,但需要推出发送
+            if ((errno == EAGAIN ||      // 套接字已标记为非阻塞,而接收操作被阻塞或者接收超时
+                 errno == EWOULDBLOCK || // 发送时套接字发送缓冲区已满,或接收时套接字接收缓冲区为空
+                 errno == EINTR))        // 操作被信号中断
+                break;
+
+            return RyanSocketFailedError;
+        }
+
+        offset += sendResult;
+        timeOut2 = platformTimerRemain(&timer);
+    }
+
+    if (offset != sendLen)
+        return RyanMqttSendPacketTimeOutError;
+
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 断开mqtt服务器连接
+ *
+ * @param userData
+ * @param platformNetwork
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformNetworkClose(void *userData, platformNetwork_t *platformNetwork)
+{
+
+    if (platformNetwork->socket >= 0)
+    {
+        closesocket(platformNetwork->socket);
+        platformNetwork->socket = -1;
+    }
+
+    return RyanMqttSuccessError;
+}

+ 47 - 0
platform/rtthread/platformNetwork.h

@@ -0,0 +1,47 @@
+
+
+#ifndef __platformNetSocket__
+#define __platformNetSocket__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <rtthread.h>
+#include "RyanMqttPublic.h"
+#include "platformTimer.h"
+
+#ifdef RT_USING_SAL
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include "sal_netdb.h"
+
+#else
+#include "lwip/opt.h"
+#include "lwip/sys.h"
+#include "lwip/api.h"
+#include <lwip/sockets.h>
+#include "lwip/netdb.h"
+#endif
+
+    typedef struct
+    {
+        int socket;
+    } platformNetwork_t;
+
+    extern RyanMqttError_e platformNetworkConnect(void *userData, platformNetwork_t *platformNetwork, const char *host, const char *port);
+    extern RyanMqttError_e platformNetworkRecvAsync(void *userData, platformNetwork_t *platformNetwork, char *recvBuf, int recvLen, int timeout);
+    extern RyanMqttError_e platformNetworkSendAsync(void *userData, platformNetwork_t *platformNetwork, char *sendBuf, int sendLen, int timeout);
+    extern RyanMqttError_e platformNetworkClose(void *userData, platformNetwork_t *platformNetwork);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 174 - 0
platform/rtthread/platformSystem.c

@@ -0,0 +1,174 @@
+
+#include "platformSystem.h"
+
+// 存放未初始化
+// #define ccmBss __attribute__((section(".ccmbss")))
+
+// ccmBss static struct rt_thread mqttThreadHandle;
+// ccmBss static char mqttThreadStack[512 * 4];
+
+void *platformMemoryMalloc(size_t size)
+{
+    return rt_malloc(size);
+}
+
+void platformMemoryFree(void *ptr)
+{
+    rt_free(ptr);
+}
+
+/**
+ * @brief ms延时
+ *
+ * @param ms
+ */
+void platformDelay(uint32_t ms)
+{
+    rt_thread_mdelay(ms);
+}
+
+/**
+ * @brief 初始化并运行线程
+ *
+ * @param userData
+ * @param platformThread
+ * @param name
+ * @param entry
+ * @param param
+ * @param stackSize
+ * @param priority
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformThreadInit(void *userData,
+                                   platformThread_t *platformThread,
+                                   const char *name,
+                                   void (*entry)(void *),
+                                   void *const param,
+                                   uint32_t stackSize,
+                                   uint32_t priority)
+{
+    platformThread->thread = rt_thread_create(name,      // 线程name
+                                              entry,     // 线程入口函数
+                                              param,     // 线程入口函数参数
+                                              stackSize, // 线程栈大小
+                                              priority,  // 线程优先级
+                                              10);       // 线程时间片
+
+    if (RT_NULL == platformThread->thread)
+        return RyanMqttNoRescourceError;
+
+    rt_thread_startup(platformThread->thread);
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 销毁指定线程
+ *
+ * @param userData
+ * @param platformThread
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformThreadDestroy(void *userData, platformThread_t *platformThread)
+{
+    rt_thread_delete(platformThread->thread);
+    rt_schedule();
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 开启线程
+ *
+ * @param userData
+ * @param platformThread
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformThreadStart(void *userData, platformThread_t *platformThread)
+{
+    rt_thread_resume(platformThread->thread);
+    rt_schedule();
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 挂起线程
+ *
+ * @param userData
+ * @param platformThread
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformThreadStop(void *userData, platformThread_t *platformThread)
+{
+    rt_thread_suspend(platformThread->thread);
+    rt_schedule(); // rtthread挂起线程后应立即调用线程上下文切换函数
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 互斥锁初始化
+ *
+ * @param userData
+ * @param platformMutex
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformMutexInit(void *userData, platformMutex_t *platformMutex)
+{
+    platformMutex->mutex = rt_mutex_create("mqttMutex", RT_IPC_FLAG_PRIO);
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 销毁互斥锁
+ *
+ * @param userData
+ * @param platformMutex
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformMutexDestroy(void *userData, platformMutex_t *platformMutex)
+{
+    rt_mutex_delete(platformMutex->mutex);
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 阻塞获取互斥锁
+ *
+ * @param userData
+ * @param platformMutex
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformMutexLock(void *userData, platformMutex_t *platformMutex)
+{
+    rt_mutex_take(platformMutex->mutex, RT_WAITING_FOREVER);
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 释放互斥锁
+ *
+ * @param userData
+ * @param platformMutex
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e platformMutexUnLock(void *userData, platformMutex_t *platformMutex)
+{
+    rt_mutex_release(platformMutex->mutex);
+    return RyanMqttSuccessError;
+}
+
+/**
+ * @brief 进入临界区 / 关中断
+ *
+ */
+void platformCriticalEnter(void)
+{
+    rt_enter_critical();
+}
+
+/**
+ * @brief 退出临界区 / 开中断
+ *
+ */
+void platformCriticalExit(void)
+{
+    rt_exit_critical();
+}

+ 60 - 0
platform/rtthread/platformSystem.h

@@ -0,0 +1,60 @@
+
+#ifndef __platformSystem__
+#define __platformSystem__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <assert.h>
+#include <rtthread.h>
+#include "RyanMqttPublic.h"
+
+#ifdef RT_ASSERT
+#define RyanMqttAssert(EX) RT_ASSERT(EX)
+#else
+#define RyanMqttAssert(EX) assert(EX)
+#endif
+
+    typedef struct
+    {
+        rt_thread_t thread;
+    } platformThread_t;
+
+    typedef struct
+    {
+        rt_mutex_t mutex;
+    } platformMutex_t;
+
+    extern void *platformMemoryMalloc(size_t size);
+    extern void platformMemoryFree(void *ptr);
+
+    extern void platformDelay(uint32_t ms);
+
+    extern RyanMqttError_e platformThreadInit(void *userData,
+                                              platformThread_t *platformThread,
+                                              const char *name,
+                                              void (*entry)(void *),
+                                              void *const param,
+                                              uint32_t stackSize,
+                                              uint32_t priority);
+    extern RyanMqttError_e platformThreadDestroy(void *userData, platformThread_t *platformThread);
+    extern RyanMqttError_e platformThreadStart(void *userData, platformThread_t *platformThread);
+    extern RyanMqttError_e platformThreadStop(void *userData, platformThread_t *platformThread);
+
+    extern RyanMqttError_e platformMutexInit(void *userData, platformMutex_t *platformMutex);
+    extern RyanMqttError_e platformMutexDestroy(void *userData, platformMutex_t *platformMutex);
+    extern RyanMqttError_e platformMutexLock(void *userData, platformMutex_t *platformMutex);
+    extern RyanMqttError_e platformMutexUnLock(void *userData, platformMutex_t *platformMutex);
+
+    extern void platformCriticalEnter(void);
+    extern void platformCriticalExit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 68 - 0
platform/rtthread/platformTimer.c

@@ -0,0 +1,68 @@
+
+
+#include "platformTimer.h"
+
+uint32_t platformUptimeMs(void)
+{
+#if (RT_TICK_PER_SECOND == 1000)
+    return (uint32_t)rt_tick_get();
+#else
+    rt_tick_t tick = 0u;
+
+    tick = rt_tick_get() * 1000;
+    return (uint32_t)((tick + RT_TICK_PER_SECOND - 1) / RT_TICK_PER_SECOND);
+#endif
+}
+
+/**
+ * @brief 将时间设置为0
+ *
+ * @param platformTimer
+ */
+void platformTimerInit(platformTimer_t *platformTimer)
+{
+    platformTimer->time = 0;
+    platformTimer->timeOut = 0;
+}
+
+/**
+ * @brief 添加计数时间
+ *
+ * @param platformTimer
+ * @param timeout
+ */
+void platformTimerCutdown(platformTimer_t *platformTimer, uint32_t timeout)
+{
+    platformTimer->timeOut = timeout;
+    platformTimer->time = platformUptimeMs();
+}
+
+/**
+ * @brief 计算time还有多长时间超时,考虑了32位溢出判断
+ *
+ * @param platformTimer
+ * @return uint32_t 返回剩余时间,超时返回0
+ */
+uint32_t platformTimerRemain(platformTimer_t *platformTimer)
+{
+    uint32_t tnow = platformUptimeMs();
+    uint32_t overTime = platformTimer->time + platformTimer->timeOut;
+    // uint32_t 没有溢出
+    if (overTime >= platformTimer->time)
+    {
+        // tnow溢出,不存在时间超时可能性
+        if (tnow < platformTimer->time)
+            return (UINT32_MAX - overTime + tnow + 1);
+
+        // tnow没有溢出
+        return tnow >= overTime ? 0 : (overTime - tnow);
+    }
+
+    // uint32_t 溢出了
+    // tnow 溢出了
+    if (tnow < platformTimer->time)
+        return tnow >= overTime ? 0 : (overTime - tnow + 1);
+
+    // tnow 没有溢出
+    return UINT32_MAX - tnow + overTime + 1;
+}

+ 27 - 0
platform/rtthread/platformTimer.h

@@ -0,0 +1,27 @@
+
+
+#ifndef __platformTimer__
+#define __platformTimer__
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <rtthread.h>
+#include <stdint.h>
+
+    typedef struct
+    {
+        uint32_t time;
+        uint32_t timeOut;
+    } platformTimer_t;
+
+    extern uint32_t platformUptimeMs(void);
+    extern void platformTimerInit(platformTimer_t *platformTimer);
+    extern void platformTimerCutdown(platformTimer_t *platformTimer, uint32_t timeout);
+    extern uint32_t platformTimerRemain(platformTimer_t *platformTimer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif