Snail 发表于 2017-8-18 15:58:54

使用 Wireshark 深入剖析 MQTT

本帖最后由 Genius 于 2017-8-18 16:32 编辑

MQTT 在 IT 领域扮演着越来越重要的角色,在之前的一篇文章中,我们初步介绍了 MQTT 协议以及它的工作方式。为了更好地理解本文中的内容,请阅读上一篇文章中的机器到机器(M2M)的基础知识。我们将使用 Catchpoint MQTT 监视器从 MQTT 代理订阅并发布一个主题。下图显示了使用 Catchpoint 进行的测试设置。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt1.png

Catchpoint 的 MQTT 监视器的指标包括发布时间、发布大小、订阅时间、订阅大小、DNS 和连接时间。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt2.png
为了更好地了解 MQTT 监视器收集到的数据,就必须知道在 MQTT 的内部到底发生了什么。幸运的是,可以使用 Wireshark 工具帮助我们完成这个深层次的探索。像 Wireshark 这样的嗅探工具可以帮助我们剖析通信过程中的每个步骤,并检查数据包。在本文的分析中,我们将使用Catchpoint MQTT 监视器设置一个样本测试,并查看测试过程中所涉及的一些细节。
Wireshark 分析MQTT 协议基于 TCP/IP 层之上,客户端和代理都需要具有 TCP/IP 协议栈的支持。下图显示了 Wireshark 工具获取到的 Catchpoint MQTT 监视器中同一个 MQTT 代理订阅、发布的消息流。此处我们使用 QoS t0 的传送级别。接下来我们逐个分析一下协议的数据包。
http://blog.catchpoint.com/wp-content/uploads/2017/07/mqtt3.png

注意:在上图中,192.168.0.11 是 MQTT 代理的 IP; 192.168.0.12 是客户端的 IP,它b扮演了发布者和订阅者的双重角色。

1. 连接消息MQTT 连接必须是一个客户端和代理之间,而不能是一个客户端与另一个客户端。客户端通过发送 CONNECT 命令给代理启动建立连接。一旦建立连接,该连接将一直打开,直到从客户端接收到断开连接的命令。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt4.png
目标端口是1883,它是 MQTT 基于TCP 的默认端口。
端口 8883 用于基于 TLS 的 MQTT。连接命令详解:
[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]Connect Flags:连接标志位包含指定 MQTT 连接行为的参数。它能够有效指示连接状态。
[*]Clean session:连接标志的第1位,该标志用来将客户端是否想要建立持久连接的信息告知给代理。标志设置为 true 时会清除会话,在断开连接时删除订阅信息;当期设置为 false 时,可以实现持久连接并保存订阅信息,在重新连接时使用高级别 QoS 传递消息。
[*]Will flag:连接标志的第2位。MQTT 的遗愿标志位的一部分。设置该标志就意味着如果接受了连接请求,那么服务器将存储一个 Will 消息。Will 消息是一个带有Will 主题和 Will 消息内容的 MQTT 消息。它用于连接断开时通知其他客户端。当客户端断开连接时,代理会传送此消息。当 Will 标志设置为1时,服务器将使用连接标志中的 Will QoS 和 Will Retain 字段。
[*]Will QoS:连接标志的3、4位。表示发布 Will 消息时要使用的 QoS 级别。
[*]Will retain:连接标志的第5位。如果 Will Retain 设置为0,则服务器必须将 Will消息作为未保留消息发布;当其设置为1时,则将 Will 消息作为保留消息进行发布。
[*]User Name and Password:分别占据连接标志的第6、第7位。设置此字段将使用证书。MQTT 允许发送用户名和密码来验证客户端和授权。如果密码未采用加密手段,则以明文形式发送。
[*]Keep alive: Keep alive 计时器用来确认 MQTT 客户端是否在线。客户端通过向代理周期性发送 PING 请求,代理返回 PING 命令响应。
[*]Client ID:它是每一个连接到 MQTT 代理的 MQTT 客户端的标识符。每个代理也是唯一的。
[*]Payload: Payload包含了客户端ID、Will 主题、Will 消息,用户名和密码,其状态由标志决定。





2. 连接确认消息在接收到 CONNECT 消息时,代理将返回 CONNACK 消息进行响应。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt5.png

[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]Session Present: Session present 标志位于连接确认字节的第0位,该标志指示代理与之前交互过的客户端是否已存在持久会话。
[*]Return Code:返回码的值和响应信息如下表所示。


http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt6.png

