操作行为

4.1 状态存储 Storing state

客户端和服务端必须存储会话状态以保证服务质量。==客户端和服务端必须在会话持续的所有时间内都存储会话状态。==[MQTT-4.1.0-1]。==只要有活动的网络连接,会话就必须保持。== [MQTT-4.1.0-2]

保留消息不构成服务器中会话状态的一部分。服务端应该保留这些消息直到客户端删除它。

非规范性注解 客户端和服务端的存储容量必然是有限的,还可能要受管理策略的限制,比如会话状态在网络连接之间的最大存储时间。包括对定义条件的自动响应在内的管理员级别的操作可以丢弃已经存储的会话状态,这会造成会话终止。这些操作可能是由资源限制或其他操作原因引发的。请慎重的评估客户端和服务端的存储容量以确保它们是充足的。 非规范性注解 硬软件故障可能会对客户端或服务端存储的会话状态造成数据损坏或者丢失。 非规范性注解 服务器和客户端操作正常可能意味着,已保存的状态丢失或损坏是管理操作或软硬件故障造成的。管理操作可能是对某个预定义条件的自动响应。这些操作可能是资源限制或其他操作原因引发的。例如,服务端可能会基于外部条件,决定不再将某个消息或某些消息分发给任何当前的或以后的客户端。 非规范性注解 MQTT用户应该评估MQTT客户端和服务端实现的存储容量,确保能满足需求。

4.1.1 非规范示例 Non normative example

例如,想要收集电表读数的用户可能会决定使用QoS 1等级的消息,因为他们需要防止数据在网络传输中丢失。但是,他们可能已经却确定电源数据是足够可靠的,所以客户端和服务端的数据可以存储在易失性存储器中而不存在太大丢失风险。

与之相反,停车计费支付应用的提供商可能要求任何情况下都不能让支付信息丢失。因此他们要求在通过网络传输之前,所有的数据必须强制写入非易失性存储器中。

4.2 网络连接 Network Connections

MQTT协议需要一个能提供有序、无损、双向字节流的底层传输协议。

非规范性注解 MQTT3.1使用的传输协议是 [RFC793] 定义的TCP/IP。TCP/IP也可用于MQTT 3.1.1。下面的协议也支持:

非规范性注解 IANA注册了TCP的8883和1883端口,分别用于MQTT的TLS和非TLS通信。

像用户数据报协议(UDP)一样的无连接网络传输协议是不合适的,因为它们可能会丢失或者重排序数据。

4.3 服务质量等级和协议流程 Quality of Service levels and protocol flows

MQTT按照这里定义的服务质量 (QoS) 等级分发应用消息。分发协议是对称的,在下面的描述中,客户端和服务端既可以是发送者也可以是接收者。分发协议仅涉及从单个发送者到单个接收者的应用消息。服务端分发应用消息给多个客户端时,每个客户端独都是独立处理。分发给客户端的出站应用消息和入站应用消息的QoS等级可能是不同的。

下面的非规范流程图展示了可能的实现方法。

4.3.1 QoS 0:最多交付一次

消息依赖于底层网络的功能进行传递。接收者不会发送响应,发送者也不会重试。消息可能送达一次也可能根本没送达。

==对于QoS 0的分发协议,发送者==

  • ==必须发送QoS等于0,DUP等于0的PUBLISH报文 [MQTT-4.3.1-1]==。

对于QoS 0的分发协议,接收者

  • 接受PUBLISH报文时同时接受消息的所有权。

图例 4.1 – 非规范示例的QoS 0协议流程图,

发送者操作 Sender Action

控制报文 Control Packet

接收者操作 Receiver Action

PUBLISH QoS 0, DUP=0

---------->

将应用消息分发给合适的收件人 Deliver Application Message to appropriate onward recipient(s)

4.3.2 QoS 1: 至少交付一次

该服务质量等级确保消息至少送达一次。QoS 1的PUBLISH报文的可变报头中包含一个报文标识符,需要PUBACK报文确认。第2.3.1节提供了有关报文标识符的更多信息。

==在QoS 1的分发协议中,发送者==

  • ==每次发送新的应用消息都必须分配一个未使用的报文标识符。==

  • ==发送的PUBLISH报文必须包含QoS=1,DUP=0的报文标识符。==

  • ==必须将这个PUBLISH报文看作是“尚未确认的” ,直到从接收者那收到对应的PUBACK报文。4.4节有一个关于未确认消息的讨论。== [MQTT-4.3.2-1]

一旦发送者收到PUBACK报文,该报文标识符就可以重用。

