MQTT 协议学习:005-发布消息 与 对应报文 (PUBLISH、PUBACK、PUBREC、PUBREL)
背景
当有订阅者订阅了有关的主题以后,通过发布消息的消息的动作,可以让订阅者收到对应主题的消息。
根据不同的QoS 等级,通信的动作也略有不同。
PUBLISH – 发布消息 报文
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
- 客户端使用PUBLISH报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。
- 服务端使用PUBLISH报文发送应用消息给每一个订阅匹配的客户端。
客户端使用带通配符的主题过滤器请求订阅时,客户端的订阅可能会重复,因此发布的消息可能会匹配多个过滤器。对于这种情况,服务端必须将消息分发给所有订阅匹配的QoS等级最高的客户端。服务端之后可以按照订阅的QoS等级,分发消息的副本给每一个匹配的订阅者。
收到一个PUBLISH报文时,接收者的动作取决于QoS等级。
如果服务端实现不授权某个客户端发布PUBLISH报文,它没有办法通知那个客户端。它必须按照正常的QoS规则发送一个正面的确认,或者关闭网络连接。
PUBLISH 固定头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文类型 (0x3) | DUP | QoS | RETAIN | ||||
0 | 0 | 1 | 1 | X | X | X | X | |
byte 2... | 剩余长度 |
下面我们直接看各个报文类型标志位
重发标志 DUP
保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重复发送等。
只适用于客户端或服务器端尝试重发PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要满足以下条件:
1)当QoS > 0
2)消息需要回复确认
位置:byte1[3]
如果DUP标志被设置为0:表示这是客户端或服务端第一次请求发送这个PUBLISH报文。对于QoS 0的消息,DUP标志必须设置为0。
如果DUP标志被设置为1:表示这可能是一个早前报文请求的重发。
客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1。
服务端发送PUBLISH报文给订阅者时,收到(入站)的PUBLISH报文的DUP标志的值不会被传播。发送(出站)的PUBLISH报文与收到(入站)的PUBLISH报文中的DUP标志是独立设置的,它的值必须单独的根据发送(出站)的PUBLISH报文是否是一个重发来确定。
非规范评注
接收者收到一个DUP标志为1的控制报文时,不能假设它看到了一个这个报文之前的一个副本。
需要特别指出的是,DUP标志关注的是控制报文本身,与它包含的应用消息无关。当使用QoS 1时,客户端可能会收到一个DUP标志为0的PUBLISH报文,这个报文包含一个它之前收到过的应用消息的副本,但是用的是不同的报文标识符。
服务质量等级 QoS
这个字段表示应用消息分发的服务质量等级保证。
关于 QoS 等级 与 流程可以参考 :《Qos等级 与 会话》
位置:byte1[2:1] 。
QoS值 | Bit 2 | Bit 1 | 描述 |
---|---|---|---|
0 | 0 | 0 | 最多分发一次 |
1 | 0 | 1 | 至少分发一次 |
2 | 1 | 0 | 只分发一次 |
- | 1 | 1 | 保留位 |
PUBLISH报文不能将QoS所有的位设置为1。如果服务端或客户端收到QoS所有位都为1的PUBLISH报文,它必须关闭网络连接。
当QoS设置为1时,客户端或服务器发布消息时,需要得到对方的确认(PUBACK),如果一段时间后没收到PUBACK,那么会再次发送当前消息,并将DUP字段标记为1。
保留标志 RETAIN
用来设定一条消息是否为保留消息;如果是,那么 服务端必须存储这个应用消息和它的服务质量等级(QoS),以便它可以被分发给未来的主题名匹配的订阅者。一个新的订阅建立时,对每个匹配的主题名,如果存在最近保留的消息,它必须被发送给这个订阅者。
参考:《Retained(保留消息) 和LWT(最后遗嘱)》
位置:byte1[0] 。
如果服务端收到一条保留(RETAIN)标志为1的QoS 0消息,它必须丢弃之前为那个主题保留的任何消息。它应该将这个新的QoS 0消息当作那个主题的新保留消息,但是任何时候都可以选择丢弃它 — 如果这种情况发生了,那个主题将没有保留消息。有关存储状态的更多信息见 4.1节。
服务端发送PUBLISH报文给客户端时,如果消息是作为客户端一个新订阅的结果发送,它必须将报文的保留标志设为1 [MQTT-3.3.1-8]。当一个PUBLISH报文发送给客户端是因为匹配一个已建立的订阅时,服务端必须将保留标志设为0,不管它收到的这个消息中保留标志的值是多少。
保留标志为1且有效载荷为零字节的PUBLISH报文会被服务端当作正常消息处理,它会被发送给订阅主题匹配的客户端。此外,同一个主题下任何现存的保留消息必须被移除,因此这个主题之后的任何订阅者都不会收到一个保留消息。当作正常 意思是现存的客户端收到的消息中保留标志未被设置。服务端不能存储零字节的保留消息。
如果客户端发给服务端的PUBLISH报文的保留标识为0,服务端不能存储这个消息也不能移除或替换任何现存的保留消息。
通俗来讲:之前发的消息A是带有保留标识位(值1)的,如果此后又发了消息B(没有带保留标志位的),那么保留消息还是A(而不是B)。
对于发布者不定期发送状态消息这个场景,保留消息 常用在 新的订阅者希望会收到某主题最近的状态。
PUBLISH 的 可变头
可变报头按顺序包含主题名(Topic Name)
和报文标识符(Packet Identifier)
。
主题名 Topic Name
主题名(Topic Name)用于识别有效载荷数据应该被发布到哪一个信息通道。
主题名必须是PUBLISH报文可变报头的第一个字段。
二进制位 | 7-0 |
---|---|
byte 1 | 字符串长度的最高有效字节(MSB) |
byte 2 | 字符串长度的最低有效字节(LSB) |
byte 3 … | 如果长度大于0,这里是UTF-8编码的字符数据。 |
PUBLISH报文中的主题名不能包含通配符。
服务端发送给订阅客户端的PUBLISH报文的主题名必须匹配该订阅的主题过滤器。
报文标识符 Packet Identifier
只有当QoS等级是1或2时,报文标识符(Packet Identifier)字段才能出现在PUBLISH报文中。
报文标识符用来区分报文,特别是在重发的报文中用来标识是否是同一个报文,并在需要应答的场景中用于确定是对哪个发送报文的应答。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里(占用2个字节)。这些报文是:
PUBLISH(QoS > 0时)
, PUBACK
,PUBREC
,PUBREL
,PUBCOMP
,SUBSCRIBE,
SUBACK
,UNSUBSCRIBE
,UNSUBACK
。
Bit | 7 - 0 |
---|---|
byte 1 | 报文标识符 MSB |
byte 2 | 报文标识符 LSB |
Packet ID默认是从1(0x01)开始并自增,最大为255(0xff)。
SUBSCRIBE
,UNSUBSCRIBE
和PUBLISH(QoS大于0)
控制报文必须包含一个非零的16位报文标识符(Packet Identifier)。
- 客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符。
- 如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。
当客户端处理完这个报文对应的确认(ACK, CMP)后,这个报文标识符就释放可重用。
例如:QoS 1的PUBLISH对应的是
PUBACK
,QoS 2的PUBLISH对应的是PUBCOMP
,与SUBSCRIBE或UNSUBSCRIBE对应的分别是SUBACK
或UNSUBACK
。
发送一个QoS 0的PUBLISH报文时,相同的条件也适用于服务端。
QoS等于0的PUBLISH报文不能包含报文标识符。
PUBACK
, PUBREC
, PUBREL
报文必须包含与最初发送的PUBLISH报文相同的报文标识符。类似地,SUBACK
和UNSUBACK
必须包含在对应的SUBSCRIBE和UNSUBSCRIBE报文中使用的报文标识符。
PUBLISH 的 有效载荷
有效载荷包含将被发布的应用消息
,即:数据的内容和格式是应用特定的。
包含零长度有效载荷的PUBLISH报文是合法的。
有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。
实际上,应该说是
固定报头中的剩余长度字段的值 = 可变报头的长度 + 有效荷载
因为只有确定了可变头与有效荷载的长度,才可以计算固定报头中的剩余长度的值。
PUBLISH报文的 响应
PUBLISH 报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应,见下面表格的描述。
表格 3.4 – PUBLISH报文的预期响应
服务质量等级 | 预期响应 |
---|---|
QoS 0 | 无响应 |
QoS 1 | PUBACK报文 |
QoS 2 | PUBREC报文 |
PUBLISH 响应
不同的Qos 等级会导致不同的通信流程。
关于 QoS 等级 与 流程可以参考 :《Qos等级 与 会话》
PUBLISH报文 中的 Packet Identifier 是什么,下面 的 Packet Identifier便是什么。
PUBACK - 发布确认报文 (QoS 1)
PUBACK 报文 比较简单,它是对QoS 1等级的PUBLISH报文的响应。
PUBACK 报文的 组成 (没有 有效载荷) = 一个固定头(0x40 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREC – 发布收到报文 (QoS 2,第一步)
PUBREC报文是对QoS等级2的PUBLISH报文的响应。
PUBREC 报文的 组成 (没有 有效载荷) = 一个固定头(0x50 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREL – 发布释放(QoS 2,第二步)
PUBREL报文是对PUBREC报文的响应。
PUBREL 报文的 组成 (没有 有效载荷) = 一个固定头(0x52 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
PUBREL控制报文固定报头的第3,2,1,0位是保留位,必须被设置为0,0,1,0。
PUBCOMP – 发布完成(QoS 2,第三步)
PUBCOMP报文是对PUBREL报文的响应。
PUBCOMP 报文的 组成 (没有 有效载荷) = 一个固定头(0x70 0x02) + Packet Identifier (from PUBLISH's Packet Identifier)。
MQTT 协议学习:005-发布消息 与 对应报文 (PUBLISH、PUBACK、PUBREC、PUBREL)的更多相关文章
- MQTT 协议学习:002- 通信报文的构成
背景 之前工作中参与有关协议调试的时候,发现对于协议帧的解析是比较重要的. 参考:<MQTT协议 -- 消息报文格式>.<基于STM32实现MQTT>.<MQTT协议从服 ...
- MQTT 协议学习:004-MQTT建立通信与 CONNECT 、CONNACK 报文
背景 上一讲 MQTT 协议学习:通信报文的构成介绍了在MQTT通信中,各报文的通信流程:从本讲开始,我们开始介绍实际中使用的报文,以及它们的组成. CONNECT - 连接请求 报文 客户端到服务端 ...
- MQTT 协议学习: 总结 与 各种定义的速查表
背景 经过几天的学习与实操,对于MQTT(主要针对 v3.1.1版本)的学习告一段落,为了方便日后的查阅 本文链接:<MQTT 协议学习: 总结 与 各种定义的速查表> 章节整理 MQTT ...
- MQTT 协议学习:000-有关概念入门
背景 从本章开始,在没有特殊说明的情况下,文章中的MQTT版本均为 3.1.1. MQTT 协议是物联网中常见的协议之一,"轻量级物联网消息推送协议",MQTT同HTTP属于第七层 ...
- MQTT协议学习总结
一.MQTT介绍 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通 ...
- MQTT 协议学习:006-订阅主题 与 对应报文(SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK)
背景 之前我们提到了怎么发布消息对应的报文:现在我们来看,订阅一个主题的报文是怎么样的. SUBSCRIBE - 订阅主题 客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅.每个订阅注册 ...
- MQTT 协议学习:007-Keep Alive 连接保活 与 对应报文(PINGREQ、PINGRESP)
背景 keep alive 是 CONNECT 报文中可变头的一部分. 我们提到过 Broker 需要知道 Client 是否非正常地断开了和它的连接,以发送遗愿消息.实际上 Client 也需要能够 ...
- MQTT 协议学习: QoS等级 与 会话
背景 QoS 等级 与 通信的流程有关,直接影响了整个通信.而且篇幅比较长,所以我觉得应该单独拎出来讲一下. 概念 QoS 代表了 服务质量等级. 设置上,由2 位 的二进制控制,且值不允许为 3(0 ...
- MQTT 协议学习:003-MQTT通信流程介绍
背景 有关博文:通信报文的构成 . 上一讲说到可变头与消息体要结合不同的报文类型才能够进行分析(实际上,官方的文档的介绍顺序就是这样的) 那么,我们就来具体看看有关的报文类型. 在此之前 我们捋一捋完 ...
随机推荐
- 一个Android音频文本同步的英文有声读物App的开发过程
转发: http://segmentfault.com/a/1190000003498111 “新概念英语”.“可可英语”.“亚马逊的audible有声书”.“扇贝听力”是我目前所知道的实现英文语音和 ...
- 获取一个元素距离顶部的位置和window的滚动值
获取一个元素距离顶部的位置: $(".box").offset().top; 获取window的滚动值: $(window).scrollTop();
- 5(计算机网络)从物理层到MAC层
故事就从我的大学宿舍开始讲起吧.作为一个八零后,我要暴露年龄了. 我们宿舍四个人,大一的时候学校不让上网,不给开通网络.但是,宿舍有一个人比较有钱,率先买了一台电脑.那买了电脑干什么呢? 首先,有单机 ...
- redhat 7.6 find 命令
1.按名字查找 find ./ -name filename //精确查找 ,./ 代表当前目录 -name 查询名称 filename具体文件名称 find ./ -na ...
- 洗牌函数[打乱数组的顺序] slice()的新运用 [原来arr.slice(start, end) 的start不是必需的]
function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1) + min) } functio ...
- Day11 - H - Euclid's Game HDU - 1525
Two players, Stan and Ollie, play, starting with two natural numbers. Stan, the first player, subtra ...
- 那些年我们踩过的坑,SQL 中的空值陷阱!
文章目录 NULL 即是空 三值逻辑 空值比较 NOT IN 与空值 函数与空值 DISTINCT.GROUP BY.UNION 与空值 ORDER BY 与空值 空值处理函数 字段约束与空值 SQL ...
- 配置web应用全局的错误页面
- c++ char* 与LPCTSTR相互转化
] = "wo shi ni baba"; , , ch, -, NULL, ); wchar_t *wide = new wchar_t[num]; MultiByteToWid ...
- 一、Servlet之14道面试题
1.什么是Servlet Servlet是用Java编写的服务端程序,它与协议和平台无关,运行于支持Java的应用服务器中,Java Servlet可以动态的扩展服务器的能力,并采用请求-响应模 ...