

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# coreMQTT 演示
<a name="mqtt-demo"></a>

**重要**  <a name="deprecation-message-demo"></a>
该演示托管在已弃用的 Amazon-FreeRTOS 存储库中。当您创建新项目时，我们建议[从此处开始](freertos-getting-started-modular.md)。如果您已经有一个基于现已弃用的 Amazon-FreeRTOS 存储库的 FreeRTOS 项目，请参阅 [Amazon-FreeRTOS Github 存储库迁移指南](github-repo-migration.md)。

这些演示可帮助您学习如何使用 coreMQTT 库。

**Topics**
+ [coreMQTT 双向身份验证演示](mqtt-demo-ma.md)
+ [coreMQTT 代理连接共享演示](mqtt-demo-cs.md)

# coreMQTT 双向身份验证演示
<a name="mqtt-demo-ma"></a>

**重要**  <a name="deprecation-message-demo"></a>
该演示托管在已弃用的 Amazon-FreeRTOS 存储库中。当您创建新项目时，我们建议[从此处开始](freertos-getting-started-modular.md)。如果您已经有一个基于现已弃用的 Amazon-FreeRTOS 存储库的 FreeRTOS 项目，请参阅 [Amazon-FreeRTOS Github 存储库迁移指南](github-repo-migration.md)。

## 简介
<a name="mqtt-demo-ma-introduction"></a>

coreMQTT 双向身份验证演示项目展示了如何使用在客户端与服务器之间具有双向身份验证的 TLS 建立与 MQTT 代理的连接。此演示使用基于 mbedTLS 的传输接口实现来建立经过服务器和客户端身份验证的 TLS 连接，并演示了 [QoS 1](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180914) 级别的 MQTT 订阅发布工作流程。它会订阅主题筛选条件，然后发布到与筛选条件匹配的主题，并等待从 QoS 1 级别收到服务器发回的消息。这种向代理发布消息并从代理接收相同消息的循环过程会无限期地重复。本演示中的消息按照 QoS 1 发送，这样可以保证至少有一次按照 MQTT 规范传递。

**注意**  
要设置和运行 FreeRTOS 演示，请按照[开始使用 FreeRTOS](freertos-getting-started.md)中的步骤操作。

## 源代码
<a name="mqtt-demo-ma-source-code"></a>

演示源文件已命名`mqtt_demo_mutual_auth.c`，可以在`freertos/demos/coreMQTT/`目录和[ GitHub](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c)网站上找到。

## 功能
<a name="mqtt-demo-ma-functionality"></a>

该演示创建了一个应用程序任务，该任务循环访问一组示例，这些示例演示如何连接代理、订阅代理上的主题、发布到代理上的主题，最后断开与代理的连接。该演示应用程序会订阅一个主题的消息，也会向同一个主题发布消息。每次演示向 MQTT 代理发布消息时，代理都会向演示应用程序发送相同的消息。

成功完成演示将生成类似于以下图像的输出。