注意:允许发送者在等待确认时使用不同的报文标识符发送后续的PUBLISH报文。

==对于QoS 1的分发协议,接收者==

  • ==必须使用PUBACK报文响应。该报文包含传入的的PUBLISH报文的报文标识符并已经接管了应用消息的所有权。==

  • ==发送了PUBACK报文之后,接收者必须将任何包含相同报文标识符的入站PUBLISH报文当作一个新的发布消息,无论它的DUP的设定值。==[MQTT-4.3.2-2].

图例 4.2 – QoS 1协议流程图,非规范示例

发送者操作 Sender Action

控制报文 Control Packet

接收者操作 Receiver Action

存储消息 Store message

Send PUBLISH QoS 1, DUP 0,

---------->

<----------

开始传送应用消息1 Initiate onward delivery of the Application Message1

丢弃消息 Discard message

1在发送PUBACK之前,接收者不需要完成应用消息的分发。原来的发送者收到PUBACK报文之后,应用消息的所有权就会转移给这个接收者。

4.3.3 QoS 2: 恰好分发一次

这是最高等级的服务质量,适用于消息既不能丢失也不能重复的情况。使用这个服务质量等级会带来额外的开销。

QoS 2的消息可变报头中有报文标识符。第2.3.1节提供了有关报文标识符的更多信息。QoS 2等级的PUBLISH报文的接收者使用两步确认过程来确认接收。

==在QoS 2的分发协议中,发送者==

  • ==必须给要发送的新应用消息分配一个未使用的报文标识符。==

  • ==发送的PUBLISH报文包含QoS = 2,DUP = 0的数据包标识符==

  • ==必须将这个PUBLISH报文看作是 未确认的 ,直到从接收者那收到对应的PUBREC报文。有关未确认消息的详细内容请参见第4.4节。==

  • ==收到PUBREC报文后必须发送一个PUBREL报文。PUBREL报文必须包含与原始PUBLISH报文相同的报文标识符。==

  • ==必须将这个PUBREL报文看作是 未确认的 ,直到从接收者那收到对应的PUBCOMP报文。==

  • ==一旦发送了对应的PUBREL报文就不能重发这个PUBLISH报文。==

[MQTT-4.3.3-1].

一旦发送者收到PUBCOMP报文,这个报文标识符就可以重用。

注意:允许发送者在等待服务端确认时使用不同的报文标识符发送后续的PUBLISH报文。

==对于QoS 2的分发协议,接收者==

  • ==必须使用PUBREC报文响应。该报文包含传入的的PUBLISH报文的报文标识符并已经接管了应用消息的所有权。==

  • ==在收到对应的PUBREL报文之前,接收者必须发送PUBREC报文确认后续是否有任何具有相同标识符的PUBLISH报文。 在这种情况下,它不能重发消息给之前的接收者。==

  • ==响应PUBREL报文的PUBCOMP报文必须包含与PUBREL报文相同的标识符。==

  • ==发送PUBCOMP报文之后,接收者必须将包含相同报文标识符的任何后续PUBLISH报文当作一个新的发布。==

[MQTT-4.3.3-2].

图例4.3-非规范示例的QoS2协议流程图

发送者操作 Sender Action

控制报文 Control Packet

接收者操作 Receiver Action

存储消息 Store message

PUBLISH QoS 2, DUP 0

---------->

存储消息或存储报文标识符之后发起之前应用消息的分发 Method A, Store message or Method B, Store then Initiate onward delivery of the Application Message1

PUBREC

<----------

丢弃消息,存储收到的PUBREC报文的报文标识符 Discard message, Store PUBREC received

PUBREL

---------->

---------->

发起应用消息的分发之后丢弃消息或者丢弃收到报文标识符 Method A, Initiate onward delivery of the Application Message1 then discard message1 or Method B, Discard

Send PUBCOMP

<----------

丢弃消息状态 Discard stored state

1 不要求接收者在发送PUBRECPUBCOMP之前完成分发应用消息。原始发送者收到PUBREC报文之后,应用消息的所有权就会转移给这个接收者。 >

图例4.3–QoS 2协议流程图,非规范示例 展示了接收者对QoS 2等级消息的两种处理方法。他们的区别是消息开始后续分发的时间点不同。两种方法都是特定与不同实现方法的。只要选择两种中一种就不会影响QoS2级别的流程的进行。

4.4 消息分发重试 Message delivery retry

==清理会话标志为0的客户端重连时,客户端和服务端必须使用原始的报文标识符重发任何未确认的PUBLISH报文(如果QoS>0)和PUBREL报文== [MQTT-4.4.0-1]。这是唯一要求客户端或服务端重发消息的情况。

