MQTT第5版更新,以及如何应用到Qt MQTT模块中

作者:Richard Lin | Jun 3, 2019 4:28:15 AM

原文作者:Maurice Kalinowski     校审:Richard Lin

之前我曾写过在MQTT消息中的topic可能会对发布的数据量产生较大影响。从那之后,MQTT已经发布了第5版标准并且诞生了第一个实现。当然,Qt MQTT也随之跟进,这篇文章将介绍Qt for Automation模块是如何应用新标准的。

在这篇文章中,我们不会详细介绍新标准,而是展示最新版本的一些亮点特性和好处。

Topic别名

请注意消息的topic始终作为字节流的一部分并且以明文形式未加任何压缩地发送。例如,报告温度值的传感器网络可以采用如下层次结构的topic

sensors/Europe/2af89b42-d2a6-11e8-a8d5-f2801f1b9fd1/Temperature

因此,消息的字节布局如下所示:

每个消息总共最多71个字节,其中4个字节包含了请求信息。

在之前的文章中,我们谈到了如下的方法

  • 减少topic长度
  • 组合多个传感器的值以优化额外开销的比值。

MQTT5引入了topic别名,允许为某个topic分配ID。

在连接握手期间,客户端和服务器就topic别名是否可用以及topic别名的最大数量达成一致。当ID可分配时可以将它们分配给各个topic。此分配过程是在消息发布期间完成的。新的发布消息同时设置topic别名的字节布局如下所示:

对于第一条消息,数据大小为74字节,比传统发布大三倍多。但是,第二次发布消息时,将从这一设计中受益

消息大小已减少到11个字节,只需将topic与ID交换并保持topic字符串为空即可。从第二条消息开始将大量的减少所需带宽。

请务必注意,一个连接仅需要设置一次topic别名的分配。关于此topic的其他订阅者,尤其是不使用topic别名的订阅者,将收到包含完整topic的消息。或者,这些连接可以创建自己的topic别名匹配。服务器负责使用完整topic或其他别名进行topic的转发。

此外,topic别名不是静态不变的,一个别名是可以重新分配给另一个topic的。

让我们继续展示如何在Qt MQTT中使用此功能吧。Qt MQTT和Qt for Automation 5.12增加了对MQTT v5的支持。

 

  • 使用topic别名连接

要为连接客户端设置topic别名支持,需要在连接到服务器之前更新QMqttClient

1
2
3
4
5
6
7
8
9
10
11
const int userMaxTopicAlias = 9;
QMqttConnectionProperties userProperties;
userProperties.setMaximumTopicAlias(userMaxTopicAlias);
client.setConnectionProperties(userProperties);
[…]
client.connectToHost();
  • 验证服务器是否支持别名

连接服务器时,查询服务器是否也支持topic别名

1
2
3
const auto serverProperties = client.serverConnectionProperties();
const quint16 serverMaxAlias = serverProperties.maximumTopicAlias(); // Returns the number of available aliases

一个连接最多可以使用的别名数量取决于客户端和服务器topic别名的数量上限。

  • 设置topic别名的同时发布消息
1
2
3
4
5
6
7
const QMqttTopicName topic(QLatin1String( "sensors/Europe/2af89b42-d2a6-11e8-a8d5-f2801f1b9fd1/Temperature" ));
QMqttPublishProperties aliasProperties;
aliasProperties.setTopicAlias(2);
client.publish(topic, aliasProperties, msgContent, 1);

Qt MQTT模块会在内部存储分配结果以简化后续消息的发布。

  • 发布后续的消息
1
client.publish(topic, msgContent, 1);

除非需要重新分配ID,否则无需为后续调用添加属性。

在用户不指定的情况下,Qt MQTT尝试自动使用topic别名。在建立连接并且Qt MQTT识别到对使用topic别名的支持后,它会把传统的发布消息方式“转换为”使用topic别名的方式,除非所有可用的别名都被分配掉了。为实现这个功能,它使用内部缓存机制,可以使用上面的QMqttPublishProperties来进行控制。

该缓存中没有包含启发式方法,因此我们仍建议手动使用topic别名。这要根据具体项目来确定哪些topic经常使用从而使用别名来实现最高的效率。

作为结束语,Qt MQTT还负责关于topic别名的取消订阅。该过程是相同的,服务器指定topic和别名匹配。然后Qt MQTT会跟踪此关系并将QMqttMessage内的完整topic字符串传递给没有指定topic的用户。

如果用户想知道topic是否具有指定的别名,可以从QMqttMessage的发布属性中获取此信息。

各种属性

您可能已经从上文中了解了发布过程中的属性类的用法。这体现了MQTT 5引入的新的概念。在保持协议精简、易用的同时,我们还花了不少精力去创建更灵活的结构。

属性可以嵌入到现有命令中,但不是必须的。默认值涵盖了最常用的场景,同时确保了与MQTT 3.1.1的行为兼容,提供了一个零字节表示没有属性被设置。

下面列出了所有可用属性及其适用的命令:

对于每个命令,都在5.12版本中加入了一个新的属性类。请查看相关文档,以了解更多详细的信息。

安全

身份验证的方式已经更新,在MQTT 5.0中这是一个重要的趋势,为了扩展它引入了新的命令AUTH。当客户端向服务器发送连接请求之后,服务器可能会要求进一步的身份验证信息。这是通过发送包含了Continue reason code的AUTH命令来实现。在客户端提供了身份验证数据后,才能连接成功,如果认证失败,服务器会要求更多认证数据,或者由于身份验证失败而关闭连接。身份验证步骤的尝试次数是没有限制的。

用于身份验证的实际方法是在服务器端决定的。这可以是OAuth或者在服务器端集成的任何其他功能。

在Qt MQTT中,QMqttClient具有authenticationRequested  和authenticationFinished  信号来表示来自服务器的通知。此外它还引入了 authenticate函数将认证数据发送到服务器。

还有什么?

描述性参数可用于提供有关消息内容的更多详细信息。更进一步,现在还允许服务器向客户端提供错误消息。这在客户端和服务器之间实现通信协议时非常有用。标准过去在发现无效行为后会要求断开连接。现在也一样,但那时现在一台服务器可以包含错误代码甚至错误字符串作为客户端或开发人员的指导。

自然地,所有这些都已经反映在Qt MQTT中了。

我们希望更多地了解您对此模块的使用情况,以及你最需要哪些功能来更快的完成您的项目。如果您有任何意见,请随时通过评论或者直接通过电子邮件与我们联系