MQTT协议探究(三)
1 回顾与本次目标
1.1 回顾
- 主题通配符
- 主题语义和用法
- WireShark进行抓包分析了报文
- 报文分析:
- SUBSCRIBE——订阅主题
- SUBACK——订阅确认
- UNNSUBSCRIBE——取消订阅
- UNSUBACK——取消订阅确认
- PUBLISH——发布消息(Qos0,服务质量等级下一节再说吧)
1.2 本节目标
- 服务质量等级
- PUBLISH——发布消息(Qos1 Qos2)
- PUBACK——发布确认
- PUBREC——发布收到
- PUBREL——发布释放
- PUBCOMP——发布完成
2 MQTT控制报文格式(补充)
2.1 控制报文的类型
| 名字 | 值 | 报文流动方向 | 描述 |
|---|---|---|---|
| PUBLISH | 3 | 双向 | 发布消息 |
| PUBACK | 4 | 双向 | QoS 1消息发布收到确认 |
| PUBREC | 5 | 双向 | QoS 2发布收到(保证交付第一步) |
| PUBREL | 6 | 双向 | QoS 2发布释放(保证交付第二步) |
| PUBCOMP | 7 | 双向 | QoS 2消息发布完成(保证交互第三步) |
2.2 标识符
| 控制报文 | 固定报文标志 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|---|
| PUBLISH | Used in MQTT 3.1.1 | DUP | QoS | QoS | RETAIN |
| PUBACK | Reserved | 0 | 0 | 0 | 0 |
| PUBREC | Reserved | 0 | 0 | 0 | 0 |
| PUBREL | Reserved | 0 | 0 | 1 | 0 |
| PUBCOMP | Reserved | 0 | 0 | 0 | 0 |
- DUP = 控制报文的重复分发标志
- Qos = PUBLISH报文的服务质量等级
- RETAIN = PUBLISH报文是否保留标志
2.3 报文标识符
- Client每次发送一个新的报文时都必须分配一个未使用的报文标识符。
- Client如果进行重发报文必须使用相同的标识符。
- 当Client处理完这个报文对应的确认后,这个报文标识符就释放可重用。
- QoS 1的PUBLISH对应的是PUBACK
- QoS 2的PUBLISH对应的是PUBCOMP
| 控制报文 | 报文标识符字段 |
|---|---|
| PUBLISH | 需要(如果QoS > 0,Qos=0时不能带) |
| PUBACK | 需要 |
| PUBREC | 需要 |
| PUBREL | 需要 |
| PUBCOMP | 需要 |
2.4 有效载荷
| 控制报文 | 有效载荷 |
|---|---|
| PUBLISH | 可选(允许发空负载) |
| PUBACK | 不需要 |
| PUBREC | 不需要 |
| PUBREL | 不需要 |
| PUBCOMP | 不需要 |
3 服务质量等级和协议流程
3.1 概述
- 分发协议是对称的,客户端和服务端既可以是发送者也可以是接收者。
- 分发协议关注的是从单个发送者到单个接收者的应用消息。
- 服务端分发应用消息给多个客户端时,每个客户端独立处理。
- 分发给客户端的出站应用消息和入站应用消息的QoS等级可能是不同的。
3.2 QoS 0:最多分发一次
- 接收者不会发送响应(无需确认送达),发送者也不会重试(DUP=0)
- 发送者必须发送QoS=0,DUP=0的PUBLISH报文
- 协议流程:
| 发送者 | 方向 | 接收者 |
|---|---|---|
| PUBLISH报文 Qos=0 DUP=0 | ||
| -------------------> | ||
| 分发应用消息给适当的后续接收者(们) |
3.3 QoS 1: 至少分发一次
- QoS 1的PUBLISH报文的可变报头中包含一个报文标识符,需要PUBACK报文(带上报文标识符)确认。
- 发送者:
- 新消息都必须分配一个未使用的报文标识符(收到PUBACK时,该报文标识符可以重用)。
- 发送的PUBLISH报文必须包含报文标识符且QoS=1,DUP=0(不重发)。
- 必须将这个PUBLISH报文看作是未确认的 ,直到从接收者那收到对应的PUBACK报文。
- 接收者:
- 响应的PUBACK报文必须包含一个报文标识符 ,这个标识符来自接收到的、已经接受所有权的PUBLISH报文。
- 发送了PUBACK报文之后,接收者必须将任何包含相同报文标识符的入站PUBLISH报文当作一个新的消息,并忽略它的DUP标志的值。
- 协议流程:
| 发送者 | 方向 | 接收者 |
|---|---|---|
| 存储消息 | ||
| PUBLISH报文 QoS=1, DUP=0 报文标识符 | -------------------> | |
| 开始应用消息的后续分发 | ||
| <------------------- | PUBACK报文,带报文标识符 | |
| 丢弃消息 |
3.4 QoS 2: 仅分发一次
消息丢失和重复都是不可接受的
消息可变报头中有报文标识符
发送者:
- 新消息都必须分配一个未使用的报文标识符(收到PUBCOMP时,该报文标识符可以重用)。
- PUBLISH报文必须包含报文标识符且报文的QoS=2,,DUP=0。
- 必须将这个PUBLISH报文看作是未确认的 ,直到从接收者那收到对应的PUBREC报文。
- 收到PUBREC报文后必须发送一个PUBREL报文(报文标识符)。
- 必须将这个PUBREL报文看作是 未确认的 ,直到从接收者那收到对应的PUBCOMP报文。
- 一旦发送了对应的PUBREL报文就不能重发这个PUBLISH报文。
接收者:
- 响应的PUBREC PUBREL PUBCOMP报文必须包含相同的报文标识符
协议流程图:Client -> Server
| 发送者 | 方向 | 接收者 |
|---|---|---|
| 存储消息 | ||
| 发送PUBLISH报文,Qos2,DUP=0,带报文标识符 | ||
| -> | ||
| 方法A:存储消息 方法B:存储报文标识符,开始向前分发这个应用消息 |
||
| 发送PUBREC报文,带报文标识符 | ||
| <- | ||
| 丢弃消息,存储PUBREC中的报文标识符 | ||
| 发送PUBREL报文,带报文标识符 | ||
| -> | ||
| 方法A:开始向前分发应用消息并丢弃 方法B:丢弃报文标识符 |
||
| 发送PUBCOMP报文,带报文标识符 | ||
| <- | ||
| 丢弃已保存的报文标识符 |
- 协议流程图:Server -> Subscriber(来自:http://www.blogjava.net/yongboy/archive/2014/02/15/409893.html)
| Server | Message and direction | Subscriber |
|---|---|---|
| QoS = 2 DUP = 0 Message ID = x |
PUBLISH ------> |
Action: Store message |
PUBREC <------- |
Message ID = x | |
| Message ID = x | PUBREL -------> |
Actions:Make message available |
PUBCOMP <----- |
Message ID = x |
4 MQTT控制报文示例
4.1 PUBLISH – 发布消息
(1)WireShark抓包获取报文
# Qos1
MQ Telemetry Transport Protocol, Publish Message
Header Flags: 0x32 (Publish Message)
0011 .... = Message Type: Publish Message (3)
.... 0... = DUP Flag: Not set
.... .01. = QoS Level: At least once delivery (Acknowledged deliver) (1)
.... ...0 = Retain: Not set
Msg Len: 18
Topic Length: 4 # 0x0004
Topic: TEST
Message Identifier: 1 # 0x0001,报文标识符
Message: HelloWorld # 18 - 2 - 4 - 2 = 10个字节
# Qos2
MQ Telemetry Transport Protocol, Publish Message
Header Flags: 0x34 (Publish Message)
0011 .... = Message Type: Publish Message (3)
.... 0... = DUP Flag: Not set
.... .10. = QoS Level: Exactly once delivery (Assured Delivery) (2)
.... ...0 = Retain: Not set
Msg Len: 18
Topic Length: 4
Topic: TEST
Message Identifier: 2
(2)固定报头
重发标志:DUP为0,代表Client第一次发这个报文;DUP为1,代表Client重发已发的报文。对于Qos为0时,DUP必须为0。
服务质量等级
| Qos | Bit2 | Bit1 | 描述 |
|---|---|---|---|
| 0 | 0 | 0 | 最大分发一次 |
| 1 | 0 | 1 | 至少一次 |
| 2 | 1 | 0 | 只分发一次 |
| - | 1 | 1 | 保留位 |
(3)响应
| 服务质量等级 | 预期响应 |
|---|---|
| Qos0 | 无响应 |
| Qos1 | PUBACK报文 |
| Qos2 | PUBREC报文 |
4.2 PUBACK –发布确认
(1)WireShark抓包获取报文
MQ Telemetry Transport Protocol, Publish Ack
Header Flags: 0x40 (Publish Ack)
0100 .... = Message Type: Publish Ack (4)
.... 0000 = Reserved: 0
Msg Len: 2
Message Identifier: 1 # 报文标识符
(2)概述
- PUBACK报文只需要表明接收者收到了某条PUBLISH报文即可。
- Client设置CleanSession=0重连时,Client和Server必须使用原始的报文标识符重发未确认的PUBLISH报文。
- 因为存在重发和报文标志符的复用(Receiver返回PUBACK后,即使有相同的报文标识符,也认为是新的PUBLISH报文),所以可能存在转发超过一次(Qos1只保证至少1次)的情况。
4.3 PUBREC – 发布收到(QoS 2,第一步)
(1)WireShark抓包获取报文
MQ Telemetry Transport Protocol, Publish Received
Header Flags: 0x50 (Publish Received)
0101 .... = Message Type: Publish Received (5)
.... 0000 = Reserved: 0
Msg Len: 2
Message Identifier: 2
(2)概述
- 3.4节提到Reciver收到Qos2的PUBLISH报文后,可以有两种处理方式
- A:存储消息
- B:存储报文标识符(不再分发相同报文标识符的PUBLISH报文),开始分发这个应用消息
- PUBREC报文是为了告诉Sender收到了消息
- 其实PUBREC与PUBACK作用相同,表示Receiver已收到消息,所以可以把存储在Sender的消息删除(消息的所有权转让)
4.4 PUBREL – 发布释放
(1)WireShark抓包获取报文
MQ Telemetry Transport Protocol, Publish Release
Header Flags: 0x62 (Publish Release)
0110 .... = Message Type: Publish Release (6)
.... 0010 = Reserved: 2
Msg Len: 2
Message Identifier: 2
(2)概述
- Sender收到PUBREC之前,Sender仍然可能在发送相同报文标识符的PUBLISH报文,所以收到PUBREC之后需要把报文标识符记录下来(不再发送该报文标识符的PUBLISH报文)。
- Sender发送PUBREL报文是为了通知Receiver,可以开始接收相同报文标识符的PUBLISH报文了(报文标识符的复用)。
- 但Sender在没有收到响应(PUBCOMP)之前,仍然不会去发送相同报文标识符的PUBLISH报文。
4.5 PUBCOMP – 发布完成
(1)WireShark抓包获取报文
MQ Telemetry Transport Protocol, Publish Complete
Header Flags: 0x70 (Publish Complete)
0111 .... = Message Type: Publish Complete (7)
.... 0000 = Reserved: 0
Msg Len: 2
Message Identifier: 2
(2)概述
- Receiver收到了PUBREL报文
- A:开始分发消息并丢弃
- B:丢弃报文标识符(可以接收相同报文标识符的PUBLISH报文)
- 发送PUBCOMP告知Sender,可以发送相同报文标识符的PUBLISH报文了。
MQTT协议探究(三)的更多相关文章
- MQTT协议探究(一)
1 准备阶段 MQTT客户端:https://www.cnblogs.com/linzhanfly/p/9923577.html WireShark MQTT服务器(iot.eclipse.org) ...
- MQTT协议探究(二)
1 回顾与本次目标 1.1 回顾 MQTT控制报文的基本格式 WireShark进行抓包分析了报文 报文分析: CONNECT--连接服务器 CONNACK--确认连接请求 PINGREQ--心跳请求 ...
- WebSocket协议探究(三):MQTT子协议
一 复习和目标 1 复习 Nodejs实现WebSocket服务器 Netty实现WebSocket服务器(附带了源码分析) Js api实现WebSocket客户端 注:Nodejs使用的Socke ...
- WebSocket协议探究(序章)
一 WebSocket协议基于HTTP和TCP协议 与往常一样,进入WebSocket协议学习之前,先进行WebSocket协议抓包,来一个第一印象. WebSocket能实现客户端和服务器间双向.基 ...
- 采用MQTT协议实现android消息推送(1)MQTT 协议简介
1.资料 mqtt官网 http://mqtt.org/ 服务端程序列表 https://github.com/mqtt/mqtt.github.io/wiki/servers 客户端库列表 http ...
- 海鑫智圣:物联网漫谈之MQTT协议
什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...
- 基于MQTT协议进行应用开发
官方协议有句如下的话来形容MQTT的设计思想: "It is designed for connections with remote locations where a "sma ...
- 云巴:基于MQTT协议的实时通信编程模型
概要 有人常问,云巴实时通信系统到底提供了一种怎样的服务,与其他提供推送或 IM 服务的厂商有何本质区别.其实,从技术角度分析,云巴与其它同类厂商都是面向开发者的通信服务,宏观的编程模型都是大同小异, ...
- MQTT协议(一)
MQTT(Message Queue Telemetry Transport),遥测传输协议,提供订阅/发布模式,更为简约.轻量,易于使用,针对受限环境(带宽低.网络延迟高.网络通信不稳定),可以简单 ...
随机推荐
- pwn学习日记Day22 《程序员的自我修养》读书笔记
知识杂项 软连接 命令: ln -s 原文件 目标文件 特征: 1.相当于windows的快捷方式 2.只是一个符号连接,所以软连接文件大小都很小 3.当运行软连接的时候,会根据连接指向找到真正的文件 ...
- Flutter移动电商实战 --(26)列表页_使用Provide控制子类-2
主要实现功能,点击一级分类,二级分类跟着变.这里主要用我们的provide 新建provide provide文件夹下创建:child_category.dart 事件上就是这个实体:BxMallSu ...
- kotlin 泛型约束
fun <T:Comparable<T>> sort(list :List<T>){} 冒号之后指定的类型就是泛型参数的上界,对于泛型参数T,只允许使用Compar ...
- 实时流Streaming大数据:Storm,Spark和Samza
当前有许多分布式计算系统能够实时处理大数据,这篇文章是对Apache的三个框架进行比较,试图提供一个快速的高屋建瓴地异同性总结. Apache Storm 在Storm中,你设计的实时计算图称为top ...
- jExcelAPI 操作 Excel 文件
在开源世界中,有两套比较有影响的API可 供使用,一个是POI,一个是jExcelAPI.其中功能相对POI比较弱一点.但jExcelAPI对中文支持非常好,API是纯Java的, 并不 依赖Wind ...
- Centos 6.8环境下OpenLDAP安装与部署
一.OpenLDAP介绍 LDAP 全称轻量级目录访问协议(英文:Lightweight Directory Access Protocol),是一个运行在 TCP/IP 上的目录访问协议.LDAP实 ...
- [C++]多源最短路径(带权有向图):【Floyd算法(动态规划法)】 VS n*Dijkstra算法(贪心算法)
1 Floyd算法 1.1 解决问题/提出背景 多源最短路径(带权有向图中,求每一对顶点之间的最短路径) 方案一:弗洛伊德(Floyd算法)算法 算法思想:动态规划法 时间复杂度:O(n^3) 形式上 ...
- lua调用的c函数模块命名规则
比如lua中调用C函数 socket.core() , 在c中需要这样命名:luaopen_socket_core(); 基本模式是添加luaopen_前缀,讲.变为_
- 提示/bin/roslyn/csc.exe权限不足
给/bin/roslyn/csc.exe 读取/执行 权限
- IDEA配置编码