非规范性注解 由于历史原因,需要重发控制报文才能解决某些早期TCP网络的数据丢包问题。部署在这些环境中的MQTT 3.1.1时,可能仍然需要关注这个问题。

4.5 消息收到 Message receipt

==服务端接管入站应用消息的所有权时,它必须将消息添加到匹配订阅的客户端的会话状态中。匹配规则定义见第4.7节== [MQTT-4.5.0-1]

正常情况下,客户端会收到对它发出的订阅的响应消息。客户端也可能会收到不匹配它的订阅的消息。如果服务端自动给客户端分配了一个订阅,可能发生这种情况。客户端正在处理UBSUBSCRIBE请求时也可能收到消息。==客户端必须按照可用的QoS规则确认它收到的PUBLISH报文,不论它是否选择处理报文包含的应用消息== [MQTT-4.5.0-2]

4.6 消息排序 Message ordering

==实现本章定义的协议流程时,客户端必须遵循下列规则:==

  • ==重发任何PUBLISH报文时,必须按原始PUBLISH报文的发送顺序重发(适用于QoS 1和QoS 2消息)==[MQTT-4.6.0-1]

  • ==必须按照对应的PUBLISH报文的顺序发送PUBACK报文(QoS 1消息)==[MQTT-4.6.0-2]

  • ==必须按照对应的PUBLISH报文的顺序发送PUBREC报文(QoS 2消息)==[MQTT-4.6.0-3]

  • ==必须按照对应的PUBREC报文的顺序发送PUBREL报文(QoS 2消息)== [MQTT-4.6.0-4]

==服务端必须默认每个主题都是“有序主题”。它可以提供一个管理或其它机制,以允许将一个或多个主题当作是“无序主题”== [MQTT-4.6.0-5]

==服务端处理发送给有序主题的消息时,必须遵循上面的规则将消息分发给每个订阅者。此外,它必须按照从客户端收到的顺序发送PUBLISH报文给消费者(对相同的主题和QoS)==[MQTT-4.6.0-6]

非规范性注解 上面列出的规则确保,使用QoS 1发布和订阅的消息流,订阅者按照消息发布时的顺序收到每条消息的最终副本,但是消息可能会重复,这可能导致在它的后继消息之后收到某个已经收到消息的重发版本。例如,发布者按顺序1,2,3,4发送消息,订阅者收到的顺序可能是1,2,3,2,3,4。 如果客户端和服务端能保证任何时刻最多有一条消息在“正在传输中”(在某条消息被确认前不发送后面的那条消息),则就不会有QoS 1的消息会在它的任何后续消息之后收到。 例如,订阅者收到的顺序可能是1,2,3,3,4,而不是1,2,3,2,3,4。将传输窗口设为1意味着,在同一个主题上,即使发布者发送了一系列不同QoS等级的消息,它们的顺序也被保留。

4.7 主题名和主题过滤器 Topic Names and Topic Filters

4.7.1 主题通配符 Topic wildcards

主题级别分隔符用于使主题名结构化。如果存在分隔符,它将主题名分割为多个“主题级别” 。

订阅的主题过滤器可以包含特殊的通配符,允许你一次订阅多个主题。

==主题过滤器中可以使用通配符,但是主题名不能使用通配符== [MQTT-4.7.1-1]

4.7.1.1 主题级别分隔符 Topic level separator

正斜杠(‘/’ U+002F)用于分隔主题的每个级别,并为主题名提供一个分层结构。当订阅客户端指定的主题过滤器遇到两组通配符中任意一组时,使用主题级别分隔符就显得十分中重要了。主题级别分隔符可以出现在主题过滤器或主题名的任何位置。相邻的主题层次分隔符表示一个零长度的主题级别。

4.7.1.2 多级通配符 Multi-level wildcard

