소스 검색

1st commit. Ver.1.0.0

caochunchen 5 년 전
부모
커밋
095e4cca1f
14개의 변경된 파일5429개의 추가작업 그리고 2개의 파일을 삭제
  1. 52 0
      .gitignore
  2. 338 2
      README.md
  3. 23 0
      SConscript
  4. BIN
      docs/pictures/umqtt_分层图.jpg
  5. 151 0
      inc/umqtt.h
  6. 38 0
      inc/umqtt_cfg.h
  7. 381 0
      inc/umqtt_internal.h
  8. 217 0
      samples/umqtt_sample.c
  9. 302 0
      src/pkgs/umqtt_pkgs_decode.c
  10. 463 0
      src/pkgs/umqtt_pkgs_encode.c
  11. 280 0
      src/trans/umqtt_transport.c
  12. 1987 0
      src/umqtt.c
  13. 893 0
      tests/umqtt_test.c
  14. 304 0
      tests/umqtt_test_table.h

+ 52 - 0
.gitignore

@@ -0,0 +1,52 @@
+# Prerequisites
+*.d
+
+# 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

+ 338 - 2
README.md

@@ -1,2 +1,338 @@
-# umqtt
-A light weight, powerful, customizable, easy-to-use and embeddable mqtt client for RT-Thread
+# uMQTT
+
+## 1、介绍
+
+uMQTT 软件包是 RT-Thread 自主研发的,基于 MQTT 3.1.1 协议的客户端实现,它提供了设备与 MQTT Broker 通讯的基本功能
+
+uMQTT 软件包功能如下:  
+
+* 实现基础的连接、订阅、发布功能;  
+* 具备多重心跳保活,设备重连机制,保证 mqtt 在线状态,适应复杂情况; 
+* 支持 QoS=0, QoS=1, QoS=2 三种发送信息质量;  
+* 支持多客户端使用;
+* 用户端接口简便,留有多种对外回调函数;
+* 支持多种技术参数可配置,易上手,便于产品化开发;
+* 功能强大,资源占用率低,支持功能可裁剪。
+
+资源占用(测试环境 W60X ):
+
+| ROM | RAM | 动态RAM | 
+| :-: | :-: | :-: |
+| 12.97KByte | 0.01KByte | 4.9KByte | 
+
+
+### 1.1 目录结构
+
+```
+umqtt
+├───docs                                // 说明文档
+├───inc  
+│   ├───umqtt_internal.h                // 内部打包及发送相关头文件
+│   ├───umqtt_cfg.h                     // 结构体配置头文件
+│   └───umqtt.h                         // 对外提供接口头文件
+├───src
+│   ├───pkgs                            // 完成对 paho_mbedded 软件库的功能裁剪移植
+│   │   ├───umqtt_pkgs_decode.c         // 打包实现源文件
+│   │   ├───umqtt_pkgs_encode.c         // 解包实现源文件
+│   ├───trans                           
+│   │   └───umqtt_trans.c               // 传输层相关源文件
+│   └───umqtt_utils.c                   // 通用接口实现文件
+├───samples                             // finsh 调试接口示例
+├───tests                               // 测试用例
+├───LICENSE                             // 软件包许可证
+├───README.md                           // 软件包使用说明
+└───SConscript                          // RT-Thread 默认的构建脚本
+```
+
+### 1.2 许可证
+uMQTT 软件包遵循 Apache-2.0 许可,详见 LICENSE 文件。
+
+### 1.3 依赖
+
+- RT_Thread 3.0+
+- SAL 层组件
+
+## 2、获取软件包
+
+**uMQTT 软件包相关配置选项介绍**
+```
+--- umqtt: A MQTT Client for RT-Thread
+[ ]   Enable MQTT example
+[ ]   Enable MQTT test
+(4)   subtopic name list numbers
+(1024) send buffer size
+(1024) receive buffer size
+(1000) uplink timer def cycle, uint:mSec
+(5)   reconnect max count
+(60)  reconnect time interval, uint:Sec
+(5)   keepalive func, max count
+(30)  heartbeat interval, uint:Sec
+(4)   connect timeout, uint:Sec
+(100) receive timeout, uint:mSec
+(4)   send timeout, uint:Sec
+(4096) receive thread stack size
+(8)   thread priority
+(4)   async message ack queue count
+(0xFFFF) connect information, keepalive interval, uint:Sec
+[ ]   Enable change connect keepalive time, uint:Sec
+      Version (latest)  --->
+```
+
+* subtopic name list numbers: 内部允许最大同时订阅数量
+* send buffer size: 发送数据缓存大小
+* receive buffer size: 接收数据缓存大小
+* uplink timer def cycle, uint:mSec: 定时器运行周期, 单位: mSec
+* reconnect max count: 最大重连次数
+* reconnect time interval, uint:Sec: 重连间隔时间, 单位: Sec
+* keepalive func, max count: 保活机制中心跳重连次数
+* heartbeat interval, uint:Sec: 心跳发送间隔, 单位: Sec
+* connect timeout, uint:Sec: 连接超时时间, 单位: Sec
+* receive timeout, uint:mSec: 接收超时时间, 单位: mSec
+* send timeout, uint:Sec: 发送超时时间, 单位: Sec
+* receive thread stack size: 内部接收线程堆栈
+* thread priority: 内部线程优先级
+* async message ack queue size: 接收线程处理完接收数据向外部传送处理结果消息队列大小
+* connect information, keepalive interval, uint:Sec:MQTT 协议中 CONNECT 命令下 KEEPALIVE 值,默认最大 0xFF, 单位: Sec
+* Enable change connect keepalive time, uint:Sec: 允许修改 MQTT 连接信息中的 keepalive 时间, 单位: Sec
+* Version: 软件版本号 
+
+## 3、使用 uMQTT 软件包
+
+### 3.1 软件包工作原理
+
+uMQTT 软件包主要用于在嵌入式设备上实现 MQTT 协议,软件包的主要工作基于 MQTT 协议实现。软件包分层图如下:
+
+  ![umqtt_分层图](./docs/pictures/umqtt_分层图.jpg)
+
+软件包实现过程中主要做了:
+
+1. 根据 MQTT 3.1.1 协议规定,进行软件包数据协议的封包解包;
+
+2. 传输层函数适配对接 SAL 层;
+
+3. umqtt 客户端层,根据协议包层和传输层编写符合应用层的接口。实现基础连接、断连、订阅、取消订阅、发布消息等功能。支持 QoS0/1/2 三种发送信息质量。利用 uplink timer 定时器,实现多重心跳保活机制和设备重连机制,增加设备在线稳定性,适应复杂情况。
+
+### 3.2 用户 API 介绍
+
+#### 3.2.1 创建对象
+```c
+umqtt_client_t umqtt_create(const struct umqtt_info *info);
+```
+创建客户端结构体对象。
+
+| 参数              | 描述                                |  
+|:------------------|:-----------------------------------|  
+| info | 用户信息配置 |  
+| **返回值** | **描述** |  
+| != RT_NULL | umqtt 客户端结构体指针 |  
+| == RT_NULL | 创建失败 |  
+
+#### 3.2.2 删除对象
+```c
+int umqtt_delete(struct umqtt_client *client);
+```
+删除客户端结构体对象,并释放内存。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| **返回值** | **描述** |  
+| UMQTT_OK | 成功 |  
+
+#### 3.2.3 启动客户端
+```c
+int umqtt_start(struct umqtt_client *client);
+```
+启动客户端会话,进行网络连接,和 MQTT 协议连接。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| **返回值** | **描述** |  
+| >=0 | 成功 |  
+| <0 | 失败 |  
+
+#### 3.2.4 停止客户端
+```c
+void umqtt_stop(struct umqtt_client *client);
+```
+停止客户端会话,关闭接收线程,暂停 uplink 定时器,发送 MQTT 断开连接命令,关闭 socket 套接字。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| **返回值** | **描述** |  
+| 无 | 无 |  
+
+#### 3.2.5 发布消息
+```c
+int umqtt_publish(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length, int timeout);
+```
+针对订阅主题发布相关质量的消息。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| qos | 消息发送质量 |  
+| topic | 发布主题 |  
+| payload | 发布消息 |  
+| length | 发布消息的长度 |  
+| timeout | 发布消息超时时间, 单位:mSec |  
+| **返回值** | **描述** |  
+| >=0 | 成功 |  
+| <0 | 失败 |  
+
+#### 3.2.6 订阅主题
+```c
+int umqtt_subscribe(struct umqtt_client *client, const char *topic, enum umqtt_qos qos, umqtt_subscribe_cb callback);
+```
+订阅主题,并设置对应主题接收 publish 消息时的回调函数。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| topic | 订阅主题 |  
+| qos | 订阅质量 |  
+| callback | 对应主题接收 publish 消息时的回调函数 |  
+| **返回值** | **描述** |  
+| >=0 | 成功 |  
+| <0 | 失败 |  
+
+#### 3.2.7 取消订阅主题
+```c
+int umqtt_unsubscribe(struct umqtt_client *client, const char *topic);
+```
+取消相关主题的订阅,并释放相关的资源。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| topic | 取消订阅主题 |  
+| **返回值** | **描述** |  
+| >=0 | 成功 |  
+| <0 | 失败 |  
+
+#### 3.2.8 异步发送消息
+```c
+int umqtt_publish_async(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length);
+```
+异步发送消息,只负责将信息发送出去,不负责阻塞接收。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| qos | 发送消息质量 |  
+| topic | 发送消息对应主题 |  
+| payload | 发布的消息 |  
+| length | 发布的消息长度 |  
+| **返回值** | **描述** |  
+| >=0 | 成功 |  
+| <0 | 失败 |  
+
+#### 3.2.9 设定获取参数
+```c
+int umqtt_control(struct umqtt_client *client, enum umqtt_cmd cmd, void *params);
+```
+根据相关命令设定或者读取内部相关参数。
+
+| 参数 | 描述 |  
+|:----|:----|  
+| client | umqtt 客户端结构体指针 |  
+| cmd | 设定或者读取内部相关参数 |  
+| params | 设定数据时,为返回值结构体;读取数据时,为 RT_NULL |  
+| **返回值** | **描述** |  
+| >=0 | 设定数据时, 成功; 读取数据时, 为具体返回数据 |  
+| >0 | 设定数据时, 失败; 读取数据时, 为具体返回数据 |  
+
+### 3.3 示例介绍
+
+#### 3.3.1 准备工作
+
+- menuconfig 配置获取软件包和示例代码
+
+    打开 RT-Thread 提供的 ENV 工具,使用 **menuconfig** 配置软件包, 
+    启用 UMQTT 软件包,并配置使能测试例程 (`Enable MQTT example`), 如下所示: 
+
+``` shell
+RT-Thread online packages
+    IoT - internet of things  --->
+        [*] umqtt: A MQTT Client for RT-Thread.  --->
+            [*] Enable MQTT example                     # 开启 UMQTT 例程
+```
+
+- 使用 `pkgs --update` 命令下载软件包;  
+- 编译下载;  
+- 使用 [emqx](https://www.emqx.io/cn/) 搭建 MQTT Broker 。 
+
+#### 3.3.2 启动例程
+
+* 启动 umqtt 客户端
+
+启动 umqtt 客户端流程:
+- 申明 `struct umqtt_info` 结构体变量作为 umqtt 客户端用户配置变量  
+- 测试 MQTT Broker 的 URI 进行赋值  
+- 创建 umqtt 客户端  
+- 声明并设置连接、在线、离线、心跳回调函数
+- 调用 `umqtt_start()` 函数,启动 umqtt 客户端
+
+```shell
+msh />umqtt_ex_start
+[D/umqtt.sample]  umqtt example start!
+[I/umqtt]  connect success!
+[I/umqtt.sample]  umqtt start success!
+```
+
+* 订阅功能
+
+```shell
+msh />umqtt_ex_subscribe "test0"
+[D/umqtt.sample]  umqtt example subscribe!
+[D/umqtt]  start assign datas !
+[D/umqtt] subscribe ack ok!
+```
+
+* 发布消息
+
+```shell
+msh />umqtt_ex_publish test 0 hello                     # 消息发送质量 qos0
+[D/umqtt.sample]  umqtt example publish!
+[D/umqtt.sample]  umqtt topic recv callback! name length: 4, name: testhello, packet id: 0, payload len: 6
+
+msh />umqtt_ex_publish test 1 hello_this                # 消息发送质量 qos1 
+[D/umqtt.sample]  umqtt example publish!
+[D/umqtt.sample]  umqtt topic recv callback! name length: 4, name: test, packet id: 1, payload len: 11
+[I/umqtt]  publish qos1 ack success!
+
+msh />umqtt_ex_publish test 1 hello_this_world          # 消息发送质量 qos2 
+[D/umqtt.sample]  umqtt example publish!
+[D/umqtt.sample]  umqtt topic recv callback! name length: 4, name: test, packet id: 2, payload len: 17
+[I/umqtt]  publish qos2 ack success!
+
+```
+
+* 取消订阅
+
+```shell
+msh />umqtt_ex_unsubscribe test
+[D/umqtt.sample]  umqtt example unsubscribe!
+[I/umqtt]  unsubscribe ack ok!
+```
+
+* 停止 umqtt 客户端
+
+```shell
+msh />umqtt_ex_stop
+[D/umqtt.sample]  umqtt example stop!
+```
+
+## 4、注意事项
+
+* 本版本暂不支持加密通信协议; 
+* 使用 [emqx](https://www.emqx.io/cn/) 搭建 MQTT Broker 。
+
+
+## 5、联系方式 & 感谢
+联系人: springcity  
+Email: caochunchen@rt-thread.com
+
+

+ 23 - 0
SConscript

@@ -0,0 +1,23 @@
+Import('RTT_ROOT')
+from building import *
+
+# get current directory
+cwd = GetCurrentDir()
+
+# The set of source files associated with this SConscript file.
+src = Glob('src/*.c')
+src += Glob('src/pkgs/*.c')
+src += Glob('src/trans/*.c');
+
+if GetDepend('PKG_USING_UMQTT_EXAMPLE'):
+    src += Glob('samples/*.c');
+
+if GetDepend('PKG_USING_UMQTT_TEST'):
+    src += Glob('tests/*.c');
+
+path = [cwd + '/inc']
+
+group = DefineGroup('umqtt', src, depend = ['PKG_USING_UMQTT'], CPPPATH = path)
+
+Return('group')
+

BIN
docs/pictures/umqtt_分层图.jpg


+ 151 - 0
inc/umqtt.h

@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-04-29    springcity      the first version
+ */
+
+#ifndef _UMQTT_H__
+#define _UMQTT_H__
+
+#include <rtdef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UMQTT_SW_VERSION            "1.0.0"
+#define UMQTT_SW_VERSION_NUM        0x10000
+
+enum umqtt_client_state
+{
+    UMQTT_CS_IDLE             = 0x00000000,             /* idle state */ 
+    UMQTT_CS_LINKING          = 0x00000001,             /* connecting */ 
+    UMQTT_CS_LINKED           = 0x00000002,             /* connected */ 
+    UMQTT_CS_UNLINK           = 0x00000004,             /* offline */ 
+    UMQTT_CS_UNLINK_LINKING   = 0x00000008,             /* offline linking */ 
+    UMQTT_CS_DISCONNECT       = 0x00000010,             /* reconnect failed */
+};
+
+enum umqtt_err_code
+{
+    UMQTT_OK                    = 0,                    /* no error code */ 
+    UMQTT_FAILED                = -1,                   /* function failed */ 
+    UMQTT_MEM_FULL              = -2,                   /* out of memory */ 
+    UMQTT_TIMEOUT               = -3,                   /* function timeout */ 
+    UMQTT_ENCODE_ERROR          = -4,                   /* encode error */ 
+    UMQTT_DECODE_ERROR          = -5,                   /* decode error */ 
+    UMQTT_SEND_TIMEOUT          = -6,                   /* send timeout */ 
+    UMQTT_SEND_FAILED           = -7,                   /* send failed */
+    UMQTT_INPARAMS_NULL         = -8,                   /* input params is null */ 
+    UMQTT_BUFFER_TOO_SHORT      = -9,                   /* buff too short */
+    UMQTT_READ_ERROR            = -10,                  /* read error */
+    UMQTT_READ_FAILED           = -11,                  /* read failed */
+    UMQTT_READ_TIMEOUT          = -12,
+    UMQTT_FIN_ACK               = -13,                  /* server send fin ack to client */ 
+    UMQTT_RECONNECT_FAILED      = -14,                  /* reconnect failed  */ 
+    UMQTT_SOCK_CONNECT_FAILED   = -15,
+    UMQTT_DISCONNECT            = -16,
+};
+
+enum umqtt_evt
+{
+    UMQTT_EVT_LINK              = 0x00,                 /* link event */ 
+    UMQTT_EVT_ONLINE            = 0x01,                 /* online event */ 
+    UMQTT_EVT_OFFLINE           = 0x02,                 /* offline event */ 
+    UMQTT_EVT_HEARTBEAT         = 0x03,                 /* heartbeat event */ 
+};
+
+enum umqtt_cmd
+{
+    UMQTT_CMD_SUB_CB            = 0x00,
+    UMQTT_CMD_EVT_CB            = 0x01,
+    UMQTT_CMD_SET_HB            = 0x02,                 /* set heartbeat time interval */
+    UMQTT_CMD_GET_CLIENT_STA    = 0x03,                 /* get client status*/
+
+    UMQTT_CMD_DISCONNECT        = 0x7E,                 /* close socket & mqtt disconnect */
+    UMQTT_CMD_DEL_HANDLE        = 0x7F,
+#ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME 
+    UMQTT_CMD_SET_CON_KP        = 0x80,
+#endif
+};
+
+enum umqtt_qos { UMQTT_QOS0 = 0, UMQTT_QOS1 = 1, UMQTT_QOS2 = 2, UMQTT_SUBFAIL = 0x80 };
+
+struct umqtt_client;
+typedef struct umqtt_client *umqtt_client_t;
+typedef int (*umqtt_user_callback)(struct umqtt_client *client, enum umqtt_evt event);
+typedef void (*umqtt_subscribe_cb)(void *client, void *msg);
+
+struct subtop_recv_handler
+{
+    char *topicfilter;
+    void (*callback)(void *client, void *message);
+    enum umqtt_qos qos;
+    rt_list_t next_list;
+};
+
+struct umqtt_info
+{
+    rt_size_t send_size, recv_size;                     /* send/receive buffer size */ 
+    const char *uri;                                    /* complete URI (include: URI + URN) */ 
+    const char *client_id;                              /* client id */ 
+    const char *lwt_topic;                              /* will topic */
+    const char *lwt_message;                            /* will message */ 
+    const char *user_name;                              /* user_name */ 
+    const char *password;                               /* password */ 
+    enum umqtt_qos lwt_qos;                             /* will qos */
+    umqtt_subscribe_cb lwt_cb;                          /* will callback */
+    rt_uint8_t reconnect_max_num;                       /* reconnect max count */ 
+    rt_uint32_t reconnect_interval;                     /* reconnect interval time */ 
+    rt_uint8_t keepalive_max_num;                       /* keepalive max count */ 
+    rt_uint32_t keepalive_interval;                     /* keepalive interval */ 
+    rt_uint32_t recv_time_ms;                           /* receive timeout */ 
+    rt_uint32_t connect_time;                           /* connect timeout */ 
+    rt_uint32_t send_timeout;                           /* uplink_timeout  publish/subscribe/unsubscribe */ 
+    rt_uint32_t thread_stack_size;                      /* thread task stack size */ 
+    rt_uint8_t thread_priority;                         /* thread priority */ 
+#ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME
+    rt_uint16_t connect_keepalive_sec;                  /* connect information, keepalive second */    
+#endif
+};
+
+
+
+/* create umqtt client according to user information */
+umqtt_client_t umqtt_create(const struct umqtt_info *info);
+
+/* delete umqtt client */
+int umqtt_delete(struct umqtt_client *client);
+
+/* start the umqtt client to work */
+int umqtt_start(struct umqtt_client *client);
+
+/* stop the umqtt client work */
+void umqtt_stop(struct umqtt_client *client);
+
+/* umqtt client publish datas to specified topic */
+int umqtt_publish(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length, int timeout);
+
+/* subscribe the client to defined topic with defined qos */
+int umqtt_subscribe(struct umqtt_client *client, const char *topic, enum umqtt_qos qos, umqtt_subscribe_cb callback);
+
+/* unsubscribe the client from defined topic */
+int umqtt_unsubscribe(struct umqtt_client *client, const char *topic);
+
+/* umqtt client publish nonblocking datas */
+int umqtt_publish_async(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length);
+
+/* set some config datas in umqtt client */
+int umqtt_control(struct umqtt_client *client, enum umqtt_cmd cmd, void *params);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+

+ 38 - 0
inc/umqtt_cfg.h

@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-04-29    springcity      the first version
+ */
+
+#ifndef _UMQTT_CFG_H__
+#define _UMQTT_CFG_H__
+
+#define PKG_UMQTT_PROTOCOL_NAME                         ("MQTC")
+#define PKG_UMQTT_PROTOCOL_NAME_LEN                     (rt_strlen(PKG_UMQTT_PROTOCOL_NAME))
+#define PKG_UMQTT_PROTOCOL_LEVEL                        (4)             /* MQTT3.1.1 ver_lvl:4;  MQTT3.1 ver_lvl:3 */ 
+
+#ifdef PKG_UMQTT_WILL_TOPIC_STRING
+#define UMQTT_WILL_TOPIC                                PKG_UMQTT_WILL_TOPIC_STRING
+#else 
+#define UMQTT_WILL_TOPIC                                ("/umqtt/test")
+#endif
+
+#ifdef PKG_UMQTT_WILL_MESSAGE_STRING
+#define UMQTT_WILL_MWSSAGE                              PKG_UMQTT_WILL_MESSAGE_STRING
+#else
+#define UMQTT_WILL_MESSAGE                              ("Goodbye!")
+#endif
+
+#define UMQTT_INFO_DEF_THREAD_TICK                      50
+#define UMQTT_MAX_PACKET_ID                             65535   
+#define UMQTT_INFO_DEF_UPLINK_TIMER_TICK                1000 
+
+// #define PKG_UMQTT_PUBLISH_RECON_MAX                     3               /* qos2 下重发次数 */
+// #define PKG_UMQTT_QOS2_QUE_MAX                          1               
+#define PKG_UMQTT_RECPUBREC_INTERVAL_TIME               (2 * UMQTT_INFO_DEF_UPLINK_TIMER_TICK)
+
+#endif

+ 381 - 0
inc/umqtt_internal.h

@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-04-29    springcity      the first version
+ */
+
+#ifndef _UMQTT_INTERNAL_H__
+#define _UMQTT_INTERNAL_H__
+
+#include <string.h>
+#include <rtdef.h>
+
+#include "umqtt_cfg.h"
+#include "umqtt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum umqtt_type
+{
+    UMQTT_TYPE_RESERVED      = 0,
+    UMQTT_TYPE_CONNECT       = 1,
+    UMQTT_TYPE_CONNACK       = 2,
+    UMQTT_TYPE_PUBLISH       = 3,
+    UMQTT_TYPE_PUBACK        = 4,
+    UMQTT_TYPE_PUBREC        = 5,
+    UMQTT_TYPE_PUBREL        = 6,
+    UMQTT_TYPE_PUBCOMP       = 7,
+    UMQTT_TYPE_SUBSCRIBE     = 8,
+    UMQTT_TYPE_SUBACK        = 9,
+    UMQTT_TYPE_UNSUBSCRIBE   = 10,
+    UMQTT_TYPE_UNSUBACK      = 11,
+    UMQTT_TYPE_PINGREQ       = 12,
+    UMQTT_TYPE_PINGRESP      = 13,
+    UMQTT_TYPE_DISCONNECT    = 14,
+};
+
+enum umqtt_connack_retcode
+{
+    UMQTT_CONNECTION_ACCEPTED        = 0,
+    UMQTT_UNNACCEPTABLE_PROTOCOL     = 1,
+    UMQTT_CLIENTID_REJECTED          = 2,
+    UMQTT_SERVER_UNAVAILABLE         = 3,
+    UMQTT_BAD_USERNAME_OR_PASSWORD   = 4,
+    UMQTT_NOT_AUTHORIZED             = 5,
+
+};
+
+#define UMQTT_MESSAGES_FIX_HEADER(TYPE, DUP, QOS, REATAIN)  \
+            ((((TYPE) << 4)  & 0xF0) |  \
+            (((DUP) << 3) & 0x80) | \
+            (((QOS) << 1) % 0x06) | \
+            ((RETAIN) & 0x01))
+
+union umqtt_pkgs_fix_header
+{
+    rt_uint8_t byte;                                /* header */ 
+    struct {        
+        rt_uint8_t retain: 1;                       /* reserved bits */ 
+        rt_uint8_t qos:    2;                       /* QoS, 0-Almost once; 1-Alteast once; 2-Exactly once */ 
+        rt_uint8_t dup:    1;                       /* dup flag */ 
+        rt_uint8_t type:   4;                       /* MQTT packet type */ 
+    } bits;
+};
+union umqtt_pkgs_connect_sign
+{
+    rt_uint8_t connect_sign;
+    struct {
+        rt_uint8_t reserved:       1;               /* reserved bits */ 
+        rt_uint8_t clean_session:  1;               /* clean session bit */ 
+        rt_uint8_t will_flag:      1;               /* will flag bit */ 
+        rt_uint8_t will_Qos:       2;               /* will Qos bit */ 
+        rt_uint8_t will_retain:    1;               /* will retain bit */ 
+        rt_uint8_t password_flag:  1;               /* password flag bit */ 
+        rt_uint8_t username_flag:  1;               /* user name flag bit */ 
+    } bits;
+};
+union umqtt_pkgs_connack_sign
+{
+    rt_uint8_t connack_sign;
+    struct {
+        rt_uint8_t sp:             1;               /* current session bit */ 
+        rt_uint8_t reserved:       7;               /* retain bit */ 
+    } bits;
+};
+union pkgs_request_qos
+{
+    rt_uint8_t request_qos;
+    struct {
+        rt_uint8_t qos:            2;               /* QoS - 0/1/2 */ 
+        rt_uint8_t reserved:       6;               /* retain bit */ 
+    } bits;
+};
+struct sub_topic_filter
+{
+    rt_uint16_t filter_len;                         /* topic filter length */ 
+    const char *topic_filter;                       /* topic name filter */ 
+    union pkgs_request_qos req_qos;                 /* request QoS */ 
+};
+struct unsub_topic_filter
+{
+    rt_uint16_t filter_len;                         /* topic filter length */ 
+    const char *topic_filter;                       /* topic filter */ 
+};
+struct umqtt_pkgs_connect
+{
+    /* variable header */ 
+    rt_uint16_t protocol_name_len;                  /* protocol name length */ 
+    const char *protocol_name;                      /* protocol name */
+    rt_uint8_t protocol_level;                      /* protocol level */ 
+    union umqtt_pkgs_connect_sign connect_flags;    /* connect flags */ 
+    rt_uint16_t keepalive_interval_sec;             /* keepalive interval second */ 
+    /* payload */ 
+    const char *client_id;                          /* client id */ 
+    const char *will_topic;                         /* will topic */ 
+    const char *will_message;                       /* will messagewill message */ 
+    const char *user_name;                          /* user name */ 
+    rt_uint16_t password_len;                       /* password length */ 
+    const char *password;                           /* password */ 
+};
+struct umqtt_pkgs_connack
+{
+    /* variable header */ 
+    union umqtt_pkgs_connack_sign connack_flags;    /* connect flags */ 
+    enum umqtt_connack_retcode ret_code;            /* connect return code */ 
+    /* payload = NULL */ 
+};
+struct umqtt_pkgs_publish
+{   
+    /* variable header */ 
+    rt_uint16_t topic_name_len;                     /* topic name length */ 
+    const char *topic_name;                         /* topic name */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload */ 
+    const char *payload;                            /* active payload */ 
+    /* not packet datas */ 
+    rt_uint32_t payload_len;                        /* retain payload length */ 
+};
+struct umqtt_pkgs_puback
+{
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload = NULL */ 
+};
+struct umqtt_pkgs_pubrec                            /* publish receive (QoS 2, step_1st) */ 
+{
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload = NULL */ 
+};
+struct umqtt_pkgs_pubrel                            /* publish release (QoS 2, step_2nd) */ 
+{
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload = NULL */ 
+};
+struct umqtt_pkgs_pubcomp                           /* publish complete (QoS 2, step_3rd) */ 
+{
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload = NULL */ 
+};
+struct umqtt_pkgs_subscribe                         /* subscribe topic */ 
+{
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload */ 
+    struct sub_topic_filter topic_filter[PKG_UMQTT_SUBRECV_DEF_LENGTH];          /* topic name filter arrays */ 
+    /* not payload datas */ 
+    rt_uint8_t topic_count;                         /* topic filter count */ 
+};
+struct umqtt_pkgs_suback                            /* subscribe ack */ 
+{   
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload */ 
+    rt_uint8_t ret_qos[PKG_UMQTT_SUBRECV_DEF_LENGTH];   /* return code - enum Qos - 0/1/2 */ 
+    /* not payload datas */ 
+    rt_uint8_t topic_count;                         /* topic name count */ 
+};
+struct umqtt_pkgs_unsubscribe                       /* unsubscribe */ 
+{   
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload */ 
+    struct unsub_topic_filter topic_filter[PKG_UMQTT_SUBRECV_DEF_LENGTH];      /* topic name filter arrays */ 
+    /* not payload datas */ 
+    rt_uint8_t topic_count;                         /* topic name count */ 
+};
+struct umqtt_pkgs_unsuback                          /* unsubscribe ack */ 
+{
+    /* variable header */ 
+    rt_uint16_t packet_id;                          /* packet id */ 
+    /* payload = NULL */ 
+};
+// struct pkgs_pingreq { }                          /* ping request = NULL */ 
+// struct pkgs_pingresp { }                         /* ping response = NULL */ 
+// struct pkgs_disconnect { }                       /* disconnect = NULL */ 
+
+union umqtt_pkgs_msg                                /* mqtt message packet type */ 
+{
+    struct umqtt_pkgs_connect     connect;          /* connect */ 
+    struct umqtt_pkgs_connack     connack;          /* connack */ 
+    struct umqtt_pkgs_publish     publish;          /* publish */ 
+    struct umqtt_pkgs_puback      puback;           /* puback */ 
+    struct umqtt_pkgs_pubrec      pubrec;           /* publish receive (QoS 2, step_1st) */ 
+    struct umqtt_pkgs_pubrel      pubrel;           /* publish release (QoS 2, step_2nd) */ 
+    struct umqtt_pkgs_pubcomp     pubcomp;          /* publish complete (QoS 2, step_3rd) */ 
+    struct umqtt_pkgs_subscribe   subscribe;        /* subscribe topic */ 
+    struct umqtt_pkgs_suback      suback;           /* subscribe ack */ 
+    struct umqtt_pkgs_unsubscribe unsubscribe;      /* unsubscribe topic */ 
+    struct umqtt_pkgs_unsuback    unsuback;         /* unsubscribe ack */ 
+};
+
+struct umqtt_msg
+{
+    union umqtt_pkgs_fix_header header;             /* fix header */ 
+    rt_uint32_t msg_len;                            /* message length */ 
+    union umqtt_pkgs_msg msg;                       /* retain payload message */ 
+};
+
+/* umqtt package datas */
+int umqtt_encode(enum umqtt_type type, rt_uint8_t *send_buf, size_t send_len, struct umqtt_msg *message);
+/* umqtt unpackage datas */
+int umqtt_decode(rt_uint8_t *recv_buf, size_t recv_buf_len, struct umqtt_msg *message);
+
+/* tcp/tls connect/disconnect/send/recv functions */
+int umqtt_trans_connect(const char *uri, int *sock);
+int umqtt_trans_disconnect(int sock);
+int umqtt_trans_send(int sock, const rt_uint8_t *send_buf, rt_uint32_t buf_len, int timeout);
+int umqtt_trans_recv(int sock, rt_uint8_t *recv_buf, rt_uint32_t buf_len);
+
+/* compatible with paho MQTT embedded c needed to do processing */
+typedef union umqtt_pkgs_fix_header MQTTHeader;
+typedef struct umqtt_pkgs_connect MQTTPacket_connectData;
+
+#define MQTTStrlen(c)               ((c == NULL) ? 0 : strlen(c))
+
+static void umqtt_writeChar(unsigned char** pptr, char c)
+{
+	**pptr = c;
+	(*pptr)++;
+}
+
+static char umqtt_readChar(unsigned char** pptr)
+{
+	char c = **pptr;
+	(*pptr)++;
+	return c;
+}
+
+static void umqtt_writeInt(unsigned char** pptr, int anInt)
+{
+	**pptr = (unsigned char)(anInt / 256);
+	(*pptr)++;
+	**pptr = (unsigned char)(anInt % 256);
+	(*pptr)++;
+}
+
+static int umqtt_readInt(unsigned char** pptr)
+{
+	unsigned char* ptr = *pptr;
+	int len = 256*(*ptr) + (*(ptr+1));
+	*pptr += 2;
+	return len;
+}
+
+static void umqtt_writeCString(unsigned char** pptr, const char* string)
+{
+	int len = 0;
+    if (string) 
+    {
+        len = strlen(string);
+        umqtt_writeInt(pptr, len);
+        memcpy(*pptr, string, len);
+        *pptr += len;
+    }
+}
+
+static void umqtt_writeMQTTString(unsigned char** pptr, const char* string)
+{
+	int len = 0;
+    if (string) 
+    {
+        len = strlen(string);
+        umqtt_writeInt(pptr, len);
+        memcpy(*pptr, string, len);
+        *pptr += len;
+    } 
+    else 
+    {
+        umqtt_writeInt(pptr, 0);
+    }
+}
+
+static int umqtt_readlenstring(int *str_len, char **p_string, unsigned char **pptr, unsigned char *enddata)
+{
+    int rc = 0;
+
+    if (enddata - (*pptr) > 1) 
+    {
+        *str_len = umqtt_readInt(pptr);
+        if (&(*pptr)[*str_len] <= enddata) 
+        {
+            *p_string = (char *)*pptr; 
+            *pptr += *str_len;
+            rc = 1;
+        }
+    }
+    return rc;
+}
+
+static int umqtt_pkgs_encode(unsigned char* buf, int length)
+{
+	int rc = 0;
+	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);
+	return rc;
+}
+
+static int umqtt_pkgs_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
+
+	*value = 0;
+	do
+	{
+		int rc = UMQTT_READ_ERROR;
+
+		if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+		{
+			rc = UMQTT_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:
+	return len;
+}
+
+static int umqtt_pkgs_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;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif

+ 217 - 0
samples/umqtt_sample.c

@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-05-11    springcity      the first version
+ */
+
+#include <string.h>
+
+#include <rtthread.h>
+
+#define DBG_TAG             "umqtt.sample"
+
+#ifdef PKG_UMQTT_USING_DEBUG
+#define DBG_LVL             DBG_LOG
+#else
+#define DBG_LVL             DBG_INFO
+#endif                      /* MQTT_DEBUG */
+#include <rtdbg.h>
+
+#include "umqtt.h"
+#include "umqtt_internal.h"
+
+// #define MQTT_URI                "tcp://test.mosquitto.org:1883" 
+// #define MQTT_URI                "tcp://mq.tongxinmao.com:18831"
+#define MQTT_URI                "tcp://192.168.12.83:1883"
+#define MQTT_SUBTOPIC           "/umqtt/test"
+#define MQTT_PUBTOPIC           "/umqtt/test"
+#define MQTT_WILLMSG            "Goodbye!"
+
+static int is_started = 0;
+static umqtt_client_t m_umqtt_client = RT_NULL;
+
+static int user_callback(struct umqtt_client *client, enum umqtt_evt event)
+{
+    RT_ASSERT(client);
+
+    switch(event)
+    {
+    case UMQTT_EVT_LINK:
+        LOG_D(" user callback, event - link!");
+        break;
+    case UMQTT_EVT_ONLINE:
+        LOG_D(" user callback, event - online!");
+        break;
+    case UMQTT_EVT_OFFLINE:
+        LOG_D(" user callback, event - offline!");
+        break;
+    case UMQTT_EVT_HEARTBEAT:
+        LOG_D(" user callback, event - heartbeat!");
+        break;
+    default:
+        LOG_D(" user callback, event:%d", event);
+        break;
+    }
+
+    return 0;
+}
+
+static void umqtt_topic_recv_callback(struct umqtt_client *client, void *msg_data)
+{
+    RT_ASSERT(client);
+    RT_ASSERT(msg_data);
+    struct umqtt_pkgs_publish *msg = (struct umqtt_pkgs_publish *)msg_data;
+    LOG_D(" umqtt topic recv callback! name length: %d, name: %s, packet id: %d, payload len: %d ",
+            msg->topic_name_len,
+            msg->topic_name,
+            msg->packet_id,
+            // msg->payload,
+            msg->payload_len);
+}
+
+static int umqtt_ex_start(int argc, char **argv)
+{
+    LOG_D(" umqtt example start!");
+
+    if (argc != 1)
+    {
+        LOG_E(" umqtt_start    --start a umqtt worker thread.");
+        return -1;
+    }
+
+    if (is_started)
+    {
+        LOG_E(" umqtt client is already connected.");
+        return -1;        
+    }
+
+    struct umqtt_info umqtt_info = { 0 };
+    umqtt_info.uri = MQTT_URI;
+
+    m_umqtt_client = umqtt_create(&umqtt_info);
+    if (m_umqtt_client == RT_NULL)
+    {
+        LOG_E(" umqtt client create failed!");
+        return -1;
+    }
+    umqtt_control(m_umqtt_client, UMQTT_CMD_EVT_CB, user_callback);
+
+    if (umqtt_start(m_umqtt_client) >= 0)
+    {
+        LOG_I(" umqtt start success!");
+        is_started = 1;
+    }
+    else
+    {
+        m_umqtt_client = RT_NULL;
+        LOG_E(" umqtt start failed!");
+    }
+
+    return 0;
+}
+
+
+static int umqtt_ex_stop(int argc, char **argv)
+{
+    LOG_D(" umqtt example stop!");
+
+    if (argc != 1)
+    {
+        LOG_D("umqtt_stop    --stop umqtt worker thread and free mqtt client object.\n");
+    }
+
+    is_started = 0;
+    
+    umqtt_stop(m_umqtt_client);
+    umqtt_delete(m_umqtt_client);
+    m_umqtt_client = RT_NULL;
+    
+    return 0;
+}
+
+static int str_to_int(const char *str)
+{
+    int _ret = 0, _cnt = 0;
+    int _strlen = strlen(str);
+    for (_cnt = 0; _cnt < _strlen; _cnt++) {
+        if ((str[_cnt] >= '0') && (str[_cnt] <= '9')) {
+            _ret = _ret * 10 + str[_cnt] - '0';
+        }
+    }
+
+    return _ret;
+}
+
+static int umqtt_ex_publish(int argc, char **argv)
+{
+    LOG_D(" umqtt example publish!");
+
+    if (is_started == 0)
+    {
+        LOG_E("mqtt client is not connected.");
+        return -1;
+    }
+	
+    if (argc == 4) {
+        int _len = str_to_int(argv[2]);
+        // LOG_D(" *argv[0]: %s, *argv[1]: %s, *argv[2]: %d, *argv[3]: %s ", argv[0], argv[1], _len, argv[3]);
+
+        umqtt_publish(m_umqtt_client, ((_len > UMQTT_QOS2) ? UMQTT_QOS1 : _len), argv[1], argv[3], strlen(argv[3]) + 1, 100);
+    } else {
+        LOG_E("mqtt_publish <topic> <0/1/2> [message]  --mqtt publish message to specified topic.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int umqtt_ex_subscribe(int argc, char **argv)
+{
+    LOG_D(" umqtt example subscribe!");
+    if (argc != 2)
+    {
+        LOG_E("umqtt_subscribe [topic]  --send an umqtt subscribe packet and wait for suback before returning.\n");
+        return -1;
+    }
+	
+	if (is_started == 0)
+    {
+        LOG_E("umqtt client is not connected.");
+        return -1;
+    }
+
+    return umqtt_subscribe(m_umqtt_client, argv[1], UMQTT_QOS1, umqtt_topic_recv_callback);
+}
+
+
+static int umqtt_ex_unsubscribe(int argc, char **argv)
+{
+    LOG_D(" umqtt example unsubscribe!");
+
+    if (argc != 2)
+    {
+        LOG_E("mqtt_unsubscribe [topic]  --send an mqtt unsubscribe packet and wait for suback before returning.\n");
+        return -1;
+    }
+	
+	if (is_started == 0)
+    {
+        LOG_E("mqtt client is not connected.");
+        return -1;
+    }
+
+    return umqtt_unsubscribe(m_umqtt_client, argv[1]);
+}
+
+#ifdef FINSH_USING_MSH
+MSH_CMD_EXPORT(umqtt_ex_start, startup umqtt client);
+MSH_CMD_EXPORT(umqtt_ex_stop, stop umqtt client);
+MSH_CMD_EXPORT(umqtt_ex_publish, umqtt publish message to specified topic);
+MSH_CMD_EXPORT(umqtt_ex_subscribe, umqtt subscribe topic);
+MSH_CMD_EXPORT(umqtt_ex_unsubscribe, umqtt unsubscribe topic);
+#endif
+

+ 302 - 0
src/pkgs/umqtt_pkgs_decode.c

@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-05-06    springcity      the first version
+ */
+
+#include "umqtt_cfg.h"
+#include "umqtt_internal.h"
+#include "umqtt.h"
+
+#define DBG_TAG             "umqtt.decode"
+
+#ifdef PKG_UMQTT_USING_DEBUG
+#define DBG_LVL             DBG_LOG
+#else
+#define DBG_LVL             DBG_INFO
+#endif                      /* MQTT_DEBUG */
+#include <rtdbg.h>
+
+static unsigned char* bufptr;
+
+static int bufchar(unsigned char* c, int count)
+{
+	int i;
+	for (i = 0; i < count; ++i)
+		*c = *bufptr++;
+	return count;
+}
+
+static int umqtt_pkgs_decodeBuf(unsigned char* buf, int* value)
+{
+	bufptr = buf;
+	return umqtt_pkgs_decode(bufchar, value);
+}
+
+static int MQTTDeserialize_publish(struct umqtt_msg *pub_msg, rt_uint8_t *buf, int buflen)
+{
+    unsigned char *curdata = buf;
+    unsigned char *enddata = NULL;
+    int rc = 0;
+    int mylen = 0;
+
+    pub_msg->header.byte = umqtt_readChar(&curdata);
+    if (pub_msg->header.bits.type != UMQTT_TYPE_PUBLISH) 
+    {
+        LOG_E(" decode datas, is not publish type! type:%d", pub_msg->header.bits.type);
+        rc = UMQTT_DECODE_ERROR;
+        goto exit;
+    }
+
+    curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen));
+    enddata = curdata + mylen;
+    
+    if (!umqtt_readlenstring(&(pub_msg->msg.publish.topic_name_len), &(pub_msg->msg.publish.topic_name), &curdata, enddata) 
+     || (enddata - curdata < 0)) 
+    {  
+        LOG_E(" decode publish, topic name error!");
+        rc = UMQTT_DECODE_ERROR;
+        goto exit;
+    }
+
+    if (pub_msg->header.bits.qos > 0) 
+        pub_msg->msg.publish.packet_id = umqtt_readInt(&curdata);
+    
+    pub_msg->msg.publish.payload_len = enddata - curdata;
+    pub_msg->msg.publish.payload = curdata;
+exit:
+    return rc;
+}
+
+static int MQTTDeserialize_ack(struct umqtt_msg *puback_msg, rt_uint8_t *buf, int buflen)
+{
+    unsigned char *curdata = buf;
+    unsigned char *enddata = NULL;
+    int rc = 0;
+    int mylen;
+
+    puback_msg->header.byte = umqtt_readChar(&curdata);
+    
+    curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen));
+    enddata = curdata + mylen;
+
+    if (enddata - curdata < 2)
+    {
+        rc = UMQTT_DECODE_ERROR;
+        goto exit;
+    }
+    puback_msg->msg.puback.packet_id = umqtt_readInt(&curdata);
+
+exit:
+    return rc;
+}
+
+static int umqtt_connack_decode(struct umqtt_pkgs_connack *connack_msg, rt_uint8_t* buf, int buflen)
+{
+    MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+
+	header.byte = umqtt_readChar(&curdata);
+	if (header.bits.type != UMQTT_TYPE_CONNACK)
+    {
+        rc = UMQTT_FAILED;
+        LOG_E(" not connack type!");
+		goto exit;
+    }
+
+	curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+	if (enddata - curdata < 2)
+    {
+        LOG_D(" enddata:%d, curdata:%d, mylen:%d", enddata, curdata, mylen);
+		goto exit;
+    }
+
+    connack_msg->connack_flags.connack_sign = umqtt_readChar(&curdata);
+    connack_msg->ret_code = umqtt_readChar(&curdata);
+exit:
+	return rc;
+}
+
+
+// static int umqtt_pingresp_decode();
+/** 
+ * parse the publish datas 
+ *
+ * @param publish_msg the output datas
+ * @param buf the input datas need to parse
+ * @param buflen the input buffer length
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+static int umqtt_publish_decode(struct umqtt_msg *publish_msg, rt_uint8_t *buf, int buflen)
+{
+    return MQTTDeserialize_publish(publish_msg, buf, buflen);
+}
+/** 
+ * parse the puback datas 
+ *
+ * @param puback_msg the output datas
+ * @param buf the input datas need to parse
+ * @param buflen the input buffer length
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+static int umqtt_puback_decode(struct umqtt_msg *puback_msg, rt_uint8_t *buf, int buflen)
+{
+    return MQTTDeserialize_ack(puback_msg, buf, buflen);
+}
+// static int umqtt_pubrec_decode();       // 
+// static int umqtt_pubrel_decode();       // 
+// static int umqtt_pubcomp_decode();      // 
+
+/** 
+ * parse the suback datas 
+ *
+ * @param suback_msg the output datas
+ * @param buf the input datas need to parse
+ * @param buflen the input buffer length
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+static int umqtt_suback_decode(struct umqtt_pkgs_suback *suback_msg, rt_uint8_t *buf, int buflen)
+{
+	MQTTHeader header = {0};
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+
+	header.byte = umqtt_readChar(&curdata);
+	if (header.bits.type != UMQTT_TYPE_SUBACK)
+    {
+        rc = UMQTT_FAILED;
+		goto exit;
+    }
+
+	curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+	if (enddata - curdata < 2)
+    {
+        rc = UMQTT_DECODE_ERROR;
+		goto exit;
+    }
+
+	suback_msg->packet_id = umqtt_readInt(&curdata);
+
+	suback_msg->topic_count = 0;
+	while (curdata < enddata)
+	{
+		if (suback_msg->topic_count > PKG_UMQTT_SUBRECV_DEF_LENGTH) 
+        {
+            rc = UMQTT_FAILED;
+			goto exit;
+		}
+        suback_msg->ret_qos[(suback_msg->topic_count)++] = umqtt_readChar(&curdata);
+	}
+
+exit:
+	return rc;
+}
+
+/** 
+ * parse the unsuback datas 
+ *
+ * @param unsuback_msg the output datas
+ * @param buf the input datas need to parse
+ * @param buflen the input buffer length
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+static int umqtt_unsuback_decode(struct umqtt_pkgs_unsuback *unsuback_msg, rt_uint8_t *buf, int buflen)
+{
+	unsigned char* curdata = buf;
+	unsigned char* enddata = NULL;
+	int rc = 0;
+	int mylen;
+
+	curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); /* read remaining length */
+	enddata = curdata + mylen;
+
+	if (enddata - curdata < 2)
+    {
+        rc = UMQTT_DECODE_ERROR;
+		goto exit;
+    }
+	unsuback_msg->packet_id = umqtt_readInt(&curdata);
+
+exit:
+	return rc;
+}
+
+/** 
+ * parse the data according to the format
+ *
+ * @param recv_buf the input, the raw buffer data, of the correct length determined by the remaining length field
+ * @param recv_buf_len the input, the length in bytes of the data in the supplied buffer
+ * @param message the output datas
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+int umqtt_decode(rt_uint8_t *recv_buf, size_t recv_buf_len, struct umqtt_msg *message)
+{
+    int _ret = 0;
+	rt_uint8_t* curdata = recv_buf;
+    enum umqtt_type type;
+    if (message == RT_NULL) 
+    {
+        _ret = UMQTT_INPARAMS_NULL;
+        LOG_E(" umqtt decode inparams null!");
+        goto exit;
+    }
+    
+    message->header.byte = umqtt_readChar(&curdata);
+    type = message->header.bits.type;
+
+    switch (type)
+    {
+    case UMQTT_TYPE_CONNACK:
+        _ret = umqtt_connack_decode(&(message->msg.connack), recv_buf, recv_buf_len);
+        break;
+    case UMQTT_TYPE_PUBLISH:
+        _ret = umqtt_publish_decode(message, recv_buf, recv_buf_len);
+        break;
+    case UMQTT_TYPE_PUBACK:
+        _ret = umqtt_puback_decode(message, recv_buf, recv_buf_len);
+        break;
+    case UMQTT_TYPE_PUBREC:
+        // _ret = umqtt_pubrec_decode();
+        break;
+    case UMQTT_TYPE_PUBREL:
+        // _ret = umqtt_pubrel_decode();
+        break;
+    case UMQTT_TYPE_PUBCOMP:
+        // _ret = umqtt_pubcomp_decode();
+        break;
+    case UMQTT_TYPE_SUBACK:
+        _ret = umqtt_suback_decode(&(message->msg.suback), recv_buf, recv_buf_len);
+        break;
+    case UMQTT_TYPE_UNSUBACK:
+        _ret = umqtt_unsuback_decode(&(message->msg.unsuback), recv_buf, recv_buf_len);
+        break;
+    case UMQTT_TYPE_PINGRESP:
+        // _ret = umqtt_pingresp_encode();
+        break;
+    default:
+        break;
+    }
+exit:
+    return _ret;
+}

+ 463 - 0
src/pkgs/umqtt_pkgs_encode.c

@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-04-30    springcity      the first version
+ */
+
+#include "umqtt_cfg.h"
+#include "umqtt_internal.h"
+
+#define DBG_TAG             "umqtt.encode"
+
+#ifdef PKG_UMQTT_USING_DEBUG
+#define DBG_LVL             DBG_LOG
+#else
+#define DBG_LVL             DBG_INFO
+#endif                      /* MQTT_DEBUG */
+#include <rtdbg.h>
+
+static int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
+{
+    int len = 0;
+    if (options->protocol_level == 3)                                   /* MQTT V3.1 */ 
+        len = 12;                                                       /* variable depending on MQTT or MQIsdp */
+    else if (options->protocol_level == 4)                              /* MQTT V3.1.1 */ 
+        len = 10;
+    
+    len += MQTTStrlen(options->client_id) + 2;
+    if (options->connect_flags.bits.will_flag) 
+        len += MQTTStrlen(options->will_topic) + 2 + MQTTStrlen(options->will_message) + 2;
+    
+    if (options->connect_flags.bits.password_flag) 
+    {
+        len += MQTTStrlen(options->password) + 2;
+        if (options->connect_flags.bits.username_flag) 
+            len += MQTTStrlen(options->user_name) + 2;
+    }
+    return len;
+}
+
+static int MQTTSerialize_subscribeLength(struct umqtt_pkgs_subscribe *params)
+{
+    int _cnt = 0;
+    int len = 2;
+    if (params && (params->topic_count > 0)) 
+    {
+        for (_cnt = 0; _cnt < params->topic_count; _cnt++) 
+            len += 2 + MQTTStrlen(params->topic_filter[_cnt].topic_filter) + 1;
+    } 
+    else 
+        len = 0;
+    return len;
+}
+
+static int MQTTSerialize_unsubscribeLength(struct umqtt_pkgs_unsubscribe *params)
+{
+	int i;
+	int len = 2;                                                        /* packetid */
+    if (params && (params->topic_count > 0)) 
+    {
+        for (i = 0; i < params->topic_count; ++i)
+            len += 2 + MQTTStrlen(params->topic_filter[i].topic_filter);/* length + topic*/
+    } 
+    else 
+        len = 0;
+    return len;
+}
+
+static int MQTTSerialize_publishLength(int qos, struct umqtt_pkgs_publish *params)
+{
+    int len = 0;
+    len += 2 + params->topic_name_len + params->payload_len;
+    if (qos > 0)
+        len += 2;
+    
+    return len;
+}
+
+static int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = { 0 };
+	int len = 0;
+	int rc = -1;
+
+	if (umqtt_pkgs_len(len = MQTTSerialize_connectLength(options)) > buflen) 
+    {
+		rc = UMQTT_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = UMQTT_TYPE_CONNECT;
+	umqtt_writeChar(&ptr, header.byte);                                 /* write header */
+
+	ptr += umqtt_pkgs_encode(ptr, len);                                 /* write remaining length */
+
+	if (options->protocol_level == 4) 
+    {
+		umqtt_writeCString(&ptr, "MQTT");
+		umqtt_writeChar(&ptr, (char) 4);
+	} 
+    else 
+    {
+		umqtt_writeCString(&ptr, "MQIsdp");
+		umqtt_writeChar(&ptr, (char) 3);
+	}
+
+	umqtt_writeChar(&ptr, options->connect_flags.connect_sign);
+	umqtt_writeInt(&ptr, options->keepalive_interval_sec);
+	// umqtt_writeInt(&ptr, PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME);                                       /* ping interval max, 0xffff */ 
+	umqtt_writeMQTTString(&ptr, options->client_id);
+	if (options->connect_flags.bits.will_flag) 
+    {
+		umqtt_writeMQTTString(&ptr, options->will_topic);
+		umqtt_writeMQTTString(&ptr, options->will_message);
+	}
+
+	if (options->connect_flags.bits.username_flag)
+		umqtt_writeMQTTString(&ptr, options->user_name);
+	if (options->connect_flags.bits.password_flag)
+		umqtt_writeMQTTString(&ptr, options->password);
+
+	rc = ptr - buf;
+
+exit:
+	return rc;
+}
+
+static int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
+{
+	MQTTHeader header = { 0 };
+	int rc = -1;
+	unsigned char *ptr = buf;
+
+	if (buflen < 2) 
+    {
+		rc = UMQTT_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+	header.byte = 0;
+	header.bits.type = packettype;
+	umqtt_writeChar(&ptr, header.byte);                                 /* write header */
+
+	ptr += umqtt_pkgs_encode(ptr, 0);                                   /* write remaining length */
+	rc = ptr - buf;
+exit:
+    return rc;
+}
+
+static int MQTTSerialize_subscribe(unsigned char* buf, int buflen, struct umqtt_pkgs_subscribe *params)
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = { 0 };
+	int rem_len = 0;
+	int rc = 0;
+	int i = 0;
+
+	if (umqtt_pkgs_len(rem_len = MQTTSerialize_subscribeLength(params)) > buflen) 
+    {
+		rc = UMQTT_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = UMQTT_TYPE_SUBSCRIBE;
+	header.bits.dup = 0;
+	header.bits.qos = 1;
+	umqtt_writeChar(&ptr, header.byte);                                 /* write header */
+
+	ptr += umqtt_pkgs_encode(ptr, rem_len);                             /* write remaining length */;
+
+	umqtt_writeInt(&ptr, params->packet_id);
+
+	for (i = 0; i < params->topic_count; ++i) 
+    {
+		umqtt_writeMQTTString(&ptr, params->topic_filter[i].topic_filter);
+		umqtt_writeChar(&ptr, params->topic_filter[i].req_qos.request_qos);
+	}
+
+	rc = ptr - buf;
+
+exit:
+	return rc;
+}
+
+static int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, struct umqtt_pkgs_unsubscribe *params)
+{
+	unsigned char *ptr = buf;
+	MQTTHeader header = { 0 };
+	int rem_len = 0;
+	int rc = 0;
+	int i = 0;
+
+	if (umqtt_pkgs_len(rem_len = MQTTSerialize_unsubscribeLength(params)) > buflen) 
+    {
+		rc = UMQTT_BUFFER_TOO_SHORT;
+		goto exit;
+	}
+
+	header.byte = 0;
+	header.bits.type = UMQTT_TYPE_UNSUBSCRIBE;
+	header.bits.dup = 0;
+	header.bits.qos = 1;
+	umqtt_writeChar(&ptr, header.byte);                                 /* write header */
+
+	ptr += umqtt_pkgs_encode(ptr, rem_len);                             /* write remaining length */;
+
+	umqtt_writeInt(&ptr, params->packet_id);
+
+	for (i = 0; i < params->topic_count; ++i)
+		umqtt_writeCString(&ptr, params->topic_filter[i].topic_filter);
+
+	rc = ptr - buf;
+exit:
+	return rc;
+}
+
+static int MQTTSerialize_publish(unsigned char* buf, int buflen, int dup, int qos, struct umqtt_pkgs_publish *message)
+{
+    unsigned char *ptr = buf;
+    MQTTHeader header = { 0 };
+    int rem_len = 0;
+    int rc = 0;
+
+    if (umqtt_pkgs_len(rem_len = MQTTSerialize_publishLength(qos, message)) > buflen) 
+    {
+        rc = UMQTT_BUFFER_TOO_SHORT;
+        goto exit;
+    }
+    
+    header.bits.type = UMQTT_TYPE_PUBLISH;
+    header.bits.dup = dup;
+    header.bits.qos = qos;
+    umqtt_writeChar(&ptr, header.byte);
+    ptr += umqtt_pkgs_encode(ptr, rem_len);
+
+	umqtt_writeCString(&ptr, message->topic_name);
+
+    if (qos > 0) 
+        umqtt_writeInt(&ptr, message->packet_id);
+
+    memcpy(ptr, message->payload, message->payload_len);
+    ptr += message->payload_len;
+
+    rc = ptr - buf;
+exit:
+    return rc;
+}
+
+static 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;
+
+    if (buflen < 4) 
+    {
+        rc = UMQTT_BUFFER_TOO_SHORT;
+        goto exit;
+    }
+    header.bits.type = packettype;
+    header.bits.dup = dup;
+    header.bits.qos = (packettype == UMQTT_TYPE_PUBREL) ? 1 : 0;
+    umqtt_writeChar(&ptr, header.byte);
+
+    ptr += umqtt_pkgs_encode(ptr, 2);
+    umqtt_writeInt(&ptr, packetid);
+    rc = ptr - buf;
+exit:
+    return rc;
+}
+
+/** 
+ * packaging the connect data
+ *
+ * @param buf the output send buf, result of the package
+ * @param buflen the output send buffer length
+ * @param params the input, connect params
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_connect_encode(rt_uint8_t *send_buf, size_t send_len, struct umqtt_pkgs_connect *params) 
+{
+    return MQTTSerialize_connect(send_buf, send_len, params);
+}
+
+/** 
+ * packaging the disconnect data
+ *
+ * @param buf the output send buf, result of the package
+ * @param buflen the output send buffer length
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_disconnect_encode(rt_uint8_t *send_buf, size_t send_len)
+{
+    return MQTTSerialize_zero(send_buf, send_len, UMQTT_TYPE_DISCONNECT);
+}
+
+/** 
+ * packaging the pingreq data
+ *
+ * @param buf the output send buf, result of the package
+ * @param buflen the output send buffer length
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_pingreq_encode(rt_uint8_t *send_buf, size_t send_len)
+{
+    return MQTTSerialize_zero(send_buf, send_len, UMQTT_TYPE_PINGREQ);
+}
+
+/** 
+ * packaging the puback data
+ *
+ * @param buf the output send buf, result of the package
+ * @param buflen the output send buffer length
+ * @param message the input message
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_publish_encode(unsigned char *buf, int buflen, int dup, int qos, struct umqtt_pkgs_publish *message)
+{
+    return MQTTSerialize_publish(buf, buflen, dup, qos, message);
+}
+
+/** 
+ * packaging the puback data
+ *
+ * @param buf the output send buf, result of the package
+ * @param buflen the output send buffer length
+ * @param packetid the input pakcet id in message
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_puback_encode(unsigned char *buf, int buflen, unsigned short packetid)
+{
+    return MQTTSerialize_ack(buf, buflen, UMQTT_TYPE_PUBACK, 0, packetid);
+}
+// static int umqtt_pubrec_encode();
+
+/** 
+ * packaging the pubcomp data
+ *
+ * @param buf the output send buf, result of the package
+ * @param buflen the output send buffer length
+ * @param dup the input Duplicate delivery of a PUBLISH packet
+ * @param packetid the input pakcet id in message
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_pubrel_encode(unsigned char *buf, int buflen, unsigned char dup, unsigned short packetid)
+{
+    return MQTTSerialize_ack(buf, buflen, UMQTT_TYPE_PUBREL, dup, packetid);
+}
+
+/** 
+ * packaging the pubcomp data
+ *
+ * @param buf the output send buf, result of the package
+ * @param buf_len the output send buffer length
+ * @param packetid the input pakcet id in message
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_pubcomp_encode(unsigned char *buf, int buflen, unsigned short packetid)
+{
+    return MQTTSerialize_ack(buf, buflen, UMQTT_TYPE_PUBCOMP, 0, packetid);
+}
+
+/** 
+ * packaging the subscribe data
+ *
+ * @param send_buf the output send buf, result of the package
+ * @param send_len the output send buffer length
+ * @param params the input message
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_subscribe_encode(rt_uint8_t *send_buf, size_t send_len, struct umqtt_pkgs_subscribe *params)
+{
+    return MQTTSerialize_subscribe(send_buf, send_len, params);
+}
+
+/** 
+ * packaging the unsubscribe data
+ *
+ * @param send_buf the output send buf, result of the package
+ * @param send_len the output send buffer length
+ * @param params the input message
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+static int umqtt_unsubscribe_encode(rt_uint8_t *send_buf, size_t send_len, struct umqtt_pkgs_unsubscribe *params)
+{
+    return MQTTSerialize_unsubscribe(send_buf, send_len, params);
+}
+
+/** 
+ * packaging the data according to the format
+ *
+ * @param type the input packaging type
+ * @param send_buf the output send buf, result of the package
+ * @param send_len the output send buffer length
+ * @param message the input message
+ *
+ * @return <=0: failed or other error
+ *         >0: package data length
+ */
+int umqtt_encode(enum umqtt_type type, rt_uint8_t *send_buf, size_t send_len, struct umqtt_msg *message)
+{
+    int _ret = 0;
+    switch (type)
+    {
+    case UMQTT_TYPE_CONNECT:
+        _ret = umqtt_connect_encode(send_buf, send_len, &(message->msg.connect));
+        break;
+    case UMQTT_TYPE_PUBLISH:
+        _ret = umqtt_publish_encode(send_buf, send_len, message->header.bits.dup, message->header.bits.qos, &(message->msg.publish));
+        break;
+    case UMQTT_TYPE_PUBACK:
+        _ret = umqtt_puback_encode(send_buf, send_len, message->msg.puback.packet_id);
+        break;
+    case UMQTT_TYPE_PUBREC:
+        // _ret = umqtt_pubrec_encode();
+        break;
+    case UMQTT_TYPE_PUBREL:
+        _ret = umqtt_pubrel_encode(send_buf, send_len, message->header.bits.dup, message->msg.pubrel.packet_id);
+        break;
+    case UMQTT_TYPE_PUBCOMP:
+        _ret = umqtt_pubcomp_encode(send_buf, send_len, message->msg.pubcomp.packet_id);
+        break;
+    case UMQTT_TYPE_SUBSCRIBE:
+        _ret = umqtt_subscribe_encode(send_buf, send_len, &(message->msg.subscribe));
+        break;
+    case UMQTT_TYPE_UNSUBSCRIBE:
+        _ret = umqtt_unsubscribe_encode(send_buf, send_len, &(message->msg.unsubscribe));
+        break;
+    case UMQTT_TYPE_PINGREQ:
+        _ret = umqtt_pingreq_encode(send_buf, send_len);
+        break;
+    case UMQTT_TYPE_DISCONNECT:
+        _ret = umqtt_disconnect_encode(send_buf, send_len);
+        break;
+    default:
+        break;
+    }
+    return _ret;
+}
+

