Browse Source

Merge pull request #16 from Ryan-CW-Code/next

优化测试代码,增加网络故障、网络韧性测试、内存故障测试。优化弱网下表现
RyanCW 3 months ago
parent
commit
600c2ae67d

+ 1 - 0
.github/copilot-instructions.md

@@ -39,6 +39,7 @@ RyanMqtt是一个严格遵循 [MQTT 3.1.1](https://docs.oasis-open.org/mqtt/mqtt
 - RyanMqtt心跳使用1.5倍时间,当心跳周期到达设置的0.9倍后会发送心跳包,如果超过1.5倍就通知用户断开连接
 
 ## 代码格式
+- 可以使用中文进行注释
 - 所有代码须符合项目 `.clang-format` 配置
 
 ## 最佳实践

+ 5 - 1
.vscode/settings.json

@@ -47,6 +47,10 @@
         "inttypes.h": "c",
         "memory": "c",
         "stdbool.h": "c",
-        "ryanmqttutil.h": "c"
+        "ryanmqttutil.h": "c",
+        "cstdlib": "c",
+        "stdatomic.h": "c",
+        "atomic": "c",
+        "cstdint": "c"
     },
 }

+ 58 - 8
README.md

@@ -24,10 +24,12 @@
 
 - ✅ **AI 辅助开发与审查**,结合  **[coderabbitai](https://www.coderabbit.ai)** 与 **[Copilot](https://github.com/features/copilot)** ,在编码与代码审查阶段持续优化代码质量,构建多层安全防线
 
-- ✅ **8 大类专项测试用例**,覆盖广泛场景,全链路内存泄漏检测,强化稳定性与可靠性
+- ✅ **11 大类专项测试用例**,覆盖广泛场景,全链路内存泄漏检测,强化稳定性与可靠性
 
 - ✅ **支持多客户端实例**,满足复杂业务场景下的多连接需求
 
+- ✅ **支持弱网/丢包严重环境**,保障连接稳定与消息可靠传输
+
 - ✅ **全等级 QoS 支持(QoS0/1/2)**,并提供**用户可控的消息丢弃策略**,防止 QoS1/QoS2 重传导致内存无限堆积
 
 - ✅ **完整的主题匹配与通配符支持**,主题层级分隔符 `/`、通配符 `#`/`+`,并正确处理以 `$` 开头的系统主题
@@ -39,9 +41,8 @@
 - ✅ **丰富的参数与事件回调接口**,丰富的参数配置与事件回调接口,满足绝大多数实际项目需求(**欢迎提出新需求**)
 
 - ✅**高性能与高并发能力**,在 Linux 环境下稳定支撑 20 个并发发送线程,每线程连续发送 1000 条 QoS1/QoS2 消息,零丢包、零异常
-  > ⚠️ 实际性能受单片机内存大小及网络硬件能力影响
-
-- ✅ **复杂线程环境下稳定运行**,已在多个商业项目中长期验证
+  
+- ✅ **复杂线程环境下稳定运行**,已在公司多个商业项目中长期验证
 
 - ✅ **跨平台设计**,仅需实现少量平台接口即可快速移植
 
@@ -65,7 +66,56 @@ RyanMqtt 设计时参考了[mqttclient](https://github.com/jiejieTop/mqttclient)
 | **系统服务管理模块** | 提供会话管理、事件调度、通配符匹配、消息链表管理等基础服务   |
 | **用户应用模块**     | 向用户提供完整的 API 接口,包括客户端生命周期管理、连接控制、发布/订阅、事件注册等 |
 
-### 3、平台接口
+### 3、测试体系
+
+**11 大类专项测试用例**,覆盖从基础功能到极限压力场景的全流程验证:
+
+| 测试类别                                                  | 测试目标                                                     |
+| --------------------------------------------------------- | ------------------------------------------------------------ |
+| 1. 客户端销毁压力测试                                     | 验证资源释放的幂等性与完整性                                 |
+| 2. 心跳与超时处理                                         | Keep-Alive、PINGREQ/RESP 机制验证                            |
+| 3. 消息链路完整性(QoS 0/1/2)                            | QoS 0/1/2 消息端到端可靠性验证                               |
+| 4. 自动/手动重连机制                                      | 状态机正确性与连接恢复能力                                   |
+| 5. 批量/重复订阅一致性                                    | 订阅表一致性、内存安全、去重逻辑                             |
+| 6. 多客户端高并发                                         | 20+ 客户端并发运行稳定性                                     |
+| 7. 单客户端多线程共享(20 线程 × 各 1000 条 QoS1/2 消息) | 锁机制、数据一致性、竞态防护                                 |
+| 8. 公共API参数校验                                        | 提升接口健壮性与可用性                                       |
+| 9. 随机网络故障内存回收检测                               | 验证异常网络情况下的内存释放,提升接口健壮性与可用性         |
+| 10. 随机网络故障弱网丢包测试                              | 在弱网及高丢包环境下,完成 QOS 全等级消息完整性与内存回收验证 |
+| 11. 随机内存故障,内存回收测试                            | 验证内存异常情况下的资源释放,提升接口健壮性与可用性         |
+
+#### 📊 测试覆盖范围
+
+- **基础连接**:100 次循环连接/断开,混合 QoS 消息与订阅
+- **消息流控**:连续发送 QoS0/1/2 等级 1000 条消息,混合 QoS 压力测试
+- **订阅管理**:批量、大量、重复、混合 QoS 订阅与取消
+- **并发压测:**
+  - 单客户端 20 线程 × 各 1000 条 QoS1/2 消息
+  - 多客户端 20 并发实例进行双向发布/订阅
+- **可靠性**:长连接、弱网、随机网络故障、随机内存故障、Keep-Alive、重连机制验证
+- **资源安全**:全链路内存泄漏、句柄泄漏检测
+
+### 4、代码质量与规范
+
+#### ✅ 工具链全面集成
+
+| 工具                                                         | 用途                                                         |
+| ------------------------------------------------------------ | ------------------------------------------------------------ |
+| **[Sanitizer](https://clang.llvm.org/docs/index.html#sanitizers)** | 运行时捕获内存与线程安全问题                                 |
+| **[clang-tidy](https://clang.llvm.org/extra/clang-tidy/#clang-tidy)** | 静态分析潜在缺陷(空指针、资源泄漏等)                       |
+| **[Cppcheck](https://cppcheck.sourceforge.io/)**             | 深度扫描内存与资源问题                                       |
+| **[ClangFormat](https://clang.llvm.org/docs/ClangFormat.html)** | 统一代码风格                                                 |
+| **编译器警告**                                               | `-Wall -Wextra`(默认)、`-Weffc++`/`-Weverything`(Clang 可选,CI 强化时开启) |
+
+#### ✅ 检查重点覆盖
+
+- 内存安全:杜绝泄漏、越界、悬空指针
+- 性能优化:减少冗余拷贝与低效算法
+- 可读性:命名规范、注释完整、逻辑清晰
+
+> ✅ **成果**:实现接近语法级"**零缺陷**",长期维护成本大幅降低
+
+### 5、平台接口
 
 _RyanMqtt 依赖于用户实现以下三类平台接口,以确保在不同系统中正常运行。_
 
@@ -117,7 +167,7 @@ _RyanMqtt 依靠函数生成毫秒时间戳,用于计算持续时间和超时
 | ---------------- | ---------------------------- |
 | platformUptimeMs | 返回系统启动以来的毫秒时间戳 |
 
-### 4、示例
+### 6、示例
 
 #### RT-Thread 平台
 
@@ -147,11 +197,11 @@ _RyanMqtt 依靠函数生成毫秒时间戳,用于计算持续时间和超时
 
 - 接口示例请参考 platform/linux 文件夹,请根据平台差异进行修改
 
-### 5、依赖
+### 7、依赖
 
 本项目 **无外部第三方库依赖**,仅依赖用户实现的平台接口。
 
-### 6、免责声明
+### 8、免责声明
 
 > ⚠️ **重要提示**
 

+ 14 - 11
RyanMqtt2.0发布说明及迁移指南.md

@@ -26,7 +26,7 @@ extern RyanMqttError_e RyanMqttUnSubscribeMany(RyanMqttClient_t *client, int32_t
             RyanMqttUnSubscribeData_t unSubscribeManyData[]);
 
 // 带用户数据的发布
-extern RyanMqttError_e RyanMqttPublishAndUserData(RyanMqttClient_t *client, char *topic, uint16_t topicLen,
+extern RyanMqttError_e RyanMqttPublishWithUserData(RyanMqttClient_t *client, char *topic, uint16_t topicLen,
         char *payload, uint32_t payloadLen, RyanMqttQos_e qos,
         RyanMqttBool_e retain, void *userData);
 
@@ -73,16 +73,19 @@ uint32_t platformUptimeMs(void);
 
 新增 **8 大类专项测试用例**,覆盖从基础功能到极限压力场景的全流程验证:
 
-| 测试类别                                                  | 测试目标                          |
-| --------------------------------------------------------- | --------------------------------- |
-| 1. 客户端销毁压力测试                                     | 验证资源释放的幂等性与完整性      |
-| 2. 心跳与超时处理                                         | Keep-Alive、PINGREQ/RESP 机制验证 |
-| 3. 消息链路完整性(QoS 0/1/2)                            | QoS 0/1/2 消息端到端可靠性验证    |
-| 4. 自动/手动重连机制                                      | 状态机正确性与连接恢复能力        |
-| 5. 批量/重复订阅一致性                                    | 订阅表一致性、内存安全、去重逻辑  |
-| 6. 多客户端高并发                                         | 20+ 客户端并发运行稳定性          |
-| 7. 单客户端多线程共享(20 线程 × 各 1000 条 QoS1/2 消息) | 锁机制、数据一致性、竞态防护      |
-| 8. 公共API参数校验                                        | 提升接口健壮性与可用性            |
+| 测试类别                                                  | 测试目标                                                     |
+| --------------------------------------------------------- | ------------------------------------------------------------ |
+| 1. 客户端销毁压力测试                                     | 验证资源释放的幂等性与完整性                                 |
+| 2. 心跳与超时处理                                         | Keep-Alive、PINGREQ/RESP 机制验证                            |
+| 3. 消息链路完整性(QoS 0/1/2)                            | QoS 0/1/2 消息端到端可靠性验证                               |
+| 4. 自动/手动重连机制                                      | 状态机正确性与连接恢复能力                                   |
+| 5. 批量/重复订阅一致性                                    | 订阅表一致性、内存安全、去重逻辑                             |
+| 6. 多客户端高并发                                         | 20+ 客户端并发运行稳定性                                     |
+| 7. 单客户端多线程共享(20 线程 × 各 1000 条 QoS1/2 消息) | 锁机制、数据一致性、竞态防护                                 |
+| 8. 公共API参数校验                                        | 提升接口健壮性与可用性                                       |
+| 9. 随机网络故障内存回收检测                               | 验证异常网络情况下的内存释放,提升接口健壮性与可用性         |
+| 10. 随机网络故障弱网丢包测试                              | 在弱网及高丢包环境下,完成 QOS 全等级消息完整性与内存回收验证 |
+| 11. 随机内存故障,内存回收测试                            | 验证内存异常情况下的资源释放,提升接口健壮性与可用性         |
 
 #### 📊 测试覆盖范围
 

+ 11 - 2
coreMqtt/core_mqtt_serializer.c

@@ -924,14 +924,23 @@ static bool incomingPacketValid( uint8_t packetType )
     {
         /* Valid incoming packet types. */
         case MQTT_PACKET_TYPE_CONNACK:
-        case MQTT_PACKET_TYPE_PUBLISH:
         case MQTT_PACKET_TYPE_PUBACK:
         case MQTT_PACKET_TYPE_PUBREC:
         case MQTT_PACKET_TYPE_PUBCOMP:
         case MQTT_PACKET_TYPE_SUBACK:
         case MQTT_PACKET_TYPE_UNSUBACK:
         case MQTT_PACKET_TYPE_PINGRESP:
-            status = true;
+            if( ( packetType & 0x0FU ) == 0U )
+            {
+                status = true;
+            }
+            break;
+
+        case MQTT_PACKET_TYPE_PUBLISH:
+            if( ( packetType & 0x06U ) != 0x06U )
+            {
+                status = true;
+            }
             break;
 
         case ( MQTT_PACKET_TYPE_PUBREL & 0xF0U ):

+ 43 - 27
mqttclient/RyanMqttClient.c

@@ -1,5 +1,5 @@
 #define RyanMqttLogLevel (RyanMqttLogLevelAssert) // 日志打印等级
-// #define RyanMqttLogLevel (RyanMqttLogLevelError) // 日志打印等级
+// #define RyanMqttLogLevel (RyanMqttLogLevelError)  // 日志打印等级
 // #define RyanMqttLogLevel (RyanMqttLogLevelDebug) // 日志打印等级
 
 #include "RyanMqttClient.h"
@@ -350,7 +350,7 @@ __exit:
 			msgMatchCriteria.topicLen = subscriptionList[i].topicFilterLength;
 			msgMatchCriteria.packetId = packetId;
 
-			RyanMqttMsgHandlerFindAndDestroyByPackId(client, &msgMatchCriteria, RyanMqttFalse);
+			RyanMqttMsgHandlerFindAndDestroyByPacketId(client, &msgMatchCriteria, RyanMqttFalse);
 		}
 	}
 
@@ -407,13 +407,13 @@ RyanMqttError_e RyanMqttUnSubscribeMany(RyanMqttClient_t *client, int32_t count,
 	}
 
 	// 申请 coreMqtt 支持的topic格式空间
-	MQTTSubscribeInfo_t *subscriptionList = platformMemoryMalloc(sizeof(MQTTSubscribeInfo_t) * count);
-	RyanMqttCheck(NULL != subscriptionList, RyanMqttNotEnoughMemError, RyanMqttLog_d);
+	MQTTSubscribeInfo_t *unSubscriptionList = platformMemoryMalloc(sizeof(MQTTSubscribeInfo_t) * count);
+	RyanMqttCheck(NULL != unSubscriptionList, RyanMqttNotEnoughMemError, RyanMqttLog_d);
 	for (int32_t i = 0; i < count; i++)
 	{
-		subscriptionList[i].qos = (MQTTQoS_t)RyanMqttQos0; // 无效数据,仅当占位符
-		subscriptionList[i].pTopicFilter = unSubscribeManyData[i].topic;
-		subscriptionList[i].topicFilterLength = unSubscribeManyData[i].topicLen;
+		unSubscriptionList[i].qos = (MQTTQoS_t)RyanMqttSubFail; // 无效数据,仅用作占位符
+		unSubscriptionList[i].pTopicFilter = unSubscribeManyData[i].topic;
+		unSubscriptionList[i].topicFilterLength = unSubscribeManyData[i].topicLen;
 	}
 
 	// 序列化数据包
@@ -422,19 +422,19 @@ RyanMqttError_e RyanMqttUnSubscribeMany(RyanMqttClient_t *client, int32_t count,
 
 		// 获取数据包大小
 		MQTTStatus_t status =
-			MQTT_GetUnsubscribePacketSize(subscriptionList, count, &remainingLength, &fixedBuffer.size);
+			MQTT_GetUnsubscribePacketSize(unSubscriptionList, count, &remainingLength, &fixedBuffer.size);
 		RyanMqttAssert(MQTTSuccess == status);
 
 		// 申请数据包的空间
 		fixedBuffer.pBuffer = platformMemoryMalloc(fixedBuffer.size);
 		RyanMqttCheckCode(NULL != fixedBuffer.pBuffer, RyanMqttNotEnoughMemError, RyanMqttLog_d,
-				  { platformMemoryFree(subscriptionList); });
+				  { platformMemoryFree(unSubscriptionList); });
 
 		// 序列化数据包
 		packetId = RyanMqttGetNextPacketId(client);
-		status = MQTT_SerializeUnsubscribe(subscriptionList, count, packetId, remainingLength, &fixedBuffer);
+		status = MQTT_SerializeUnsubscribe(unSubscriptionList, count, packetId, remainingLength, &fixedBuffer);
 		RyanMqttCheckCode(MQTTSuccess == status, RyanMqttSerializePacketError, RyanMqttLog_d, {
-			platformMemoryFree(subscriptionList);
+			platformMemoryFree(unSubscriptionList);
 			platformMemoryFree(fixedBuffer.pBuffer);
 		});
 	}
@@ -443,20 +443,23 @@ RyanMqttError_e RyanMqttUnSubscribeMany(RyanMqttClient_t *client, int32_t count,
 	for (int32_t i = 0; i < count; i++)
 	{
 		// ?不判断是否订阅,统一都发送取消
-		RyanMqttMsgHandler_t msgMatchCriteria = {.topic = (char *)subscriptionList[i].pTopicFilter,
-							 .topicLen = subscriptionList[i].topicFilterLength};
+		RyanMqttMsgHandler_t msgMatchCriteria = {.topic = (char *)unSubscriptionList[i].pTopicFilter,
+							 .topicLen = unSubscriptionList[i].topicFilterLength};
+
+		platformMutexLock(client->config.userData, &client->msgHandleLock);
 		result =
 			RyanMqttMsgHandlerFind(client, &msgMatchCriteria, RyanMqttFalse, &subMsgHandler, RyanMqttFalse);
 		if (RyanMqttSuccessError == result)
 		{
 			// !有线程安全问题,subMsgHandler是指针,但用户层只要不是特别的混乱重复取消订阅这里应该就问题,暂时不管成本太高
 			// 同步msg qos等级,之后unsub回调使用
-			subscriptionList[i].qos = (MQTTQoS_t)subMsgHandler->qos;
+			unSubscriptionList[i].qos = (MQTTQoS_t)subMsgHandler->qos;
 		}
+		platformMutexUnLock(client->config.userData, &client->msgHandleLock);
 
-		result = RyanMqttMsgHandlerCreate(client, subscriptionList[i].pTopicFilter,
-						  subscriptionList[i].topicFilterLength, RyanMqttMsgInvalidPacketId,
-						  (RyanMqttQos_e)subscriptionList[i].qos, NULL, &msgHandler);
+		result = RyanMqttMsgHandlerCreate(client, unSubscriptionList[i].pTopicFilter,
+						  unSubscriptionList[i].topicFilterLength, packetId,
+						  (RyanMqttQos_e)unSubscriptionList[i].qos, NULL, &msgHandler);
 		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, result, RyanMqttLog_d,
 					  { goto __RyanMqttUnSubCreateAckErrorExit; });
 
@@ -473,7 +476,7 @@ RyanMqttError_e RyanMqttUnSubscribeMany(RyanMqttClient_t *client, int32_t count,
 
 __RyanMqttUnSubCreateAckErrorExit:
 		RyanMqttClearAckSession(client, MQTT_PACKET_TYPE_UNSUBACK, packetId);
-		platformMemoryFree(subscriptionList);
+		platformMemoryFree(unSubscriptionList);
 		platformMemoryFree(fixedBuffer.pBuffer);
 		return RyanMqttNotEnoughMemError;
 	}
@@ -484,11 +487,11 @@ __RyanMqttUnSubCreateAckErrorExit:
 	RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttLog_d, {
 		RyanMqttClearAckSession(client, MQTT_PACKET_TYPE_UNSUBACK, packetId);
 
-		platformMemoryFree(subscriptionList);
+		platformMemoryFree(unSubscriptionList);
 		platformMemoryFree(fixedBuffer.pBuffer);
 	});
 
-	platformMemoryFree(subscriptionList);
+	platformMemoryFree(unSubscriptionList);
 	platformMemoryFree(fixedBuffer.pBuffer);
 	return result;
 }
@@ -508,9 +511,9 @@ RyanMqttError_e RyanMqttUnSubscribe(RyanMqttClient_t *client, char *topic)
 	return RyanMqttUnSubscribeMany(client, 1, &subscribeManyData);
 }
 
-RyanMqttError_e RyanMqttPublishAndUserData(RyanMqttClient_t *client, char *topic, uint16_t topicLen, char *payload,
-					   uint32_t payloadLen, RyanMqttQos_e qos, RyanMqttBool_e retain,
-					   void *userData)
+RyanMqttError_e RyanMqttPublishWithUserData(RyanMqttClient_t *client, char *topic, uint16_t topicLen, char *payload,
+					    uint32_t payloadLen, RyanMqttQos_e qos, RyanMqttBool_e retain,
+					    void *userData)
 {
 	RyanMqttError_e result = RyanMqttSuccessError;
 	uint16_t packetId;
@@ -595,7 +598,8 @@ RyanMqttError_e RyanMqttPublishAndUserData(RyanMqttClient_t *client, char *topic
 		RyanMqttAckListAddToUserAckList(client, userAckHandler);
 
 		result = RyanMqttSendPacket(client, userAckHandler->packet, userAckHandler->packetLen);
-		RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttLog_d, {
+		RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttLog_e, {
+			RyanMqttLog_e("RyanMqttSendPacket failed, clear user ack session");
 			// userAck 必须通过这个执行,因为可能已经复制到mqtt内核空间了
 			RyanMqttClearAckSession(client, packetType, packetId);
 		});
@@ -620,7 +624,8 @@ RyanMqttError_e RyanMqttPublish(RyanMqttClient_t *client, char *topic, char *pay
 {
 	RyanMqttCheck(NULL != topic, RyanMqttParamInvalidError, RyanMqttLog_d);
 
-	return RyanMqttPublishAndUserData(client, topic, RyanMqttStrlen(topic), payload, payloadLen, qos, retain, NULL);
+	return RyanMqttPublishWithUserData(client, topic, RyanMqttStrlen(topic), payload, payloadLen, qos, retain,
+					   NULL);
 }
 
 /**
@@ -748,8 +753,8 @@ RyanMqttError_e RyanMqttSafeFreeSubscribeResources(RyanMqttMsgHandler_t *msgHand
 {
 	RyanMqttError_e result = RyanMqttSuccessError;
 	RyanMqttCheck(NULL != msgHandles, RyanMqttParamInvalidError, RyanMqttLog_d);
-	// RyanMqttGetSubscribeSafe 返回地址的话 subscribeNum 一定大于0,这里就是要判断大于0才行
-	RyanMqttCheck(subscribeNum > 0, RyanMqttParamInvalidError, RyanMqttLog_d);
+	// RyanMqttGetSubscribeTotalCount 内部调用的时候可以会等于0
+	RyanMqttCheck(subscribeNum >= 0, RyanMqttParamInvalidError, RyanMqttLog_d);
 
 	for (int32_t i = 0; i < subscribeNum; i++)
 	{
@@ -1084,6 +1089,17 @@ RyanMqttError_e RyanMqttDiscardAckHandler(RyanMqttClient_t *client, uint8_t pack
 	return result;
 }
 
+RyanMqttError_e RyanMqttGetEventId(RyanMqttClient_t *client, RyanMqttEventId_e *eventId)
+{
+	RyanMqttCheck(NULL != client, RyanMqttParamInvalidError, RyanMqttLog_d);
+	RyanMqttCheck(NULL != eventId, RyanMqttParamInvalidError, RyanMqttLog_d);
+
+	platformCriticalEnter(client->config.userData, &client->criticalLock);
+	*eventId = client->eventFlag;
+	platformCriticalExit(client->config.userData, &client->criticalLock);
+	return RyanMqttSuccessError;
+}
+
 RyanMqttError_e RyanMqttRegisterEventId(RyanMqttClient_t *client, RyanMqttEventId_e eventId)
 {
 	RyanMqttCheck(NULL != client, RyanMqttParamInvalidError, RyanMqttLog_d);

+ 20 - 14
mqttclient/RyanMqttThread.c

@@ -1,4 +1,5 @@
 #define RyanMqttLogLevel (RyanMqttLogLevelAssert) // 日志打印等级
+// #define RyanMqttLogLevel (RyanMqttLogLevelError) // 日志打印等级
 // #define RyanMqttLogLevel (RyanMqttLogLevelDebug) // 日志打印等级
 
 #include "RyanMqttThread.h"
@@ -34,16 +35,6 @@ static RyanMqttError_e RyanMqttKeepalive(RyanMqttClient_t *client)
 
 	uint32_t timeRemain = RyanMqttTimerRemain(&client->keepaliveTimer);
 
-	// 超过设置的 1.5 倍心跳周期,主动通知用户断开连接
-	if (0 == timeRemain)
-	{
-		RyanMqttConnectStatus_e connectState = RyanMqttKeepaliveTimeout;
-		RyanMqttEventMachine(client, RyanMqttEventDisconnected, (void *)&connectState);
-		RyanMqttLog_d("ErrorCode: %d, strError: %s", RyanMqttKeepaliveTimeout,
-			      RyanMqttStrError(RyanMqttKeepaliveTimeout));
-		return RyanMqttFailedError;
-	}
-
 	// 当剩余时间大于 recvtimeout 并且小于 keepaliveTimeoutS 的 0.9 倍时间时不进行发送心跳包
 	if (timeRemain > (uint32_t)(client->config.recvTimeout + 100))
 	{
@@ -61,6 +52,16 @@ static RyanMqttError_e RyanMqttKeepalive(RyanMqttClient_t *client)
 		}
 	}
 
+	// 超过设置的 1.5 倍心跳周期,主动通知用户断开连接
+	if (0 == timeRemain)
+	{
+		RyanMqttConnectStatus_e connectState = RyanMqttKeepaliveTimeout;
+		RyanMqttEventMachine(client, RyanMqttEventDisconnected, (void *)&connectState);
+		RyanMqttLog_d("ErrorCode: %d, strError: %s", RyanMqttKeepaliveTimeout,
+			      RyanMqttStrError(RyanMqttKeepaliveTimeout));
+		return RyanMqttFailedError;
+	}
+
 	// 发送mqtt心跳包
 	{
 		// MQTT_PACKET_PINGREQ_SIZE
@@ -81,7 +82,6 @@ static RyanMqttError_e RyanMqttKeepalive(RyanMqttClient_t *client)
 	return RyanMqttSuccessError;
 }
 
-// todo 也可以考虑有ack链表的时候recvTime可以短一些,有坑点
 // todo 也可以考虑将发送操作独立出去,异步发送,目前没有遇到性能瓶颈,需要超高性能的时候再考虑吧
 /**
  * @brief 遍历ack链表,进行相应的处理
@@ -96,7 +96,7 @@ static void RyanMqttAckListScan(RyanMqttClient_t *client, RyanMqttBool_e waitFla
 	RyanMqttList_t *curr, *next;
 	RyanMqttAckHandler_t *ackHandler;
 	RyanMqttTimer_t ackScanRemainTimer;
-	uint32_t ackScanThrottleTime = 1000; // 最长一秒
+	uint32_t ackScanThrottleTime = 1000; // ack扫描节流最长一秒
 	RyanMqttAssert(NULL != client);
 
 	// mqtt没有连接就退出
@@ -166,7 +166,7 @@ static void RyanMqttAckListScan(RyanMqttClient_t *client, RyanMqttBool_e waitFla
 		case MQTT_PACKET_TYPE_PUBCOMP: // 理论不会出现,冗余措施
 		{
 			// 设置重发标志位
-			if (ackHandler->packet && 0 == ackHandler->repeatCount)
+			if (0 == ackHandler->repeatCount && ackHandler->packet)
 			{
 				MQTT_UpdateDuplicatePublishFlag(ackHandler->packet, true);
 			}
@@ -192,7 +192,7 @@ static void RyanMqttAckListScan(RyanMqttClient_t *client, RyanMqttBool_e waitFla
 		// 订阅 / 取消订阅超时就认为失败
 		case MQTT_PACKET_TYPE_SUBACK: {
 			RyanMqttMsgHandler_t *msgMatchCriteria = ackHandler->msgHandler;
-			RyanMqttMsgHandlerFindAndDestroyByPackId(client, msgMatchCriteria, RyanMqttFalse);
+			RyanMqttMsgHandlerFindAndDestroyByPacketId(client, msgMatchCriteria, RyanMqttFalse);
 			RyanMqttEventMachine(client, RyanMqttEventSubscribedFailed, (void *)ackHandler->msgHandler);
 			RyanMqttAckListRemoveToAckList(client, ackHandler);
 			RyanMqttAckHandlerDestroy(client, ackHandler); // 清除句柄
@@ -220,6 +220,11 @@ static void RyanMqttAckListScan(RyanMqttClient_t *client, RyanMqttBool_e waitFla
 	{
 		// 启动ack scan节流定时器
 		RyanMqttTimerCutdown(&client->ackScanThrottleTimer, ackScanThrottleTime);
+		client->pendingAckFlag = RyanMqttFalse;
+	}
+	else
+	{
+		client->pendingAckFlag = RyanMqttTrue;
 	}
 }
 
@@ -536,6 +541,7 @@ void RyanMqttThread(void *argument)
 
 		case RyanMqttConnectState: // 连接状态
 			RyanMqttLog_d("连接状态");
+            // 不对返回值进行处理
 			RyanMqttProcessPacketHandler(client);
 			RyanMqttAckListScan(client, RyanMqttTrue);
 			RyanMqttKeepalive(client);

+ 144 - 91
mqttclient/RyanMqttThreadProcessPacket.c

@@ -1,5 +1,6 @@
 #define RyanMqttLogLevel (RyanMqttLogLevelAssert) // 日志打印等级
-// #define RyanMqttLogLevel (RyanMqttLogLevelDebug)  // 日志打印等级
+// #define RyanMqttLogLevel (RyanMqttLogLevelError) // 日志打印等级
+// #define RyanMqttLogLevel (RyanMqttLogLevelDebug) // 日志打印等级
 
 #include "RyanMqttThread.h"
 #include "RyanMqttLog.h"
@@ -69,8 +70,8 @@ static RyanMqttError_e RyanMqttPubrelPacketHandler(RyanMqttClient_t *client, MQT
 
 	// ?这里没法判断packetid是否非法,只能每次都回复咯
 	// 每次收到PUBREL都返回消息
-	result = RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
-	RyanMqttCheck(RyanMqttSuccessError == result, result, RyanMqttLog_d);
+	// 不管结果,因为失败了也没有好的处理形式,等待broker重发吧
+	RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
 
 	return RyanMqttSuccessError;
 }
@@ -85,62 +86,58 @@ static RyanMqttError_e RyanMqttPubrecPacketHandler(RyanMqttClient_t *client, MQT
 {
 	RyanMqttError_e result = RyanMqttSuccessError;
 	uint16_t packetId;
-	RyanMqttAckHandler_t *ackHandlerPubrec;
 
 	RyanMqttAssert(NULL != client);
 
-	// 反序列化ack
+	// 反序列化 pubrec 
 	MQTTStatus_t status = MQTT_DeserializeAck(pIncomingPacket, &packetId, NULL);
-	RyanMqttCheck(MQTTSuccess == status, RyanMqttSerializePacketError, RyanMqttLog_d);
+	RyanMqttCheck(MQTTSuccess == status, RyanMqttSerializePacketError, RyanMqttLog_e);
 
-	// 每次收到 PUBREC 都返回ack,确保服务器可以认为数据包被发送了
+	// 序列化 pubrel 包
 	uint8_t buffer[MQTT_PUBLISH_ACK_PACKET_SIZE];
 	MQTTFixedBuffer_t fixedBuffer = {.pBuffer = buffer, .size = sizeof(buffer)};
-
-	// 序列化ack数据包
 	status = MQTT_SerializeAck(&fixedBuffer, MQTT_PACKET_TYPE_PUBREL, packetId);
-	RyanMqttCheck(MQTTSuccess == status, RyanMqttSerializePacketError, RyanMqttLog_d);
+	RyanMqttCheck(MQTTSuccess == status, RyanMqttSerializePacketError, RyanMqttLog_e);
 
-	// 只在首次收到pubrec, 并pubcomp不存在于ack链表时,才创建pubcmp到ack链表,再删除pubrec记录
-	result = RyanMqttAckListNodeFind(client, MQTT_PACKET_TYPE_PUBREC, packetId, &ackHandlerPubrec, RyanMqttFalse);
+	// 若已存在 PUBCOMP ack,说明此前已处理过 PUBREC;仅重发 PUBREL 即可
+	RyanMqttAckHandler_t *ackHandlerPubcomp;
+	result = RyanMqttAckListNodeFind(client, MQTT_PACKET_TYPE_PUBCOMP, packetId, &ackHandlerPubcomp, RyanMqttFalse);
+	if (RyanMqttSuccessError == result)
+	{
+		goto __next;
+	}
+
+	RyanMqttAckHandler_t *ackHandlerPubrec;
+	result = RyanMqttAckListNodeFind(client, MQTT_PACKET_TYPE_PUBREC, packetId, &ackHandlerPubrec, RyanMqttTrue);
 	if (RyanMqttSuccessError == result)
 	{
 		RyanMqttMsgHandler_t *msgHandler;
-		RyanMqttAckHandler_t *ackHandler;
+		RyanMqttAckHandler_t *ackHandlerNewPubcomp;
 
 		// 首次收到消息,创建 pubcomp ack
 		result = RyanMqttMsgHandlerCreate(client, ackHandlerPubrec->msgHandler->topic,
 						  ackHandlerPubrec->msgHandler->topicLen, RyanMqttMsgInvalidPacketId,
 						  ackHandlerPubrec->msgHandler->qos,
 						  ackHandlerPubrec->msgHandler->userData, &msgHandler);
-		RyanMqttCheck(RyanMqttSuccessError == result, result, RyanMqttLog_d);
+		RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttLog_d,
+				  { RyanMqttAckListAddToAckList(client, ackHandlerPubrec); });
 
 		// 期望收到pubcomp否则会重复发送pubrel
 		result = RyanMqttAckHandlerCreate(client, MQTT_PACKET_TYPE_PUBCOMP, packetId,
 						  MQTT_PUBLISH_ACK_PACKET_SIZE, fixedBuffer.pBuffer, msgHandler,
-						  &ackHandler, RyanMqttFalse);
-		RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttLog_d,
-				  { RyanMqttMsgHandlerDestroy(client, msgHandler); });
-		RyanMqttAckListAddToAckList(client, ackHandler);
+						  &ackHandlerNewPubcomp, RyanMqttFalse);
+		RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttLog_d, {
+			RyanMqttMsgHandlerDestroy(client, msgHandler);
+			RyanMqttAckListAddToAckList(client, ackHandlerPubrec);
+		});
 
-		// 清除pubrec记录,必须使用find函数进行重新查找才能确保线程安全
-		result = RyanMqttAckListNodeFind(client, MQTT_PACKET_TYPE_PUBREC, packetId, &ackHandlerPubrec,
-						 RyanMqttTrue);
-		if (RyanMqttSuccessError == result)
-		{
-			RyanMqttAckHandlerDestroy(client, ackHandlerPubrec);
-		}
-	}
-	else
-	{
-		// 没有pubrec ,并且没有pubcomp,说明这个报文是非法报文,不进行mqtt服务器回复
-		result = RyanMqttAckListNodeFind(client, MQTT_PACKET_TYPE_PUBCOMP, packetId, &ackHandlerPubrec,
-						 RyanMqttFalse);
-		RyanMqttCheck(RyanMqttSuccessError == result, RyanMqttInvalidPacketError, RyanMqttLog_d);
+		RyanMqttAckListAddToAckList(client, ackHandlerNewPubcomp);
+		RyanMqttAckHandlerDestroy(client, ackHandlerPubrec);
 	}
 
-	result = RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
-	RyanMqttCheck(RyanMqttSuccessError == result, result, RyanMqttLog_d);
+__next:
+	// 不管结果,因为失败了也没有好的处理形式,等待broker重发吧
+	RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
 
 	return result;
 }
@@ -199,8 +196,8 @@ static RyanMqttError_e RyanMqttPublishPacketHandler(RyanMqttClient_t *client, MQ
 		MQTTStatus_t status = MQTT_SerializeAck(&fixedBuffer, MQTT_PACKET_TYPE_PUBACK, packetId);
 		RyanMqttCheck(MQTTSuccess == status, RyanMqttSerializePacketError, RyanMqttLog_d);
 
-		result = RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
-		RyanMqttCheck(RyanMqttSuccessError == result, result, RyanMqttLog_d);
+		// 不管结果,因为失败了也没有好的处理形式,等待broker重发吧
+		RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
 	}
 
 	break;
@@ -216,8 +213,8 @@ static RyanMqttError_e RyanMqttPublishPacketHandler(RyanMqttClient_t *client, MQ
 		// 上面代码不太可能出错,如果出错了就让服务器重新发送吧
 		RyanMqttCheck(MQTTSuccess == status, RyanMqttSerializePacketError, RyanMqttLog_d);
 
-		// 收到 publish 就期望收到 PUBREL ,如果 PUBREL 报文已经存在说明不是首次收到 publish,不进行qos2 PUBREC
-		// 消息处理
+		// 收到 publish 就期望收到 PUBREL .
+		// 如果 PUBREL 报文已经存在说明不是首次收到 publish,不进行qos2 PUBREC消息处理
 		result = RyanMqttAckListNodeFind(client, MQTT_PACKET_TYPE_PUBREL, msgData.packetId, &ackHandler,
 						 RyanMqttFalse);
 		if (RyanMqttSuccessError != result)
@@ -237,10 +234,15 @@ static RyanMqttError_e RyanMqttPublishPacketHandler(RyanMqttClient_t *client, MQ
 					  { RyanMqttMsgHandlerDestroy(client, msgHandler); });
 			RyanMqttAckListAddToAckList(client, ackHandler);
 		}
+		else
+		{
+			// 非首次收到同一 packetId 的 PUBLISH(正常重传场景),不再分发
+			RyanMqttLog_d("Duplicate QoS2 PUBLISH, packetId: %d", msgData.packetId);
+		}
 
 		// 无论是不是第一次收到,都回复 pub ack报文
-		result = RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
-		RyanMqttCheck(RyanMqttSuccessError == result, result, RyanMqttLog_d);
+		// 不管结果,因为失败了也没有好的处理形式,等待broker重发吧
+		RyanMqttSendPacket(client, fixedBuffer.pBuffer, MQTT_PUBLISH_ACK_PACKET_SIZE);
 	}
 
 	break;
@@ -329,12 +331,13 @@ static RyanMqttError_e RyanMqttSubackHandler(RyanMqttClient_t *client, MQTTPacke
 		// 处理非订阅ack
 		if (MQTT_PACKET_TYPE_SUBACK != ackHandler->packetType)
 		{
+			RyanMqttLog_e("packetType error: %02x", ackHandler->packetType);
 			goto __next;
 		}
 
 		platformMutexLock(client->config.userData, &client->msgHandleLock);
 		// 查找同名订阅但是packetid不一样的进行删除,保证订阅主题列表只有一个最新的
-		RyanMqttMsgHandlerFindAndDestroyByPackId(client, ackHandler->msgHandler, RyanMqttTrue);
+		RyanMqttMsgHandlerFindAndDestroyByPacketId(client, ackHandler->msgHandler, RyanMqttTrue);
 
 		// 到这里就可以保证没有同名订阅了
 		// 查找之前记录的topic句柄,根据服务器授权Qos进行更新
@@ -415,6 +418,7 @@ static RyanMqttError_e RyanMqttUnSubackHandler(RyanMqttClient_t *client, MQTTPac
 			continue;
 		}
 
+		// 必须先判断packetId是否相等,再判断类型
 		if (MQTT_PACKET_TYPE_UNSUBACK != ackHandler->packetType)
 		{
 			goto __next;
@@ -466,51 +470,99 @@ RyanMqttError_e RyanMqttGetPacketInfo(RyanMqttClient_t *client, MQTTPacketInfo_t
 {
 	RyanMqttError_e result = RyanMqttSuccessError;
 	RyanMqttAssert(NULL != client);
+	uint8_t pBuffer[5]; // MQTT 固定报头最大 5 字节
+	uint8_t needReadSize = 2;
+	size_t readIndex = 0;
+	MQTTStatus_t status;
 
-	NetworkContext_t pNetworkContext = {.client = client};
-	// todo 可以考虑增加包大小限制,目前不准备加
-	MQTTStatus_t status =
-		MQTT_GetIncomingPacketTypeAndLength(coreMqttTransportRecv, &pNetworkContext, pIncomingPacket);
+	// // todo 可以考虑增加包大小限制,目前不准备加,错误需要更复杂的实现
+	// MQTTStatus_t status =
+	// 	MQTT_GetIncomingPacketTypeAndLength(coreMqttTransportRecv, &pNetworkContext, pIncomingPacket);
 
-	// 先同步用户接口的ack链表
-	RyanMqttSyncUserAckHandle(client);
-
-	if (MQTTSuccess == status)
+	do
 	{
-		// 申请断开连接数据包的空间
-		if (pIncomingPacket->remainingLength > 0)
+		// 第一次直接读取 2 个字节
+		result = RyanMqttRecvPacket(client, pBuffer + readIndex, needReadSize);
+		if (RyanMqttRecvPacketTimeOutError == result)
 		{
-			pIncomingPacket->pRemainingData = platformMemoryMalloc(pIncomingPacket->remainingLength);
-			RyanMqttCheck(NULL != pIncomingPacket->pRemainingData, RyanMqttNotEnoughMemError,
-				      RyanMqttLog_d);
+			goto __next; // 超时直接退出
+		}
+		else if (RyanMqttSuccessError != result)
+		{
+			RyanMqttLog_e("读取固定报头失败");
+			goto __next;
 		}
-	}
-	else if (MQTTNoDataAvailable == status)
-	{
-		return RyanMqttRecvPacketTimeOutError;
-	}
-	else
-	{
-		RyanMqttLog_e("获取包长度失败");
-		return RyanMqttFailedError;
-	}
 
-	// 3.读取mqtt载荷数据并放到读取缓冲区
-	if (pIncomingPacket->remainingLength > 0)
-	{
-		result = RyanMqttRecvPacket(client, pIncomingPacket->pRemainingData, pIncomingPacket->remainingLength);
-		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, result, RyanMqttLog_d, { goto __exit; });
-	}
+		readIndex += needReadSize; // 更新读取位置
 
-	return result;
+		// 尝试解析
+		status = MQTT_ProcessIncomingPacketTypeAndLength(pBuffer, &readIndex, pIncomingPacket);
+		if (MQTTNeedMoreBytes == status)
+		{
+			needReadSize = 3; // 最多还需要 3 个字节
 
-__exit:
-	if (NULL != pIncomingPacket->pRemainingData)
-	{
-		platformMemoryFree(pIncomingPacket->pRemainingData);
-		pIncomingPacket->pRemainingData = NULL;
-	}
+			// ?冗余,header最多为5字节,理论上不可能发生的,所以还是使用上面的形式吧
+			// if (readIndex >= sizeof(pBuffer))
+			// {
+			// 	result = RyanMqttFailedError;
+			// 	goto __next;
+			// }
+
+			// needReadSize = sizeof(pBuffer) - readIndex;
+			continue;
+		}
+
+		if (MQTTSuccess != status)
+		{
+			RyanMqttLog_e("解析固定报头失败 %d", status);
+			result = RyanMqttDeserializePacketError;
+			goto __next;
+		}
+
+		if (pIncomingPacket->remainingLength <= 0)
+		{
+			break; // 不包含可变长度报文
+		}
+
+		// 申请 payload 的空间
+		pIncomingPacket->pRemainingData = platformMemoryMalloc(pIncomingPacket->remainingLength);
+		RyanMqttCheckCode(NULL != pIncomingPacket->pRemainingData, RyanMqttNotEnoughMemError, RyanMqttLog_d, {
+			result = RyanMqttNotEnoughMemError;
+			goto __next;
+		});
+
+		// 如果固定报头解析时已经多读了 payload 的一部分
+		uint8_t alreadyRead = readIndex - pIncomingPacket->headerLength;
+		for (uint8_t i = 0; i < alreadyRead; i++)
+		{
+			pIncomingPacket->pRemainingData[i] = *(pBuffer + pIncomingPacket->headerLength + i);
+		}
+		// // ? memcpy可能性能更高
+		// if (alreadyRead > 0)
+		// {
+		// 	RyanMqttMemcpy(pIncomingPacket->pRemainingData, (char *)pBuffer + pIncomingPacket->headerLength,
+		// 		       alreadyRead);
+		// }
+
+		// 读取剩余 payload
+		if (alreadyRead < pIncomingPacket->remainingLength)
+		{
+			result = RyanMqttRecvPacket(client, pIncomingPacket->pRemainingData + alreadyRead,
+						    pIncomingPacket->remainingLength - alreadyRead);
+			// 返回 result 没错
+			RyanMqttCheckCode(RyanMqttSuccessError == result, result, RyanMqttLog_d, {
+				platformMemoryFree(pIncomingPacket->pRemainingData);
+				pIncomingPacket->pRemainingData = NULL;
+				goto __next;
+			});
+		}
 
+		break;
+	} while (1);
+
+__next:
+	// 先同步用户接口的ack链表
+	RyanMqttSyncUserAckHandle(client);
 	return result;
 }
 
@@ -548,15 +600,10 @@ RyanMqttError_e RyanMqttProcessPacketHandler(RyanMqttClient_t *client)
 		result = RyanMqttPublishPacketHandler(client, &pIncomingPacket);
 		break;
 
-	case MQTT_PACKET_TYPE_CONNACK: // 连接报文确认
-	{
-		// ?客户端已处于连接状态时又收到CONNACK报文,应该视为严重错误,断开连接
-		RyanMqttLog_e("收到 CONNACK 时已连接,正在断开连接");
-		RyanMqttConnectStatus_e connectState = RyanMqttConnectProtocolError;
-		RyanMqttEventMachine(client, RyanMqttEventDisconnected, &connectState);
-		result = RyanMqttHaveRescourceError;
-	}
-	break;
+	case MQTT_PACKET_TYPE_PINGRESP: // 心跳响应
+		RyanMqttRefreshKeepaliveTime(client);
+		result = RyanMqttSuccessError;
+		break;
 
 	case MQTT_PACKET_TYPE_PUBACK:  // 客户端发送QoS 1消息,服务端发布收到确认
 	case MQTT_PACKET_TYPE_PUBCOMP: // 发送QOS2 发布完成
@@ -570,7 +617,7 @@ RyanMqttError_e RyanMqttProcessPacketHandler(RyanMqttClient_t *client)
 	case (MQTT_PACKET_TYPE_PUBREL & 0xF0U): // 客户端接收QOS2 已经发布PUBREC,等待服务器发布释放 pubrel
 		result = RyanMqttPubrelPacketHandler(client, &pIncomingPacket);
 
-		// !RyanMqttGetPacketInfo 检查报文type错误时不会惊醒返回,所以下面逻辑暂时没用
+		// !RyanMqttGetPacketInfo 检查报文type错误时不会进行返回,所以下面逻辑暂时没用
 		// // PUBREL 控制报文固定报头的第 3,2,1,0
 		// // 位必须被设置为0,0,1,0。必须将其它的任何值都当做是不合法的并关闭网络连接
 		// if (pIncomingPacket.type & 0x02U)
@@ -595,10 +642,16 @@ RyanMqttError_e RyanMqttProcessPacketHandler(RyanMqttClient_t *client)
 		result = RyanMqttUnSubackHandler(client, &pIncomingPacket);
 		break;
 
-	case MQTT_PACKET_TYPE_PINGRESP: // 心跳响应
-		RyanMqttRefreshKeepaliveTime(client);
-		result = RyanMqttSuccessError;
-		break;
+	case MQTT_PACKET_TYPE_CONNACK: // 连接报文确认
+	{
+		// ?没必要这么严格,考虑兼容性多一些吧
+		// // ?客户端已处于连接状态时又收到CONNACK报文,应该视为严重错误,断开连接
+		// RyanMqttLog_e("收到 CONNACK 时已连接,正在断开连接");
+		// RyanMqttConnectStatus_e connectState = RyanMqttConnectProtocolError;
+		// RyanMqttEventMachine(client, RyanMqttEventDisconnected, &connectState);
+		// result = RyanMqttHaveRescourceError;
+	}
+	break;
 
 	default:
 		RyanMqttLog_w("Unhandled packet type: 0x%02X", pIncomingPacket.type & 0xF0U);

+ 50 - 27
mqttclient/RyanMqttUtil.c

@@ -5,6 +5,10 @@
 #include "RyanMqttLog.h"
 #include "RyanMqttThread.h"
 
+#ifdef RyanMqttLinuxTestEnable
+#include "RyanMqttTest.h"
+#endif
+
 /**
  * @brief 字符串拷贝,需要手动释放内存
  *
@@ -34,33 +38,6 @@ RyanMqttError_e RyanMqttDupString(char **dest, const char *src, uint32_t strLen)
 	return RyanMqttSuccessError;
 }
 
-/**
- * @brief RyanMqtt 针对 coreMqtt 特殊场景专用
- *
- * @param pNetworkContext
- * @param pBuffer
- * @param bytesToRecv
- * @return int32_t
- */
-int32_t coreMqttTransportRecv(NetworkContext_t *pNetworkContext, void *pBuffer, size_t bytesToRecv)
-{
-	RyanMqttAssert(NULL != pNetworkContext);
-	RyanMqttAssert(NULL != pNetworkContext->client);
-	RyanMqttAssert(NULL != pBuffer);
-	RyanMqttAssert(bytesToRecv > 0);
-
-	RyanMqttError_e result = RyanMqttRecvPacket(pNetworkContext->client, pBuffer, bytesToRecv);
-
-	switch (result)
-	{
-	case RyanMqttRecvPacketTimeOutError: return 0;
-	case RyanMqttSuccessError: return (int32_t)bytesToRecv;
-
-	case RyanSocketFailedError:
-	default: return -1;
-	}
-}
-
 /**
  * @brief mqtt读取报文,此函数仅Mqtt线程进行调用
  *
@@ -79,6 +56,19 @@ RyanMqttError_e RyanMqttRecvPacket(RyanMqttClient_t *client, uint8_t *recvBuf, u
 	RyanMqttAssert(NULL != recvBuf);
 	RyanMqttAssert(0 != recvLen);
 
+	// 如果需要处理ack,就缩短读取超时时间,避免阻塞太久(保留用户配置的上限)
+	if (RyanMqttTrue == client->pendingAckFlag)
+	{
+		if (client->config.recvTimeout > 100)
+		{
+			timeOut = 100;
+		}
+		else
+		{
+			timeOut = client->config.recvTimeout;
+		}
+	}
+
 	RyanMqttTimerCutdown(&timer, timeOut);
 
 	while ((offset < recvLen) && (timeOut > 0))
@@ -113,6 +103,22 @@ RyanMqttError_e RyanMqttRecvPacket(RyanMqttClient_t *client, uint8_t *recvBuf, u
 		return RyanMqttRecvPacketTimeOutError;
 	}
 
+#ifdef RyanMqttLinuxTestEnable
+	RyanMqttTestEnableCritical();
+	if (RyanMqttTrue == isEnableRandomNetworkFault)
+	{
+		randomCount++;
+		if (randomCount >= RyanRand(10, 100))
+		{
+			randomCount = 0;
+			RyanMqttTestExitCritical();
+			// printf("模拟接收超时\r\n");
+			return RyanMqttRecvPacketTimeOutError;
+		}
+	}
+	RyanMqttTestExitCritical();
+#endif
+
 	return RyanMqttSuccessError;
 }
 
@@ -134,6 +140,23 @@ RyanMqttError_e RyanMqttSendPacket(RyanMqttClient_t *client, uint8_t *sendBuf, u
 	RyanMqttAssert(NULL != sendBuf);
 	RyanMqttAssert(0 != sendLen);
 
+#ifdef RyanMqttLinuxTestEnable
+	RyanMqttTestEnableCritical();
+	if (RyanMqttTrue == isEnableRandomNetworkFault)
+	{
+		sendRandomCount++;
+		if (sendRandomCount >= RyanRand(1, 10))
+		{
+			sendRandomCount = 0;
+			RyanMqttTestExitCritical();
+
+			// printf("模拟发送超时\r\n");
+			return RyanMqttSendPacketTimeOutError;
+		}
+	}
+	RyanMqttTestExitCritical();
+#endif
+
 	RyanMqttTimerCutdown(&timer, timeOut);
 
 	platformMutexLock(client->config.userData, &client->sendLock); // 获取互斥锁

+ 1 - 1
mqttclient/RyanMqttUtileAck.c

@@ -30,7 +30,7 @@ RyanMqttError_e RyanMqttAckHandlerCreate(RyanMqttClient_t *client, uint8_t packe
 	// 为非预分配的数据包分配额外空间
 	if (RyanMqttTrue != packetAllocatedExternally)
 	{
-		mallocSize += packetLen + 1;
+		mallocSize += packetLen;
 	}
 
 	// 为非预分配包申请额外空间

+ 4 - 4
mqttclient/RyanMqttUtileMsg.c

@@ -220,7 +220,7 @@ RyanMqttError_e RyanMqttMsgHandlerCreate(RyanMqttClient_t *client, const char *t
 	RyanMqttAssert(NULL != client);
 	RyanMqttAssert(NULL != topic);
 	RyanMqttAssert(NULL != pMsgHandler);
-	RyanMqttAssert(RyanMqttQos0 == qos || RyanMqttQos1 == qos || RyanMqttQos2 == qos);
+	RyanMqttAssert(RyanMqttQos0 == qos || RyanMqttQos1 == qos || RyanMqttQos2 == qos || RyanMqttSubFail == qos);
 
 	uint32_t mallocSize = sizeof(RyanMqttMsgHandler_t) + topicLen + 1;
 	RyanMqttMsgHandler_t *msgHandler = (RyanMqttMsgHandler_t *)platformMemoryMalloc(mallocSize);
@@ -233,7 +233,7 @@ RyanMqttError_e RyanMqttMsgHandlerCreate(RyanMqttClient_t *client, const char *t
 	msgHandler->userData = userData;
 	msgHandler->topic = (char *)msgHandler + sizeof(RyanMqttMsgHandler_t);
 	RyanMqttMemcpy(msgHandler->topic, topic, topicLen);
-	msgHandler->topic[topicLen] = '\0';
+	msgHandler->topic[topicLen] = '\0'; // 兼容旧版本
 
 	*pMsgHandler = msgHandler;
 	return RyanMqttSuccessError;
@@ -309,8 +309,8 @@ __exit:
  * @param msgMatchCriteria
  * @param skipSamePacketId
  */
-void RyanMqttMsgHandlerFindAndDestroyByPackId(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgMatchCriteria,
-					      RyanMqttBool_e skipSamePacketId)
+void RyanMqttMsgHandlerFindAndDestroyByPacketId(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgMatchCriteria,
+						RyanMqttBool_e skipSamePacketId)
 {
 	RyanMqttList_t *curr, *next;
 	RyanMqttMsgHandler_t *msgHandler;

+ 30 - 26
mqttclient/include/RyanMqttClient.h

@@ -40,25 +40,25 @@ typedef struct
 
 typedef struct
 {
-	RyanMqttBool_e packetAllocatedExternally; // packet 是外部分配的
-	uint8_t packetType;                       // 期望接收到的ack报文类型
-	uint16_t repeatCount;                     // 当前ack超时重发次数
-	uint16_t packetId;                        // 报文标识符 系统生成,用户勿动
-	uint32_t packetLen;                       // 报文长度
-	RyanMqttList_t list;                      // 链表节点,用户勿动
-	RyanMqttTimer_t timer;                    // ack超时定时器,用户勿动
-	RyanMqttMsgHandler_t *msgHandler;         // msg信息
+	RyanMqttBool_e packetAllocatedExternally: 1; // packet 是外部分配的
+	uint8_t packetType;                          // 期望接收到的ack报文类型
+	uint16_t repeatCount;                        // 当前ack超时重发次数
+	uint16_t packetId;                           // 报文标识符 系统生成,用户勿动
+	uint32_t packetLen;                          // 报文长度
+	RyanMqttList_t list;                         // 链表节点,用户勿动
+	RyanMqttTimer_t timer;                       // ack超时定时器,用户勿动
+	RyanMqttMsgHandler_t *msgHandler;            // msg信息
 	uint8_t *packet; // 没有收到期望ack,重新发送的原始报文,不要求必须是最后一位,但最好保持这样
 } RyanMqttAckHandler_t;
 
 typedef struct
 {
-	RyanMqttBool_e lwtFlag; // 遗嘱标志位
-	RyanMqttBool_e retain;  // 遗嘱保留标志位
-	RyanMqttQos_e qos;      // 遗嘱qos等级
-	uint32_t payloadLen;    // 消息长度
-	char *topic;            // 遗嘱主题
-	char *payload;          // 遗嘱消息
+	RyanMqttBool_e lwtFlag: 1; // 遗嘱标志位
+	RyanMqttBool_e retain: 1;  // 遗嘱保留标志位
+	RyanMqttQos_e qos;         // 遗嘱qos等级
+	uint32_t payloadLen;       // 消息长度
+	char *topic;               // 遗嘱主题
+	char *payload;             // 遗嘱消息
 } lwtOptions_t;
 
 typedef struct
@@ -87,8 +87,8 @@ typedef struct
 	uint16_t taskPrio;  // mqtt线程优先级
 	uint16_t taskStack; // 线程栈大小
 
-	RyanMqttBool_e autoReconnectFlag; // 自动重连标志位
-	RyanMqttBool_e cleanSessionFlag;  // 清除会话标志位
+	RyanMqttBool_e autoReconnectFlag: 1; // 自动重连标志位
+	RyanMqttBool_e cleanSessionFlag: 1;  // 清除会话标志位
 
 	// ack重发超过这个数值后触发事件回调,根据实际硬件选择。典型值为 5
 	uint16_t ackHandlerRepeatCountWarning;
@@ -101,7 +101,7 @@ typedef struct
 	uint16_t sendTimeout;       // mqtt发送命令超时时间, 根据实际硬件选择。单位ms
 	uint16_t ackTimeout;        // mqtt ack 等待回复的超时时间, 典型值为5 - 60秒。单位ms
 	uint16_t keepaliveTimeoutS; // mqtt心跳时间间隔。单位S
-	uint16_t reconnectTimeout;  // mqtt重连间隔时间。单位S
+	uint16_t reconnectTimeout;  // mqtt重连间隔时间。单位ms
 
 	RyanMqttEventHandle mqttEventHandle; // mqtt事件回调函数
 	void *userData;                      // 用户自定义数据,用户需要保证指针指向内容的持久性
@@ -109,11 +109,12 @@ typedef struct
 
 typedef struct
 {
-	RyanMqttBool_e destroyFlag;  // 销毁标志位
-	uint16_t ackHandlerCount;    // 等待ack的记录个数
-	uint16_t packetId;           // mqtt报文标识符,控制报文必须包含一个非零的 16 位报文标识符
-	uint32_t eventFlag;          // 事件标志位
-	RyanMqttState_e clientState; // mqtt客户端的状态
+	RyanMqttBool_e destroyFlag: 1;    // 销毁标志位
+	RyanMqttBool_e pendingAckFlag: 1; // 需要处理ack, 缩短recv超时时间,避免阻塞太久
+	uint16_t ackHandlerCount;         // 等待ack的记录个数
+	uint16_t packetId;                // mqtt报文标识符,控制报文必须包含一个非零的 16 位报文标识符
+	uint32_t eventFlag;               // 事件标志位
+	RyanMqttState_e clientState;      // mqtt客户端的状态
 
 	// 维护消息处理列表,这是mqtt协议必须实现的内容,所有来自服务器的publish报文都会被处理(前提是订阅了对应的消息,或者设置了拦截器)
 	RyanMqttList_t msgHandlerList;
@@ -141,12 +142,13 @@ extern RyanMqttError_e RyanMqttStart(RyanMqttClient_t *client);
 extern RyanMqttError_e RyanMqttDisconnect(RyanMqttClient_t *client, RyanMqttBool_e sendDiscFlag);
 extern RyanMqttError_e RyanMqttReconnect(RyanMqttClient_t *client);
 
-extern RyanMqttError_e RyanMqttPublishAndUserData(RyanMqttClient_t *client, char *topic, uint16_t topicLen,
-						  char *payload, uint32_t payloadLen, RyanMqttQos_e qos,
-						  RyanMqttBool_e retain, void *userData);
-// !推荐使用 RyanMqttPublishAndUserData , RyanMqttPublish不能正确处理topic结尾为0的情况
+extern RyanMqttError_e RyanMqttPublishWithUserData(RyanMqttClient_t *client, char *topic, uint16_t topicLen,
+						   char *payload, uint32_t payloadLen, RyanMqttQos_e qos,
+						   RyanMqttBool_e retain, void *userData);
+// !推荐使用 RyanMqttPublishWithUserData , RyanMqttPublish不能正确处理topic结尾为0的情况
 extern RyanMqttError_e RyanMqttPublish(RyanMqttClient_t *client, char *topic, char *payload, uint32_t payloadLen,
 				       RyanMqttQos_e qos, RyanMqttBool_e retain);
+#define RyanMqttPublishAndUserData RyanMqttPublishWithUserData // 兼容旧版本
 
 // !推荐使用 RyanMqttSubscribeMany , RyanMqttSubscribe不能正确处理topic结尾为0的情况
 extern RyanMqttError_e RyanMqttSubscribe(RyanMqttClient_t *client, char *topic, RyanMqttQos_e qos);
@@ -173,6 +175,8 @@ extern RyanMqttError_e RyanMqttSetLwt(RyanMqttClient_t *client, char *topicName,
 				      RyanMqttQos_e qos, RyanMqttBool_e retain);
 
 extern RyanMqttError_e RyanMqttDiscardAckHandler(RyanMqttClient_t *client, uint8_t packetType, uint16_t packetId);
+
+extern RyanMqttError_e RyanMqttGetEventId(RyanMqttClient_t *client, RyanMqttEventId_e *eventId);
 extern RyanMqttError_e RyanMqttRegisterEventId(RyanMqttClient_t *client, RyanMqttEventId_e eventId);
 extern RyanMqttError_e RyanMqttCancelEventId(RyanMqttClient_t *client, RyanMqttEventId_e eventId);
 

+ 1 - 6
mqttclient/include/RyanMqttUtil.h

@@ -17,11 +17,6 @@ extern "C" {
 // 定义枚举类型
 
 // 定义结构体类型
-struct NetworkContext
-{
-	RyanMqttClient_t *client;
-};
-int32_t coreMqttTransportRecv(NetworkContext_t *pNetworkContext, void *pBuffer, size_t bytesToRecv);
 
 /* extern variables-----------------------------------------------------------*/
 
@@ -42,7 +37,7 @@ extern void RyanMqttMsgHandlerDestroy(RyanMqttClient_t *client, RyanMqttMsgHandl
 extern RyanMqttError_e RyanMqttMsgHandlerFind(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgMatchCriteria,
 					      RyanMqttBool_e isTopicMatchedFlag, RyanMqttMsgHandler_t **pMsgHandler,
 					      RyanMqttBool_e removeOnMatch);
-extern void RyanMqttMsgHandlerFindAndDestroyByPackId(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgMatchCriteria,
+extern void RyanMqttMsgHandlerFindAndDestroyByPacketId(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgMatchCriteria,
 						     RyanMqttBool_e skipSamePacketId);
 extern RyanMqttError_e RyanMqttMsgHandlerAddToMsgList(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgHandler);
 extern RyanMqttError_e RyanMqttMsgHandlerRemoveToMsgList(RyanMqttClient_t *client, RyanMqttMsgHandler_t *msgHandler);

+ 2 - 1
platform/linux/platformNetwork.c

@@ -1,4 +1,5 @@
-#define RyanMqttLogLevel (RyanMqttLogLevelDebug) // 日志打印等级
+#define RyanMqttLogLevel (RyanMqttLogLevelAssert) // 日志打印等级
+// #define RyanMqttLogLevel (RyanMqttLogLevelDebug) // 日志打印等级
 
 #include "RyanMqttPlatform.h"
 #include "RyanMqttLog.h"

+ 20 - 0
platform/linux/platformSystem.c

@@ -2,6 +2,10 @@
 #include "platformSystem.h"
 #include "RyanMqttPlatform.h"
 
+#ifdef RyanMqttLinuxTestEnable
+#include "RyanMqttTest.h"
+#endif
+
 /**
  * @brief 申请内存
  *
@@ -10,6 +14,22 @@
  */
 inline void *platformMemoryMalloc(size_t size)
 {
+#ifdef RyanMqttLinuxTestEnable
+	RyanMqttTestEnableCritical();
+	if (RyanMqttTrue == isEnableRandomMemoryFault)
+	{
+		memoryRandomCount++;
+		if (memoryRandomCount >= RyanRand(10, 100))
+		{
+			memoryRandomCount = 0;
+			RyanMqttTestExitCritical();
+			// printf("模拟没有空闲内存\r\n");
+			return NULL;
+		}
+	}
+	RyanMqttTestExitCritical();
+#endif
+
 	return malloc(size);
 }
 

+ 8 - 11
test/RyanMqttDestroyTest.c

@@ -1,12 +1,13 @@
 #include "RyanMqttTest.h"
 
-static RyanMqttError_e RyanMqttConnectDestroy(uint32_t count, uint32_t delayms)
+static RyanMqttError_e RyanMqttConnectDestroy(uint32_t count)
 {
 	for (uint32_t i = 0; i < count; i++)
 	{
-		RyanMqttClient_t *client;
+		RyanMqttClient_t *client = NULL;
 
-		RyanMqttTestInit(&client, i == count - 1 ? RyanMqttTrue : RyanMqttFalse, RyanMqttTrue, 120, NULL, NULL);
+		RyanMqttTestInit(&client, (i == count - 1) ? RyanMqttTrue : RyanMqttFalse, RyanMqttTrue, 120, NULL,
+				 NULL);
 
 		// 增加一些测试量
 		RyanMqttSubscribe(client, "testlinux/pub3", RyanMqttQos2);
@@ -19,17 +20,13 @@ static RyanMqttError_e RyanMqttConnectDestroy(uint32_t count, uint32_t delayms)
 		RyanMqttPublish(client, "testlinux/pub1", "helloworld", RyanMqttStrlen("helloworld"), RyanMqttQos0,
 				RyanMqttFalse);
 
-		if (delayms)
+		// 偶尔让订阅主题全部订阅成功
+		if (i % 7 == 0)
 		{
-			delay(delayms);
+			delay(2);
 		}
 
 		RyanMqttTestDestroyClient(client);
-
-		if (i == count - 1) // 最后一次同步释放
-		{
-			delay(100);
-		}
 	}
 
 	return RyanMqttSuccessError;
@@ -38,7 +35,7 @@ static RyanMqttError_e RyanMqttConnectDestroy(uint32_t count, uint32_t delayms)
 RyanMqttError_e RyanMqttDestroyTest(void)
 {
 	RyanMqttError_e result = RyanMqttSuccessError;
-	result = RyanMqttConnectDestroy(100, 0);
+	result = RyanMqttConnectDestroy(100);
 	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
 	checkMemory;
 

+ 260 - 0
test/RyanMqttMemoryFaultToleranceTest.c

@@ -0,0 +1,260 @@
+#include "RyanMqttTest.h"
+
+/**
+ * @brief 混乱随机的qos消息测试
+ *
+ * @param count
+ * @param delayms
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttNetworkFaultPublishHybridTest(int32_t count, uint32_t delayms)
+{
+
+#define RyanMqttPubHybridTestPubTopic "testlinux/aa/pub/adfa/kkk"
+#define RyanMqttPubHybridTestSubTopic "testlinux/aa/+/#"
+
+	RyanMqttError_e result = RyanMqttSuccessError;
+	RyanMqttClient_t *client;
+	char *pubStr = "hello, this is a mqtt publish test string.";
+	uint32_t pubStrLen = strlen(pubStr);
+
+	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, NULL, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+	// 等待订阅主题成功
+	RyanMqttSubscribe(client, RyanMqttPubHybridTestSubTopic, RyanMqttQos2);
+	RyanMqttSubscribe(client, RyanMqttPubHybridTestPubTopic, RyanMqttQos2);
+	delay(2);
+
+	enableRandomMemoryFault();
+	for (int32_t i = 0; i < count; i++)
+	{
+		if (RyanMqttConnectState != RyanMqttGetState(client))
+		{
+			RyanMqttLog_e("mqtt 发布测试,销毁mqtt客户端 %d", i);
+			result = RyanMqttFailedError;
+			goto __exit;
+		}
+
+		char *pubTopic = RyanMqttPubHybridTestPubTopic;
+		RyanMqttPublishWithUserData(client, pubTopic, RyanMqttStrlen(pubTopic), pubStr, pubStrLen, i % 3,
+					    RyanMqttFalse, NULL);
+
+		if (delayms)
+		{
+			delay(delayms);
+		}
+	}
+	delay(RyanMqttAckTimeout * 2 + 200);
+	RyanMqttUnSubscribe(client, RyanMqttPubHybridTestSubTopic);
+
+__exit:
+	disableRandomMemoryFault();
+	RyanMqttLog_i("mqtt 发布测试,销毁mqtt客户端");
+	RyanMqttTestDestroyClient(client);
+
+	return result;
+}
+
+static RyanMqttError_e RyanMqttNetworkFaultSubscribeHybridTest(int32_t count, int32_t testCount)
+{
+	RyanMqttError_e result = RyanMqttSuccessError;
+	RyanMqttClient_t *client;
+	RyanMqttUnSubscribeData_t *unSubscribeManyData = NULL;
+	RyanMqttSubscribeData_t *subscribeManyData = NULL;
+
+	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, NULL, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+	// 生成需要订阅的主题数据
+	{
+		subscribeManyData = (RyanMqttSubscribeData_t *)malloc(sizeof(RyanMqttSubscribeData_t) * count);
+		if (NULL == subscribeManyData)
+		{
+			RyanMqttLog_e("内存不足");
+			result = RyanMqttNotEnoughMemError;
+			goto __exit;
+		}
+
+		for (int32_t i = 0; i < count; i++)
+		{
+			subscribeManyData[i].qos = i % 3;
+			char *topic = (char *)malloc(64);
+			if (NULL == topic)
+			{
+				RyanMqttLog_e("内存不足");
+				result = RyanMqttNotEnoughMemError;
+				goto __exit;
+			}
+			RyanMqttSnprintf(topic, 64, "test/subscribe/%d", i);
+			subscribeManyData[i].topic = topic;
+			subscribeManyData[i].topicLen = RyanMqttStrlen(topic);
+		}
+	}
+
+	// 生成取消所有订阅消息
+	unSubscribeManyData = malloc(sizeof(RyanMqttUnSubscribeData_t) * count);
+	if (NULL == unSubscribeManyData)
+	{
+		RyanMqttLog_e("内存不足");
+		result = RyanMqttNotEnoughMemError;
+		goto __exit;
+	}
+	for (int32_t i = 0; i < count; i++)
+	{
+		char *topic = (char *)malloc(64);
+		if (NULL == topic)
+		{
+			RyanMqttLog_e("内存不足");
+			result = RyanMqttNotEnoughMemError;
+			goto __exit;
+		}
+		RyanMqttSnprintf(topic, 64, "test/subscribe/%d", i);
+		unSubscribeManyData[i].topic = topic;
+		unSubscribeManyData[i].topicLen = RyanMqttStrlen(topic);
+	}
+
+	enableRandomMemoryFault();
+	for (int32_t testCount2 = 0; testCount2 < testCount; testCount2++)
+	{
+		// 订阅全部主题
+		RyanMqttSubscribeMany(client, count - 1, subscribeManyData);
+
+		RyanMqttSubscribe(client, subscribeManyData[count - 1].topic, subscribeManyData[count - 1].qos);
+		// 测试重复订阅,不修改qos等级
+		RyanMqttSubscribeMany(client, count / 2, subscribeManyData);
+
+		// 测试重复订阅并且修改qos等级
+		for (int32_t i = count; i > 0; i--)
+		{
+			subscribeManyData[count - i].qos = i % 3;
+		}
+		RyanMqttSubscribeMany(client, count, subscribeManyData);
+		RyanMqttUnSubscribeMany(client, count - 1, unSubscribeManyData);
+		RyanMqttUnSubscribe(client, unSubscribeManyData[count - 1].topic);
+
+		// 重复取消订阅主题
+		RyanMqttUnSubscribeMany(client, count / 2, unSubscribeManyData);
+	}
+
+	delay(RyanMqttAckTimeout * 2 + 200);
+
+__exit:
+	disableRandomMemoryFault();
+	// 删除
+	for (int32_t i = 0; i < count; i++)
+	{
+		if (NULL != subscribeManyData && NULL != subscribeManyData[i].topic)
+		{
+			free(subscribeManyData[i].topic);
+		}
+
+		if (NULL != unSubscribeManyData && NULL != unSubscribeManyData[i].topic)
+		{
+			free(unSubscribeManyData[i].topic);
+		}
+	}
+
+	if (NULL != subscribeManyData)
+	{
+		free(subscribeManyData);
+	}
+
+	if (NULL != unSubscribeManyData)
+	{
+		free(unSubscribeManyData);
+	}
+
+	RyanMqttLog_i("mqtt 订阅测试,销毁mqtt客户端");
+	RyanMqttTestDestroyClient(client);
+
+	return result;
+}
+
+static RyanMqttError_e RyanMqttNetworkFaultHybridTest(int32_t count)
+{
+	RyanMqttError_e result = RyanMqttSuccessError;
+	RyanMqttClient_t *client;
+
+	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, NULL, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+	// 等待订阅主题成功
+	RyanMqttSubscribe(client, RyanMqttPubHybridTestSubTopic, RyanMqttQos2);
+	RyanMqttSubscribe(client, RyanMqttPubHybridTestPubTopic, RyanMqttQos2);
+	delay(2);
+
+	enableRandomMemoryFault();
+	for (int32_t i = 0; i < count; i++)
+	{
+		RyanMqttMsgHandler_t *msgHandles = NULL;
+		int32_t subscribeNum = 0;
+		RyanMqttGetSubscribeSafe(client, &msgHandles, &subscribeNum);
+		RyanMqttSafeFreeSubscribeResources(msgHandles, subscribeNum);
+
+		RyanMqttMsgHandler_t msgHandlesStatic[3] = {0};
+		RyanMqttGetSubscribe(client, msgHandlesStatic, getArraySize(msgHandlesStatic), &subscribeNum);
+
+		int32_t subscribeTotalCount = 0;
+		RyanMqttGetSubscribeTotalCount(client, &subscribeTotalCount);
+
+		RyanMqttGetState(client);
+
+		uint32_t keepAliveRemain = 0;
+		RyanMqttGetKeepAliveRemain(client, &keepAliveRemain);
+
+		RyanMqttClientConfig_t *pclientConfig = NULL;
+		RyanMqttGetConfig(client, &pclientConfig);
+
+		// 不管 pclientConfig 是否有效
+		RyanMqttSetConfig(client, pclientConfig);
+
+		RyanMqttFreeConfigFromGet(pclientConfig);
+
+		RyanMqttSetLwt(client, "pub/lwt/test", "this is will", RyanMqttStrlen("this is will"), RyanMqttQos2, 0);
+
+		RyanMqttDiscardAckHandler(client, MQTT_PACKET_TYPE_PUBACK, 1);
+
+		RyanMqttEventId_e eventId = RyanMqttEventAnyId;
+
+		RyanMqttGetEventId(client, &eventId);
+
+		RyanMqttCancelEventId(client, eventId);
+
+		RyanMqttRegisterEventId(client, eventId);
+	}
+
+	RyanMqttUnSubscribe(client, RyanMqttPubHybridTestSubTopic);
+
+__exit:
+	disableRandomMemoryFault();
+	RyanMqttTestDestroyClient(client);
+	return result;
+}
+
+/**
+ * @brief 内存故障测试
+ *
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttMemoryFaultToleranceTest(void)
+{
+	RyanMqttError_e result = RyanMqttSuccessError;
+
+	result = RyanMqttNetworkFaultPublishHybridTest(2000, 1);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	result = RyanMqttNetworkFaultSubscribeHybridTest(2000, 5);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	result = RyanMqttNetworkFaultHybridTest(5000);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	return RyanMqttSuccessError;
+
+__exit:
+	return RyanMqttFailedError;
+}

+ 267 - 0
test/RyanMqttNetworkFaultQosResilienceTest.c

@@ -0,0 +1,267 @@
+#include "RyanMqttTest.h"
+
+static int32_t pubTestPublishedEventCount = 0;
+static int32_t pubTestDataEventCount = 0;
+static char *pubStr = NULL;
+static int32_t pubStrLen = 0;
+static char *pubStr2 = "a";
+static int32_t pubStr2Len = 1;
+static RyanMqttQos_e exportQos = RyanMqttSubFail;
+
+static int32_t pubTestDataEventCountNotQos0 = 0;
+
+static void RyanMqttPublishEventHandle(void *pclient, RyanMqttEventId_e event, const void *eventData)
+{
+	RyanMqttClient_t *client = (RyanMqttClient_t *)pclient;
+	switch (event)
+	{
+	case RyanMqttEventPublished: {
+		RyanMqttMsgHandler_t *msgHandler = ((RyanMqttAckHandler_t *)eventData)->msgHandler;
+		RyanMqttLog_w("qos1 / qos2发送成功事件回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
+		RyanMqttQos_e qos = (RyanMqttQos_e)(uintptr_t)msgHandler->userData;
+		if (qos == msgHandler->qos)
+		{
+			pubTestPublishedEventCount++;
+		}
+		else
+		{
+			RyanMqttLog_e("pub测试发送的qos不一致 msgQos: %d, userDataQos: %d", msgHandler->qos, qos);
+			RyanMqttTestDestroyClient(client);
+			return;
+		}
+		break;
+	}
+
+	case RyanMqttEventData: {
+		RyanMqttMsgData_t *msgData = (RyanMqttMsgData_t *)eventData;
+		// RyanMqttLog_i("接收到mqtt消息事件回调 topic: %.*s, packetId: %d, payload len: %d, qos: %d",
+		//        msgData->topicLen, msgData->topic, msgData->packetId, msgData->payloadLen, msgData->qos);
+		if (RyanMqttSubFail != exportQos && exportQos != msgData->qos)
+		{
+			RyanMqttLog_e("pub测试收到qos等级不一致 exportQos: %d, msgQos: %d", exportQos, msgData->qos);
+			RyanMqttTestDestroyClient(client);
+			return;
+		}
+
+		if (((msgData->payloadLen == (uint32_t)pubStrLen) &&
+		     0 == memcmp(msgData->payload, pubStr, pubStrLen)) ||
+		    ((msgData->payloadLen == (uint32_t)pubStr2Len) &&
+		     0 == memcmp(msgData->payload, pubStr2, pubStr2Len)))
+		{
+			pubTestDataEventCount++;
+
+			if (RyanMqttQos0 != msgData->qos)
+			{
+				pubTestDataEventCountNotQos0++;
+			}
+		}
+		else
+		{
+			RyanMqttLog_e("pub测试收到数据不一致 %.*s", msgData->payloadLen, msgData->payload);
+			RyanMqttTestDestroyClient(client);
+			return;
+		}
+
+		break;
+	}
+
+	default: mqttEventBaseHandle(pclient, event, eventData); break;
+	}
+}
+
+/**
+ * @brief 混乱随机的qos消息测试
+ *
+ * @param count
+ * @param delayms
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttNetworkFaultQosResiliencePublishTest(int32_t count, uint32_t delayus, RyanMqttQos_e qos)
+{
+
+#define RyanMqttPubHybridTestPubTopic "testlinux/aa/pub/adfa/ddd"
+#define RyanMqttPubHybridTestSubTopic "testlinux/aa/+/#"
+
+	RyanMqttError_e result = RyanMqttSuccessError;
+	RyanMqttClient_t *client;
+	int32_t sendNeedAckCount = 0;
+
+	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, RyanMqttPublishEventHandle, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+	// 等待订阅主题成功
+	result = RyanMqttSubscribe(client, RyanMqttPubHybridTestSubTopic, qos);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	for (int32_t i = 0;; i++)
+	{
+		int32_t subscribeTotal = 0;
+
+		result = RyanMqttGetSubscribeTotalCount(client, &subscribeTotal);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+		if (1 == subscribeTotal)
+		{
+			break;
+		}
+
+		if (i > 3000)
+		{
+			RyanMqttLog_e("订阅主题失败");
+			result = RyanMqttFailedError;
+			goto __exit;
+		}
+
+		delay(1);
+	}
+
+	exportQos = qos;
+
+	// 生成随机的数据包大小
+	{
+		pubStr = (char *)malloc(2048);
+		RyanMqttCheck(NULL != pubStr, RyanMqttNotEnoughMemError, RyanMqttLog_e);
+		RyanMqttMemset(pubStr, 0, 2048);
+
+		for (uint32_t i = 0; i < RyanRand(20, 220); i++)
+		{
+			snprintf(pubStr + 4 * i, 2048 - 4 * i, "%c%c%c%c", (char)RyanRand(32, 126),
+				 (char)RyanRand(32, 126), (char)RyanRand(32, 126), (char)RyanRand(32, 126));
+		}
+		pubStrLen = (int32_t)RyanMqttStrlen(pubStr);
+	}
+
+	pubTestPublishedEventCount = 0;
+	pubTestDataEventCount = 0;
+	pubTestDataEventCountNotQos0 = 0;
+
+	for (int32_t i = 0; i < count; i++)
+	{
+		if (RyanMqttConnectState != RyanMqttGetState(client))
+		{
+			RyanMqttLog_e("mqtt 发布测试,销毁mqtt客户端 %d", i);
+			result = RyanMqttFailedError;
+			goto __exit;
+		}
+
+		if (delayus)
+		{
+			delay_us(delayus);
+		}
+
+		if (0 == i % (count / 5))
+		{
+			toggleRandomNetworkFault();
+		}
+
+		char *pubTopic = RyanMqttPubHybridTestPubTopic;
+		uint32_t randNumber = RyanRand(1, 10);
+		result = RyanMqttPublishWithUserData(
+			client, pubTopic, RyanMqttStrlen(pubTopic), (0 == i % randNumber) ? pubStr2 : pubStr,
+			(0 == i % randNumber) ? pubStr2Len : pubStrLen, qos, RyanMqttFalse,
+			// NOLINTNEXTLINE(clang-diagnostic-int-to-void-pointer-cast,performance-no-int-to-ptr)
+			(void *)(uintptr_t)(qos));
+		if (RyanMqttSuccessError == result)
+		{
+			if (RyanMqttQos0 != qos)
+			{
+				sendNeedAckCount++;
+			}
+		}
+		// else
+		// {
+		// 	RyanMqttLog_e("mqtt 发布测试,网络故障 %d", i);
+		// }
+	}
+
+	delay(RyanMqttAckTimeout * 1 + 100);
+
+	disableRandomNetworkFault();
+	// 检查收到的数据是否正确
+	for (int32_t i = 0;; i++)
+	{
+		if (RyanMqttQos2 == qos)
+		{
+			// qos2 发送成功数等于需要ack数,同时也等于接收到的数
+			if (pubTestPublishedEventCount == sendNeedAckCount &&
+			    pubTestPublishedEventCount == pubTestDataEventCount)
+			{
+				break;
+			}
+		}
+		else if (RyanMqttQos1 == qos)
+		{
+			if (pubTestPublishedEventCount >= sendNeedAckCount && pubTestDataEventCount >= sendNeedAckCount)
+			{
+				break;
+			}
+		}
+		else
+		{
+			break;
+		}
+
+		if (i > (RyanMqttAckTimeout * 5 / 100 + 500))
+		{
+			RyanMqttLog_e("QOS测试失败, PublishedEventCount: %d, pubTestDataEventCountNotQos0: %d, "
+				      "sendNeedAckCount: %d, pubTestDataEventCount: %d",
+				      pubTestPublishedEventCount, pubTestDataEventCountNotQos0, sendNeedAckCount,
+				      pubTestDataEventCount);
+			result = RyanMqttFailedError;
+			goto __exit;
+		}
+
+		// 测试代码可以临时访问ack链表
+		platformMutexLock(client->config.userData, &client->ackHandleLock);
+		if (RyanMqttListIsEmpty(&client->ackHandlerList))
+		{
+			RyanMqttLog_e("ack链表为空, PublishedEventCount: %d, pubTestDataEventCountNotQos0: %d, "
+				      "sendNeedAckCount: %d, pubTestDataEventCount: %d, pubStrLen: %d",
+				      pubTestPublishedEventCount, pubTestDataEventCountNotQos0, sendNeedAckCount,
+				      pubTestDataEventCount, pubStrLen);
+		}
+		platformMutexUnLock(client->config.userData, &client->ackHandleLock);
+
+		delay(100);
+	}
+
+	result = RyanMqttUnSubscribe(client, RyanMqttPubHybridTestSubTopic);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+	result = checkAckList(client);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+__exit:
+	disableRandomNetworkFault();
+	free(pubStr);
+	pubStr = NULL;
+	RyanMqttLog_i("mqtt 测试,销毁mqtt客户端");
+	RyanMqttTestDestroyClient(client);
+	return result;
+}
+
+/**
+ * @brief mqtt网络故障QOS韧性测试
+ *
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttNetworkFaultQosResilienceTest(void)
+{
+	RyanMqttError_e result = RyanMqttSuccessError;
+
+	result = RyanMqttNetworkFaultQosResiliencePublishTest(700, 0, RyanMqttQos0);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, result, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	result = RyanMqttNetworkFaultQosResiliencePublishTest(1300, 2, RyanMqttQos1);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, result, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	result = RyanMqttNetworkFaultQosResiliencePublishTest(1700, 4, RyanMqttQos2);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, result, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	return RyanMqttSuccessError;
+
+__exit:
+	return RyanMqttFailedError;
+}

+ 191 - 0
test/RyanMqttNetworkFaultToleranceMemoryTest.c

@@ -0,0 +1,191 @@
+#include "RyanMqttTest.h"
+
+/**
+ * @brief 混乱随机的qos消息测试
+ *
+ * @param count
+ * @param delayms
+ * @return RyanMqttError_e
+ */
+static RyanMqttError_e RyanMqttNetworkFaultPublishHybridTest(int32_t count, uint32_t delayms)
+{
+
+#define RyanMqttPubHybridTestPubTopic "testlinux/aa/pub/adfa/kkk"
+#define RyanMqttPubHybridTestSubTopic "testlinux/aa/+/#"
+
+	RyanMqttError_e result = RyanMqttSuccessError;
+	RyanMqttClient_t *client;
+	char *pubStr = "hello, this is a mqtt publish test string.";
+	uint32_t pubStrLen = strlen(pubStr);
+
+	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, NULL, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+	// 等待订阅主题成功
+	RyanMqttSubscribe(client, RyanMqttPubHybridTestSubTopic, RyanMqttQos2);
+	RyanMqttSubscribe(client, RyanMqttPubHybridTestPubTopic, RyanMqttQos2);
+	delay(2);
+
+	enableRandomNetworkFault();
+	for (int32_t i = 0; i < count; i++)
+	{
+		if (RyanMqttConnectState != RyanMqttGetState(client))
+		{
+			RyanMqttLog_e("mqtt 发布测试,销毁mqtt客户端 %d", i);
+			result = RyanMqttFailedError;
+			goto __exit;
+		}
+
+		char *pubTopic = RyanMqttPubHybridTestPubTopic;
+		RyanMqttPublishWithUserData(client, pubTopic, RyanMqttStrlen(pubTopic), pubStr, pubStrLen, i % 3,
+					    RyanMqttFalse, NULL);
+
+		if (delayms)
+		{
+			delay(delayms);
+		}
+	}
+
+	RyanMqttUnSubscribe(client, RyanMqttPubHybridTestSubTopic);
+
+__exit:
+	disableRandomNetworkFault();
+	RyanMqttLog_i("mqtt 发布测试,销毁mqtt客户端");
+	RyanMqttTestDestroyClient(client);
+	return result;
+}
+
+static RyanMqttError_e RyanMqttNetworkFaultSubscribeHybridTest(int32_t count, int32_t testCount)
+{
+	RyanMqttError_e result = RyanMqttSuccessError;
+	RyanMqttClient_t *client;
+	RyanMqttUnSubscribeData_t *unSubscribeManyData = NULL;
+	RyanMqttSubscribeData_t *subscribeManyData = NULL;
+
+	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, NULL, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+
+	// 生成需要订阅的主题数据
+	{
+		subscribeManyData = (RyanMqttSubscribeData_t *)malloc(sizeof(RyanMqttSubscribeData_t) * count);
+		if (NULL == subscribeManyData)
+		{
+			RyanMqttLog_e("内存不足");
+			result = RyanMqttNotEnoughMemError;
+			goto __exit;
+		}
+
+		for (int32_t i = 0; i < count; i++)
+		{
+			subscribeManyData[i].qos = i % 3;
+			char *topic = (char *)malloc(64);
+			if (NULL == topic)
+			{
+				RyanMqttLog_e("内存不足");
+				result = RyanMqttNotEnoughMemError;
+				goto __exit;
+			}
+			RyanMqttSnprintf(topic, 64, "test/subscribe/%d", i);
+			subscribeManyData[i].topic = topic;
+			subscribeManyData[i].topicLen = RyanMqttStrlen(topic);
+		}
+	}
+
+	// 生成取消所有订阅消息
+	unSubscribeManyData = malloc(sizeof(RyanMqttUnSubscribeData_t) * count);
+	if (NULL == unSubscribeManyData)
+	{
+		RyanMqttLog_e("内存不足");
+		result = RyanMqttNotEnoughMemError;
+		goto __exit;
+	}
+	for (int32_t i = 0; i < count; i++)
+	{
+		char *topic = (char *)malloc(64);
+		if (NULL == topic)
+		{
+			RyanMqttLog_e("内存不足");
+			result = RyanMqttNotEnoughMemError;
+			goto __exit;
+		}
+		RyanMqttSnprintf(topic, 64, "test/subscribe/%d", i);
+		unSubscribeManyData[i].topic = topic;
+		unSubscribeManyData[i].topicLen = RyanMqttStrlen(topic);
+	}
+
+	enableRandomNetworkFault();
+	for (int32_t testCount2 = 0; testCount2 < testCount; testCount2++)
+	{
+		// 订阅全部主题
+		RyanMqttSubscribeMany(client, count - 1, subscribeManyData);
+
+		RyanMqttSubscribe(client, subscribeManyData[count - 1].topic, subscribeManyData[count - 1].qos);
+		// 测试重复订阅,不修改qos等级
+		RyanMqttSubscribeMany(client, count / 2, subscribeManyData);
+
+		// 测试重复订阅并且修改qos等级
+		for (int32_t i = count; i > 0; i--)
+		{
+			subscribeManyData[count - i].qos = i % 3;
+		}
+		RyanMqttSubscribeMany(client, count, subscribeManyData);
+		RyanMqttUnSubscribeMany(client, count - 1, unSubscribeManyData);
+		RyanMqttUnSubscribe(client, unSubscribeManyData[count - 1].topic);
+
+		// 重复取消订阅主题
+		RyanMqttUnSubscribeMany(client, count / 2, unSubscribeManyData);
+	}
+
+__exit:
+	disableRandomNetworkFault();
+	// 删除
+	for (int32_t i = 0; i < count; i++)
+	{
+		if (NULL != subscribeManyData && NULL != subscribeManyData[i].topic)
+		{
+			free(subscribeManyData[i].topic);
+		}
+
+		if (NULL != unSubscribeManyData && NULL != unSubscribeManyData[i].topic)
+		{
+			free(unSubscribeManyData[i].topic);
+		}
+	}
+
+	if (NULL != subscribeManyData)
+	{
+		free(subscribeManyData);
+	}
+
+	if (NULL != unSubscribeManyData)
+	{
+		free(unSubscribeManyData);
+	}
+
+	RyanMqttLog_i("mqtt 订阅测试,销毁mqtt客户端");
+	RyanMqttTestDestroyClient(client);
+	return result;
+}
+
+/**
+ * @brief 网络容错内存测试
+ *
+ * @return RyanMqttError_e
+ */
+RyanMqttError_e RyanMqttNetworkFaultToleranceMemoryTest(void)
+{
+	RyanMqttError_e result = RyanMqttSuccessError;
+
+	result = RyanMqttNetworkFaultPublishHybridTest(2000, 1);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	result = RyanMqttNetworkFaultSubscribeHybridTest(1000, 5);
+	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	checkMemory;
+
+	return RyanMqttSuccessError;
+
+__exit:
+	return RyanMqttFailedError;
+}

+ 25 - 32
test/RyanMqttPubTest.c

@@ -4,6 +4,8 @@ static int32_t pubTestPublishedEventCount = 0;
 static int32_t pubTestDataEventCount = 0;
 static char *pubStr = NULL;
 static int32_t pubStrLen = 0;
+static char *pubStr2 = "a";
+static int32_t pubStr2Len = 1;
 static RyanMqttQos_e exportQos = RyanMqttSubFail;
 
 static int32_t pubTestDataEventCountNotQos0 = 0;
@@ -41,7 +43,10 @@ static void RyanMqttPublishEventHandle(void *pclient, RyanMqttEventId_e event, c
 			return;
 		}
 
-		if ((msgData->payloadLen == (uint32_t)pubStrLen) && (0 == memcmp(msgData->payload, pubStr, pubStrLen)))
+		if (((msgData->payloadLen == (uint32_t)pubStrLen) &&
+		     0 == memcmp(msgData->payload, pubStr, pubStrLen)) ||
+		    ((msgData->payloadLen == (uint32_t)pubStr2Len) &&
+		     0 == memcmp(msgData->payload, pubStr2, pubStr2Len)))
 		{
 			pubTestDataEventCount++;
 
@@ -78,7 +83,6 @@ static RyanMqttError_e RyanMqttPublishTest(RyanMqttQos_e qos, int32_t count, uin
 #define RyanMqttPubTestSubTopic "testlinux/+/pub"
 	RyanMqttError_e result = RyanMqttSuccessError;
 	RyanMqttClient_t *client;
-	time_t timeStampNow = 0;
 
 	exportQos = qos;
 	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, RyanMqttPublishEventHandle, NULL);
@@ -112,20 +116,14 @@ static RyanMqttError_e RyanMqttPublishTest(RyanMqttQos_e qos, int32_t count, uin
 
 	// 生成随机的数据包大小
 	{
-		// NOLINTNEXTLINE(cert-err33-c)
-		time(&timeStampNow);
-
-		pubStr = (char *)platformMemoryMalloc(2048);
+		pubStr = (char *)malloc(2048);
 		RyanMqttCheck(NULL != pubStr, RyanMqttNotEnoughMemError, RyanMqttLog_e);
 		RyanMqttMemset(pubStr, 0, 2048);
 
-		// NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp)
-		srand(timeStampNow);
-		// NOLINTNEXTLINE(concurrency-mt-unsafe,cert-msc30-c,cert-msc50-cpp)
-		for (uint32_t i = 0; i < (uint32_t)(rand() % 250 + 1 + 100); i++)
+		for (uint32_t i = 0; i < RyanRand(100, 220); i++)
 		{
-			// NOLINTNEXTLINE(concurrency-mt-unsafe,cert-msc30-c,cert-msc50-cpp)
-			snprintf(pubStr + 4 * i, 2048 - 4 * i, "%04d", rand());
+			snprintf(pubStr + 4 * i, 2048 - 4 * i, "%c%c%c%c", (char)RyanRand(32, 126),
+				 (char)RyanRand(32, 126), (char)RyanRand(32, 126), (char)RyanRand(32, 126));
 		}
 		pubStrLen = (int32_t)RyanMqttStrlen(pubStr);
 	}
@@ -136,10 +134,10 @@ static RyanMqttError_e RyanMqttPublishTest(RyanMqttQos_e qos, int32_t count, uin
 	for (int32_t i = 0; i < count; i++)
 	{
 		char *pubTopic = RyanMqttPubTestPubTopic;
-		result = RyanMqttPublishAndUserData(client, pubTopic, RyanMqttStrlen(pubTopic), pubStr, pubStrLen, qos,
-						    RyanMqttFalse,
-						    // NOLINTNEXTLINE(performance-no-int-to-ptr)
-						    (void *)qos);
+		uint32_t randNumber = RyanRand(1, 10);
+		result = RyanMqttPublishWithUserData(
+			client, pubTopic, RyanMqttStrlen(pubTopic), (0 == i % randNumber) ? pubStr2 : pubStr,
+			(0 == i % randNumber) ? pubStr2Len : pubStrLen, qos, RyanMqttFalse, (void *)(uintptr_t)qos);
 		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
 					  { goto __exit; });
 
@@ -183,7 +181,7 @@ static RyanMqttError_e RyanMqttPublishTest(RyanMqttQos_e qos, int32_t count, uin
 	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
 
 __exit:
-	platformMemoryFree(pubStr);
+	free(pubStr);
 	pubStr = NULL;
 	RyanMqttLog_i("mqtt 发布测试,销毁mqtt客户端");
 	RyanMqttTestDestroyClient(client);
@@ -205,7 +203,6 @@ static RyanMqttError_e RyanMqttPublishHybridTest(int32_t count, uint32_t delayms
 
 	RyanMqttError_e result = RyanMqttSuccessError;
 	RyanMqttClient_t *client;
-	time_t timeStampNow = 0;
 	int32_t sendNeedAckCount = 0;
 
 	result = RyanMqttTestInit(&client, RyanMqttTrue, RyanMqttTrue, 120, RyanMqttPublishEventHandle, NULL);
@@ -240,20 +237,14 @@ static RyanMqttError_e RyanMqttPublishHybridTest(int32_t count, uint32_t delayms
 
 	// 生成随机的数据包大小
 	{
-		// NOLINTNEXTLINE(cert-err33-c)
-		time(&timeStampNow);
-
-		pubStr = (char *)platformMemoryMalloc(2048);
+		pubStr = (char *)malloc(2048);
 		RyanMqttCheck(NULL != pubStr, RyanMqttNotEnoughMemError, RyanMqttLog_e);
 		RyanMqttMemset(pubStr, 0, 2048);
 
-		// NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp)
-		srand(timeStampNow);
-		// NOLINTNEXTLINE(concurrency-mt-unsafe,cert-msc30-c,cert-msc50-cpp)
-		for (uint32_t i = 0; i < (uint32_t)(rand() % 250 + 1 + 100); i++)
+		for (uint32_t i = 0; i < RyanRand(100, 220); i++)
 		{
-			// NOLINTNEXTLINE(concurrency-mt-unsafe,cert-msc30-c,cert-msc50-cpp)
-			snprintf(pubStr + 4 * i, 2048 - 4 * i, "%04d", rand());
+			snprintf(pubStr + 4 * i, 2048 - 4 * i, "%c%c%c%c", (char)RyanRand(32, 126),
+				 (char)RyanRand(32, 126), (char)RyanRand(32, 126), (char)RyanRand(32, 126));
 		}
 		pubStrLen = (int32_t)RyanMqttStrlen(pubStr);
 	}
@@ -264,8 +255,10 @@ static RyanMqttError_e RyanMqttPublishHybridTest(int32_t count, uint32_t delayms
 	for (int32_t i = 0; i < count; i++)
 	{
 		char *pubTopic = RyanMqttPubHybridTestPubTopic;
-		result = RyanMqttPublishAndUserData(
-			client, pubTopic, RyanMqttStrlen(pubTopic), pubStr, pubStrLen, i % 3, RyanMqttFalse,
+		uint32_t randNumber = RyanRand(1, 10);
+		result = RyanMqttPublishWithUserData(
+			client, pubTopic, RyanMqttStrlen(pubTopic), (0 == i % randNumber) ? pubStr2 : pubStr,
+			(0 == i % randNumber) ? pubStr2Len : pubStrLen, i % 3, RyanMqttFalse,
 			// NOLINTNEXTLINE(clang-diagnostic-int-to-void-pointer-cast,performance-no-int-to-ptr)
 			(void *)(uintptr_t)(i % 3));
 		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
@@ -310,7 +303,7 @@ static RyanMqttError_e RyanMqttPublishHybridTest(int32_t count, uint32_t delayms
 	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
 
 __exit:
-	platformMemoryFree(pubStr);
+	free(pubStr);
 	pubStr = NULL;
 	RyanMqttLog_i("mqtt 发布测试,销毁mqtt客户端");
 	RyanMqttTestDestroyClient(client);
@@ -334,7 +327,7 @@ RyanMqttError_e RyanMqttPubTest(void)
 	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
 	checkMemory;
 
-	result = RyanMqttPublishHybridTest(1000, 5);
+	result = RyanMqttPublishHybridTest(1000, 1);
 	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
 	checkMemory;
 

+ 42 - 10
test/RyanMqttPublicApiParamCheckTest.c

@@ -70,6 +70,15 @@ static RyanMqttError_e RyanMqttBaseApiParamCheckTest(void)
 	result = RyanMqttDiscardAckHandler(validClient, MQTT_PACKET_TYPE_PUBACK, RyanMqttMaxPacketId + 1);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
 
+	RyanMqttEventId_e eventId = RyanMqttEventConnected;
+	// NULL客户端指针
+	result = RyanMqttGetEventId(NULL, &eventId);
+	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
+
+	// NULL eventId指针
+	result = RyanMqttGetEventId(validClient, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
+
 	// NULL客户端指针
 	result = RyanMqttRegisterEventId(NULL, RyanMqttEventConnected);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
@@ -374,6 +383,10 @@ static RyanMqttError_e RyanMqttSubApiParamCheckTest(void)
 		unsubscribeData[i].topicLen = strlen("test/topic2");
 	}
 
+	/**
+	 * @brief
+	 *
+	 */
 	RyanMqttMsgHandler_t *msgHandles = NULL;
 	int32_t subscribeNum = 0;
 	int32_t totalCount = 0;
@@ -393,10 +406,6 @@ static RyanMqttError_e RyanMqttSubApiParamCheckTest(void)
 	result = RyanMqttSafeFreeSubscribeResources(NULL, 5);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
 
-	// !一定失败否则要报错
-	result = RyanMqttSafeFreeSubscribeResources((void *)&msgHandles, 0);
-	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
-
 	// NULL客户端指针
 	result = RyanMqttGetSubscribeTotalCount(NULL, &totalCount);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
@@ -405,6 +414,24 @@ static RyanMqttError_e RyanMqttSubApiParamCheckTest(void)
 	result = RyanMqttGetSubscribeTotalCount(validClient, NULL);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e,
 				  { goto __exit; }); // 清理资源
+
+	/**
+	 * @brief
+	 *
+	 */
+	RyanMqttMsgHandler_t msgHandlesStatic[3] = {0};
+	RyanMqttGetSubscribe(NULL, msgHandlesStatic, getArraySize(msgHandlesStatic), &subscribeNum);
+	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
+
+	RyanMqttGetSubscribe(validClient, NULL, getArraySize(msgHandlesStatic), &subscribeNum);
+	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
+
+	RyanMqttGetSubscribe(validClient, msgHandlesStatic, 0, &subscribeNum);
+	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
+
+	RyanMqttGetSubscribe(validClient, msgHandlesStatic, getArraySize(msgHandlesStatic), NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
+
 	if (validClient)
 	{
 		RyanMqttTestDestroyClient(validClient);
@@ -453,23 +480,28 @@ static RyanMqttError_e RyanMqttPubApiParamCheckTest(void)
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
 
 	// NULL客户端指针
-	result = RyanMqttPublishAndUserData(NULL, "test/topic", 10, "payload", 7, RyanMqttQos1, RyanMqttFalse, NULL);
+	result = RyanMqttPublishWithUserData(NULL, "test/topic", 10, "payload", 7, RyanMqttQos1, RyanMqttFalse, NULL);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
 
 	// NULL主题指针
-	result = RyanMqttPublishAndUserData(validClient, NULL, 10, "payload", 7, RyanMqttQos1, RyanMqttFalse, NULL);
+	result = RyanMqttPublishWithUserData(validClient, NULL, 10, "payload", 7, RyanMqttQos1, RyanMqttFalse, NULL);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
 
 	// 主题长度为0
-	result = RyanMqttPublishAndUserData(validClient, "test/topic", 0, "payload", 7, RyanMqttQos1, RyanMqttFalse,
-					    NULL);
+	result = RyanMqttPublishWithUserData(validClient, "test/topic", 0, "payload", 7, RyanMqttQos1, RyanMqttFalse,
+					     NULL);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
 
 	// 无效QoS级别
-	result = RyanMqttPublishAndUserData(validClient, "test/topic", strlen("test/topic"), "payload", 7, invalidQos(),
-					    RyanMqttFalse, NULL);
+	result = RyanMqttPublishWithUserData(validClient, "test/topic", strlen("test/topic"), "payload", 7,
+					     invalidQos(), RyanMqttFalse, NULL);
 	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e,
 				  { goto __exit; }); // 清理资源
+
+	// 负载长度>0但负载指针为NULL
+	result = RyanMqttPublishWithUserData(validClient, "test/topic", strlen("test/topic"), NULL, 7, RyanMqttQos1,
+					     RyanMqttFalse, NULL);
+	RyanMqttCheckCodeNoReturn(RyanMqttParamInvalidError == result, result, RyanMqttLog_e, { goto __exit; });
 	if (validClient)
 	{
 		RyanMqttTestDestroyClient(validClient);

+ 2 - 1
test/RyanMqttReconnectTest.c

@@ -88,7 +88,8 @@ static RyanMqttError_e manualReconnectTest(uint32_t count, uint32_t delayms)
 					  { goto __exit; });
 
 		// todo
-		// 这里可能还没有调度mqtt线程就更新状态了,目前通过延时强制等待mqtt线程调度完成,这里可以使用信号量也通知应用层,但又要增加plarform移植难度和内存占用
+		// 这里可能还没有调度mqtt线程就更新状态了,目前通过延时强制等待mqtt线程调度完成
+		// 这里可以使用信号量也通知应用层,但又要增加plarform移植难度和内存占用
 		delay(20);
 
 		// 应该成功

+ 81 - 77
test/RyanMqttSubTest.c

@@ -3,7 +3,7 @@
 static RyanMqttSubscribeData_t *subscribeManyData = NULL;
 static int32_t subTestCount = 0;
 
-static RyanMqttSubscribeData_t *topicIsSubscribeArr(char *topic)
+static RyanMqttSubscribeData_t *topicIsSubscribeArr(char *topic, uint32_t topicLen)
 {
 	if (NULL == topic)
 	{
@@ -12,7 +12,7 @@ static RyanMqttSubscribeData_t *topicIsSubscribeArr(char *topic)
 
 	for (int32_t i = 0; i < subTestCount; i++)
 	{
-		if (0 == RyanMqttStrncmp(topic, subscribeManyData[i].topic, RyanMqttStrlen(topic)))
+		if (0 == RyanMqttStrncmp(topic, subscribeManyData[i].topic, topicLen))
 		{
 			return &subscribeManyData[i];
 		}
@@ -30,7 +30,7 @@ static void RyanMqttSubEventHandle(void *pclient, RyanMqttEventId_e event, const
 	case RyanMqttEventSubscribed: {
 		RyanMqttMsgHandler_t *msgHandler = (RyanMqttMsgHandler_t *)eventData;
 		RyanMqttLog_i("mqtt订阅成功回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
-		RyanMqttSubscribeData_t *subscribeData = topicIsSubscribeArr(msgHandler->topic);
+		RyanMqttSubscribeData_t *subscribeData = topicIsSubscribeArr(msgHandler->topic, msgHandler->topicLen);
 		if (NULL == subscribeData)
 		{
 			RyanMqttLog_e("mqtt 订阅主题非法 topic: %s", msgHandler->topic);
@@ -58,7 +58,7 @@ static void RyanMqttSubEventHandle(void *pclient, RyanMqttEventId_e event, const
 	case RyanMqttEventUnSubscribed: {
 		RyanMqttMsgHandler_t *msgHandler = (RyanMqttMsgHandler_t *)eventData;
 		RyanMqttLog_i("mqtt取消订阅成功回调 topic: %s, qos: %d", msgHandler->topic, msgHandler->qos);
-		RyanMqttSubscribeData_t *subscribeData = topicIsSubscribeArr(msgHandler->topic);
+		RyanMqttSubscribeData_t *subscribeData = topicIsSubscribeArr(msgHandler->topic, msgHandler->topicLen);
 		if (NULL == subscribeData)
 		{
 			RyanMqttLog_e("mqtt 订阅主题非法 topic: %s", msgHandler->topic);
@@ -66,7 +66,7 @@ static void RyanMqttSubEventHandle(void *pclient, RyanMqttEventId_e event, const
 			return;
 		}
 
-		if (subscribeData->qos != msgHandler->qos)
+		if (msgHandler->qos != RyanMqttSubFail && subscribeData->qos != msgHandler->qos)
 		{
 			RyanMqttLog_e("mqtt 取消订阅主题信息不对 topic: %s, exportQos: %d, qos: %d", msgHandler->topic,
 				      subscribeData->qos, msgHandler->qos);
@@ -147,7 +147,7 @@ static RyanMqttError_e RyanMqttSubscribeCheckMsgHandle(RyanMqttClient_t *client)
 	// 检查订阅主题是否正确
 	for (int32_t i = 0; i < subscribeNum; i++)
 	{
-		if (NULL == topicIsSubscribeArr(msgHandles[i].topic))
+		if (NULL == topicIsSubscribeArr(msgHandles[i].topic, msgHandles[i].topicLen))
 		{
 			RyanMqttLog_e("主题不匹配或者qos不对, topic: %s, qos: %d", msgHandles[i].topic,
 				      msgHandles[i].qos);
@@ -164,7 +164,7 @@ __exit:
 	return result;
 }
 
-static RyanMqttError_e RyanMqttSubscribeHybridTest(int32_t count)
+static RyanMqttError_e RyanMqttSubscribeHybridTest(int32_t count, int32_t testCount)
 {
 	RyanMqttError_e result = RyanMqttSuccessError;
 	RyanMqttClient_t *client;
@@ -177,7 +177,7 @@ static RyanMqttError_e RyanMqttSubscribeHybridTest(int32_t count)
 	// 生成需要订阅的主题数据
 	{
 		subscribeManyData =
-			(RyanMqttSubscribeData_t *)platformMemoryMalloc(sizeof(RyanMqttSubscribeData_t) * count);
+			(RyanMqttSubscribeData_t *)malloc(sizeof(RyanMqttSubscribeData_t) * count);
 		if (NULL == subscribeManyData)
 		{
 			RyanMqttLog_e("内存不足");
@@ -188,45 +188,21 @@ static RyanMqttError_e RyanMqttSubscribeHybridTest(int32_t count)
 		for (int32_t i = 0; i < count; i++)
 		{
 			subscribeManyData[i].qos = i % 3;
-			char *topic = (char *)platformMemoryMalloc(64);
+			char *topic = (char *)malloc(32);
 			if (NULL == topic)
 			{
 				RyanMqttLog_e("内存不足");
 				result = RyanMqttNotEnoughMemError;
 				goto __exit;
 			}
-			RyanMqttSnprintf(topic, 64, "test/subscribe/%d", i);
+			RyanMqttSnprintf(topic, 32, "test/subscribe/%d", i);
 			subscribeManyData[i].topic = topic;
 			subscribeManyData[i].topicLen = RyanMqttStrlen(topic);
 		}
 	}
 
-	// 订阅全部主题
-	result = RyanMqttSubscribeMany(client, count - 1, subscribeManyData);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-	result = RyanMqttSubscribe(client, subscribeManyData[count - 1].topic, subscribeManyData[count - 1].qos);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-	result = RyanMqttSubscribeCheckMsgHandle(client);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-
-	// 测试重复订阅,不修改qos等级
-	result = RyanMqttSubscribeMany(client, count / 2, subscribeManyData);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-	result = RyanMqttSubscribeCheckMsgHandle(client);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-
-	// 测试重复订阅并且修改qos等级
-	for (int32_t i = count; i > 0; i--)
-	{
-		subscribeManyData[count - i].qos = i % 3;
-	}
-	result = RyanMqttSubscribeMany(client, count, subscribeManyData);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-	result = RyanMqttSubscribeCheckMsgHandle(client);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-
-	// 测试取消所有订阅消息
-	unSubscribeManyData = platformMemoryMalloc(sizeof(RyanMqttUnSubscribeData_t) * count);
+	// 生成取消所有订阅消息
+	unSubscribeManyData = malloc(sizeof(RyanMqttUnSubscribeData_t) * count);
 	if (NULL == unSubscribeManyData)
 	{
 		RyanMqttLog_e("内存不足");
@@ -235,60 +211,87 @@ static RyanMqttError_e RyanMqttSubscribeHybridTest(int32_t count)
 	}
 	for (int32_t i = 0; i < count; i++)
 	{
-		char *topic = (char *)platformMemoryMalloc(64);
+		char *topic = (char *)malloc(32);
 		if (NULL == topic)
 		{
 			RyanMqttLog_e("内存不足");
 			result = RyanMqttNotEnoughMemError;
 			goto __exit;
 		}
-		RyanMqttSnprintf(topic, 64, "test/subscribe/%d", i);
+		RyanMqttSnprintf(topic, 32, "test/subscribe/%d", i);
 		unSubscribeManyData[i].topic = topic;
 		unSubscribeManyData[i].topicLen = RyanMqttStrlen(topic);
 	}
 
-	result = RyanMqttUnSubscribeMany(client, count - 1, unSubscribeManyData);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-	result = RyanMqttUnSubscribe(client, unSubscribeManyData[count - 1].topic);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-
-	// 重复取消订阅主题
-	result = RyanMqttUnSubscribeMany(client, count / 2, unSubscribeManyData);
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
-
-	delay(100);
-	for (int32_t i = 0; i < 600; i++)
+	for (int32_t testCount2 = 0; testCount2 < testCount; testCount2++)
 	{
-		RyanMqttMsgHandler_t *msgHandles = NULL;
-		int32_t subscribeNum = 0;
-		result = RyanMqttGetSubscribeSafe(client, &msgHandles, &subscribeNum);
-		if (RyanMqttSuccessError != result)
+		// 订阅全部主题
+		result = RyanMqttSubscribeMany(client, count - 1, subscribeManyData);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+		result =
+			RyanMqttSubscribe(client, subscribeManyData[count - 1].topic, subscribeManyData[count - 1].qos);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+		result = RyanMqttSubscribeCheckMsgHandle(client);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+
+		// // delay(10);
+
+		// 测试重复订阅,不修改qos等级
+		result = RyanMqttSubscribeMany(client, count / 2, subscribeManyData);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+		result = RyanMqttSubscribeCheckMsgHandle(client);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+
+		// 测试重复订阅并且修改qos等级
+		for (int32_t i = count; i > 0; i--)
 		{
-			RyanMqttLog_e("获取订阅主题数失败!!!");
+			subscribeManyData[count - i].qos = i % 3;
 		}
-
-		if (subscribeNum > 0)
+		result = RyanMqttSubscribeMany(client, count, subscribeManyData);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+		result = RyanMqttSubscribeCheckMsgHandle(client);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+
+		// 测试取消订阅
+		result = RyanMqttUnSubscribeMany(client, count - 1, unSubscribeManyData);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+		result = RyanMqttUnSubscribe(client, unSubscribeManyData[count - 1].topic);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+
+		// !emqx服务器有时候会误判新的订阅请求为重复订阅主题,导致订阅失败
+		// 重复取消订阅主题
+		result = RyanMqttUnSubscribeMany(client, count / 2, unSubscribeManyData);
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e,
+					  { goto __exit; });
+
+		for (int32_t i = 0; i < 600; i++)
 		{
-			RyanMqttSafeFreeSubscribeResources(msgHandles, subscribeNum);
-		}
-
-		// result = RyanMqttGetSubscribe(client, msgHandles, count, &subscribeNum);
-		// if (RyanMqttNoRescourceError == result)
-		// {
-		// 	RyanMqttLog_w("订阅主题数超过缓冲区%d个,已截断,请修改msgHandles缓冲区", count);
-		// }
+			delay(100);
 
-		if (0 == subscribeNum)
-		{
-			break;
-		}
+			int32_t subscribeNum = 0;
+			RyanMqttGetSubscribeTotalCount(client, &subscribeNum);
+			if (0 == subscribeNum)
+			{
+				break;
+			}
 
-		if (i > 500)
-		{
-			result = RyanMqttFailedError;
-			goto __exit;
+			if (i > 500)
+			{
+				result = RyanMqttFailedError;
+				goto __exit;
+			}
 		}
 
+		// 有重复取消订阅主题,增加延时,防止emqx服务器误判新的订阅请求
 		delay(100);
 	}
 
@@ -302,23 +305,23 @@ __exit:
 	{
 		if (NULL != subscribeManyData && NULL != subscribeManyData[i].topic)
 		{
-			platformMemoryFree(subscribeManyData[i].topic);
+			free(subscribeManyData[i].topic);
 		}
 
 		if (NULL != unSubscribeManyData && NULL != unSubscribeManyData[i].topic)
 		{
-			platformMemoryFree(unSubscribeManyData[i].topic);
+			free(unSubscribeManyData[i].topic);
 		}
 	}
 
 	if (NULL != subscribeManyData)
 	{
-		platformMemoryFree(subscribeManyData);
+		free(subscribeManyData);
 	}
 
 	if (NULL != unSubscribeManyData)
 	{
-		platformMemoryFree(unSubscribeManyData);
+		free(unSubscribeManyData);
 	}
 
 	RyanMqttLog_i("mqtt 订阅测试,销毁mqtt客户端");
@@ -329,7 +332,8 @@ __exit:
 RyanMqttError_e RyanMqttSubTest(void)
 {
 	RyanMqttError_e result = RyanMqttSuccessError;
-	result = RyanMqttSubscribeHybridTest(1000);
+
+	result = RyanMqttSubscribeHybridTest(1000, 3);
 	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
 	checkMemory;
 

+ 144 - 76
test/RyanMqttTest.c

@@ -1,7 +1,5 @@
 #include "RyanMqttTest.h"
 
-static pthread_spinlock_t spin;
-
 /**
  * @brief mqtt事件回调处理函数
  * 事件的详细定义可以查看枚举定义
@@ -12,7 +10,6 @@ static pthread_spinlock_t spin;
  */
 void mqttEventBaseHandle(void *pclient, RyanMqttEventId_e event, const void *eventData)
 {
-	RyanMqttClient_t *client = (RyanMqttClient_t *)pclient;
 
 	switch (event)
 	{
@@ -70,7 +67,7 @@ void mqttEventBaseHandle(void *pclient, RyanMqttEventId_e event, const void *eve
 			      ackHandler->packetType, ackHandler->packetId, ackHandler->msgHandler->topic,
 			      ackHandler->msgHandler->qos);
 
-		printfArrStr(ackHandler->packet, ackHandler->packetLen, "重发数据: ");
+		// printfArrStr(ackHandler->packet, ackHandler->packetLen, "重发数据: ");
 		break;
 	}
 
@@ -90,6 +87,7 @@ void mqttEventBaseHandle(void *pclient, RyanMqttEventId_e event, const void *eve
 
 	case RyanMqttEventAckRepeatCountWarning: // 重发次数到达警戒值事件
 	{
+		RyanMqttClient_t *client = (RyanMqttClient_t *)pclient;
 		// 这里选择直接丢弃该消息
 		RyanMqttAckHandler_t *ackHandler = (RyanMqttAckHandler_t *)eventData;
 		RyanMqttLog_e("ack重发次数超过警戒值回调 packetType: %d, packetId: %d, topic: %s, qos: %d",
@@ -107,13 +105,22 @@ void mqttEventBaseHandle(void *pclient, RyanMqttEventId_e event, const void *eve
 	}
 
 	case RyanMqttEventDestroyBefore:
-		RyanMqttLog_e("销毁mqtt客户端前回调");
+		RyanMqttLog_w("销毁mqtt客户端前回调");
+
+		RyanMqttClient_t *client = (RyanMqttClient_t *)pclient;
 		struct RyanMqttTestEventUserData *eventUserData =
 			(struct RyanMqttTestEventUserData *)client->config.userData;
+		if (RyanMqttTestEventUserDataMagic != eventUserData->magic)
+		{
+			RyanMqttLog_e("eventUserData野指针");
+			break;
+		}
+
 		if (eventUserData->syncFlag)
 		{
 			sem_post(&eventUserData->sem);
 		}
+
 		break;
 
 	case RyanMqttEventUnsubscribedData: {
@@ -141,7 +148,7 @@ RyanMqttError_e RyanMqttTestInit(RyanMqttClient_t **client, RyanMqttBool_e syncF
 	RyanMqttSnprintf(aaa, sizeof(aaa), "%s%d", RyanMqttClientId, count);
 
 	struct RyanMqttTestEventUserData *eventUserData =
-		(struct RyanMqttTestEventUserData *)platformMemoryMalloc(sizeof(struct RyanMqttTestEventUserData));
+		(struct RyanMqttTestEventUserData *)malloc(sizeof(struct RyanMqttTestEventUserData));
 	if (NULL == eventUserData)
 	{
 		RyanMqttLog_e("内存不足");
@@ -149,6 +156,8 @@ RyanMqttError_e RyanMqttTestInit(RyanMqttClient_t **client, RyanMqttBool_e syncF
 	}
 
 	RyanMqttMemset(eventUserData, 0, sizeof(struct RyanMqttTestEventUserData));
+
+	eventUserData->magic = RyanMqttTestEventUserDataMagic;
 	eventUserData->syncFlag = syncFlag;
 	eventUserData->userData = userData;
 	if (eventUserData->syncFlag)
@@ -166,14 +175,14 @@ RyanMqttError_e RyanMqttTestInit(RyanMqttClient_t **client, RyanMqttBool_e syncF
 					     .taskPrio = 16,
 					     .taskStack = 4096,
 					     .mqttVersion = 4,
-					     .ackHandlerRepeatCountWarning = 6,
+					     .ackHandlerRepeatCountWarning = 600,
 					     .ackHandlerCountWarning = 60000,
 					     .autoReconnectFlag = autoReconnectFlag,
 					     .cleanSessionFlag = RyanMqttTrue,
-					     .reconnectTimeout = 3000,
-					     .recvTimeout = 2000,
-					     .sendTimeout = 1800,
-					     .ackTimeout = 10000,
+					     .reconnectTimeout = RyanMqttReconnectTimeout,
+					     .recvTimeout = RyanMqttRecvTimeout,
+					     .sendTimeout = RyanMqttSendTimeout,
+					     .ackTimeout = RyanMqttAckTimeout,
 					     .keepaliveTimeoutS = keepaliveTimeoutS,
 					     .mqttEventHandle =
 						     mqttEventCallback ? mqttEventCallback : mqttEventBaseHandle,
@@ -231,9 +240,85 @@ RyanMqttError_e RyanMqttTestInit(RyanMqttClient_t **client, RyanMqttBool_e syncF
 	return RyanMqttSuccessError;
 }
 
+typedef struct
+{
+	void *ptr;
+	timer_t timerid;
+} FreeTimerArg;
+
+static void RyanMqttTestFreeTimerCallback(union sigval arg)
+{
+	FreeTimerArg *fta = arg.sival_ptr;
+	free(fta->ptr);
+
+	RyanMqttTestEnableCritical();
+	destroyCount--;
+	RyanMqttTestExitCritical();
+
+	timer_t timerid = fta->timerid;
+	free(fta); // 释放参数结构
+
+	if (0 != timer_delete(timerid))
+	{
+		RyanMqttLog_e("timer_delete failed");
+	}
+}
+
+static void RyanMqttTestScheduleFreeAfterMs(void *ptr, uint32_t delayMs)
+{
+	RyanMqttTestEnableCritical();
+	destroyCount++;
+	RyanMqttTestExitCritical();
+
+	timer_t timerid;
+	struct sigevent sev = {0};
+	struct itimerspec its = {0};
+
+	FreeTimerArg *fta = malloc(sizeof(FreeTimerArg));
+	fta->ptr = ptr;
+
+	sev.sigev_notify = SIGEV_THREAD;
+	sev.sigev_value.sival_ptr = fta;                           // 传递给回调的参数
+	sev.sigev_notify_function = RyanMqttTestFreeTimerCallback; // 定时到期时调用的函数
+
+	if (0 != timer_create(CLOCK_MONOTONIC, &sev, &timerid))
+	{
+		RyanMqttLog_e("timer_create failed");
+		free(fta);
+		return;
+	}
+
+	fta->timerid = timerid;
+
+	// 毫秒转秒和纳秒
+	its.it_value.tv_sec = delayMs / 1000;
+	its.it_value.tv_nsec = (uint32_t)((delayMs % 1000) * 1000000);
+
+	if (0 != timer_settime(timerid, 0, &its, NULL))
+	{
+		RyanMqttLog_e("timer_settime failed");
+
+		if (0 != timer_delete(timerid))
+		{
+			RyanMqttLog_e("timer_delete failed");
+		}
+
+		free(fta);
+		return;
+	}
+}
+
 RyanMqttError_e RyanMqttTestDestroyClient(RyanMqttClient_t *client)
 {
 	struct RyanMqttTestEventUserData *eventUserData = (struct RyanMqttTestEventUserData *)client->config.userData;
+
+	if (RyanMqttTestEventUserDataMagic != eventUserData->magic)
+	{
+		RyanMqttLog_e("eventUserData野指针");
+	}
+
+	RyanMqttDisconnect(client, RyanMqttTrue);
+
 	// 启动mqtt客户端线程
 	RyanMqttDestroy(client);
 
@@ -241,18 +326,22 @@ RyanMqttError_e RyanMqttTestDestroyClient(RyanMqttClient_t *client)
 	{
 		sem_wait(&eventUserData->sem);
 		sem_destroy(&eventUserData->sem);
+
+		delay(20); // 等待mqtt线程回收资源
+		free(eventUserData);
+	}
+	else
+	{
+		RyanMqttTestScheduleFreeAfterMs(eventUserData, RyanMqttRecvTimeout + 20);
 	}
 
-	// RyanMqttEventDestroyBefore 后mqtt客户端还要执行清除操作
-	delay(10);
-	platformMemoryFree(eventUserData);
 	return RyanMqttSuccessError;
 }
 
 RyanMqttError_e checkAckList(RyanMqttClient_t *client)
 {
 	RyanMqttLog_w("等待检查ack链表,等待 recvTime: %d", client->config.recvTimeout);
-	delay(client->config.recvTimeout + 500);
+	delay(RyanMqttRecvTimeout + 50);
 
 	platformMutexLock(client->config.userData, &client->ackHandleLock);
 	int ackEmpty = RyanMqttListIsEmpty(&client->ackHandlerList);
@@ -284,92 +373,71 @@ RyanMqttError_e checkAckList(RyanMqttClient_t *client)
 	return RyanMqttSuccessError;
 }
 
-void printfArrStr(uint8_t *buf, uint32_t len, char *userData)
-{
-	RyanMqttLog_raw("%s", userData);
-	for (uint32_t i = 0; i < len; i++)
-	{
-		RyanMqttLog_raw("%x", buf[i]);
-	}
-
-	RyanMqttLog_raw("\r\n");
-}
-
-void RyanMqttTestEnableCritical(void)
-{
-	pthread_spin_lock(&spin);
-}
-
-void RyanMqttTestExitCritical(void)
-{
-	pthread_spin_unlock(&spin);
-}
-
+// 注意测试代码只有特定emqx服务器才可以通过,用户的emqx服务器大概率通过不了,
+// 因为有些依赖emqx的配置,比如消息重试间隔,最大飞行窗口,最大消息队列等
 // todo 增加session测试
 // !当测试程序出错时,并不会回收内存。交由父进程进行回收
 int main(void)
 {
-	RyanMqttError_e result = RyanMqttSuccessError;
-	vallocInit();
-
-	pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE);
-
-	// 多线程测试必须设置这个,否则会导致 heap-use-after-free, 原因如下
-	// 虽然也有办法解决,不过RyanMqtt目标为嵌入式场景,不想引入需要更多资源的逻辑,嵌入式场景目前想不到有这么频繁而且还是本机emqx的场景。
+	RyanMqttTestUtileInit();
 
-	// 用户线程send -> emqx回复报文 -> mqtt线程recv。
-	// recv线程收到数据后,会释放用户线程send的sendbuf缓冲区。
-	// 但是在本机部署的emqx并且多核心同时运行,发送的数据量非常大的情况下会出现mqtt线程recv已经收到数据,但是用户线程send函数还没有返回。
-	cpu_set_t cpuset;
-	CPU_ZERO(&cpuset);
-	CPU_SET(0, &cpuset);
-	pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
-	sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
-
-	result = RyanMqttPublicApiParamCheckTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	RyanMqttError_e result = RyanMqttSuccessError;
 
-	result = RyanMqttSubTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	uint32_t testRunCount = 0;
+	uint32_t funcStartMs;
+#define runTestWithLogAndTimer(fun)                                                                                    \
+	do                                                                                                             \
+	{                                                                                                              \
+		testRunCount++;                                                                                        \
+		RyanMqttLog_raw("┌── [TEST %d] 开始执行: " #fun "()\r\n", testRunCount);                               \
+		funcStartMs = platformUptimeMs();                                                                      \
+		result = fun();                                                                                        \
+		RyanMqttLog_raw("└── [TEST %d] 结束执行: 返回值 = %d %s | 耗时: %d ms\x1b[0m\r\n\r\n", testRunCount,   \
+				result, (result == RyanMqttSuccessError) ? "✅" : "❌",                                \
+				platformUptimeMs() - funcStartMs);                                                     \
+		RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, result, RyanMqttLog_e, { goto __exit; });    \
+	} while (0)
 
-	result = RyanMqttPubTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	uint32_t totalElapsedStartMs = platformUptimeMs();
+	runTestWithLogAndTimer(RyanMqttPublicApiParamCheckTest);
+	runTestWithLogAndTimer(RyanMqttMemoryFaultToleranceTest);
 
-	result = RyanMqttDestroyTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	runTestWithLogAndTimer(RyanMqttSubTest);
+	runTestWithLogAndTimer(RyanMqttPubTest);
 
-	result = RyanMqttMultiThreadMultiClientTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	runTestWithLogAndTimer(RyanMqttDestroyTest);
 
-	result = RyanMqttMultiThreadSafetyTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	runTestWithLogAndTimer(RyanMqttNetworkFaultToleranceMemoryTest);
+	runTestWithLogAndTimer(RyanMqttNetworkFaultQosResilienceTest);
 
-	result = RyanMqttReconnectTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	runTestWithLogAndTimer(RyanMqttMultiThreadMultiClientTest);
+	runTestWithLogAndTimer(RyanMqttMultiThreadSafetyTest);
 
-	result = RyanMqttKeepAliveTest();
-	RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit; });
+	runTestWithLogAndTimer(RyanMqttReconnectTest);
+	runTestWithLogAndTimer(RyanMqttKeepAliveTest);
 
-	// result = RyanMqttWildcardTest();
-	// RyanMqttCheckCodeNoReturn(RyanMqttSuccessError == result, RyanMqttFailedError, RyanMqttLog_e, { goto __exit;
-	// });
+	// 暂时不开放出来
+	// runTestWithLogAndTimer(RyanMqttWildcardTest);
 
 __exit:
-	pthread_spin_destroy(&spin);
+
+	RyanMqttLog_raw("测试总耗时: %.3f S\r\n", (platformUptimeMs() - totalElapsedStartMs) / 1000.0);
 
 	if (RyanMqttSuccessError == result)
 	{
-		RyanMqttLog_i("测试成功---------------------------");
+		RyanMqttLog_raw("测试成功---------------------------\r\n");
 	}
 	else
 	{
-		RyanMqttLog_e("测试失败---------------------------");
+		RyanMqttLog_raw("测试失败---------------------------\r\n");
 	}
 
-	for (uint32_t i = 0; i < 5; i++)
+	for (uint32_t i = 0; i < 3; i++)
 	{
 		displayMem();
-		delay(2 * 1000);
+		delay(300);
 	}
+
+	RyanMqttTestUtileDeInit();
 	return 0;
 }

+ 16 - 28
test/RyanMqttTest.h

@@ -9,6 +9,7 @@ extern "C" {
 #include <stdio.h>
 #include <stdint.h>
 #include <string.h>
+#include <time.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <unistd.h>
@@ -16,15 +17,17 @@ extern "C" {
 #include <pthread.h>
 #include <sched.h>
 #include "valloc.h"
-#define malloc  v_malloc
-#define calloc  v_calloc
-#define free    v_free
-#define realloc v_realloc
 
-#define RyanMqttLogLevel (RyanMqttLogLevelDebug) // 日志打印等级
+#ifndef RyanMqttLogLevel
+#define RyanMqttLogLevel (RyanMqttLogLevelError) // 日志打印等级
+// #define RyanMqttLogLevel (RyanMqttLogLevelDebug) // 日志打印等级
+#endif
 
 #include "RyanMqttLog.h"
 #include "RyanMqttClient.h"
+#include "RyanMqttUtil.h"
+
+#include "RyanMqttTestUtile.h"
 
 #define RyanMqttClientId ("RyanMqttTest888") // 填写mqtt客户端id,要求唯一
 // #define RyanMqttHost ("127.0.0.1")           // 填写你的mqtt服务器ip
@@ -35,46 +38,28 @@ extern "C" {
 // #define RyanMqttUserName (NULL) // 填写你的用户名,没有填NULL
 // #define RyanMqttPassword (NULL) // 填写你的密码,没有填NULL
 
-#define delay(ms)         usleep((ms) * 1000)
-#define delay_us(us)      usleep((us))
-#define getArraySize(arr) ((int32_t)(sizeof(arr) / sizeof((arr)[0])))
-#define checkMemory                                                                                                    \
-	do                                                                                                             \
-	{                                                                                                              \
-		int area = 0, use = 0;                                                                                 \
-		v_mcheck(&area, &use);                                                                                 \
-		if (area != 0 || use != 0)                                                                             \
-		{                                                                                                      \
-			RyanMqttLog_e("内存泄漏");                                                                     \
-			while (1)                                                                                      \
-			{                                                                                              \
-				v_mcheck(&area, &use);                                                                 \
-				RyanMqttLog_e("|||----------->>> area = %d, size = %d", area, use);                    \
-				delay(3000);                                                                           \
-			}                                                                                              \
-		}                                                                                                      \
-	} while (0)
+#define RyanMqttReconnectTimeout (3000) // 重连间隔时间,单位ms
+#define RyanMqttRecvTimeout      (2000) // 接收数据超时时间,单位ms
+#define RyanMqttSendTimeout      (1800) // 发送数据超时时间,单位ms
+#define RyanMqttAckTimeout       (3000) // 等待ack超时时间,单位ms
 
 // 定义枚举类型
 
 // 定义结构体类型
 struct RyanMqttTestEventUserData
 {
+	uint32_t magic; // 防止野指针
 	RyanMqttBool_e syncFlag;
 	sem_t sem;
 	void *userData;
 };
 /* extern variables-----------------------------------------------------------*/
-
-extern void RyanMqttTestEnableCritical(void);
-extern void RyanMqttTestExitCritical(void);
 extern RyanMqttError_e RyanMqttTestInit(RyanMqttClient_t **client, RyanMqttBool_e syncFlag,
 					RyanMqttBool_e autoReconnectFlag, uint16_t keepaliveTimeoutS,
 					RyanMqttEventHandle mqttEventCallback, void *userData);
 extern RyanMqttError_e RyanMqttTestDestroyClient(RyanMqttClient_t *client);
 extern void mqttEventBaseHandle(void *pclient, RyanMqttEventId_e event, const void *eventData);
 extern RyanMqttError_e checkAckList(RyanMqttClient_t *client);
-extern void printfArrStr(uint8_t *buf, uint32_t len, char *userData);
 
 extern RyanMqttError_e RyanMqttDestroyTest(void);
 extern RyanMqttError_e RyanMqttKeepAliveTest(void);
@@ -85,6 +70,9 @@ extern RyanMqttError_e RyanMqttWildcardTest(void);
 extern RyanMqttError_e RyanMqttMultiThreadMultiClientTest(void);
 extern RyanMqttError_e RyanMqttMultiThreadSafetyTest(void);
 extern RyanMqttError_e RyanMqttPublicApiParamCheckTest(void);
+extern RyanMqttError_e RyanMqttNetworkFaultToleranceMemoryTest(void);
+extern RyanMqttError_e RyanMqttNetworkFaultQosResilienceTest(void);
+extern RyanMqttError_e RyanMqttMemoryFaultToleranceTest(void);
 
 #ifdef __cplusplus
 }

+ 113 - 0
test/RyanMqttTestUtile.c

@@ -0,0 +1,113 @@
+#include "RyanMqttTest.h"
+
+static pthread_spinlock_t spin;
+uint32_t destroyCount = 0;
+
+uint32_t randomCount = 0;
+uint32_t sendRandomCount = 0;
+uint32_t memoryRandomCount = 0;
+RyanMqttBool_e isEnableRandomNetworkFault = RyanMqttFalse;
+RyanMqttBool_e isEnableRandomMemoryFault = RyanMqttFalse;
+void enableRandomNetworkFault(void)
+{
+	RyanMqttTestEnableCritical();
+	isEnableRandomNetworkFault = RyanMqttTrue;
+	RyanMqttTestExitCritical();
+}
+
+void disableRandomNetworkFault(void)
+{
+	RyanMqttTestEnableCritical();
+	isEnableRandomNetworkFault = RyanMqttFalse;
+	RyanMqttTestExitCritical();
+}
+
+void toggleRandomNetworkFault(void)
+{
+	RyanMqttTestEnableCritical();
+	isEnableRandomNetworkFault = !isEnableRandomNetworkFault;
+	RyanMqttTestExitCritical();
+}
+
+void enableRandomMemoryFault(void)
+{
+	RyanMqttTestEnableCritical();
+	isEnableRandomMemoryFault = RyanMqttTrue;
+	RyanMqttTestExitCritical();
+}
+
+void disableRandomMemoryFault(void)
+{
+	RyanMqttTestEnableCritical();
+	isEnableRandomMemoryFault = RyanMqttFalse;
+	RyanMqttTestExitCritical();
+}
+
+void toggleRandomMemoryFault(void)
+{
+	RyanMqttTestEnableCritical();
+	isEnableRandomMemoryFault = !isEnableRandomMemoryFault;
+	RyanMqttTestExitCritical();
+}
+
+uint32_t RyanRand(int32_t min, int32_t max)
+{
+	static uint32_t isSeed = 0;
+	static uint32_t seedp = 0;
+	if (isSeed > 1024 || 0 == seedp)
+	{
+		seedp = platformUptimeMs();
+		isSeed = 0;
+	}
+
+	if (min >= max)
+	{
+		return min;
+	}
+	isSeed++;
+	return (rand_r(&seedp) % (max - min + 1)) + min;
+}
+
+void RyanMqttTestEnableCritical(void)
+{
+	pthread_spin_lock(&spin);
+}
+
+void RyanMqttTestExitCritical(void)
+{
+	pthread_spin_unlock(&spin);
+}
+
+void printfArrStr(uint8_t *buf, uint32_t len, char *userData)
+{
+	RyanMqttLog_raw("%s len: %d ", userData, len);
+	for (uint32_t i = 0; i < len; i++)
+	{
+		RyanMqttLog_raw("%c", buf[i]);
+	}
+
+	RyanMqttLog_raw("\r\n");
+}
+
+void RyanMqttTestUtileInit(void)
+{
+	pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE);
+
+	// 多线程测试必须设置这个,否则会导致 heap-use-after-free, 原因如下
+	// 虽然也有办法解决,不过RyanMqtt目标为嵌入式场景,不想引入需要更多资源的逻辑,嵌入式场景目前想不到有这么频繁而且还是本机emqx的场景。
+	// 用户线程send -> emqx回复报文 -> mqtt线程recv。
+	// recv线程收到数据后,会释放用户线程send的sendbuf缓冲区。
+	// 但是在本机部署的emqx并且多核心同时运行,发送的数据量非常大的情况下会出现mqtt线程recv已经收到数据,但是用户线程send函数还没有返回。
+	cpu_set_t cpuset;
+	CPU_ZERO(&cpuset);
+	CPU_SET(0, &cpuset);
+	pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset);
+	sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
+
+	vallocInit();
+}
+
+void RyanMqttTestUtileDeInit(void)
+{
+	pthread_spin_destroy(&spin);
+}

+ 94 - 0
test/RyanMqttTestUtile.h

@@ -0,0 +1,94 @@
+#ifndef __RyanMqttTestUtile__
+#define __RyanMqttTestUtile__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <sched.h>
+#include "valloc.h"
+#define malloc  v_malloc
+#define calloc  v_calloc
+#define free    v_free
+#define realloc v_realloc
+
+#include "RyanMqttLog.h"
+#include "RyanMqttClient.h"
+#include "RyanMqttUtil.h"
+
+#define delay(ms)         usleep((ms) * 1000)
+#define delay_us(us)      usleep((us))
+#define getArraySize(arr) ((int32_t)(sizeof(arr) / sizeof((arr)[0])))
+extern uint32_t destroyCount;
+
+#define checkMemory                                                                                                    \
+	do                                                                                                             \
+	{                                                                                                              \
+		for (uint32_t aaa = 0;; aaa++)                                                                         \
+		{                                                                                                      \
+			RyanMqttTestEnableCritical();                                                                  \
+			uint32_t destoryCount2 = destroyCount;                                                         \
+			RyanMqttTestExitCritical();                                                                    \
+			if (0 == destoryCount2) break;                                                                 \
+			if (aaa > 10 * 1000)                                                                           \
+			{                                                                                              \
+				printf("aaaaaaaa %d\r\n", destoryCount2);                                              \
+				break;                                                                                 \
+			}                                                                                              \
+			delay(1);                                                                                      \
+		}                                                                                                      \
+		int area = 0, use = 0;                                                                                 \
+		v_mcheck(&area, &use);                                                                                 \
+		if (area != 0 || use != 0)                                                                             \
+		{                                                                                                      \
+			RyanMqttLog_e("内存泄漏");                                                                     \
+			while (1)                                                                                      \
+			{                                                                                              \
+				v_mcheck(&area, &use);                                                                 \
+				RyanMqttLog_e("|||----------->>> area = %d, size = %d", area, use);                    \
+				delay(3000);                                                                           \
+			}                                                                                              \
+		}                                                                                                      \
+	} while (0)
+
+extern uint32_t randomCount;
+extern uint32_t sendRandomCount;
+extern uint32_t memoryRandomCount;
+extern RyanMqttBool_e isEnableRandomNetworkFault;
+extern RyanMqttBool_e isEnableRandomMemoryFault;
+extern void enableRandomNetworkFault(void);
+extern void disableRandomNetworkFault(void);
+extern void toggleRandomNetworkFault(void);
+extern void enableRandomMemoryFault(void);
+extern void disableRandomMemoryFault(void);
+extern void toggleRandomMemoryFault(void);
+extern uint32_t RyanRand(int32_t min, int32_t max);
+
+// 定义枚举类型
+
+// 定义结构体类型
+#define RyanMqttTestEventUserDataMagic (123456789)
+
+/* extern variables-----------------------------------------------------------*/
+
+extern void RyanMqttTestEnableCritical(void);
+extern void RyanMqttTestExitCritical(void);
+extern void printfArrStr(uint8_t *buf, uint32_t len, char *userData);
+
+extern void RyanMqttTestUtileInit(void);
+extern void RyanMqttTestUtileDeInit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 4 - 2
xmake.lua

@@ -2,7 +2,7 @@ add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"})
 target("RyanMqtt",function()
     set_kind("binary")
 
-    add_syslinks("pthread")
+    add_syslinks("pthread","rt")
     set_toolchains("gcc")  -- 确保使用 GCC
     -- set_toolchains("clang-20")  
     set_languages("gnu99") -- 启用 GNU 扩展
@@ -88,11 +88,13 @@ target("RyanMqtt",function()
     add_includedirs('./platform/linux', {public = true})
     add_includedirs('./platform/linux/valloc', {public = true})
 
-    add_files('./test/*.c', {public = true})
     add_files('./common/*.c', {public = true})
     add_files('./coreMqtt/*.c', {public = true})
     add_files('./mqttclient/*.c', {public = true})
     add_files('./platform/linux/*.c', {public = true})
     add_files('./platform/linux/valloc/*.c', {public = true})
 
+    add_includedirs('./test', {public = true})
+    add_files('./test/*.c', {public = true})
+
 end)