数字标志(‘#’ U+0023)是可以匹配主题中任意级别的通配符。多级通配符可以表示父级和任意数量的子级别。==多级通配符必须位于它自己的级别或者跟在主题级别分隔符后面。不管哪种情况,它都必须是主题过滤器的指定的最后一个字符== [MQTT-4.7.1-2]

非规范性注解 例如,如果客户端订阅主题 “sport/tennis/player1/#”,它会收到使用下列主题名发布的消息:

  • “sport/tennis/player1”

  • “sport/tennis/player1/ranking”

  • “sport/tennis/player1/score/wimbledon”

非规范性注解

  • “sport/#”也匹配单独的 “sport” ,因为#包括它的父级。

  • “#”是有效通配符并会收到所有的应用消息。

  • “sport/tennis/#”也是有效的。

  • “sport/tennis#”是无效的。

  • “sport/tennis/#/ranking”是无效的。

4.7.1.3 单级通配符 Single level wildcard

加号 (‘+’ U+002B) 是仅能匹配一个主题级别的通配符。

==在主题过滤器的任意级别都可以使用单级通配符,包括第一个和最后一个级别。然而在使用的级别,它必须占据过滤器的整个级别==[MQTT-4.7.1-3]。可以在主题过滤器中的多个级别中使用它,并可以可以和多级通配符一起使用。

非规范性注解 例如, “sport/tennis/+” 匹配 “sport/tennis/player1” 和 “sport/tennis/player2” ,但是不匹配 “sport/tennis/player1/ranking” 。同时,由于单级通配符只能匹配一个级别, “sport/+” 不匹配 “sport” 但是却匹配 “sport/”。 非规范性注解

  • “+” 有效通配符。

  • “+/tennis/#” 有效通配符。

  • “sport+” 无效通配符。

  • “sport/+/player1” 有效通配符。

  • “/finance” 匹配 “+/+” 和 “/+” ,但是不匹配 “+”。

4.7.2 以$开头的主题 Topics beginning with $

==服务端不能将$字符开头的主题名匹配以通配符 (#或+) 开头的主题过滤器== [MQTT-4.7.2-1]。服务端应该阻止客户端使用这种主题名与其它客户端交换消息。服务端实现可以将$开头的主题名用作其他目的。

非规范性注解

  • $SYS/ 被广泛用作包含服务器特定信息或控制API的主题的前缀。

  • 应用不能将$字符开头的主题用于自身。

非规范性注解

  • 订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息。

  • 订阅 “+/monitor/Clients” 的客户端不会收到任何发布到 “$SYS/monitor/Clients” 的消息。

  • 订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息。

  • 订阅 “$SYS/monitor/+” 的客户端会收到发布到 “$SYS/monitor/Clients” 主题的消息。

  • 如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以$开头主题的消息,它需要同时订阅 “#” 和 ““$SYS/#”。

4.7.3 主题语义和用法 Topic semantic and usage

主题名和主题过滤器必须符合下列规则:

  • ==所有的主题名和主题过滤器都必须有一个字符长度== [MQTT-4.7.3-1]

  • 主题名和主题过滤器是区分大小写的。

  • 主题名和主题过滤器可以包含空格字符。

  • 主题名或主题过滤器以前置或后置斜杠 “/” 区分。

  • 只包含斜杠 “/” 的主题名或主题过滤器是合法的。

  • ==主题名和主题过滤器不能包含空字符 (Unicode U+0000)== [Unicode] [MQTT-4.7.3-2]

  • ==主题名和主题过滤器是UTF-8编码字符串,它们不能超过65535字节== [MQTT-4.7.3-3]。见 1.5.3节。

除了不能超过UTF-8编码字符串的长度限制之外,主题名或主题过滤器的级别数量没有其它限制。

==匹配订阅时,服务端不能对主题名或主题过滤器执行任何规范化处理,也不能修改或替换任何未识别的字符== [MQTT-4.7.3-4]。主题过滤器中的每个非通配符级别需要逐字符匹配命中主题名中对应的级别才能匹配成功。

非规范性注解 使用UTF-8编码规则意味着,可以通过比较UTF-8编码字节或者解码后的Unicode字符来实现主题过滤器和主题名的比较. 非规范性注解

  • “ACCOUNTS” 和 “Accounts” 是不同的主题名。

  • “Accounts payable” 是有效的主题名

  • “/finance” 和 “finance” 是不同的。

应用消息会分发给匹配的客户端,匹配规则是,客户端订阅的主题过滤器与应用消息的主题名相匹配。主题资源可能是管理员在服务端预先定义好的,也可能是服务端收到第一个订阅或使用那个主题名的应用消息时动态创建的。 服务端可以使用安全组件来对给定客户端的主题资源进行选择性授权操作.

4.8 错误处理 Handling errors

==除非另有说明,如果服务端或客户端遇到了协议违规的行为,必须关闭传输导致协议违规的控制报文的网络连接== [MQTT-4.8.0-1]

客户端或服务端可能会遇到瞬态错误(例如内部缓冲区满态)导致无法成功处理MQTT报文。

==如果客户端或服务端处理入站控制报文时遇到了瞬态错误,就必须关闭传输那个控制报文的网络连接== [MQTT-4.8.0-2]。==如果服务端发现了瞬态错误,它不应该断开连接或者执行任何对其它客户端有影响的操作==。

Last updated