+ 280 - 0
src/trans/umqtt_transport.c

@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-05-07    springcity      the first version
+ */
+
+#include <string.h>
+
+#include "umqtt_cfg.h"
+#include "umqtt_internal.h"
+#include "umqtt.h"
+
+#include <rtthread.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <sal_tls.h>
+
+#define DBG_TAG             "umqtt.transport"
+
+#ifdef PKG_UMQTT_USING_DEBUG
+#define DBG_LVL             DBG_LOG
+#else
+#define DBG_LVL             DBG_INFO
+#endif                      /* MQTT_DEBUG */
+#include <rtdbg.h>
+
+#ifdef UMQTT_USING_TLS
+#define UMQTT_SOCKET_PROTOCOL           PROTOCOL_TLS
+#else
+#define UMQTT_SOCKET_PROTOCOL           0
+#endif
+
+/*
+ * resolve server address
+ * @param server the server sockaddress
+ * @param url the input URL address.
+ * @param host_addr the buffer pointer to save server host address
+ * @param request the pointer to point the request url, for example, /index.html
+ *
+ * @return 0 on resolve server address OK, others failed
+ *
+ * URL example:
+ * tcp://192.168.10.151:1883
+ * tls://192.168.10.151:61614
+ * tcp://[fe80::20c:29ff:fe9a:a07e]:1883
+ * tls://[fe80::20c:29ff:fe9a:a07e]:61614
+ * ws://echo.websocket.org:80 websocket
+ */
+static int umqtt_resolve_uri(const char *umqtt_uri, struct addrinfo **res)
+{
+    int rc = 0;
+    int uri_len = 0, host_addr_len = 0, port_len = 0;
+    char *ptr;
+    char port_str[6] = { 0 };           /* default port of mqtt(http) */
+
+    struct addrinfo hint;
+    int ret;
+
+    const char *host_addr = 0;
+    char *host_addr_new = RT_NULL;
+    const char *uri = umqtt_uri;
+    uri_len = strlen(uri);
+
+    /* strip protocol(tcp or ssl) */
+    if (strncmp(uri, "tcp://", 6) == 0) 
+    {
+        host_addr = uri + 6;
+    } 
+    else if (strncmp(uri, "ssl://", 6) == 0) 
+    {
+        host_addr = uri + 6;
+    // } else if (strncmp(uri, "ws://", 5) == 0) {
+    } 
+    else 
+    {
+        rc = UMQTT_INPARAMS_NULL;
+        goto exit;
+    }
+
+    if (host_addr[0] == '[')    /* ipv6 address */
+    {  
+        host_addr += 1;
+        ptr = strstr(host_addr, "]");
+        if (!ptr) {
+            rc = UMQTT_INPARAMS_NULL;
+            goto exit;
+        }
+        host_addr_len = ptr - host_addr;
+        if ((host_addr_len < 1) || (host_addr_len > uri_len)) {
+            rc = UMQTT_INPARAMS_NULL;
+            goto exit;
+        }
+
+        port_len = uri_len - 6 - host_addr_len - 3;
+        if ((port_len >= 6) || (port_len < 1)) {
+            rc = UMQTT_INPARAMS_NULL;
+            goto exit;
+        }
+
+        strncpy(port_str, host_addr + host_addr_len + 2, port_len);
+        port_str[port_len] = '\0';
+        // LOG_D("ipv6 address port: %s", port_str);
+    } 
+    else    /* ipv4 or domain. */
+    {                    
+        ptr = strstr(host_addr, ":");
+        if (!ptr) 
+        {
+            rc = UMQTT_INPARAMS_NULL;
+            goto exit;
+        }
+        host_addr_len = ptr - host_addr;
+        if ((host_addr_len < 1) || (host_addr_len > uri_len)) 
+        {
+            rc = UMQTT_INPARAMS_NULL;
+            goto exit;
+        }
+        port_len = uri_len - 6 - host_addr_len - 1;
+        if ((port_len >= 6) || (port_len < 1)) 
+        {
+            rc = UMQTT_INPARAMS_NULL;
+            goto exit;
+        }
+
+        strncpy(port_str, host_addr + host_addr_len + 1, port_len);
+        port_str[port_len] = '\0';
+        // LOG_D("ipv4 address port: %s", port_str);
+    }
+
+    /* get host addr ok. */
+    {
+        /* resolve the host name. */
+        host_addr_new = rt_malloc(host_addr_len + 1);
+
+        if (!host_addr_new) 
+        {
+            rc = UMQTT_MEM_FULL;
+            goto exit;
+        }
+
+        memcpy(host_addr_new, host_addr, host_addr_len);
+        host_addr_new[host_addr_len] = '\0';
+
+        memset(&hint, 0, sizeof(hint));
+
+        ret = getaddrinfo(host_addr_new, port_str, &hint, res);
+        if (ret != 0) 
+        {
+            LOG_E("getaddrinfo err: %d '%s'", ret, host_addr_new);
+            rc = UMQTT_FAILED;
+            goto exit;
+        }
+    }
+
+exit:
+    if (host_addr_new != RT_NULL) 
+    {
+        rt_free(host_addr_new);
+        host_addr_new = RT_NULL;
+    }
+    return rc;
+}
+
+/** 
+ * TCP/TLS Connection Complete for configured transport
+ *
+ * @param uri the input server URI address
+ * @param sock the output socket
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+int umqtt_trans_connect(const char *uri, int *sock)
+{
+    int _ret = 0;
+    struct addrinfo *addr_res = RT_NULL;
+    
+    *sock = -1;
+    _ret = umqtt_resolve_uri(uri, &addr_res);
+    if ((_ret < 0) || (addr_res == RT_NULL)) 
+    {
+        LOG_E("resolve uri err");
+        _ret = UMQTT_FAILED;
+        goto exit;
+    }
+
+    if ((*sock = socket(addr_res->ai_family, SOCK_STREAM, UMQTT_SOCKET_PROTOCOL)) < 0) 
+    {
+        LOG_E("create socket error!");
+        _ret = UMQTT_FAILED;
+        goto exit;
+    }
+
+    _ret = ioctlsocket(*sock, FIONBIO, 0);
+    if (_ret < 0) 
+    {
+        LOG_E(" iocontrol socket error!");
+        _ret = UMQTT_FAILED;
+        goto exit;
+    }
+
+    if ((_ret = connect(*sock, addr_res->ai_addr, addr_res->ai_addrlen)) < 0) 
+    {
+        LOG_E(" connect err!");
+        closesocket(*sock);
+        *sock = -1;
+        _ret = UMQTT_FAILED;
+        goto exit;
+    }
+
+exit:
+    if (addr_res) {
+        freeaddrinfo(addr_res);
+        addr_res = RT_NULL;
+    }
+    return _ret;
+}
+
+/** 
+ * TCP/TLS transport disconnection requests on configured transport.
+ *
+ * @param sock the input socket
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+int umqtt_trans_disconnect(int sock)
+{
+    int _ret = 0;
+    _ret = closesocket(sock);
+    if (_ret < 0) 
+        return -errno;
+    return _ret;
+}
+
+/** 
+ * TCP/TLS send datas on configured transport.
+ *
+ * @param sock the input socket
+ * @param send_buf the input, transport datas buffer
+ * @param buf_len the input, transport datas buffer length
+ * @param timeout the input, tcp/tls transport timeout
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+int umqtt_trans_send(int sock, const rt_uint8_t *send_buf, rt_uint32_t buf_len, int timeout)
+{
+    int _ret = 0;
+    rt_uint32_t offset = 0U;
+    while (offset < buf_len) 
+    {
+        _ret = send(sock, send_buf + offset, buf_len - offset, 0);
+        if (_ret < 0) 
+            return -errno;
+        offset += _ret;
+    }
+
+    return _ret;
+}
+
+/** 
+ * TCP/TLS receive datas on configured transport.
+ *
+ * @param sock the input socket
+ * @param recv_buf the output, receive datas buffer
+ * @param buf_len the input, receive datas buffer length
+ *
+ * @return <=0: failed or other error
+ *         >0: receive datas length
+ */
+int umqtt_trans_recv(int sock, rt_uint8_t *recv_buf, rt_uint32_t buf_len)
+{
+    return recv(sock, recv_buf, buf_len, 0);
+    // return read(sock, recv_buf, buf_len);
+}