[*]Payload: CONNACK 数据包没有 Payload。





3. 订阅消息客户端向 MQTT 代理发送 SUBSCRIBE 消息从而获取相关消息。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt7.png

[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]Message Identifier:用来标识客户端和代理之间消息流中的消息的标识符。这对于大于零的 QoS 尤为重要。
[*]Topic and QoS Level:每一个订阅都由主题过滤器和 QoS 级别两部分组成。客户端通过主题来获取自己感兴趣的消息。
[*]Payload: Payload 包含了订阅列表,它在 SUBSCRIBE 数据包中是强制使用的。





4. 订阅确认消息MQTT 代理通过给客户端发送 SUBACK 消息来确认订阅。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt8.png

[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]Message Identifier:与 SUBSCRIBE 消息中的 Identifier 相同,它与 QoS 大于零的消息密切相关。
[*]Return Code: MQTT 代理为 SUBSCRIBE 消息中接收的每一对 Topic/ QoS 发送一个返回码。返回码与成功的 QoS 等级一致。下表中展示了返回码的值和响应信息。


http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt9.png

[*]Payload: Payload 包含返回码列表。





5. 发布消息MQTT 客户端和代理建立连接之后,就可以发布消息了。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt10.png

[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]DUP flag:当 DUP 标志为0时,意味着是首次尝试发送此 PUBLISH 数据包;如果标志为1,则表示重新尝试发送。
[*]**QoS:**QoS 级别决定了消息传输可靠性级别。
[*]Retain Flag:如果 Retain Flag 设置为1,则服务器必须存储消息及其QoS,以便可以满足与该主题匹配的订阅。当 PUBLISH 数据包发送到订阅客户端时,如果由于新的订阅而发送数据包,则服务器必须将 Retain Flag 设置为1。当匹配到已经存在的订阅而发送数据包时,不管 Retain Flag 是否被设置,此时服务器都必须将 Retain Flag 设置为0。
[*]Topic Name:由 UTF-8 字符串组成,当需要分层结构时,可以使用斜杠。发布的消息必须包含一个主题,以便代理进行基本的过滤。这样,代理只向已订阅该主题的客户端发送消息。
[*]Message:消息是包含要发送的实际数据的主题的 Payload。由于 MQTT 的数据格式是多变的,因此 Payload 可以根据实际用例进行结构化。
[*]Payload: Payload 包含正在发布的消息。





6. 断开请求消息断开连接消息是客户端向代理发送的最后一个控制包。意味着客户端断开连接。
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt11.png

[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]Payload:断开数据包没有 Payload。





7. MQTT 保持在线保持在线功能的前提是确保连接已打开,并且客户端和代理之间互相建立了连接。当建立连接时,客户端将 keep-alive 的时间间隔(以秒为单位)发送给代理。MQTT规范里说到:“客户端有责任确保发送的控制报文的时间间隔不超过 Keep Alive 值。在没有发送任何其他控制数据包的情况下,客户端必须发送一个 PINGREQ 数据包。”保持在线的机制由 PINGREQ 和 PINGRESP 消息实现和维护。典型的保持在线的消息流如下图所示:
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt12.png
PINGREQ
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt13.png
在没有发送其他 MQTT 控制数据包时,客户端将周期性地发送 PINGREQ 给代理,以示它自己仍然在线。如果代理没有收到 PINGREQ 或任何其他数据包,它将关闭连接并发送 LWT 消息,当然前提是客户端已经指定了 LWT。
[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]**Payload:**PINGREQ 数据包没有 Payload。

PINGRESP
http://blog.catchpoint.com/wp-content/uploads/2017/06/mqtt14.png
代理接收到客户端的 PINGREQ 数据包时,将发送 PINGRESP 作为答复,表明自己在线并且是可用的。
[*]Header Flags:保存有关 MQTT 控制包类型的信息。
[*]Payload: PINGREQ 数据包没有 Payload。





结论MQTT 推动了物联网领域的创新,并已成为 IT 领域不可或缺的一部分。上一篇 MQTT 的文章初步介绍了该协议,而在本文中,我们借助 Wireshark 讨论了 MQTT 客户端和MQTT 代理之间通信的每一个步骤的细节。通过一系列指标和图表有助于我们深入了解 MQTT。原文:Dissecting MQTT using Wireshark
作者:Abhinaya Balaji
翻译:安翔
审校:苏宓

页: [1]
查看完整版本: 使用 Wireshark 深入剖析 MQTT