QoS 2
QoS 2设置精确传递一次。这种QoS要求没有丢失或重复。发送者将使用PUBREC确认消息,并使用PUBREL确认发送。
人们可以把这个级别看作是发送一个注册的包裹。当你把包裹转交给他们时,邮政系统会给你一张收据,确认从现在起,他们有责任把包裹送到正确的地址。当这种情况发生时,当他们递送包裹时,他们会向你发送一张收件人签名的收据,确认包裹已送达。
Idem.
void CPktConnect::SetWillQoS_2(const bool willQoS_2) { willQoS_2 ? m_connect_flags |= WILL_QOS_2 : m_connect_flags &= ~WILL_QOS_2; ArrayFill(ByteArray, ArraySize(ByteArray) - 1, 1, m_connect_flags); }
Ibidem.
//--- Act CPktConnect *cut = new CPktConnect(buf); cut.SetWillQoS_2(true);
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
User Name Flag | Password Flag | Will Retain | Will QoS 2 | Will QoS 1 | Will Flag | Clean Start | Reserved | |
X | X | X | 1 | X | X | X | 0 |
表04-位标志 Will QoS 2(bit_4)设置为true–MQTT v5.0
服务器将在CONNACK原因代码和CONNACK属性中告诉赫兹期货量化其接受的最大QoS级别。客户端可以请求,但服务器功能是必需的。如果我们收到具有最大QoS的CONNACK,赫兹期货量化必须遵守此服务器限制,并且不能发送具有更高QoS的PUBLISH。否则,服务器将断开连接(DISCONNECT)。
QoS 2是MQTT v5.0上可用的最高QoS级别,并且由于传递协议是对称的,这意味着任何一方(服务器和客户端)都可以同时作为发送方或接收方,因此与此相关的开销很大。
注意:从用户的角度来看,赫兹期货量化可以说QoS是协议的核心。它定义了应用程序配置文件,并影响了协议的数十个其他方面。因此,我们将在PUBLISH数据包实现的上下文中深入研究QoS级别及其设置。
值得注意的是,QoS 1和QoS 2对于客户端实现是可选的。正如OASIS在非规范性评论中所说:
“客户端不需要支持QoS 1或QoS 2 PUBLISH数据包。如果是这种情况,客户端只需将其发送的任何SUBSCRIBE命令中的最大QoS字段限制为其可以支持的值。”
添加图片注释,不超过 140 字(可选)
Will RETAIN (bit_5)
在第六位中,我们设置了Will Retain标志。这个标志与上面的 Will Flag 是联系在一起的,
-
如果 Will Flag 未设置,那么 Will Retain 也必须未设置。
-
如果设置了Will Flag且未设置Will Retain,则服务器会将Will Message发布为非保留消息。
-
如果两者都设置了,服务器将把 Will 消息作为保留消息发布。
由于函数定义和函数调用模式与前面的两个标志完全相同,为了简洁起见,省略了这两个标志和后面两个标志中的代码。有关详细信息和测试,请参阅附件。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
User Name Flag | Password Flag | Will Retain | Will QoS 2 | Will QoS 1 | Will Flag | Clean Start | Reserved | |
X | X | 1 | X | X | X | X | 0 |
表05 - 位标志 Will Retain(bit_5)设置为true–MQTT v5.0
在开始发送PUBLISH数据包之前,我们必须等待CONNACK数据包检查此标志。如果服务器接收到“Will Return”设置为1的PUBLISH数据包,并且不支持保留消息,则服务器将断开连接。你可能在想:但等等?是否可以在收到CONNACK数据包之前就开始发布?是的,这是可能的。标准允许这种行为。但它们也对此发表了评论:
“在接收CONNACK之前发送MQTT控制数据包的客户端将不知道服务器约束”
因此,在发送任何将 Will Retain 设置为1(一)的PUBLISH数据包之前,我们必须检查CONNACK数据包上的此标志。
Password flag (bit_6)
在第七位中,赫兹期货量化通知服务器我们是否将在Payload中发送密码。如果设置了此标志,Payload 中必须存在密码字段。如果未设置,则Payload中不得存在密码字段。
“此版本的协议允许发送不带用户名的密码,而MQTT v3.1.1没有。这反映了密码对密码以外的凭据的常见使用。”(OASIS标准,3.1.2.9)
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
User Name Flag | Password Flag | Will Retain | Will QoS 2 | Will QoS 1 | Will Flag | Clean Start | Reserved | |
X | 1 | X | X | X | X | X | 0 |
表06-位标志 Password Flag(bit_6)设置为true–MQTT v5.0
User Name flag (bit_7)
最后,在8位上,我们通知服务器我们是否要在Payload中发送用户名(User Name)。与上面的密码标志一样,如果设置了此标志,则 Payload 中必须存在用户名字段。否则,Payload 中不得存在用户名字段。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
User Name Flag | Password Flag | Will Retain | Will QoS 2 | Will QoS 1 | Will Flag | Clean Start | Reserved | |
1 | X | X | X | X | X | X | 0 |
表07-位标志 User Name(bit_7)设置为true–MQTT v5.0
因此,一个具有以下位序列的连接标志字节…
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
User Name Flag | Password Flag | Will Retain | Will QoS 2 | Will QoS 1 | Will Flag | Clean Start | Reserved | |
X | X | 1 | 1 | X | 1 | 1 | 0 |
表08-为Clean Start、Will Flag、Will QoS2和Will Retain设置的位标志–MQTT v5.0
…可以翻译为:为QoS级别为2的新会话打开连接,并准备存储我的 Will 消息并将其作为保留消息发布。顺便说一句,服务器先生,我不需要使用用户名和密码进行身份验证。
如果它能满足我们的意愿,服务器会很乐意回答。它可能能够完全、部分或根本不实现它们。服务器将以CONNACK数据包中的连接原因代码的形式发送其答案。
(CONNACK)获取与连接标志相关的原因代码
MQTT v5.0中有四十四个原因代码(Reason Codes),我们已经在Defines.mqh标题中收集了它们。CONNACK(和其他数据包类型)有一个单独的原因代码作为变量头的一部分,它们被命名为“连接原因代码”(Connect Reason Codes)。
数值 | 十六进制值 | 原因代码名称 | 描述 |
---|---|---|---|
0 | 0x00 | Success | 已接受连接。 |
128 | 0x80 | Unspecified error | 服务器不希望透露失败的原因,或者其他原因代码都不适用。 |
129 | 0x81 | Malformed Packet | CONNECT数据包中的数据无法正确解析。 |
130 | 0x82 | Protocol Error | CONNECT数据包中的数据不符合此规范。 |
131 | 0x83 | Implementation specific error | CONNECT有效,但不被此服务器接受。 |
132 | 0x84 | Unsupported Protocol Version | 服务器不支持客户端请求的MQTT协议版本。 |
133 | 0x85 | Client Identifier not valid | 客户端标识符是一个有效的字符串,但服务器不允许使用。 |
134 | 0x86 | Bad User Name or Password | 服务器不接受客户端指定的用户名或密码 |
135 | 0x87 | Not authorized | 客户端无权连接。 |
136 | 0x88 | Server unavailable | MQTT服务器不可用。 |
137 | 0x89 | Server busy | 服务器正忙,请稍后再试。 |
138 | 0x8A | Banned | 此客户端已被管理方禁止行动,请与服务器管理员联系。 |
140 | 0x8C | Bad authentication method | 不支持该身份验证方法,或者该方法与当前使用的身份验证方法不匹配。 |
144 | 0x90 | Topic Name invalid | Will 主题名称格式正确,但此服务器不接受该名称。 |
149 | 0x95 | Packet too large | CONNECT数据包超出了允许的最大大小。 |
151 | 0x97 | Quota exceeded | 已超过执行或管理方强制限制。 |
153 | 0x99 | Payload format invalid | Will Payload与指定的Payload Format Indicator不匹配。 |
154 | 0x9A | Retain not supported | 服务器不支持保留邮件,而“Will Retain”设置为1。 |
155 | 0x9B | QoS not supported | 服务器不支持Will QoS中设置的QoS。 |
156 | 0x9C | Use another server | 客户端应暂时使用另一台服务器。 |
157 | 0x9D | Server moved | 客户端应永久使用另一台服务器。 |
159 | 0x9F | Connection rate exceeded | 已超过连接速率限制。 |
表08- 连接原因代码值
该标准明确规定了服务器需要在CONNACK上发送连接原因代码:
“发送CONNACK数据包的服务器必须使用其中一个连接原因代码值[MQTT-3.2.2-8]。”
在这一点上,我们对连接原因代码特别感兴趣。因为在进行沟通之前,我们需要对它们进行检查。它们将告知我们一些服务器功能和限制,如可用的QoS级别和保留消息的可用性。此外,正如您在上表中他们的姓名和描述中所看到的,他们会通知我们CONNECT尝试是否成功。
为了获得原因码,首先,我们需要识别数据包类型,因为我们只对CONNACK数据包感兴趣。
我们将利用这样一个事实,即我们需要一个非常简单的函数来获取数据包类型,来描述我们如何使用测试驱动开发,对该技术进行一些推理,并提供几个简短的示例。您可以在附件中获取所有详细信息。
(CONNACK)识别服务器数据包类型
我们确信,任何MQTT控制数据包的第一个字节都会对数据包的类型进行编码。因此,我们将尽快读取第一个字节,并获得服务器数据包类型。
uchar pkt_type = server_response_buffer[0];
停下,完成,下一个问题。对吗?
当然,这并没有错。代码很清楚,变量的名称也很好,而且它应该是高性能的和轻量级的。
但是等等!将使用我们库的代码应该如何调用此语句?数据包类型将由公共函数调用返回?或者这些信息可以作为实现细节隐藏在私有成员后面?如果它是由函数调用返回的,那么这个函数应该放在哪里?在CPktConnect类里面吗?或者它应该托管在我们的任何头文件中,因为它将被许多不同的类使用?如果它存储在一个私有成员中,它应该放在在哪个类中?
“有不止一种方法可以做到这一点”(TMTOWTD*,There is more than one way to do it)是一个非常流行的缩写词。TDD是另一个非常流行的缩写词,原因各不相同。正如过去一样,这两个缩写词都被滥用和炒作,其中一些甚至成为了“时尚”,TDD就是这样:
__ “I’m tddying, mom! It’s cool.”
但是,创造这些代码的团队是在多年努力解决同一个基本问题后才做到这一点的:如何在提高开发人员生产力的同时编写更高性能、更惯用、更健壮的代码?如何让开发人员专注于必须做的事情,而不是徘徊在可能做的事情上?如何让他们每个人一次只专注于一项任务?如何确保他们所做的不会引入回归错误并破坏系统?
总之,这些缩写词、它们所承载的思想以及它们所推荐的技术,巩固了数百名在软件开发方面具有专业知识的不同个人多年的实践。TDD不是一种理论,而是一种实践。我们可以说,TDD是一种通过将问题分解为其组成部分来缩小范围来解决问题的技术。我们必须确定将使我们前进一步的单一任务。只需一步,经常是一小步。
那么,我们现在的问题是什么?我们需要确定服务器响应是否是CONNACK数据包,就这么简单。因为,根据规范,我们需要阅读CONNACK响应代码来决定下一步要做什么。我的意思是,识别我们从服务器接收的数据包类型作为响应是我们从连接状态前进到发布状态所必需的。
我们如何识别服务器响应是否是CONNACK数据包?嗯,这很容易。它有一个特定的类型,在我们的MQTT.mqh头中编码为枚举,即ENUM_PKT_TYPE。