+ 1987 - 0
src/umqtt.c

@@ -0,0 +1,1987 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-05-08    springcity      the first version
+ */
+
+#include <string.h>
+
+#include <rtthread.h>
+#include <rtdef.h>
+
+#include "umqtt_cfg.h"
+#include "umqtt_internal.h"
+#include "umqtt.h"
+
+#define DBG_TAG             "umqtt"
+
+#ifdef PKG_UMQTT_USING_DEBUG
+#define DBG_LVL             DBG_LOG
+#else
+#define DBG_LVL             DBG_INFO
+#endif                      /* MQTT_DEBUG */
+#include <rtdbg.h>
+
+#define MAX_NO_OF_REMAINING_LENGTH_BYTES                    4
+
+#define UMQTT_CLIENT_LOCK(CLIENT)                           rt_mutex_take(CLIENT->lock_client, RT_WAITING_FOREVER)
+#define UMQTT_CLIENT_UNLOCK(CLIENT)                         rt_mutex_release(CLIENT->lock_client)
+
+#define UMQTT_SET_CONNECT_FLAGS(user_name_flag, password_flag, will_retain, will_qos, will_flag, clean_session, reserved)    \
+    (((user_name_flag & 0x01) << 7) |    \
+    ((password_flag & 0x01) << 6) |  \
+    ((will_retain & 0x01) << 5) |   \
+    ((will_qos & 0x01) << 3) |   \
+    ((will_flag & 0x01) << 2) | \
+    ((clean_session & 0x01) << 1) | \
+    (reserved & 0x01))
+#define UMQTT_DEF_CONNECT_FLAGS                             (UMQTT_SET_CONNECT_FLAGS(0,0,0,0,0,1,0))
+
+struct umqtt_msg_ack
+{
+    rt_uint16_t packet_id;
+    rt_uint8_t msg_type;
+};
+
+struct umqtt_qos2_msg
+{
+    rt_uint16_t topic_name_len;
+    char *topic_name;
+    rt_uint16_t packet_id;
+    char *payload;
+    rt_uint32_t payload_len;
+    rt_list_t next_list;
+};
+
+struct umqtt_pubrec_msg
+{
+    int cnt;
+    int packet_id;
+    int next_tick;                                              /* next tick*/
+};
+
+struct umqtt_client
+{
+    int sock;                                                   /* socket sock */ 
+    enum umqtt_client_state connect_state;                      /* mqtt client status */ 
+
+    struct umqtt_info mqtt_info;                                /* user mqtt config information */ 
+    rt_uint8_t reconnect_count;                                 /* mqtt client reconnect count */ 
+    rt_uint8_t keepalive_count;                                 /* mqtt keepalive count */ 
+    rt_uint32_t pingreq_last_tick;                              /* mqtt ping request message */
+    rt_uint32_t uplink_next_tick;                               /* uplink (include: publish/subscribe/unsub/connect/ping/... client->broker) next tick(ping) */ 
+    rt_uint32_t uplink_last_tick;                               /* uplink (include: publish/subscribe/unsub/connect/ping/... client->broker) next tick(ping) */ 
+    rt_uint32_t reconnect_next_tick;                            /* client unlink, reconnect next tick */ 
+    rt_uint32_t reconnect_last_tick;                            /* client unlink, reconnect last tick */ 
+
+    rt_uint8_t *send_buf, *recv_buf;                            /* send data buffer, receive data buffer */ 
+    rt_size_t send_len, recv_len;                               /* send datas length, receive datas length */ 
+
+    rt_uint16_t packet_id;                                      /* mqtt packages id */ 
+
+    rt_mutex_t lock_client;                                     /* mqtt client lock */ 
+    rt_mq_t msg_queue;                                          /* fro receive thread with other thread to communicate message */ 
+
+    rt_timer_t uplink_timer;                                    /* client send message to broker manager timer */ 
+
+    int sub_recv_list_len;                                      /* subscribe topic, receive topicname to deal datas */ 
+    rt_list_t sub_recv_list;                                    /* subscribe information list header */ 
+
+    rt_list_t qos2_msg_list;                                    /* qos2 message list */
+    struct umqtt_pubrec_msg pubrec_msg[PKG_UMQTT_QOS2_QUE_MAX]; /* pubrec message array */                   
+
+    umqtt_user_callback user_handler;                           /* user handler */ 
+
+    void *user_data;                                            /* user data */ 
+    rt_thread_t task_handle;                                    /* task thread */ 
+
+    rt_list_t list;                                             /* list header */ 
+};
+
+enum tick_item
+{
+    UPLINK_LAST_TICK        = 0,
+    UPLINK_NEXT_TICK        = 1,
+    RECON_LAST_TICK         = 2,
+    RECON_NEXT_TICK         = 3,
+};
+
+static int umqtt_handle_readpacket(struct umqtt_client *client);
+static void umqtt_deliver_message(struct umqtt_client *client, 
+                                const char *topic_name, int len, 
+                                struct umqtt_pkgs_publish *msg);
+
+static int get_next_packetID(struct umqtt_client *client)
+{
+    return client->packet_id = (client->packet_id == UMQTT_MAX_PACKET_ID) ? 1 : (client->packet_id + 1);
+}
+
+static int add_one_qos2_msg(struct umqtt_client *client, struct umqtt_pkgs_publish *pdata)
+{
+    int _ret = UMQTT_OK;
+    struct umqtt_qos2_msg *msg = RT_NULL;
+    if ((pdata) && (client))
+    {
+        if (rt_list_len(&client->qos2_msg_list) >= PKG_UMQTT_QOS2_QUE_MAX)
+        {
+            LOG_W(" qos2 message list is over !");
+        }
+        else
+        {
+            msg = (struct umqtt_qos2_msg *)rt_calloc(1, sizeof(struct umqtt_qos2_msg));
+            if (msg)
+            {
+                msg->topic_name_len = pdata->topic_name_len;
+                msg->packet_id = pdata->packet_id;
+                msg->payload_len = pdata->payload_len;
+                if ((pdata->topic_name != RT_NULL) && (pdata->topic_name_len != 0)) 
+                {
+                    msg->topic_name = (char *)rt_calloc(1, sizeof(char) * pdata->topic_name_len);
+                    if (msg->topic_name) 
+                    {
+                        rt_strncpy(msg->topic_name, pdata->topic_name, msg->topic_name_len);
+                    }
+                    else
+                    {
+                        _ret = UMQTT_MEM_FULL;
+                        LOG_E(" calloc umqtt qos2 message topic name failed! memory full! ");
+                    }
+                }
+                if ((pdata->payload != RT_NULL) && (pdata->payload_len != 0))
+                {
+                    msg->payload = (char *)rt_calloc(1, sizeof(char) * pdata->payload_len);
+                    if (msg->payload)
+                    {
+                        rt_strncpy(msg->payload, pdata->payload, msg->payload_len);
+                    }
+                    else
+                    {
+                        _ret = UMQTT_MEM_FULL;
+                        LOG_E(" calloc umqtt qos2 message payload failed! memory full! ");                        
+                    }
+                }
+            }
+            else
+            {
+                _ret = UMQTT_MEM_FULL;
+                LOG_E(" calloc umqtt qos2 message failed! ");
+            }
+        }
+    }
+    else
+    {
+        _ret = UMQTT_INPARAMS_NULL;
+        LOG_E(" add qos2 message failed! input params is valid! ");
+    }
+
+_exit:
+    if (_ret == UMQTT_MEM_FULL)
+    {
+        if (msg)
+        {
+            if (msg->topic_name) { rt_free(msg->topic_name); msg->topic_name = RT_NULL; }
+            if (msg->payload) { rt_free(msg->payload); msg->payload = RT_NULL; }
+            rt_free(msg); msg = RT_NULL;
+        }
+    }
+
+    return _ret;
+}
+
+static int qos2_publish_delete(struct umqtt_client *client, int packet_id)
+{
+    int _ret = UMQTT_OK, tmp_ret = 0;
+    struct umqtt_qos2_msg *p_msg = RT_NULL;
+    struct umqtt_qos2_msg *p_msg_tmp = RT_NULL;
+    struct umqtt_pkgs_publish publish_msg = { 0 };
+    /* call publish, delete packet id delete */
+
+    if ((tmp_ret == rt_list_isempty(&client->qos2_msg_list)) == 0)
+    {
+        rt_list_for_each_entry_safe(p_msg, p_msg_tmp, &client->qos2_msg_list, next_list)
+        {
+            if (p_msg->packet_id == packet_id)
+            {
+                LOG_D(" qos2, deliver message! topic nme: %s ", p_msg->topic_name);
+                publish_msg.topic_name_len = p_msg->topic_name_len;
+                publish_msg.payload_len = p_msg->payload_len;
+                publish_msg.packet_id = p_msg->payload;
+                publish_msg.topic_name = p_msg->topic_name;
+                publish_msg.payload = p_msg->payload;
+                umqtt_deliver_message(client, p_msg->topic_name, p_msg->topic_name_len, &publish_msg);
+                if (p_msg->topic_name) { rt_free(p_msg->topic_name); p_msg->topic_name = RT_NULL; }
+                if (p_msg->payload) { rt_free(p_msg->payload); p_msg->payload = RT_NULL; }
+                rt_list_remove(&(p_msg->next_list));
+                rt_free(p_msg); p_msg = RT_NULL;
+                goto _exit;
+            }
+        }
+    }
+_exit:
+    return _ret;
+}
+
+static int add_one_pubrec_msg(struct umqtt_client *client, int packet_id)
+{
+    int _ret = UMQTT_OK, _cnt = 0;
+
+    for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++)
+    {
+        if (client->pubrec_msg[_cnt].cnt == -1)
+        {
+            UMQTT_CLIENT_LOCK(client);
+            client->pubrec_msg[_cnt].cnt = PKG_UMQTT_PUBLISH_RECON_MAX;
+            client->pubrec_msg[_cnt].packet_id = packet_id;
+            client->pubrec_msg[_cnt].next_tick = rt_tick_get() + PKG_UMQTT_RECPUBREC_INTERVAL_TIME;
+            UMQTT_CLIENT_UNLOCK(client);
+            break;
+        }
+    }
+    if (_cnt >= PKG_UMQTT_QOS2_QUE_MAX)
+    {
+        LOG_W(" add one pubrec message is full! ");
+        _ret = UMQTT_MEM_FULL;
+    }
+    return _ret;
+}
+
+static int clear_one_pubrec_msg(struct umqtt_client *client, int packet_id)
+{
+    int _ret = UMQTT_OK, _cnt = 0;
+
+    for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++)
+    {
+        if (client->pubrec_msg[_cnt].packet_id == packet_id)
+        {
+            UMQTT_CLIENT_LOCK(client);
+            client->pubrec_msg[_cnt].cnt = -1;
+            client->pubrec_msg[_cnt].packet_id = -1;
+            client->pubrec_msg[_cnt].next_tick = -1;
+            UMQTT_CLIENT_UNLOCK(client);
+            break;
+        }
+    }
+
+    return _ret;
+}
+
+static int pubrec_cycle_callback(struct umqtt_client *client)
+{
+    int _ret = UMQTT_OK, _cnt = 0;
+    struct umqtt_msg encode_msg = { 0 };
+
+    /* search pubrec packet id, encode, transport, change next tick time */
+    for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++)
+    {
+        if ((client->pubrec_msg[_cnt].cnt != -1)
+         && (client->pubrec_msg[_cnt].packet_id != -1)
+         && (client->pubrec_msg[_cnt].next_tick != -1)) 
+        {
+            if (client->pubrec_msg[_cnt].next_tick <= rt_tick_get())
+            {
+                UMQTT_CLIENT_LOCK(client);
+                client->pubrec_msg[_cnt].cnt--;
+                client->pubrec_msg[_cnt].next_tick = rt_tick_get() + PKG_UMQTT_RECPUBREC_INTERVAL_TIME;
+                UMQTT_CLIENT_UNLOCK(client);
+
+                if (client->pubrec_msg[_cnt].cnt < 0)
+                {
+                    qos2_publish_delete(client, client->pubrec_msg[_cnt].packet_id);
+                    LOG_W(" pubrec failed!");
+                    UMQTT_CLIENT_LOCK(client);
+                    client->pubrec_msg[_cnt].cnt = -1;
+                    client->pubrec_msg[_cnt].packet_id = -1;
+                    client->pubrec_msg[_cnt].next_tick = -1;
+                    UMQTT_CLIENT_UNLOCK(client);
+                }
+                else
+                {
+                    rt_memset(&encode_msg, 0, sizeof(struct umqtt_msg));
+                    encode_msg.header.bits.qos = UMQTT_QOS2;
+                    encode_msg.header.bits.dup = 0;
+                    encode_msg.header.bits.type = UMQTT_TYPE_PUBREC;
+                    encode_msg.msg.pubrec.packet_id = client->pubrec_msg[_cnt].packet_id;
+
+                    _ret = umqtt_encode(encode_msg.header.bits.type, client->send_buf, client->mqtt_info.send_size, 
+                                       &encode_msg);
+                    if (_ret < 0)
+                    {
+                        _ret = UMQTT_ENCODE_ERROR;
+                        LOG_E(" pubrec failed!");
+                        goto _exit;
+                    }
+                    client->send_len = _ret;
+
+                    _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 
+                                            client->mqtt_info.send_timeout);
+                    if (_ret < 0) 
+                    {
+                        _ret = UMQTT_SEND_FAILED;
+                        LOG_E(" trans send failed!");
+                        goto _exit;                    
+                    }
+                }
+            }
+
+        }
+    }
+
+_exit:
+    return _ret;
+}
+
+static void set_connect_status(struct umqtt_client *client, enum umqtt_client_state status)
+{
+    UMQTT_CLIENT_LOCK(client);
+    client->connect_state = status;
+    UMQTT_CLIENT_UNLOCK(client);
+}
+
+static void set_uplink_recon_tick(struct umqtt_client *client, enum tick_item item)
+{
+    RT_ASSERT(client);
+    switch (item)
+    {
+    case UPLINK_LAST_TICK:
+        UMQTT_CLIENT_LOCK(client);
+        client->uplink_last_tick = rt_tick_get();
+        UMQTT_CLIENT_UNLOCK(client);
+        break;
+    case UPLINK_NEXT_TICK:
+        UMQTT_CLIENT_LOCK(client);
+        client->uplink_next_tick = client->mqtt_info.keepalive_interval * 1000 + rt_tick_get();
+        UMQTT_CLIENT_UNLOCK(client);
+        break;
+    case RECON_LAST_TICK:
+        UMQTT_CLIENT_LOCK(client);
+        client->reconnect_last_tick = rt_tick_get();
+        UMQTT_CLIENT_UNLOCK(client);
+        break;
+    case RECON_NEXT_TICK:
+        UMQTT_CLIENT_LOCK(client);
+        client->reconnect_next_tick = client->mqtt_info.reconnect_interval * 1000 + rt_tick_get();
+        UMQTT_CLIENT_UNLOCK(client);
+        break;
+    default:
+        LOG_W(" set tick item outof set! value: %d", item);
+        break;
+    }
+};
+
+static int umqtt_connect(struct umqtt_client *client, int block)
+{
+    int _ret = 0, _length = 0, _cnt = 0;
+    struct umqtt_msg encode_msg = { 0 };
+    struct umqtt_msg_ack msg_ack = { 0 };
+    RT_ASSERT(client);
+
+_reconnect:
+    client->reconnect_count++;
+    if (client->reconnect_count > client->mqtt_info.reconnect_max_num)
+    {
+        _ret = UMQTT_RECONNECT_FAILED;
+        client->reconnect_count = 0;
+        LOG_E(" reconnect failed!");
+        goto exit;
+    }
+    _ret = umqtt_trans_connect(client->mqtt_info.uri, &(client->sock));
+    if (_ret < 0) 
+    {
+        _ret = UMQTT_SOCK_CONNECT_FAILED;
+        LOG_E(" umqtt connect, transport connect failed!");
+        goto disconnect;
+    }
+    
+    encode_msg.msg.connect.protocol_name_len = PKG_UMQTT_PROTOCOL_NAME_LEN;
+    encode_msg.msg.connect.protocol_name = PKG_UMQTT_PROTOCOL_NAME;
+    encode_msg.msg.connect.protocol_level = PKG_UMQTT_PROTOCOL_LEVEL;
+    encode_msg.msg.connect.connect_flags.connect_sign = UMQTT_DEF_CONNECT_FLAGS;
+#ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME
+    encode_msg.msg.connect.keepalive_interval_sec = ((client->mqtt_info.connect_keepalive_sec == 0) ? PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME : client->mqtt_info.connect_keepalive_sec);
+#else
+    encode_msg.msg.connect.keepalive_interval_sec = PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME;
+#endif
+    encode_msg.msg.connect.client_id = client->mqtt_info.client_id;
+    encode_msg.msg.connect.will_topic = client->mqtt_info.lwt_topic;
+    encode_msg.msg.connect.will_message = client->mqtt_info.lwt_message;
+    encode_msg.msg.connect.user_name = client->mqtt_info.user_name;
+    if (client->mqtt_info.user_name) 
+    {
+        encode_msg.msg.connect.connect_flags.bits.username_flag = 1;
+    }
+    encode_msg.msg.connect.password = client->mqtt_info.password;
+    if (client->mqtt_info.password) {
+        encode_msg.msg.connect.connect_flags.bits.password_flag = 1;
+        encode_msg.msg.connect.password_len = rt_strlen(client->mqtt_info.password);
+    }
+
+    _length = umqtt_encode(UMQTT_TYPE_CONNECT, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+    if (_length <= 0) 
+    {
+        _ret = UMQTT_ENCODE_ERROR;
+        LOG_E(" connect encode failed!");
+        goto exit;
+    }
+    client->send_len = _length;
+    
+    _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout);
+    if (_ret < 0) 
+    {
+        _ret = UMQTT_SEND_FAILED;
+        LOG_E(" connect trans send failed! errno:0x%04x", errno);
+        goto exit;
+    }
+
+    set_uplink_recon_tick(client, UPLINK_LAST_TICK);
+
+    if (block != 0) 
+    {
+        _ret = umqtt_handle_readpacket(client);
+        if (_ret == UMQTT_FIN_ACK)
+        {
+            LOG_D(" server fin ack, connect failed!");
+            goto disconnect;
+        }
+        else if (_ret < 0) 
+        {
+            _ret = UMQTT_READ_ERROR;
+            LOG_E(" connect trans recv failed!");
+            goto exit;
+        } 
+
+        while (1)
+        {
+            if (_cnt++ >= (client->mqtt_info.connect_time << 1))
+            {
+                _ret = UMQTT_TIMEOUT;
+                LOG_E(" connect recv message timeout!");
+                goto exit; 
+            }
+            else 
+            {
+                if (client->connect_state == UMQTT_CS_LINKED)
+                {
+                    _cnt = 0;
+                    _ret = UMQTT_OK;
+                    LOG_I(" connect success!");
+                    goto exit;
+                }
+            }
+            rt_thread_mdelay(500);
+        }
+    }
+    _ret = UMQTT_OK;
+    LOG_D(" connect sucess!");
+
+disconnect:
+    if ((_ret == UMQTT_FIN_ACK) || (_ret == UMQTT_SOCK_CONNECT_FAILED))
+    {
+        _ret = UMQTT_FIN_ACK;
+        umqtt_trans_disconnect(client->sock);
+        client->sock = -1;
+        rt_thread_delay(RT_TICK_PER_SECOND * 5);            
+        LOG_E(" server send fin ack, need to reconnect!");
+        goto _reconnect;
+    }
+
+exit:
+    if (_ret < 0) 
+    {
+        set_connect_status(client, UMQTT_CS_DISCONNECT);            /* reconnect failed */
+        LOG_W(" set client status disconnect! ");
+    }
+    return _ret;
+}
+
+static int umqtt_disconnect(struct umqtt_client *client)
+{
+    int _ret = 0, _length = 0;
+    RT_ASSERT(client);
+
+    _length = umqtt_encode(UMQTT_TYPE_DISCONNECT, client->send_buf, client->mqtt_info.send_size, RT_NULL);
+    if (_length < 0) 
+    {
+        _ret = UMQTT_ENCODE_ERROR;
+        LOG_E(" disconnect encode failed!");
+        goto exit;
+    }
+    client->send_len = _length;
+
+    _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 
+                            client->mqtt_info.send_timeout);
+    if (_ret < 0) 
+    {
+        _ret = UMQTT_SEND_FAILED;
+        LOG_E(" disconnect trans send failed!");
+        goto exit;
+    }
+    _ret = UMQTT_OK;
+exit:
+    return _ret;
+}
+
+static rt_uint8_t topicname_is_matched(char *topic_filter, char *topic_name, int len)
+{
+    RT_ASSERT(topic_filter);
+    RT_ASSERT(topic_name);
+
+    char *cur_f = topic_filter;
+    char *cur_n = topic_name;
+    char *cur_n_end = cur_n + len;
+
+    while (*cur_f && (cur_n < cur_n_end))
+    {
+        if ((*cur_n == '/') && (*cur_f != '/'))
+            break;
+        if ((*cur_f != '+') && (*cur_f != '#') && (*cur_f != *cur_n))
+            break;
+        if (*cur_f == '+')
+        {
+            char *nextpos = cur_n + 1;
+            while (nextpos < cur_n_end && *nextpos != '/')
+                nextpos = ++cur_n + 1;
+        }
+        else if (*cur_f == '#')
+        {
+            cur_n = cur_n_end - 1;
+        }
+        cur_f++;
+        cur_n++;
+    };
+
+    return ((cur_n == cur_n_end) && (*cur_f == '\0'));
+}
+
+static void umqtt_deliver_message(struct umqtt_client *client, 
+                                const char *topic_name, int len, 
+                                struct umqtt_pkgs_publish *msg)
+{
+    RT_ASSERT(client);
+    RT_ASSERT(topic_name);
+    RT_ASSERT(msg);
+
+    struct subtop_recv_handler *p_subtop = RT_NULL;
+    rt_list_for_each_entry(p_subtop, &client->sub_recv_list, next_list)
+    {
+        
+        if ((p_subtop->topicfilter)
+         && (topicname_is_matched(p_subtop->topicfilter, (char *)topic_name, len)))
+        {
+            if (p_subtop->callback != RT_NULL)
+            {
+                p_subtop->callback(client, msg);
+            }
+        }
+    }
+}
+
+static int umqtt_readpacket(struct umqtt_client *client, unsigned char *buf, int len, int timeout)
+{
+    int bytes = 0, _ret = 0;
+    rt_tick_t timeout_tick = (rt_tick_from_millisecond(timeout) & (RT_TICK_MAX >> 1));
+    rt_tick_t end_tick = rt_tick_get() + timeout_tick;
+
+    RT_ASSERT(client);
+    RT_ASSERT(buf);
+    if (len == 0)
+    {
+        _ret = UMQTT_OK;
+        return _ret;
+    }
+
+    while (bytes < len) 
+    {
+        _ret = umqtt_trans_recv(client->sock, &buf[bytes], (size_t)(len - bytes));
+        if (_ret == -1) 
+        {
+            if (!(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)) 
+            {
+                _ret = UMQTT_READ_FAILED;
+                LOG_E(" readpacket error! errno(%d)", errno);
+                goto exit;
+            }
+        } 
+        else if (_ret == 0) 
+        {
+            _ret = UMQTT_FIN_ACK;
+            LOG_W(" readpacket return 0!");
+            goto exit;
+        } 
+        else 
+        {
+            bytes += _ret;
+        }
+        if ((rt_tick_get() - end_tick) < (RT_TICK_MAX >> 1)) 
+        {
+            _ret = UMQTT_READ_TIMEOUT;
+            LOG_W(" readpacket timeout! now_tick(%ld), endtick(%ld), timeout(%ld) ", 
+                rt_tick_get(), end_tick, timeout_tick);
+            goto exit;
+        }
+    }
+    _ret = UMQTT_OK;
+
+exit:
+    return _ret;
+}
+
+static int umqtt_handle_readpacket(struct umqtt_client *client)
+{
+    int _ret = 0, _onedata = 0, _cnt = 0, _loop_cnt = 0, _remain_len = 0;
+    int _temp_ret = 0;
+    int _pkt_len = 0;
+    int _multiplier = 1;
+    int _pkt_type = 0;
+    struct umqtt_msg decode_msg = { 0 };
+    struct umqtt_msg_ack msg_ack = { 0 };
+    struct umqtt_msg encode_msg = { 0 };
+    RT_ASSERT(client);
+
+    /* 1. read the heade type */
+    _temp_ret = umqtt_trans_recv(client->sock, client->recv_buf, 1);
+    if (_temp_ret <= 0) 
+    {
+        _ret = UMQTT_FIN_ACK;
+        LOG_W(" server fin ack! connect failed! need to reconnect!");
+        goto exit;
+    } 
+
+    /* 2. read length */
+    do {
+        if (++_cnt > MAX_NO_OF_REMAINING_LENGTH_BYTES) 
+        {
+            _ret = UMQTT_FAILED;
+            LOG_E(" umqtt packet length error!");
+            goto exit;
+        }
+        _ret = umqtt_readpacket(client, (unsigned char *)&_onedata, 1, client->mqtt_info.recv_time_ms);
+        if (_ret == UMQTT_FIN_ACK)
+        {
+            LOG_W(" server fin ack! connect failed! need to reconnect!");
+            goto exit;
+        }
+        else if (_ret != UMQTT_OK) 
+        {
+            _ret = UMQTT_READ_FAILED;
+            goto exit;
+        }
+        *(client->recv_buf + _cnt) = _onedata;
+        _pkt_len += (_onedata & 0x7F) * _multiplier;
+        _multiplier *= 0x80; 
+    } while ((_onedata & 0x80) != 0);
+
+    /* read and delete if the data length is greater than the cache buff */
+    if ((_pkt_len + 1 + _cnt) > client->mqtt_info.recv_size)
+    {
+        LOG_W(" socket read buffer too short! will read and delete socket buff! ");
+        _loop_cnt = _pkt_len / client->mqtt_info.recv_size;
+
+        do 
+        {
+            if (_loop_cnt == 0)
+            {
+                umqtt_readpacket(client, client->recv_buf, _pkt_len, client->mqtt_info.recv_time_ms);
+                _ret = UMQTT_BUFFER_TOO_SHORT;
+                LOG_W(" finish read and delete socket buff!");
+                goto exit;
+            }
+            else 
+            {
+                _loop_cnt--;
+                umqtt_readpacket(client, client->recv_buf, client->mqtt_info.recv_size, client->mqtt_info.recv_time_ms);
+                _pkt_len -= client->mqtt_info.recv_size;
+            }
+        }while(1);
+    }
+
+    /* 3. read the remain datas */
+    _ret = umqtt_readpacket(client, client->recv_buf + _cnt + 1, _pkt_len, client->mqtt_info.recv_time_ms);
+    if (_ret == UMQTT_FIN_ACK)
+    {
+        LOG_W(" server fin ack! connect failed! need to reconnect!");
+        goto exit;
+    }
+    else if (_ret != UMQTT_OK)
+    {
+        _ret = UMQTT_READ_FAILED;
+        LOG_E(" read remain datas error!");
+        goto exit;
+    }
+
+    /* 4. encode packet datas */
+    rt_memset(&decode_msg, 0, sizeof(decode_msg));
+    _ret = umqtt_decode(client->recv_buf, _pkt_len + _cnt + 1, &decode_msg);
+    if (_ret < 0) 
+    {
+        _ret = UMQTT_DECODE_ERROR;
+        LOG_E(" decode error!");
+        goto exit;
+    }
+    _pkt_type = decode_msg.header.bits.type;
+    switch (_pkt_type)
+    {
+    case UMQTT_TYPE_CONNACK:
+        {
+            LOG_D(" read connack cmd information!");
+            set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+            set_connect_status(client, UMQTT_CS_LINKED);
+        }
+        break;
+    case UMQTT_TYPE_PUBLISH:
+        {
+            LOG_D(" read publish cmd information!");
+            set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+            
+            if (decode_msg.header.bits.qos != UMQTT_QOS2) 
+            {
+                LOG_D(" qos: %d, deliver message! topic nme: %s ", decode_msg.header.bits.qos, decode_msg.msg.publish.topic_name);
+                umqtt_deliver_message(client, decode_msg.msg.publish.topic_name, decode_msg.msg.publish.topic_name_len, 
+                                    &(decode_msg.msg.publish));
+            }
+
+            if (decode_msg.header.bits.qos != UMQTT_QOS0)
+            {   
+                rt_memset(&encode_msg, 0, sizeof(encode_msg));
+                encode_msg.header.bits.qos = decode_msg.header.bits.qos;
+                encode_msg.header.bits.dup = decode_msg.header.bits.dup;
+                if (decode_msg.header.bits.qos == UMQTT_QOS1)
+                {
+                    encode_msg.header.bits.type = UMQTT_TYPE_PUBACK;
+                    encode_msg.msg.puback.packet_id = decode_msg.msg.publish.packet_id;
+                }
+                else if (decode_msg.header.bits.qos == UMQTT_QOS2)
+                {
+                    encode_msg.header.bits.type = UMQTT_TYPE_PUBREC;
+                    add_one_qos2_msg(client, &(decode_msg.msg.publish));
+                    encode_msg.msg.pubrel.packet_id = decode_msg.msg.publish.packet_id;
+                    add_one_pubrec_msg(client, encode_msg.msg.pubrel.packet_id);        /* add pubrec message */
+                }
+                
+                _ret = umqtt_encode(encode_msg.header.bits.type, client->send_buf, client->mqtt_info.send_size, 
+                                    &encode_msg);
+                if (_ret < 0)
+                {
+                    _ret = UMQTT_ENCODE_ERROR;
+                    LOG_E(" puback / pubrec failed!");
+                    goto exit;
+                }
+                client->send_len = _ret;
+
+                _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 
+                                        client->mqtt_info.send_timeout);
+                if (_ret < 0) 
+                {
+                    _ret = UMQTT_SEND_FAILED;
+                    LOG_E(" trans send failed!");
+                    goto exit;                    
+                }
+                
+            }
+        }
+        break;
+    case UMQTT_TYPE_PUBACK:
+        {
+            LOG_D(" read puback cmd information!");
+            rt_memset(&msg_ack, 0, sizeof(msg_ack));
+            msg_ack.msg_type = UMQTT_TYPE_PUBACK;
+            msg_ack.packet_id = decode_msg.msg.puback.packet_id;
+            _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack));
+            if (_ret != RT_EOK) 
+            {
+                _ret = UMQTT_SEND_FAILED;
+                LOG_E(" mq send failed!");
+                goto exit;
+            }
+            set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+        }
+        break;
+    case UMQTT_TYPE_PUBREC:
+        {
+            LOG_D(" read pubrec cmd information!");
+            rt_memset(&msg_ack, 0, sizeof(msg_ack));
+            msg_ack.msg_type = UMQTT_TYPE_PUBREC;
+            msg_ack.packet_id = decode_msg.msg.puback.packet_id;
+            _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack));
+            if (_ret != RT_EOK)
+            {
+                _ret = UMQTT_SEND_FAILED;
+                LOG_E(" mq send failed!");
+                goto exit;
+            }
+            set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+        }
+        break;
+    case UMQTT_TYPE_PUBREL:
+        {
+            LOG_D(" read pubrel cmd information!");   
+
+            rt_memset(&encode_msg, 0, sizeof(encode_msg));
+            encode_msg.header.bits.type = UMQTT_TYPE_PUBCOMP;
+            encode_msg.header.bits.qos = decode_msg.header.bits.qos;
+            encode_msg.header.bits.dup = decode_msg.header.bits.dup;            
+            encode_msg.msg.pubrel.packet_id = decode_msg.msg.pubrec.packet_id;
+
+            /* publish callback, and delete callback */
+            qos2_publish_delete(client, encode_msg.msg.pubrel.packet_id);
+
+            /* delete array numbers! */
+            clear_one_pubrec_msg(client, encode_msg.msg.pubrel.packet_id);
+
+            _ret = umqtt_encode(UMQTT_TYPE_PUBCOMP, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+            if (_ret < 0)
+            {
+                _ret = UMQTT_ENCODE_ERROR;
+                LOG_E(" pubcomp failed!");
+                goto exit;
+            }
+            client->send_len = _ret;
+
+            _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 
+                                    client->mqtt_info.send_timeout);
+            if (_ret < 0) 
+            {
+                _ret = UMQTT_SEND_FAILED;
+                LOG_E(" trans send failed!");
+                goto exit;                    
+            }
+
+        }
+        break;
+    case UMQTT_TYPE_PUBCOMP:
+        {
+            LOG_D(" read pubcomp cmd information!");
+
+            rt_memset(&msg_ack, 0, sizeof(msg_ack));
+            msg_ack.msg_type = UMQTT_TYPE_PUBCOMP;
+            msg_ack.packet_id = decode_msg.msg.pubcomp.packet_id;
+            _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack));
+            if (_ret != RT_EOK) 
+            {
+                _ret = UMQTT_SEND_FAILED;
+                goto exit;
+            }            
+        }
+        break;
+    case UMQTT_TYPE_SUBACK:
+        {
+            LOG_D(" read suback cmd information!");
+
+            rt_memset(&msg_ack, 0, sizeof(msg_ack));
+            msg_ack.msg_type = UMQTT_TYPE_SUBACK;
+            msg_ack.packet_id = decode_msg.msg.suback.packet_id;
+
+            set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+
+            _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack));
+            if (_ret != RT_EOK) 
+            {
+                _ret = UMQTT_SEND_FAILED;
+                goto exit;
+            }
+        }
+        break;
+    case UMQTT_TYPE_UNSUBACK:
+        {
+            LOG_D(" read unsuback cmd information!");
+
+            rt_memset(&msg_ack, 0, sizeof(msg_ack));
+            msg_ack.msg_type = UMQTT_TYPE_UNSUBACK;
+            msg_ack.packet_id = decode_msg.msg.unsuback.packet_id;
+
+            set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+
+            _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack));
+            if (_ret != RT_EOK) 
+            {
+                _ret = UMQTT_SEND_FAILED;
+                goto exit;
+            }
+
+        }
+        break;
+    case UMQTT_TYPE_PINGRESP:
+        {
+            LOG_I(" ping resp! broker -> client! now tick: %d ", rt_tick_get());
+            set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+        }
+        break;
+    default:
+        {
+            LOG_W(" not right type(0x%02x)!", _pkt_type);
+        }
+        break;
+    }
+
+exit:
+    return _ret;
+}
+
+
+static void umqtt_thread(void *params)
+{
+    int _ret = 0;
+    struct umqtt_client *client = (struct umqtt_client *)params;
+    RT_ASSERT(client);
+
+    while (1) {
+        
+        _ret = umqtt_handle_readpacket(client);
+        if (_ret == UMQTT_FIN_ACK) 
+        {
+            LOG_W("reconnect start! stop timer -> trans disconnect delay 5000ms -> umqtt connect! ");
+            rt_timer_stop(client->uplink_timer);
+
+            umqtt_trans_disconnect(client->sock);
+            client->sock = -1;
+            rt_thread_mdelay(5000);        
+
+            _ret = umqtt_connect(client, 1);
+            if (_ret == UMQTT_RECONNECT_FAILED)
+            {
+                LOG_E(" umqtt reconnect failed!");
+                goto _exit;
+            }
+        }
+
+    }
+ 
+ _exit:
+    LOG_I(" umqtt thread is quit! ");
+    client->task_handle = RT_NULL;
+    return;
+}
+
+static void umqtt_check_def_info(struct umqtt_info *info)
+{
+    if (info) 
+    {
+        if (info->send_size == 0) { info->send_size = PKG_UMQTT_INFO_DEF_SENDSIZE; }
+        if (info->recv_size == 0) { info->recv_size = PKG_UMQTT_INFO_DEF_RECVSIZE; }
+        if (info->reconnect_max_num == 0) { info->reconnect_max_num = PKG_UMQTT_INFO_DEF_RECONNECT_MAX_NUM; }
+        if (info->reconnect_interval == 0) { info->reconnect_interval = PKG_UMQTT_INFO_DEF_RECONNECT_INTERVAL; }
+        if (info->keepalive_max_num == 0) { info->keepalive_max_num = PKG_UMQTT_INFO_DEF_KEEPALIVE_MAX_NUM; }
+        if (info->keepalive_interval == 0) { info->keepalive_interval = PKG_UMQTT_INFO_DEF_HEARTBEAT_INTERVAL; }
+        if (info->connect_time == 0) { info->connect_time = PKG_UMQTT_INFO_DEF_CONNECT_TIMEOUT; }
+        if (info->recv_time_ms == 0) { info->recv_time_ms = PKG_UMQTT_INFO_DEF_RECV_TIMEOUT_MS; }
+        if (info->send_timeout == 0) { info->send_timeout = PKG_UMQTT_INFO_DEF_SEND_TIMEOUT; }
+        if (info->thread_stack_size == 0) { info->thread_stack_size = PKG_UMQTT_INFO_DEF_THREAD_STACK_SIZE; }
+        if (info->thread_priority == 0) { info->thread_priority = PKG_UMQTT_INFO_DEF_THREAD_PRIORITY; }
+    }
+}
+
+static int umqtt_keepalive_callback(struct umqtt_client *client)
+{
+    int _ret = 0, _length = 0;
+    rt_uint32_t _connect_kp_time = 0;
+    RT_ASSERT(client);
+
+#ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME
+    _connect_kp_time = (client->mqtt_info.connect_keepalive_sec == 0) ? (PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME >> 1) : (client->mqtt_info.connect_keepalive_sec >> 1);
+#else
+    _connect_kp_time = PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME >> 1;
+#endif
+
+    if (client->connect_state == UMQTT_CS_LINKED) 
+    {
+        if (((client->uplink_next_tick <= rt_tick_get())
+          && (client->uplink_next_tick > client->uplink_last_tick))
+         || ((client->pingreq_last_tick + _connect_kp_time) < rt_tick_get()))
+        {
+            _length = umqtt_encode(UMQTT_TYPE_PINGREQ, client->send_buf, 2, NULL);
+            if (_length == 2) 
+                umqtt_trans_send(client->sock, client->send_buf, _length, 0);
+            
+            if (client->user_handler)
+                client->user_handler(client, UMQTT_EVT_HEARTBEAT);
+
+            set_uplink_recon_tick(client, UPLINK_LAST_TICK);
+            client->pingreq_last_tick = rt_tick_get();
+        } 
+        else if ((client->uplink_last_tick >= client->uplink_next_tick) 
+              && ((client->uplink_last_tick + client->mqtt_info.send_timeout * 1000) < rt_tick_get())) 
+        {
+            if (++client->keepalive_count >=  client->mqtt_info.keepalive_max_num) {
+                set_connect_status(client, UMQTT_CS_UNLINK);
+            } 
+            else 
+            {
+                _length = umqtt_encode(UMQTT_TYPE_PINGREQ, client->send_buf, 2, NULL);
+                if (_length == 2) 
+                    umqtt_trans_send(client->sock, client->send_buf, _length, 0);
+                
+                if (client->user_handler)
+                    client->user_handler(client, UMQTT_EVT_HEARTBEAT);
+                
+                set_uplink_recon_tick(client, UPLINK_LAST_TICK);
+            }
+        }
+    }
+    return _ret;
+}
+
+static int umqtt_reconnect_callback(struct umqtt_client *client)
+{
+    int _ret = 0;
+    RT_ASSERT(client);
+ 
+    if (client->connect_state == UMQTT_CS_UNLINK) 
+    {
+        if (client->user_handler) 
+            client->user_handler(client, UMQTT_EVT_OFFLINE);
+        
+        set_connect_status(client, UMQTT_CS_UNLINK_LINKING);
+        umqtt_trans_disconnect(client->sock);
+        client->sock = -1;        
+
+    } 
+    else if (client->connect_state == UMQTT_CS_UNLINK_LINKING) 
+    {    
+        if (client->reconnect_next_tick <= rt_tick_get()) 
+        {
+            if (client->sock < 0)
+            {
+                if (client->reconnect_count >= client->mqtt_info.reconnect_max_num) 
+                {
+                    set_connect_status(client, UMQTT_CS_DISCONNECT);
+                    /* stop timer, delete task handle */
+                    if (client->task_handle)
+                    {
+                        rt_thread_delete(client->task_handle);
+                        client->task_handle = RT_NULL;
+                    }
+                    if (client->uplink_timer) 
+                    {
+                        rt_timer_stop(client->uplink_timer);
+                    }
+                } 
+                else 
+                {
+                    client->keepalive_count = 0;
+                    set_uplink_recon_tick(client, RECON_NEXT_TICK);
+                    umqtt_connect(client, 0);
+                    if (client->user_handler)
+                        client->user_handler(client, UMQTT_EVT_LINK);
+                }
+            }
+            else 
+            {
+                umqtt_trans_disconnect(client->sock);
+                client->sock = -1;
+            }
+        }
+
+    }
+
+    return _ret;
+}
+
+static void umqtt_uplink_timer_callback(void *params)
+{
+    struct umqtt_client *client = (struct umqtt_client *)params;
+    umqtt_keepalive_callback(client);
+    umqtt_reconnect_callback(client);
+    pubrec_cycle_callback(client);
+}
+
+/** 
+ * delete the umqtt client, release resources
+ *
+ * @param client the input, umqtt client
+ *
+ * @return <0: failed or other error
+ *         =0: success
+ */
+int umqtt_delete(struct umqtt_client *client)
+{   
+    int _ret = 0, _cnt = 0;
+    struct subtop_recv_handler *p_subtop = RT_NULL;
+    struct subtop_recv_handler *p_temp = RT_NULL;
+    struct umqtt_qos2_msg *p_msg = RT_NULL;
+    struct umqtt_qos2_msg *p_msg_tmp = RT_NULL;
+    if (client == RT_NULL)
+        return _ret;
+    if (client->task_handle)
+    {
+        rt_thread_delete(client->task_handle);
+        client->task_handle = RT_NULL;
+        client->user_handler = RT_NULL;
+    }
+    if (client->uplink_timer)
+    {
+        rt_timer_stop(client->uplink_timer);
+        rt_timer_delete(client->uplink_timer);
+        client->uplink_timer = RT_NULL;
+    }
+    if (client->msg_queue) 
+    {
+        rt_mq_delete(client->msg_queue);
+        client->msg_queue = RT_NULL;
+    }
+    client->send_len = client->recv_len = 0;
+    if (client->lock_client) 
+    {
+        rt_mutex_delete(client->lock_client);
+        client->lock_client = RT_NULL;
+    }
+    if (client->recv_buf) 
+    {
+        rt_free(client->recv_buf);
+        client->recv_buf = RT_NULL;
+    }
+    if (client->send_buf)
+    {
+        rt_free(client->send_buf);
+        client->send_buf = RT_NULL;
+    }
+    if ((_ret = rt_list_isempty(&client->sub_recv_list)) == 0)
+    {
+        rt_list_for_each_entry_safe(p_subtop, p_temp, &client->sub_recv_list, next_list)
+        {
+            if (p_subtop->topicfilter) {        // const char *, cannot free!
+                rt_free(p_subtop->topicfilter);
+                p_subtop->topicfilter = RT_NULL;
+                p_subtop->callback = RT_NULL;
+            }
+            rt_list_remove(&(p_subtop->next_list));
+            rt_free(p_subtop); p_subtop = RT_NULL;
+        }
+    }
+
+    if ((_ret == rt_list_isempty(&client->qos2_msg_list)) == 0)
+    {
+        rt_list_for_each_entry_safe(p_msg, p_msg_tmp, &client->qos2_msg_list, next_list)
+        {
+            if (p_msg->topic_name) { rt_free(p_msg->topic_name); p_msg->topic_name = RT_NULL; }
+            if (p_msg->payload) { rt_free(p_msg->payload); p_msg->payload = RT_NULL; }
+            rt_list_remove(&(p_msg->next_list));
+            rt_free(p_msg); p_msg = RT_NULL;
+        }
+    }
+
+    for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++)
+    {
+        client->pubrec_msg[_cnt].cnt = -1;
+        client->pubrec_msg[_cnt].packet_id = -1;
+        client->pubrec_msg[_cnt].next_tick = -1;
+    }
+
+    rt_list_remove(&(client->list));       /* delete this list  */ 
+    rt_free(client);
+    _ret = UMQTT_OK;
+    return _ret;
+}
+/** 
+ * create umqtt client according to user information
+ *
+ * @param info the input, user config information
+ *
+ * @return RT_NULL: create failed
+ *         not RT_NULL: create success, client point
+ */
+umqtt_client_t umqtt_create(const struct umqtt_info *info)
+{
+    RT_ASSERT(info);
+    static rt_uint8_t lock_cnt = 0;
+    int _ret = 0, _length = 0, _cnt = 0;
+    umqtt_client_t mqtt_client = RT_NULL;
+    struct subtop_recv_handler *p_subtop = RT_NULL;
+    char _name[RT_NAME_MAX];
+
+    mqtt_client = (umqtt_client_t)rt_calloc(1, sizeof(struct umqtt_client));
+    if (mqtt_client == RT_NULL) 
+    {
+        LOG_E(" umqtt create failed!");
+        _ret = UMQTT_MEM_FULL;
+        goto exit;
+    }
+    rt_memcpy(&(mqtt_client->mqtt_info), info, sizeof(struct umqtt_info));
+    umqtt_check_def_info(&(mqtt_client->mqtt_info));
+    
+    /* will topic/message send/recv*/
+    mqtt_client->sub_recv_list_len = PKG_UMQTT_SUBRECV_DEF_LENGTH;
+    rt_list_init(&mqtt_client->sub_recv_list);
+    if (mqtt_client->mqtt_info.lwt_topic != RT_NULL)
+    {
+        p_subtop = (struct subtop_recv_handler *)rt_calloc(1, sizeof(struct subtop_recv_handler));
+        if (p_subtop != RT_NULL)
+        {
+            p_subtop->qos = mqtt_client->mqtt_info.lwt_qos;
+            _length = strlen(mqtt_client->mqtt_info.lwt_topic) + 1;
+            p_subtop->topicfilter = (char *)rt_calloc(1, sizeof(char) * _length);
+            rt_strncpy(p_subtop->topicfilter, mqtt_client->mqtt_info.lwt_topic, _length);
+            p_subtop->callback = mqtt_client->mqtt_info.lwt_cb;
+            rt_list_insert_after(&mqtt_client->sub_recv_list, &p_subtop->next_list);
+        }
+    }
+
+    /* qos2 msg list init */
+    rt_list_init(&mqtt_client->qos2_msg_list);
+
+    for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++)
+    {
+        mqtt_client->pubrec_msg[_cnt].cnt = -1;
+        mqtt_client->pubrec_msg[_cnt].packet_id = -1;
+        mqtt_client->pubrec_msg[_cnt].next_tick = -1;
+    }
+
+    mqtt_client->recv_buf = rt_calloc(1, sizeof(rt_uint8_t) * mqtt_client->mqtt_info.recv_size);
+    if (mqtt_client->recv_buf == RT_NULL) 
+    {
+        LOG_E(" client receive buff calloc failed!");
+        _ret = UMQTT_MEM_FULL;
+        goto exit;
+    }
+
+    mqtt_client->send_buf = rt_calloc(1, sizeof(rt_uint8_t) * mqtt_client->mqtt_info.send_size);
+    if (mqtt_client->send_buf == RT_NULL) 
+    {
+        LOG_E(" client send buff calloc failed!");
+        _ret = UMQTT_MEM_FULL;
+        goto exit;
+    }
+
+    rt_memset(_name, 0x00, sizeof(_name));
+    rt_snprintf(_name, RT_NAME_MAX, "umqtt_l%d", lock_cnt);
+    mqtt_client->lock_client = rt_mutex_create(_name, RT_IPC_FLAG_FIFO);
+    if (mqtt_client->lock_client == RT_NULL) 
+    {
+        LOG_E(" create lock_client failed!");
+        _ret = UMQTT_MEM_FULL;
+        goto exit;
+    }
+
+    rt_memset(_name, 0x00, sizeof(_name));
+    rt_snprintf(_name, RT_NAME_MAX, "umqtt_q%d", lock_cnt);
+    mqtt_client->msg_queue = rt_mq_create(_name, 
+                                            sizeof(struct umqtt_msg_ack), 
+                                            PKG_UMQTT_MSG_QUEUE_ACK_DEF_SIZE, 
+                                            RT_IPC_FLAG_FIFO);
+    if (mqtt_client->msg_queue == RT_NULL) 
+    {
+        LOG_E(" create msg_queue failed!");
+        _ret = UMQTT_MEM_FULL;
+        goto exit;
+    }
+
+    rt_memset(_name, 0x00, sizeof(_name));
+    rt_snprintf(_name, RT_NAME_MAX, "umqtt_m%d", lock_cnt);
+    mqtt_client->uplink_timer = rt_timer_create(_name, 
+                                                umqtt_uplink_timer_callback, 
+                                                mqtt_client, 
+                                                UMQTT_INFO_DEF_UPLINK_TIMER_TICK, 
+                                                RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC);
+    if (mqtt_client->uplink_timer == RT_NULL) 
+    {
+        LOG_E(" create uplink timer failed!");
+        _ret = UMQTT_MEM_FULL;
+        goto exit;
+    }
+
+    rt_list_init(&mqtt_client->list);           /* objects, multi mqttclient */ 
+
+    rt_memset(_name, 0x00, sizeof(_name));
+    rt_snprintf(_name, RT_NAME_MAX, "umqtt_t%d", lock_cnt++);
+    
+    mqtt_client->task_handle = rt_thread_create(_name, 
+                                                umqtt_thread, 
+                                                (void *)mqtt_client, 
+                                                mqtt_client->mqtt_info.thread_stack_size, 
+                                                mqtt_client->mqtt_info.thread_priority, 
+                                                UMQTT_INFO_DEF_THREAD_TICK);
+    if (mqtt_client->task_handle == RT_NULL) 
+    {
+        LOG_E(" create thread failed!");
+        _ret = UMQTT_MEM_FULL;
+        goto exit;
+    }
+
+exit:
+    if (_ret < 0) 
+    {
+        umqtt_delete(mqtt_client);
+        mqtt_client = RT_NULL;
+    }
+    return mqtt_client;
+}
+
+/** 
+ * start the umqtt client to work
+ *
+ * @param client the input, umqtt client
+ *
+ * @return < 0: failed
+ *         >= 0: success
+ */
+int umqtt_start(struct umqtt_client *client)
+{
+    int _ret = 0, _length = 0;
+    struct subtop_recv_handler *p_subtop = RT_NULL;
+    struct umqtt_msg encode_msg = { 0 };
+    struct umqtt_msg_ack msg_ack = { 0 };
+    if (client == RT_NULL) {
+        _ret = UMQTT_INPARAMS_NULL;
+        LOG_E(" umqtt start, client is NULL!");
+        goto exit;
+    }
+
+    set_connect_status(client, UMQTT_CS_LINKING);
+
+    _ret = umqtt_connect(client, 1);
+    if (_ret < 0)
+        goto exit;
+
+    if (client->task_handle) 
+    {
+        rt_thread_startup(client->task_handle);
+    }
+
+    if (client->uplink_timer)
+    {
+        if (rt_timer_start(client->uplink_timer) != RT_EOK) 
+        {
+            _ret = UMQTT_FAILED;
+            LOG_E(" timer start failed!");
+            goto exit;
+        }
+    }
+
+    /* will message topic to send & recv */
+    if (0 == rt_list_isempty(&client->sub_recv_list))
+    {
+        rt_list_for_each_entry(p_subtop, &client->sub_recv_list, next_list)
+        {
+            rt_memset(&encode_msg, 0, sizeof(encode_msg));
+            encode_msg.header.bits.qos = UMQTT_QOS1;
+            encode_msg.msg.subscribe.packet_id = get_next_packetID(client);
+            encode_msg.msg.subscribe.topic_filter[0].topic_filter = p_subtop->topicfilter;
+            encode_msg.msg.subscribe.topic_filter[0].filter_len = strlen(p_subtop->topicfilter);
+            encode_msg.msg.subscribe.topic_filter[0].req_qos.request_qos = p_subtop->qos;
+            encode_msg.msg.subscribe.topic_count = 1;
+            rt_memset(client->send_buf, 0, sizeof(rt_uint8_t) * client->mqtt_info.send_size);
+            _length = umqtt_encode(UMQTT_TYPE_SUBSCRIBE, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+            if (_length <= 0)
+            {
+                _ret = UMQTT_ENCODE_ERROR;
+                LOG_W(" subscribe encode failed! topic: %s", p_subtop->topicfilter);
+                continue;
+            }
+            client->send_len = _length;
+
+            _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout);
+            if (_ret < 0)
+            {
+                _ret = UMQTT_SEND_FAILED;
+                LOG_W(" subscribe trans send failed! ");
+                continue;
+            }
+
+            set_uplink_recon_tick(client, UPLINK_LAST_TICK);
+
+            rt_memset(&msg_ack, 0, sizeof(msg_ack));
+            if (RT_EOK == rt_mq_recv(client->msg_queue,
+                                    &msg_ack, sizeof(struct umqtt_msg_ack),
+                                    rt_tick_from_millisecond(client->mqtt_info.send_timeout * 1000)))
+            {
+                if (msg_ack.msg_type == UMQTT_TYPE_SUBACK)
+                {
+                    set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+                    _ret = UMQTT_OK;
+                    LOG_I(" subscribe ack ok! ");
+                    continue;
+                }
+                else
+                {
+                    _ret = UMQTT_READ_ERROR;
+                    LOG_W(" subscribe ack error! message type: %d ", msg_ack.msg_type);
+                    continue;
+                }
+            }
+            else
+            {
+                _ret = UMQTT_READ_FAILED;
+                LOG_W(" subscribe recv message timeout! topic: %s ", p_subtop->topicfilter);
+                continue;
+            }
+        }
+    }
+
+exit:
+    if (_ret == UMQTT_RECONNECT_FAILED)
+        LOG_W(" connect and reconnect failed!");
+
+    return _ret;
+}
+
+/** 
+ * stop the umqtt client to work
+ *
+ * @param client the input, umqtt client
+ *
+ * @return < 0: failed
+ *         >= 0: success
+ */
+void umqtt_stop(struct umqtt_client *client)
+{
+    if (client == RT_NULL)
+        return;
+
+    if (client->task_handle)
+    {
+        rt_thread_delete(client->task_handle);
+        client->task_handle = RT_NULL;
+    }
+
+    if (client->uplink_timer) 
+        rt_timer_stop(client->uplink_timer);
+    
+    umqtt_disconnect(client);
+    if (client->sock != -1)
+    {
+        umqtt_trans_disconnect(client->sock);
+        client->sock = -1;
+    }
+}
+
+/** 
+ * Client to send a publish message to the broker
+ *
+ * @param client the input, umqtt client
+ * @param qos the input, qos of publish message
+ * @param topic the input, topic string
+ * @param payload the input, mqtt message payload
+ * @param length the input, mqtt message payload length
+ * @param timeout the input, msg queue wait timeout, uint:mSec
+ *
+ * @return < 0: failed
+ *         >= 0: success
+ */
+int umqtt_publish(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length, int timeout)
+{
+    int _ret = 0, _length = 0;
+    int _cnt = 0;
+    rt_uint16_t packet_id = 0;
+    struct umqtt_msg_ack msg_ack = { 0 };
+    struct umqtt_msg encode_msg = { 0 };
+
+    RT_ASSERT(client);
+    RT_ASSERT(topic);
+    RT_ASSERT(payload);
+    RT_ASSERT(length);
+
+    packet_id = ((qos == UMQTT_QOS0) ? 0 : get_next_packetID(client));
+
+    encode_msg.header.bits.qos = qos;
+    encode_msg.header.bits.dup = 0;
+    encode_msg.msg.publish.packet_id = packet_id;
+    encode_msg.msg.publish.payload = payload;
+    encode_msg.msg.publish.payload_len = length;
+    encode_msg.msg.publish.topic_name = topic;
+    encode_msg.msg.publish.topic_name_len = strlen(topic);
+    _length = umqtt_encode(UMQTT_TYPE_PUBLISH, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+    if (_length <= 0) 
+    {
+        _ret = UMQTT_ENCODE_ERROR;
+        LOG_E(" publish encode failed! topic: %d", topic);
+        goto exit;
+    }
+    client->send_len = _length;
+
+_republish:
+    _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout);
+    if (_ret < 0) 
+    {
+        _ret = UMQTT_SEND_FAILED;
+        LOG_E(" publish trans send failed!");
+        goto exit;
+    }
+    _ret = UMQTT_OK;
+    if (qos == UMQTT_QOS1) 
+    {
+        if (RT_EOK == rt_mq_recv(client->msg_queue, 
+                                &msg_ack, sizeof(struct umqtt_msg_ack), 
+                                rt_tick_from_millisecond(timeout))) 
+        {
+            if (msg_ack.msg_type == UMQTT_TYPE_PUBACK) 
+            {
+                _ret = UMQTT_OK;
+                LOG_I(" publish qos1 ack success!");
+                goto exit;
+            } 
+            else 
+            {
+                _ret = UMQTT_READ_ERROR;
+                LOG_E(" publish qos1 msg_type(%d) error!", msg_ack.msg_type);
+                goto exit;
+            }
+        } 
+        else 
+        {
+            if (++_cnt >= PKG_UMQTT_PUBLISH_RECON_MAX)
+            {
+                _ret = UMQTT_READ_ERROR;
+                LOG_E(" publish qos1 recv error!");
+                goto exit;
+            }
+            else
+            {
+                LOG_W(" qos1 publish failed! republish!");
+                goto _republish;
+            }
+
+        }
+    } 
+    else if (qos == UMQTT_QOS2) 
+    {
+        if (RT_EOK == rt_mq_recv(client->msg_queue, 
+                                &msg_ack, sizeof(struct umqtt_msg_ack), 
+                                rt_tick_from_millisecond(timeout))) 
+        {
+            if (msg_ack.msg_type == UMQTT_TYPE_PUBREC)
+            {
+                LOG_D(" qos2 publish datas! success! then, pubrel msg! ");
+            }
+            else
+            {
+                _ret = UMQTT_READ_ERROR;
+                LOG_E(" publish qos2 msg_type(%d) error!", msg_ack.msg_type);
+                goto exit;                
+            }
+        }
+        else 
+        {
+            if (++_cnt >= PKG_UMQTT_PUBLISH_RECON_MAX)
+            {
+                _ret = UMQTT_READ_ERROR;
+                LOG_E(" publish qos2 recv error!");
+                goto exit;
+            }
+            else
+            {
+                goto _republish;
+            }
+        }
+
+        /* send pubreal datas */
+_repubrel:
+        _cnt = 0;
+        rt_memset(&encode_msg, 0, sizeof(encode_msg));
+        encode_msg.header.bits.type = UMQTT_TYPE_PUBREL;
+        encode_msg.header.bits.qos = qos;
+        encode_msg.header.bits.dup = 0;
+        encode_msg.msg.publish.packet_id = packet_id;
+        _length = umqtt_encode(encode_msg.header.bits.type, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+        if (_length <= 0) 
+        {
+            _ret = UMQTT_ENCODE_ERROR;
+            LOG_E(" pubrel encode failed! topic: %d", topic);
+            goto exit;
+        }
+        client->send_len = _length;        
+
+        _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 
+                                client->mqtt_info.send_timeout);
+        if (_ret < 0) 
+        {
+            _ret = UMQTT_SEND_FAILED;
+            LOG_E(" publish trans send failed!");
+            goto exit;
+        }
+        _ret = UMQTT_OK;        
+
+        if (RT_EOK == rt_mq_recv(client->msg_queue, 
+                                &msg_ack, sizeof(struct umqtt_msg_ack), 
+                                rt_tick_from_millisecond(timeout))) 
+        {
+            if (msg_ack.msg_type == UMQTT_TYPE_PUBCOMP) 
+            {
+                _ret = UMQTT_OK;
+                LOG_I(" pubcomp ack! publish qos2 ack success!");
+                goto exit;
+            } 
+            else 
+            {
+                _ret = UMQTT_READ_ERROR;
+                LOG_E(" publish qos2 msg_type(%d) error!", msg_ack.msg_type);
+                goto exit;
+            }
+        }        
+        else
+        {
+            if (++_cnt >= PKG_UMQTT_PUBLISH_RECON_MAX)
+            {
+                _ret = UMQTT_READ_ERROR;
+                LOG_E(" pubrel qos2 recv error!");
+                goto exit;
+            }
+            else
+            {
+                goto _repubrel;
+            }
+        }
+
+    }
+
+exit:
+    return _ret;
+}
+
+/** 
+ * Subscribe the client to defined topic with defined qos
+ *
+ * @param client the input, umqtt client
+ * @param topic the input, topic string
+ * @param qos the input, qos of publish message
+ * @param callback the input, when broke publish the topic is the sanme as sub topic, will run this callback
+ *
+ * @return < 0: failed
+ *         >= 0: success
+ */
+int umqtt_subscribe(struct umqtt_client *client, const char *topic, enum umqtt_qos qos, umqtt_subscribe_cb callback)
+{
+    int _ret = 0;
+    int _length = 0;
+    int _cnt = 0;
+    struct subtop_recv_handler *p_subtop = RT_NULL;
+    struct umqtt_msg encode_msg = { 0 };
+    struct umqtt_msg_ack msg_ack = { 0 };
+
+    RT_ASSERT(client);
+    RT_ASSERT(topic);
+
+    _cnt = rt_list_isempty(&client->sub_recv_list);
+
+    if (_cnt == 0) 
+    {
+        _cnt = 0;
+        rt_list_for_each_entry(p_subtop, &client->sub_recv_list, next_list) 
+        {    
+            if (p_subtop->topicfilter 
+            && (rt_strncmp(p_subtop->topicfilter, topic, rt_strlen(topic)) == 0)) 
+            {
+                LOG_D(" subscribe topic(%s) is already subscribed.", topic);
+                goto exit;
+            }
+        }
+        _length = rt_list_len(&client->sub_recv_list);
+    }
+
+    if (_length > client->sub_recv_list_len) 
+    {
+        _ret = UMQTT_MEM_FULL;
+        LOG_E(" subscribe size(%d) is not enough! now length(%d)!", client->sub_recv_list_len, _length);
+        goto exit;
+    } 
+    else 
+    {
+        rt_memset(&encode_msg, 0, sizeof(encode_msg));
+        encode_msg.header.bits.qos = UMQTT_QOS1;
+        encode_msg.msg.subscribe.packet_id = get_next_packetID(client);
+        encode_msg.msg.subscribe.topic_filter[0].topic_filter = topic;
+        encode_msg.msg.subscribe.topic_filter[0].filter_len = strlen(topic);
+        encode_msg.msg.subscribe.topic_filter[0].req_qos.request_qos = qos;
+        encode_msg.msg.subscribe.topic_count = 1;
+        rt_memset(client->send_buf, 0, sizeof(rt_uint8_t) * client->mqtt_info.send_size);
+        _length = umqtt_encode(UMQTT_TYPE_SUBSCRIBE, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+        if (_length <= 0) 
+        {
+            _ret = UMQTT_ENCODE_ERROR;
+            LOG_E(" subscribe encode failed! topic: %s", topic);
+            goto exit;
+        }
+        client->send_len = _length;
+
+        _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout);
+        if (_ret < 0) 
+        {
+            _ret = UMQTT_SEND_FAILED;
+            LOG_E(" subscribe trans send failed!");
+            goto exit;
+        }
+
+        set_uplink_recon_tick(client, UPLINK_LAST_TICK);
+
+        rt_memset(&msg_ack, 0, sizeof(msg_ack));
+        if (RT_EOK == rt_mq_recv(client->msg_queue, 
+                                &msg_ack, sizeof(struct umqtt_msg_ack), 
+                                rt_tick_from_millisecond(client->mqtt_info.send_timeout * 1000))) 
+        {
+            if (msg_ack.msg_type == UMQTT_TYPE_SUBACK) 
+            {
+                p_subtop = RT_NULL;
+                p_subtop = (struct subtop_recv_handler *)rt_calloc(1, sizeof(struct subtop_recv_handler));
+                RT_ASSERT(p_subtop);
+                LOG_D(" start assign datas !");
+                p_subtop->topicfilter = rt_strdup(topic);
+                p_subtop->qos = qos;
+                if (callback) 
+                {
+                    p_subtop->callback = callback;
+                }
+                rt_list_insert_after(&client->sub_recv_list, &p_subtop->next_list);
+                set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+
+                _ret = UMQTT_OK;
+                LOG_I("subscribe ack ok! ");
+                goto exit;
+            } 
+            else 
+            {
+                _ret = UMQTT_READ_ERROR;
+                LOG_E("subscribe ack error!");
+                goto exit;
+            }
+        } 
+        else 
+        {
+            _ret = UMQTT_READ_FAILED;
+            LOG_E(" subscribe recv message timeout! topic: %s", topic);
+            goto exit;
+        }
+    }
+
+exit:
+    return _ret;
+}
+
+/** 
+ * Unsubscribe the client from defined topic
+ *
+ * @param client the input, umqtt client
+ * @param topic the input, topic string
+ *
+ * @return < 0: failed
+ *         >= 0: success
+ */
+int umqtt_unsubscribe(struct umqtt_client *client, const char *topic)
+{
+    int _ret = 0, _cnt = 0, _length = 0;
+    struct subtop_recv_handler *p_subtop = RT_NULL;
+    struct subtop_recv_handler *p_temp = RT_NULL;
+    struct umqtt_msg encode_msg = { 0 };
+    struct umqtt_msg_ack msg_ack = { 0 };
+
+    RT_ASSERT(client);
+    RT_ASSERT(topic);
+    _cnt = rt_list_len(&client->sub_recv_list);
+
+    if (_cnt > 0)
+    {
+        rt_list_for_each_entry_safe(p_subtop, p_temp, &client->sub_recv_list, next_list) 
+        {
+            _cnt--;
+            if (p_subtop->topicfilter
+            && (rt_strncmp(p_subtop->topicfilter, topic, strlen(topic)) == 0)) 
+            {
+                rt_memset(&encode_msg, 0, sizeof(encode_msg));
+                encode_msg.header.bits.qos = UMQTT_QOS1;
+                encode_msg.msg.unsubscribe.packet_id = get_next_packetID(client);
+                encode_msg.msg.unsubscribe.topic_count = 1;
+                encode_msg.msg.unsubscribe.topic_filter[0].topic_filter = topic,
+                encode_msg.msg.unsubscribe.topic_filter[0].filter_len = strlen(topic);
+                _length = umqtt_encode(UMQTT_TYPE_UNSUBSCRIBE, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+                if (_length <= 0) 
+                {
+                    _ret = UMQTT_ENCODE_ERROR;
+                    LOG_E(" unsubscribe encode failed! topic: %s", topic);
+                    goto exit;
+                }
+                client->send_len = _length;
+
+                _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout);
+                if (_ret < 0) 
+                {
+                    _ret = UMQTT_SEND_FAILED;
+                    LOG_E(" unsubscribe trans send failed!");
+                    goto exit;
+                }
+
+                set_uplink_recon_tick(client, UPLINK_LAST_TICK);
+
+                rt_memset(&msg_ack, 0, sizeof(msg_ack));
+                if (RT_EOK == rt_mq_recv(client->msg_queue, 
+                                        &msg_ack, sizeof(struct umqtt_msg_ack), 
+                                        rt_tick_from_millisecond(client->mqtt_info.send_timeout * 1000))) 
+                {
+                    if (msg_ack.msg_type == UMQTT_TYPE_UNSUBACK) 
+                    {
+                        if (p_subtop->topicfilter) {
+                            rt_free(p_subtop->topicfilter);
+                            p_subtop->topicfilter = RT_NULL;
+                        }
+                        p_subtop->callback = RT_NULL;
+                        rt_list_remove(&(p_subtop->next_list));
+                        rt_free(p_subtop); p_subtop = RT_NULL;
+                        set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+
+                        _ret = UMQTT_OK;
+                        LOG_I(" unsubscribe ack ok! ");
+                        goto exit;
+                    } 
+                    else 
+                    {
+                        _ret = UMQTT_READ_ERROR;
+                        LOG_E(" unsubscribe ack error!");
+                        goto exit;
+                    }
+                } 
+                else 
+                {
+                    _ret = UMQTT_TIMEOUT;
+                    LOG_E(" unsubscribe recv message timeout! topic: %s", topic);
+                    goto exit;
+                }
+            }
+        }
+
+    }
+    else 
+    {
+        LOG_D(" unsubscribe topic(%s) is not exit!", topic);
+    }
+
+exit:
+    return _ret;
+}
+
+/** 
+ * umqtt client publish nonblocking datas
+ *
+ * @param client the input, umqtt client
+ * @param qos the input, qos of publish message
+ * @param topic the input, topic string
+ * @param payload the input, mqtt message payload
+ * @param length the input, mqtt message payload length
+ * 
+ * @return < 0: failed
+ *         >= 0: success
+ */
+int umqtt_publish_async(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, 
+                        void *payload, size_t length)
+{
+    int _ret = 0, _length = 0;
+    rt_uint16_t packet_id = 0;
+    struct umqtt_msg encode_msg = { 0 };
+
+    RT_ASSERT(client);
+    RT_ASSERT(topic);
+    RT_ASSERT(payload);
+    RT_ASSERT(length);
+    
+    packet_id = ((qos == UMQTT_QOS0) ? 0 : get_next_packetID(client));
+
+    encode_msg.header.bits.qos = qos;
+    encode_msg.header.bits.dup = 0;
+    encode_msg.msg.publish.packet_id = packet_id;
+    encode_msg.msg.publish.payload = payload;
+    encode_msg.msg.publish.payload_len = length;
+    encode_msg.msg.publish.topic_name = topic;
+    encode_msg.msg.publish.topic_name_len = strlen(topic);
+    _length = umqtt_encode(UMQTT_TYPE_PUBLISH, client->send_buf, client->mqtt_info.send_size, &encode_msg);
+    if (_length <= 0) 
+    {
+        _ret = UMQTT_ENCODE_ERROR;
+        LOG_E(" publish encode failed! topic: %d", topic);
+        goto exit;
+    }
+    client->send_len = _length;
+
+    _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout);
+    if (_ret < 0) 
+    {
+        _ret = UMQTT_SEND_FAILED;
+        LOG_E(" publish trans send failed!");
+        goto exit;
+    }
+
+    set_uplink_recon_tick(client, UPLINK_LAST_TICK);
+    set_uplink_recon_tick(client, UPLINK_NEXT_TICK);
+exit:
+    return _ret;
+}
+
+/** 
+ * umqtt client publish nonblocking datas
+ *
+ * @param client the input, umqtt client
+ * @param cmd the input, UMQTT_CMD_SUB_CB / UMQTT_CMD_EVT_CB
+ * @param params the input, params according to cmd
+ * 
+ */
+int umqtt_control(struct umqtt_client *client, enum umqtt_cmd cmd, void *params)
+{
+    struct subtop_recv_handler *p_subtop = RT_NULL;
+    char *topic = RT_NULL;
+    int _ret = 0;
+    RT_ASSERT(client);
+    switch(cmd)
+    {
+    case UMQTT_CMD_SUB_CB:
+        {
+            RT_ASSERT(params);
+            if (0 == rt_list_isempty(&client->sub_recv_list))       /* has list item */
+            {
+                rt_list_for_each_entry(p_subtop, &client->sub_recv_list, next_list)
+                {
+                    topic = ((struct subtop_recv_handler *)params)->topicfilter;
+                    if ((p_subtop->topicfilter != RT_NULL)
+                     && (rt_strncmp(p_subtop->topicfilter, topic, rt_strlen(topic)) == 0))
+                    {
+                        goto _exit;
+                    }
+                }
+            }
+            rt_list_insert_after(&client->sub_recv_list, &((struct subtop_recv_handler *)params)->next_list);
+        }
+        break;
+    case UMQTT_CMD_EVT_CB:
+        {
+            client->user_handler = params;
+        }
+        break;
+    case UMQTT_CMD_SET_HB:
+        {
+            client->mqtt_info.keepalive_interval = (rt_uint32_t *)params;
+        }
+        break;
+    case UMQTT_CMD_GET_CLIENT_STA:
+        {
+            return ((int)(client->connect_state));
+        }
+        break;
+    case UMQTT_CMD_DEL_HANDLE:
+        {
+            if (RT_EOK == rt_thread_delete(client->task_handle))
+            {
+                client->task_handle = RT_NULL;
+                LOG_D(" delete thread success! ");
+            }
+            else
+            {
+                LOG_E(" delete thread failed! ");
+            }
+        }
+        break;
+    case UMQTT_CMD_DISCONNECT:
+        {
+            umqtt_disconnect(client);
+        }
+        break;
+#ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME 
+    case UMQTT_CMD_SET_CON_KP:
+        {
+            client->mqtt_info.connect_keepalive_sec = (rt_uint16_t *)params;
+        }
+        break;
+#endif
+    default:
+        LOG_W(" control cmd:%d", cmd);
+        break;
+    }
+_exit:
+    return UMQTT_OK;
+}
+