![\[MQTT 演示终端在成功完成后的输出\]](http://docs.aws.amazon.com/zh_cn/freertos/latest/userguide/images/coremqtt-mad-output.png)


 AWS IoT 控制台将生成类似于下图的输出。

![\[MQTT 演示控制台在成功完成后的输出\]](http://docs.aws.amazon.com/zh_cn/freertos/latest/userguide/images/coremqtt-mad-console.png)


## 使用指数回退和抖动重试逻辑
<a name="mqtt-demo-ma-retry-logic"></a>

[ prvBackoffFor重试](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L671-L717)函数显示如何通过指数退避和抖动重试服务器失败的网络操作（例如 TLS 连接或 MQTT 订阅请求）。该函数可计算下一次重试尝试的回退周期，如果重试尝试未用完，则执行回退延迟。由于计算退避周期需要生成随机数，因此该函数使用 PKCS11 模块生成随机数。如果供应商平台支持，则使用该 PKCS11 模块允许访问真随机数生成器 (TRNG)。我们建议您在随机数生成器中加入特定于设备的熵源，这样可以降低设备在连接重试过程中发生冲突的可能性。

## 连接到 MQTT 代理
<a name="mqtt-demo-ma-connecting"></a>

该[ prvConnectToServerWithBackoffRetries](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L721-L782)函数尝试与 MQTT 代理建立相互身份验证的 TLS 连接。如果连接失败，则会在回退周期后重试。回退周期将呈指数级增长，直到达到最大尝试次数或达到最大回退周期。`BackoffAlgorithm_GetNextBackoff` 函数提供指数级增长的回退值，并在达到最大尝试次数后返回 `RetryUtilsRetriesExhausted`。如果在配置的尝试次数后仍无法建立与代理的 TLS 连接，则 `prvConnectToServerWithBackoffRetries` 函数将返回失败状态。

[prvCreate MQTTConnection WithBroker](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L785-L848) 函数演示了如何通过干净的会话与 MQTT 代理建立 MQTT 连接。它使用 TLS 传输接口，该接口在 `FreeRTOS-Plus/Source/Application-Protocols/platform/freertos/transport/src/tls_freertos.c` 文件中实现。请记住，我们正在 `xConnectInfo` 中为代理设置保持活动状态的秒数。

下一个函数展示了如何使用 `MQTT_Init` 函数在 MQTT 环境中设置 TLS 传输接口和时间函数，还展示了事件回调函数指针 (`prvEventCallback`) 是如何设置的。该回调用于报告传入的消息。

## 订阅 MQTT 主题
<a name="mqtt-demo-ma-subscribing"></a>

p [rv MQTTSubscribe WithBackoffRetries](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L871-L969) 函数演示如何在 MQTT 代理上订阅主题过滤器。该示例演示了如何订阅一个主题筛选条件，还可以在同一个订阅 API 调用中传递主题筛选条件列表来订阅多个主题筛选条件。此外，对于 `RETRY_MAX_ATTEMPTS`，如果 MQTT 代理拒绝订阅请求，则订阅将以指数回退的方式重试。

## 向主题发布
<a name="mqtt-demo-ma-publishing"></a>

p [rv MQTTPublish ToTopic](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L972-L1004) 函数演示如何在 MQTT 代理上发布到某个主题。

## 接收传入的消息
<a name="mqtt-demo-ma-receiving"></a>

如前所述，该应用程序会在连接到代理之前注册一个事件回调函数。`prvMQTTDemoTask` 函数调用 `MQTT_ProcessLoop` 函数来接收传入的消息。当收到传入的 MQTT 消息时，它会调用应用程序注册的事件回调函数。该[ prvEventCallback](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L1139-L1154)函数就是这样一个事件回调函数的示例。 `prvEventCallback`检查传入的数据包类型并调用相应的处理程序。在下面的示例中，该函数要么调用 `prvMQTTProcessIncomingPublish()` 来处理传入的发布消息，要么调用 `prvMQTTProcessResponse()` 来处理确认消息 (ACK)。

## 处理传入的 MQTT 发布数据包
<a name="mqtt-demo-ma-processing"></a>

[prv MQTTProcess IncomingPublish](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L1108-L1135) 函数演示如何处理来自 MQTT 代理的发布数据包。

## 取消订阅主题
<a name="mqtt-demo-ma-unsubscribing"></a>

该工作流程的最后一步是取消订阅主题，这样代理就不会从 `mqttexampleTOPIC` 发送任何已发布的消息。这是函数 [prv MQTTUnsubscribe FromTopic](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT/mqtt_demo_mutual_auth.c#L1007-L1043) 的定义。

## 更改演示中使用的根 CA
<a name="mqtt-demo-ma-root-ca"></a>

默认情况下，FreeRTOS 演示使用 Amazon Root CA 1 证书（RSA 2048 位密钥）向服务器进行身份验证。 AWS IoT Core 您可以使用其他 [CA 证书来进行服务器身份验证](https://docs.aws.amazon.com/iot/latest/developerguide/server-authentication.html#server-authentication-certs)，包括 Amazon Root CA 3 证书（ECC 256 位密钥）。要更改 coreMQTT 双向身份验证演示的根 CA，请执行以下操作：

1. 在文本编辑器中，打开 `freertos/vendors/vendor/boards/board/aws_demos/config_files/mqtt_demo_mutual_auth_config.h`文件。

1. 在文件中找到以下行。

   ```
    * #define democonfigROOT_CA_PEM    "...insert here..." 
   ```

   取消注释此行，如有必要，将其移到注释块末尾的 ` */` 之后。

1. 复制要使用的 CA 证书，然后将其粘贴到文本 `"...insert here..."` 处。结果应该类似以下示例。

   ```
   #define democonfigROOT_CA_PEM   "-----BEGIN CERTIFICATE-----\n"\
   "MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5\n"\
   "MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g\n"\
   "Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG\n"\
   "A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg\n"\
   "Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl\n"\
   "ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j\n"\
   "QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr\n"\
   "ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr\n"\
   "BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM\n"\
   "YyRIHN8wfdVoOw==\n"\
   "-----END CERTIFICATE-----\n"
   ```

1. （可选）您可以更改其他演示的根 CA。对每个 `freertos/vendors/vendor/boards/board/aws_demos/config_files/demo-name_config.h` 文件重复步骤 1 到 3。

# coreMQTT 代理连接共享演示
<a name="mqtt-demo-cs"></a>

**重要**  <a name="deprecation-message-demo"></a>
该演示托管在已弃用的 Amazon-FreeRTOS 存储库中。当您创建新项目时，我们建议[从此处开始](freertos-getting-started-modular.md)。如果您已经有一个基于现已弃用的 Amazon-FreeRTOS 存储库的 FreeRTOS 项目，请参阅 [Amazon-FreeRTOS Github 存储库迁移指南](github-repo-migration.md)。

## 简介
<a name="mqtt-demo-cs-introduction"></a>

CoreMQTT 连接共享演示项目向您展示了如何使用多线程应用程序通过 TLS 建立与 AWS MQTT 代理的连接，并在客户端和服务器之间进行相互身份验证。此演示使用基于 mbedTLS 的传输接口实现来建立经过服务器和客户端身份验证的 TLS 连接，并演示了 [QoS 1](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html#_Toc442180914) 级别的 MQTT 订阅发布工作流程。该演示订阅主题筛选条件，发布与筛选条件匹配的主题，然后等待从 QoS 1 级别的服务器接收这些消息。对于每个创建的任务，这种向代理发布消息并从代理接收相同消息的循环过程会无限期地重复。本演示中的消息按照 QoS 1 发送，这样可以保证至少有一次按照 MQTT 规范传递。

**注意**  
要设置和运行 FreeRTOS 演示，请按照[开始使用 FreeRTOS](freertos-getting-started.md)中的步骤操作。

此演示使用线程安全队列来保存与 MQTT API 交互的命令。此演示中有两个任务需要注意。
+ MQTT 代理（主）任务处理命令队列中的命令，而其他任务则将它们排入队列。此任务会进入循环，它会在此期间处理命令队列中的命令。如果收到终止命令，则此任务将退出循环。
+ 演示 subpub 任务会创建对 MQTT 主题的订阅，然后创建发布操作并将其推送到命令队列中。然后，这些发布操作由 MQTT 代理任务运行。演示 subpub 任务会等待发布完成（通过执行命令完成回调来指示），然后在开始下一次发布之前进入短暂的延迟。此任务展示了应用程序任务如何使用 coreMQTT 代理 API 的示例。

对于传入的发布消息，coreMQTT 代理会调用单个回调函数。此演示还包括一个订阅管理器，它允许任务指定一个回调，以便为它们已订阅的主题的传入发布消息进行调用。在此演示中，代理的传入发布回调会调用订阅管理器，将发布分散到任何已注册订阅的任务中。

此演示使用具有双向身份验证的 TLS 连接到 AWS。如果网络在演示过程中意外断开连接，则客户端会尝试使用指数回退逻辑重新连接。如果客户端重新连接成功，但代理无法恢复之前的会话，则客户端将重新订阅与上一个会话相同的主题。

### 单线程与多线程
<a name="mqtt-demo-cs-single-vs-multi"></a>

coreMQTT 有两种使用模式，即单线程和多线程（多任务处理）。单线程模型使用来自仅一个线程的 coreMQTT 库，并要求您在 MQTT 库中重复进行显式调用。多线程使用案例可以在代理（或进程守护程序）任务的后台运行 MQTT 协议，如此处记录的演示所示。在代理任务中运行 MQTT 协议时，无需显式管理任何 MQTT 状态或调用 `MQTT_ProcessLoop` API 函数。此外，如果您使用代理任务，则多个应用程序任务可共享单个 MQTT 连接，而无需同步原语（例如，互斥锁）。

## 源代码
<a name="mqtt-demo-cs-source-code"></a>

演示源文件已命名`mqtt_agent_task.c`，可以在`freertos/demos/coreMQTT_Agent/`目录和[GitHub](https://github.com/aws/amazon-freertos/tree/main/demos/coreMQTT_Agent/)网站上找到。`simple_sub_pub_demo.c`

## 功能
<a name="mqtt-demo-cs-functionality"></a>

此演示至少创建两个任务：一个用于处理 MQTT API 调用请求的主要任务，以及用于创建这些请求的可配置子任务数量。在此演示中，主任务会创建子任务，调用处理循环，然后进行清理。主任务创建与代理的单个 MQTT 连接，该连接在子任务之间共享。子任务通过代理创建 MQTT 订阅，然后向其发布消息。每个子任务的发布都使用唯一的主题。

## 主任务
<a name="mqtt-demo-cs-main-task"></a>

主应用程序任务 [ RunCoreMQTTAgentDemo](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT_Agent/mqtt_agent_task.c#L435-L480) 建立 MQTT 会话、创建子任务并运行处理循环 [ MQTTAgent\$1](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT_Agent/mqtt_agent_task.c#L856)，CommandLoop直到收到终止命令。如果网络意外断开连接，演示将在后台重新连接到代理，并与代理重新建立订阅。处理循环终止后，它会断开与代理的连接。

### 命令
<a name="mqtt-demo-cs-main-task-commands"></a>

当您调用 coreMQTT 代理 API 时，它会创建一个发送到代理任务队列的命令，然后在 `MQTTAgent_CommandLoop()` 中进行处理。在创建命令时，可以传递可选的完成回调和上下文参数。相应的命令完成后，将使用传递的上下文以及作为命令的结果创建的任何返回值来调用完成回调。完成回调的签名如下：

```
typedef void (* MQTTAgentCommandCallback_t )( void * pCmdCallbackContext,
                                              MQTTAgentReturnInfo_t * pReturnInfo );
```

命令完成上下文是用户定义的；对于这个演示，它是：[struct。 MQTTAgent CommandContext](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT_Agent/simple_sub_pub_demo.c#L105-L115)

在以下情况下即认为命令已完成：
+ 订阅、取消订阅和发布 (QoS > 0)：收到相应的确认数据包后。
+ 所有其他操作：调用相应的 coreMQTT API 后。

命令使用的任何结构（包括发布信息、订阅信息和完成上下文）都必须保持在作用范围内，直到命令完成。在调用完成回调之前，调用任务不得重用命令的任何结构。请注意，由于完成回调由 MQTT 代理调用，因此，它将使用代理任务的线程上下文运行，而不是创建命令的任务。进程间通信机制（例如，任务通知或队列）可用于向调用任务发出命令完成的信号。

### 运行命令循环
<a name="mqtt-demo-cs-command-loop"></a>

命令在`MQTTAgent_CommandLoop()` 中持续处理。如果没有要处理的命令，则循环将等待向队列中添加一个命令的最长等待时间 `MQTT_AGENT_MAX_EVENT_QUEUE_WAIT_TIME`；如果未添加任何命令，则循环将运行 `MQTT_ProcessLoop()` 的一次迭代。这样既可确保管理 MQTT Keep-Alive，又可确保即使队列中没有命令，也能收到传入的发布。

在以下原因下，命令循环函数将返回：
+ 命令返回除 `MQTTSuccess` 之外的任何状态代码。命令循环返回错误状态，因此您可以决定如何处理。在此演示中，重新建立了 TCP 连接并尝试重新连接。如果出现任何错误，则可以在后台进行重新连接，而没有其他使用 MQTT 的任务产生的任何干预。
+ 处理了断开连接命令 (`MQTTAgent_Disconnect`)。退出了命令循环，以便断开 TCP 连接。
+ 处理了终止命令 (`MQTTAgent_Terminate`)。此命令还将任何仍在队列中或正在等待确认数据包的命令标记为错误，返回代码为 `MQTTRecvFailed`。

### 订阅管理器
<a name="mqtt-demo-cs-subscription-manager"></a>

由于该演示使用多个主题，因此，订阅管理器是将订阅的主题与唯一的回调或任务相关联的便捷方式。此演示中的订阅管理器是单线程的，因此不应同时用于多个任务。在此演示中，订阅管理器函数只能从传递给 MQTT 代理的回调函数中调用，并且只能在代理任务的线程上下文中运行。

## 简单的订阅-发布任务
<a name="mqtt-demo-cs-sub-pub"></a>

的每个实例都会[ prvSimpleSubscribePublishTask](https://github.com/aws/amazon-freertos/blob/main/demos/coreMQTT_Agent/simple_sub_pub_demo.c#L447-L569)创建对 MQTT 主题的订阅，并为该主题创建发布操作。为了演示多种发布类型，偶数编号的任务使用 QoS 0（发送发布数据包后即完成），奇数任务使用 QoS 1（收到 PUBACK 数据包后即完成）。