+ 893 - 0
tests/umqtt_test.c

@@ -0,0 +1,893 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author         Notes
+ * 2020-05-14    springcity      the first version
+ */
+
+#include <rtthread.h>
+#include "utest.h"
+#include "umqtt_internal.h"
+#include "umqtt.h"
+#include "umqtt_test_table.h"
+
+#define UMQTT_URI                   "tcp://192.168.12.140:1883"
+#define UMQTT_URI_ERR               "tcp://192.168.12.140:183"
+
+
+#define UMQTT_SUBTOPIC_WILL         "/umqtt/test_will"              /* qos1 */
+#define UMQTT_WILL_MSG              "Will Message!"                 /* will message */
+#define UMQTT_SUBTOPIC_QOS0         "/umqtt/test_qos0"
+#define UMQTT_SUBTOPIC_QOS1         "/umqtt/test_qos1"
+#define UMQTT_SUBTOPIC_QOS2         "/umqtt/test_qos2"
+#define UMQTT_PUB_MSG               "Hello_this_world!"
+#define UMQTT_PUBMSG_QOS0           "publish message qos0"
+#define UMQTT_PUBMSG_QOS1           "publish message qos1"
+#define UMQTT_PUBMSG_QOS2           "publish message qos2"
+
+#define UMQTT_USER_NAME             "rtt_user"
+#define UMQTT_USER_PWD              "thread_pwd"
+
+
+static umqtt_client_t m_client = RT_NULL;
+#define MULTISTARTTEST_DEF_TIME             (10)   
+
+static rt_err_t umqtt_test_cd(char *uri)
+{
+    int _cnt = 0, _ret = -1;
+    struct umqtt_info user_info = {
+                        .uri = uri,
+                        };
+
+    for (_cnt = 0; _cnt < MULTISTARTTEST_DEF_TIME; _cnt++)
+    {
+        m_client = umqtt_create(&user_info);
+        if (m_client)
+        {
+            LOG_I("1st_test_item. after create. _cnt: %d", _cnt);
+            msh_exec("free", strlen("free"));
+            _ret = umqtt_delete(m_client);
+            m_client = RT_NULL;
+            LOG_I("1st_test_item. after delete. _cnt: %d", _cnt);
+            msh_exec("free", strlen("free"));
+        }
+    }
+    rt_thread_mdelay(1000);
+
+    return _ret;
+}
+
+static rt_err_t umqtt_test_cdss(char *uri)
+{
+    int _ret = -1;
+    int _cnt = 0, _lp_cnt = 0;
+    struct umqtt_info user_info = {
+                        .uri = uri,
+                        };   
+
+    for (_cnt = 0; _cnt < MULTISTARTTEST_DEF_TIME; _cnt++)
+    {
+        m_client = umqtt_create(&user_info);
+        LOG_I(" _cnt: %d, after umqtt create, ram status!", _cnt);
+        msh_exec("free", strlen("free"));
+        if (m_client)
+        {
+            for (_lp_cnt = 0; _lp_cnt < 5; _lp_cnt++)
+            {
+                _ret = umqtt_start(m_client);
+                LOG_D(" _cnt: %d, _lp_cnt:%d, after umqtt start, ram status!", _cnt, _lp_cnt);
+                msh_exec("free", strlen("free"));
+                rt_thread_mdelay(200);
+
+                umqtt_stop(m_client);
+                LOG_D(" _cnt: %d, _lp_cnt:%d, after umqtt stop, ram status!", _cnt, _lp_cnt);
+                msh_exec("free", strlen("free"));
+                rt_thread_mdelay(200);
+            }
+        }
+        _ret = umqtt_delete(m_client);
+        LOG_I(" _cnt: %d, after umqtt delete, ram status!", _cnt);
+        msh_exec("free", strlen("free"));
+        if (_ret >= 0)
+        {
+            m_client = RT_NULL;
+        }
+    }
+    rt_thread_mdelay(1000);   
+    return _ret;
+}
+
+static rt_err_t umqtt_stop_sometimes(rt_uint8_t number)
+{
+    int _cnt = 0, _ret;
+    for (_cnt = 0; _cnt < number; _cnt++)
+    {
+        umqtt_stop(m_client);
+        _ret = RT_EOK;
+        LOG_D(" cnt:%d, umqtt stop result: %d", _cnt, _ret);
+
+    }
+    rt_thread_mdelay(1000);
+    return _ret;
+}
+
+static rt_err_t umqtt_start_multitest(void)
+{
+    int _ret = -1, _cnt = 0;
+    LOG_I(" ======================================================> 1st test_tem start!");
+    _ret = umqtt_test_cd(UMQTT_URI);
+    LOG_I(" ======================================================> 1st test_tem end!");
+    msh_exec("free", strlen("free"));
+    LOG_I("*******************************************************************************");
+
+    LOG_I(" ======================================================> 2nd test_tem start!");
+    _ret = umqtt_test_cdss(UMQTT_URI);
+    LOG_I(" ======================================================> 2nd test_tem end!");
+    msh_exec("free", strlen("free"));
+    LOG_I("*******************************************************************************");
+
+    LOG_I(" ======================================================> 3rd test_tem start!");
+    _ret = umqtt_test_cd(UMQTT_URI_ERR);
+    LOG_I(" ======================================================> 3rd test_tem end!");
+    msh_exec("free", strlen("free"));
+    LOG_I("*******************************************************************************");
+
+    LOG_I(" ======================================================> 4th test_tem start!");
+    _ret = umqtt_test_cdss(UMQTT_URI_ERR);
+    LOG_I(" ======================================================> 4th test_tem end!");
+    msh_exec("free", strlen("free"));
+    LOG_I("*******************************************************************************");
+
+    LOG_I(" ======================================================> 5th test_tem start!");
+    msh_exec("free", strlen("free"));
+    _ret = umqtt_stop_sometimes(MULTISTARTTEST_DEF_TIME);
+    LOG_I(" ======================================================> 5th test_tem end!");
+    msh_exec("free", strlen("free"));
+
+    return RT_EOK;
+}
+
+static int user_callback(struct umqtt_client *client, enum umqtt_evt event)
+{
+    RT_ASSERT(client);
+    switch(event)
+    {
+    case UMQTT_EVT_LINK:
+        LOG_D(" *********************************** =======> user callback, event link! ");
+        break;
+    case UMQTT_EVT_ONLINE:
+        LOG_D(" *********************************** =======> user callback, event online! ");
+        break;
+    case UMQTT_EVT_OFFLINE:
+        LOG_D(" *********************************** =======> user callback, event offline! ");
+        break;
+    case UMQTT_EVT_HEARTBEAT:
+        LOG_D(" *********************************** =======> user callback, event heartbeat! ");
+        break;
+    default:
+        LOG_D(" *********************************** =======> user callback, event: %d", event);
+        break;
+    }
+    return 0;
+}
+
+static void umqtt_new_sub_qos0_callback(struct umqtt_client *client, void *msg_data)
+{
+    RT_ASSERT(client);
+    RT_ASSERT(msg_data);
+    struct umqtt_pkgs_publish *msg = (struct umqtt_pkgs_publish *)msg_data;   
+    // LOG_D(" umqtt new sub qos0 callback! topic name len: %d, topic name: %s, packet id: %d, payload: %s, payload len: %d ",
+    //         msg->topic_name_len,
+    //         msg->topic_name,
+    //         msg->packet_id,
+    //         msg->payload,
+    //         msg->payload_len);
+
+}
+
+static void umqtt_new_sub_qos1_callback(struct umqtt_client *client, void *msg_data)
+{
+    RT_ASSERT(client);
+    RT_ASSERT(msg_data);
+    struct umqtt_pkgs_publish *msg = (struct umqtt_pkgs_publish *)msg_data;
+    LOG_D(" umqtt new sub qos1 callback! topic name len: %d, topic name: %s, packet id: %d, payload len: %d ",
+            msg->topic_name_len,
+            msg->topic_name,
+            msg->packet_id,
+            // msg->payload,
+            msg->payload_len);
+}
+
+static void umqtt_new_sub_qos2_callback(struct umqtt_client *client, void *msg_data)
+{
+    RT_ASSERT(client);
+    RT_ASSERT(msg_data);
+    struct umqtt_pkgs_publish *msg = (struct umqtt_pkgs_publish *)msg_data;
+    LOG_D(" umqtt new sub qos2 callback! topic name len: %d, topic name: %s, packet id: %d, payload: %s, payload len: %d ",
+            msg->topic_name_len,
+            msg->topic_name,
+            msg->packet_id,
+            msg->payload,
+            msg->payload_len);
+}
+
+static rt_err_t umqtt_sub_multitest(void)
+{
+    int _ret = RT_EOK;
+    int _cnt = 0;
+    static char cid[20] = { 0 };
+    rt_snprintf(cid, sizeof(cid), "rtthread_%d", rt_tick_get());
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI,
+                        .client_id = cid,
+                        .lwt_topic = UMQTT_SUBTOPIC_WILL,
+                        // .lwt_message = UMQTT_WILL_MSG,
+                        .user_name = UMQTT_USER_NAME,
+                        .password = UMQTT_USER_PWD,
+                        };   
+
+    LOG_I(" ======================================================> sub multi start test! ");
+
+    m_client = umqtt_create(&user_info);                /* create client*/
+    if (m_client == RT_NULL)
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    umqtt_control(m_client, UMQTT_CMD_EVT_CB, user_callback);   /* register event user callback */
+
+    _ret = umqtt_start(m_client);                       /* start connect */
+    if (_ret != 0)
+    {
+        _ret = RT_ERROR;
+        LOG_E(" umqtt client start failed! ");
+        goto _exit;
+    }
+
+    for (_cnt = 0; _cnt < 10; _cnt++) 
+    {
+        LOG_D(" %d - start new sub qos0! ", _cnt);
+        if (UMQTT_OK != umqtt_subscribe(m_client, UMQTT_SUBTOPIC_QOS0, UMQTT_QOS0, umqtt_new_sub_qos0_callback)) {
+            _ret = RT_ERROR;
+            LOG_E(" %d - umqtt subscribe failed! ", _cnt);
+            goto _exit;
+        }        /* register qos0 subtopic cb */
+    }
+    for (_cnt = 0; _cnt < 10; _cnt++) 
+    {
+        LOG_D(" %d - start new sub qos1! ", _cnt);
+        if (UMQTT_OK != umqtt_subscribe(m_client, UMQTT_SUBTOPIC_QOS1, UMQTT_QOS1, umqtt_new_sub_qos1_callback)) {
+            _ret = RT_ERROR;
+            LOG_E(" %d - umqtt subscribe failed! ", _cnt);
+            goto _exit;
+        }        /* register qos0 subtopic cb */
+    }
+    for (_cnt = 0; _cnt < 10; _cnt++) 
+    {
+        LOG_D(" %d - start new sub qos2! ", _cnt);
+        if (UMQTT_OK != umqtt_subscribe(m_client, UMQTT_SUBTOPIC_QOS2, UMQTT_QOS2, umqtt_new_sub_qos2_callback)) {
+            _ret = RT_ERROR;
+            LOG_E(" %d - umqtt subscribe failed! ", _cnt);
+            goto _exit;
+        }        /* register qos0 subtopic cb */
+    }
+
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        LOG_D(" %d unsub topic qos0! ", _cnt);
+        if (UMQTT_OK != umqtt_unsubscribe(m_client, UMQTT_SUBTOPIC_QOS0)) 
+        {
+            _ret = RT_ERROR;
+            LOG_E(" %d unsub qos0 topic failed! ", _cnt);
+            goto _exit;
+        }
+    }
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        LOG_D(" %d unsub topic qos1! ", _cnt);
+        if (UMQTT_OK != umqtt_unsubscribe(m_client, UMQTT_SUBTOPIC_QOS1))
+        {
+            _ret = RT_ERROR;
+            LOG_E(" unsub qos1 topic failed! ");
+            goto _exit;
+        }
+    }
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        LOG_D(" %d unsub topic qos2! ", _cnt);
+        if (UMQTT_OK != umqtt_unsubscribe(m_client, UMQTT_SUBTOPIC_QOS2))
+        {
+            _ret = RT_ERROR;
+            LOG_E(" %d unsub qos2 topic failed! ", _cnt);
+            goto _exit;
+        }
+    }
+
+_exit:
+    LOG_I(" ======================================================> sub multi end test! stop umqtt, delete umqtt client! ");
+    umqtt_stop(m_client);                               /* stop umqtt */
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);                      /* delete umqtt client */
+    if (_ret != 0)
+        _ret = RT_ERROR;
+    m_client = RT_NULL;
+
+    return _ret;
+}
+
+static rt_err_t umqtt_publish_multitest(void)
+{
+    int _ret = RT_EOK;
+    int _cnt = 0;
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI,
+                        };
+
+    LOG_I(" ======================================================> publish multi start test! ");
+
+    m_client = umqtt_create(&user_info);
+    if (m_client == RT_NULL)
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    umqtt_control(m_client, UMQTT_CMD_EVT_CB, user_callback);
+
+    _ret = umqtt_start(m_client);
+    if (_ret != 0)
+    {
+        _ret = RT_ERROR;
+        LOG_E(" umqtt client start failed! ");
+        goto _exit;
+    }
+    LOG_D(" ==============================================> start sub topic QoS0/1/2! ");
+    if (UMQTT_OK != umqtt_subscribe(m_client, UMQTT_SUBTOPIC_QOS0, UMQTT_QOS0, umqtt_new_sub_qos0_callback))
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    } 
+    if (UMQTT_OK != umqtt_subscribe(m_client, UMQTT_SUBTOPIC_QOS1, UMQTT_QOS1, umqtt_new_sub_qos1_callback))
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    if (UMQTT_OK != umqtt_subscribe(m_client, UMQTT_SUBTOPIC_QOS2, UMQTT_QOS2, umqtt_new_sub_qos2_callback))
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    LOG_D(" ==============================================> start publish QoS0/1/2 messages! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS0, UMQTT_SUBTOPIC_WILL, UMQTT_PUB_MSG, strlen(UMQTT_PUBMSG_QOS0), 10);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            LOG_E(" publish qos0 will topic & message error!");
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS1, UMQTT_SUBTOPIC_WILL, UMQTT_PUB_MSG, strlen(UMQTT_PUBMSG_QOS1), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            LOG_E(" publish qos1 will topic & message error!");
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS2, UMQTT_SUBTOPIC_WILL, UMQTT_PUB_MSG, strlen(UMQTT_PUBMSG_QOS2), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            LOG_E(" publish qos2 will topic & message error!");
+            goto _exit;
+        }
+    }
+    LOG_D(" ==============================================> start publish QoS0 messages! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS0, UMQTT_SUBTOPIC_QOS0, UMQTT_PUBMSG_QOS0, strlen(UMQTT_PUBMSG_QOS0), 10);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+    }
+    LOG_D(" ==============================================> start publish QoS1 messages! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS1, UMQTT_SUBTOPIC_QOS1, UMQTT_PUBMSG_QOS1, strlen(UMQTT_PUBMSG_QOS1), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+    }
+    LOG_D(" ==============================================> start publish QoS2 messages! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS2, UMQTT_SUBTOPIC_QOS2, UMQTT_PUBMSG_QOS2, strlen(UMQTT_PUBMSG_QOS2), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+    }
+    LOG_D(" ==============================================> start publish QoS0/1/2, QoS0 messages! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS0, UMQTT_SUBTOPIC_QOS0, UMQTT_PUBMSG_QOS0, strlen(UMQTT_PUBMSG_QOS0), 10);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS1, UMQTT_SUBTOPIC_QOS0, UMQTT_PUBMSG_QOS0, strlen(UMQTT_PUBMSG_QOS0), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS2, UMQTT_SUBTOPIC_QOS0, UMQTT_PUBMSG_QOS0, strlen(UMQTT_PUBMSG_QOS0), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+    }
+    LOG_D(" ==============================================> start publish QoS0/1/2, QoS1 messages! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS0, UMQTT_SUBTOPIC_QOS1, UMQTT_PUBMSG_QOS1, strlen(UMQTT_PUBMSG_QOS1), 10);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS1, UMQTT_SUBTOPIC_QOS1, UMQTT_PUBMSG_QOS1, strlen(UMQTT_PUBMSG_QOS1), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS2, UMQTT_SUBTOPIC_QOS1, UMQTT_PUBMSG_QOS1, strlen(UMQTT_PUBMSG_QOS1), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+    }
+    LOG_D(" ==============================================> start publish QoS0/1/2, QoS2 messages! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS0, UMQTT_SUBTOPIC_QOS2, UMQTT_PUBMSG_QOS2, strlen(UMQTT_PUBMSG_QOS2), 10);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS1, UMQTT_SUBTOPIC_QOS2, UMQTT_PUBMSG_QOS2, strlen(UMQTT_PUBMSG_QOS2), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+        _ret = umqtt_publish(m_client, UMQTT_QOS2, UMQTT_SUBTOPIC_QOS2, UMQTT_PUBMSG_QOS2, strlen(UMQTT_PUBMSG_QOS2), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            goto _exit;
+        }
+    }
+
+    LOG_D(" ==============================================> start publish QoS1, QoS1 big messages! continue publish! ");
+    for (_cnt = 0; _cnt < 10; _cnt++)
+    {
+        _ret = umqtt_publish(m_client, UMQTT_QOS1, UMQTT_SUBTOPIC_QOS1, user_big_msg, strlen(user_big_msg), 10000);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            LOG_E(" cnt: %d, publish user big messages error! ", _cnt);
+            goto _exit;
+        }
+        rt_thread_mdelay(1000);            /* big number need delay, at least 10 tick! */
+    }
+
+    rt_thread_mdelay(500);
+_exit:
+    LOG_I(" ======================================================> publish mulit end test! stop umqtt, delete umqtt client!");
+    umqtt_stop(m_client);
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);
+    m_client = RT_NULL;
+    if (_ret != 0)
+        _ret = RT_ERROR;
+
+    return _ret;
+}
+
+    /*
+    * 1. 心跳机制,在最大的 max 中需要发送到周期上面
+    * 2. 测试定时器中保活机制,重连机制
+    * 3. 接收数据为 finack 的时候重连, 在接收阶段的重连机制
+    * 4. 开始阶段,connect 函数中重连机制
+    * 5. publish/subscribe 大数据请求和ping相冲突的时候
+    */
+
+static rt_err_t umqtt_kp_recon_1st_item(void)
+{
+    int _ret = RT_EOK, _cnt = 0;
+    rt_uint32_t _u32data = 0;
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI,
+                        };
+
+    /* 1. 方案: 时间间隔放短一点,这样子连接时间就可以放的长一点。超过心跳时间,Broker 会向 Client 发送 FinAck,之后进行重新连接测试 */
+    LOG_I(" ======================================================> 1. hb time interval - 1Sec, start test! ");
+
+    m_client = umqtt_create(&user_info);
+    if (m_client == RT_NULL)
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    umqtt_control(m_client, UMQTT_CMD_EVT_CB, user_callback);
+
+#ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME
+    _u32data = 1;
+    umqtt_control(m_client, UMQTT_CMD_SET_CON_KP, _u32data);
+#endif
+
+    _u32data = 5;
+    umqtt_control(m_client, UMQTT_CMD_SET_HB, _u32data);
+    LOG_D(" before start umqtt client! ");
+    _ret = umqtt_start(m_client);
+    if (_ret != 0)
+    {
+        _ret = RT_ERROR;
+        LOG_E(" umqtt client start failed! ");
+        goto _exit;
+    }
+    LOG_D(" before start wait 120 Sec! ");
+    while(++_cnt <= 120)
+    {
+        if (_cnt % 10 == 0)
+            LOG_D(" test 1st style! test heart beat! cnt:%d, now tick: %d", _cnt, rt_tick_get());
+        _u32data = umqtt_control(m_client, UMQTT_CMD_GET_CLIENT_STA, NULL);
+        LOG_D("  get client status: %d, now tick: %d ", _u32data, rt_tick_get());
+        if (_u32data == UMQTT_CS_DISCONNECT)
+        {
+            goto _exit;
+        }
+        rt_thread_mdelay(1000);                  /* delay 120 Sec */ 
+    }
+
+_exit:
+    LOG_I(" ======================================================> 1. hb time interval - 1Sec, end test! reset hb time %d Sec ", _u32data);
+    umqtt_stop(m_client);
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);
+    m_client = RT_NULL;
+    if (_ret != 0)
+        _ret = RT_ERROR;
+
+    return _ret;
+}
+static rt_err_t umqtt_kp_recon_2nd_item(void)
+{
+    int _ret = RT_EOK, _cnt = 0;
+    rt_uint32_t _u32data = 0;
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI,
+                        };
+
+    /* 2. 方案: 连接结束后,关闭接收线程,那么就会启动定时器, 进行重连。 */
+    LOG_I(" ======================================================> 2. test keepalive and reconnect style, start test! ");
+
+    m_client = umqtt_create(&user_info);
+    if (m_client == RT_NULL)
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    umqtt_control(m_client, UMQTT_CMD_EVT_CB, user_callback);
+    _u32data = 1;
+    umqtt_control(m_client, UMQTT_CMD_SET_HB, _u32data);
+    _ret = umqtt_start(m_client);
+    if (_ret != 0)
+    {
+        _ret = RT_ERROR;
+        LOG_E(" umqtt client start failed! ");
+        goto _exit;
+    }
+
+    LOG_D(" a. delete umqtt recv thread!");
+    LOG_D("**************************** before delete thread ****************************************");
+    msh_exec("free", strlen("free"));
+    umqtt_control(m_client, UMQTT_CMD_DEL_HANDLE, NULL);
+    rt_thread_mdelay(100);
+    LOG_D("**************************** after delete thread *****************************************");
+    msh_exec("free", strlen("free"));
+
+    LOG_D(" b. wait 800 Sec!");
+    while(++_cnt <= 800)          /* reconnect time 10Sec */
+    {   
+        if (_cnt % 10 == 0)
+            LOG_D(" **************************** wait time **************************, cnt: %d, now tick: %d ", _cnt, rt_tick_get());
+        rt_thread_mdelay(1000);
+    }
+
+_exit:
+    LOG_I(" ======================================================> 2. test keepalive and reconnect style, end test! ");
+    umqtt_stop(m_client);
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);
+    m_client = RT_NULL;
+    if (_ret != 0)
+        _ret = RT_ERROR;
+
+    return _ret;
+}
+static rt_err_t umqtt_kp_recon_3rd_item(void)
+{
+    int _ret = RT_EOK, _cnt = 0, _recnt = 0;
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI,
+                        };
+    /* 3. 方案: 接收到finack 进行重连机制测试, 发送断线连接 */
+    LOG_I(" ======================================================> 3. test keepalive and reconnect style, start test! ");
+    m_client = umqtt_create(&user_info);
+    if (m_client == RT_NULL)
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    umqtt_control(m_client, UMQTT_CMD_EVT_CB, user_callback);
+
+    _ret = umqtt_start(m_client);
+    if (_ret != 0)
+    {
+        _ret = RT_ERROR;
+        LOG_E(" umqtt client start failed! ");
+        goto _exit;
+    }
+
+    for (_recnt = 0; _recnt < PKG_UMQTT_INFO_DEF_RECONNECT_MAX_NUM; _recnt++)
+    {
+        LOG_D(" a. send disconnect mqtt message, and send closesocket! recount: %d ", _recnt);
+        umqtt_control(m_client, UMQTT_CMD_DISCONNECT, NULL);
+    
+        LOG_D(" b. wait 10 Sec! recount: %d", _recnt);
+        while(++_cnt <= 20)
+        {   
+            if (_cnt % 10 == 0)
+                LOG_D(" **************************** wait time **************************, cnt: %d, now tick: %d ", _cnt, rt_tick_get());
+            rt_thread_mdelay(1000);
+        }
+        _cnt = 0;
+    }
+
+_exit:
+    LOG_I(" ======================================================> 3. test keepalive and reconnect style, end test! ");
+    umqtt_stop(m_client);
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);
+    m_client = RT_NULL;
+    if (_ret != 0)
+        _ret = RT_ERROR;
+
+    return _ret;
+}
+
+static rt_err_t umqtt_kp_recon_4th_item(void)
+{
+    int _ret = RT_EOK, _cnt = 0;
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI_ERR,
+                        };
+    /* 4. 方案: 设定 URI 为错误 URI,那么一开始就会不停地重连 */
+    
+    LOG_I(" ======================================================> 4. test keepalive and reconnect style, start test! ");
+
+    m_client = umqtt_create(&user_info);
+    if (m_client == RT_NULL)
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    umqtt_control(m_client, UMQTT_CMD_EVT_CB, user_callback);
+
+    _ret = umqtt_start(m_client);
+    if (_ret != 0)
+    {
+        _ret = RT_ERROR;
+        LOG_E(" umqtt client start failed! ");
+        goto _exit;
+    }
+
+    rt_thread_mdelay(1000);
+
+_exit:
+    LOG_I(" ======================================================> 4. test keepalive and reconnect style, end test! ");
+    umqtt_stop(m_client);
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);
+    m_client = RT_NULL;
+    if (_ret != 0)
+        _ret = RT_ERROR;
+
+    return _ret;
+}
+
+static rt_err_t umqtt_kp_recon_5th_item(void)
+{
+    int _ret = RT_EOK, _cnt = 0;
+    rt_uint32_t _u32data = 0;
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI,
+                        };
+    /* 5. 方案: 心跳周期设置短一点,在这段时间内大量的数据订阅和发送不停地推送,和心跳请求产生冲突 */
+
+    LOG_I(" ======================================================> 5. test keepalive and reconnect style, start test! ");
+    m_client = umqtt_create(&user_info);
+    if (m_client == RT_NULL)
+    {
+        _ret = RT_ERROR;
+        goto _exit;
+    }
+    umqtt_control(m_client, UMQTT_CMD_EVT_CB, user_callback);
+
+#ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME
+    _u32data = 10;
+    umqtt_control(m_client, UMQTT_CMD_SET_CON_KP, _u32data);
+#endif
+    _u32data = 2;
+    umqtt_control(m_client, UMQTT_CMD_SET_HB, _u32data);
+    LOG_D(" before start umqtt client! ");
+    _ret = umqtt_start(m_client);
+    if (_ret != 0)
+    {
+        _ret = RT_ERROR;
+        LOG_E(" umqtt client start failed! ");
+        goto _exit;
+    }
+
+    if (UMQTT_OK != umqtt_subscribe(m_client, UMQTT_SUBTOPIC_QOS0, UMQTT_QOS0, umqtt_new_sub_qos0_callback))
+    {
+        _ret = RT_ERROR;
+        LOG_E(" subscribe qos0 error! ");
+        goto _exit;
+    }
+
+    for (_cnt = 0; _cnt < 100000; _cnt++)
+    {   
+        if (_cnt % 10 == 0)
+            LOG_D(" **************************** before publish **************************, cnt: %d, now tick: %d ", _cnt, rt_tick_get());
+        _ret = umqtt_publish(m_client, UMQTT_QOS0, UMQTT_SUBTOPIC_QOS0, UMQTT_PUBMSG_QOS0, strlen(UMQTT_PUBMSG_QOS0), 10);
+        if (_ret != UMQTT_OK)
+        {
+            _ret = RT_ERROR;
+            LOG_E(" publish error! cnt:%d ", _cnt);
+            goto _exit;
+        }        
+        rt_thread_mdelay(10);
+    }
+
+    rt_thread_mdelay(1000);
+
+_exit:
+    LOG_I(" ======================================================> 5. test keepalive and reconnect style, end test! ");
+    umqtt_stop(m_client);
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);
+    m_client = RT_NULL;
+    if (_ret != 0)
+        _ret = RT_ERROR;
+
+    return _ret;
+}
+
+static rt_err_t umqtt_kp_recon_multitest(void)
+{
+    int _ret = RT_EOK, _cnt = 0;
+    rt_uint32_t _u32data = 0;
+    struct umqtt_info user_info = {
+                        .uri = UMQTT_URI,
+                        };
+    char _name[RT_NAME_MAX];
+    static int _lock_cnt = 0;
+
+    _ret = umqtt_kp_recon_1st_item();       /* test result ok! */
+    if (_ret != RT_EOK)
+    {
+        LOG_E(" ******************** keepalive reconnect 1st item! ");
+        goto _exit;
+    }
+    _ret = umqtt_kp_recon_2nd_item();       /* test result ok! */
+    if (_ret != RT_EOK)
+    {
+        LOG_E(" ******************** keepalive reconnect 2nd item! ");
+        goto _exit;
+    }
+    _ret = umqtt_kp_recon_3rd_item();       /* test result ok! */
+    if (_ret != RT_EOK)
+    {
+        LOG_E(" ******************** keepalive reconnect 3rd item! ");
+        goto _exit;
+    }
+    _ret = umqtt_kp_recon_4th_item();       /* test result ok! */
+    if (_ret != RT_EOK)
+    {
+        LOG_E(" ******************** keepalive reconnect 4th item! ");
+        goto _exit;
+    }
+    _ret = umqtt_kp_recon_5th_item();       /* test result ok! */
+    if (_ret != RT_EOK)
+    {
+        LOG_E(" ******************** keepalive reconnect 5th item! ");
+        goto _exit;
+    }
+
+_exit:
+    LOG_I(" ======================================================> keepalive & reconnect function, mulit end test! stop umqtt, delete umqtt client!");
+    umqtt_stop(m_client);
+    LOG_D(" before delete umqtt client! ");
+    _ret = umqtt_delete(m_client);
+    m_client = RT_NULL;
+    if (_ret != 0)
+        _ret = RT_ERROR;
+
+    return _ret;
+}
+
+static void test_umqtt_start(void)
+{
+    uassert_true(umqtt_start_multitest() == RT_EOK);
+}
+
+static void test_umqtt_subscribe(void)
+{
+    uassert_true(umqtt_sub_multitest() == RT_EOK);
+}
+
+static void test_umqtt_publish(void)
+{
+    uassert_true(umqtt_publish_multitest() == RT_EOK);
+}
+
+static void test_umqtt_kp_recon(void)
+{
+    uassert_true(umqtt_kp_recon_multitest() == RT_EOK);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+    LOG_I(" utest tc init! ");
+    m_client = RT_NULL;
+    return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+    LOG_I(" utest tc cleanup! ");
+    return RT_EOK;
+}
+
+static void testcase(void)
+{
+    LOG_I(" in testcase func... ");
+
+    UTEST_UNIT_RUN(test_umqtt_start);
+    UTEST_UNIT_RUN(test_umqtt_subscribe);
+    UTEST_UNIT_RUN(test_umqtt_publish);
+    UTEST_UNIT_RUN(test_umqtt_kp_recon);
+}
+
+
+UTEST_TC_EXPORT(testcase, "utest.umqtt_test.c", utest_tc_init, utest_tc_cleanup, 10);
+
+
+
+
+

+ 304 - 0
tests/umqtt_test_table.h

@@ -0,0 +1,304 @@
+
+#define UMQTT_BIG_MSG_NUM           3000
+static const char user_big_msg[UMQTT_BIG_MSG_NUM] = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 \
+0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
+
+