imx6ull/linux应用编程学习(14) MQTT基础知识

什么是mqtt?

HTTP 协议一样, MQTT 协议也是应用层协议工作在 TCP/IP 四层模型中的最上层(应用层) ,**构建于 TCP/IP协议上。 MQTT 最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。**作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用

MQTT 协议是为工作在低带宽、不可靠网络的远程传感器和控制设备之间的通讯而设计的协议,它具有以下主要的几项特性:

①、 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。

②、基于 TCP/IP 提供网络连接。

主流的 MQTT 是基于 TCP 连接进行数据推送的,但是同样也有基于 UDP 的版本,叫做 MQTT-SN。这两种版本由于基于不同的连接方式,优缺点自然也就各有不同了。

③、支持 QoS 服务质量等级。

根据消息的重要性不同设置不同的服务质量等级。

④、 小型传输, 开销很小,协议交换最小化,以降低网络流量。

这就是为什么在介绍里说它非常适合"在物联网领域,传感器与服务器的通信,信息的收集" ,要知道嵌入式设备的运算能力和带宽都相对薄弱,使用这种协议来传递消息再适合不过了, 在手机移动应用方面, MQTT 是一种不错的 Android 消息推送方案。

⑤、使用 will 遗嘱机制来通知客户端异常断线。

⑥、基于主题发布/订阅消息,对负载内容屏蔽的消息传输。

⑦、支持心跳机制。

MQTT 通信基本原理

服务端和客户端

MQTT 是一种基于客户端-服务端架构的消息传输协议,所以在 MQTT 协议通信中,有两个最为重要的角色,它们便是服务端和客户端

MQTT 主题

客户端想要从服务器获取信息,首先需要订阅信息,"主题"在 MQTT 通信中是一个非常重要的概念,客户端发布信息以及订阅信息都是围绕"主题"来进行的。

客户端发布消息时需要为消息指定一个"主题" , 表示将消息发布到该主题 ;而对于**订阅消息的客户端来说,可通过订阅"主题" 来订阅消息,**这样当其它客户端或自己(当前客户端)向该主题发布消息时, MQTT 服务端就会将该主题的信息发送给该主题的订阅者(客户端)。

在以上图示中一共有三个 MQTT 客户端, 它们分别是开发板、 手机和电脑 。 MQTT 服务端在管理 MQTT通信时使用了**"主题"来对信息进行管理**。比如上图所示,假设我们需要利用手机和电脑获取开发板在运行过程中 SoC 芯片的温度,那么首先电脑和手机这两个客户端需要向 MQTT 服务器订阅主题"芯片温度" ;接下来,当开发板客户端向服务端的"芯片温度"主题发布信息(假设信息的内容就是当前的温度值) 后服务端就会首先检查都有哪些客户端订阅了"芯片温度"这一主题的信息,而当它发现订阅了该主题的客户端有一个手机和一个电脑,于是服务端就会将刚刚收到的"芯片温度"信息转发给订阅了该主题的手机和电脑客户端。

以上实例中, 开发板是"芯片温度"主题的发布者,而手机和电脑则是该主题的订阅者。

值得注意的是, MQTT 客户端在通信时,角色往往不是单一的, 一个客户端既可以作为信息发布者也可以同时作为信息订阅者。如下图所示:

图中的所有客户端都是围绕"LED 控制"这一主题进行通信。此时,对于"LED 控制"这一主题来说,手机和电脑客户端成为了 MQTT 信息的发布者而开发板则成为了 MQTT 信息的订阅者(接收者)。

MQTT 发布/订阅特性

客户端相互独立

MQTT 客户端是一个个独立的个体, 它们无需了解彼此的存在,依然可以实现信息交流。

空间上分离

MQTT 客户端以及 MQTT 服务端它们在通信时是处于同一个通信网络中的, 这个网络可以是互联网或者局域网; 只要客户端联网,无论他们远在天边还是近在眼前,都可以实现彼此间的通讯交流;其实网络通信本就是如此,所以并不是 MQTT 通信所特有的。

时间上可异步

MQTT 客户端在发送和接收信息时无需同步。这一特点对物联网设备尤为重要

连接 MQTT 服务端

MQTT 客户端连接服务端总共包含了两个步骤:

①、首先客户端需要向服务端发送连接请求 ,这个连接请求实际上就是向服务端发送一个 CONNECT报文,也就是发送了一个 CONNECT 数据包。

②、MQTT 服务端收到连接请求后,会向客户端发送连接确认 。 连接确认实际上是向客户端发送一个CONNACK 报文 ,也就是 CONNACK 数据包。

总结一句话就是:客户端先向服务端发送 CONNECT报文,服务端收到连接请求后,再向待连接的客户端发送 CONNACK 报文

CONNECT 报文

如果此 CONNECT 报文的格式或内容不符合 MQTT 规范,则服务器会拒绝客户端的连接请求。

CONNECT 报文包含的信息如下图所示:

所谓报文就是一个数据包, MQTT 报文组成分为三个部分:固定头(Fixed header)、可变头(Variable header)以及有效载荷(Payload,消息体)。这里我们简单地介绍一下:

固定头(Fixed header) : 存在于所有 MQTT 报文中, 固定头中有报文类型标识,可用于识别是哪种 MQTT 报文,譬如该报文是 CONNECT 报文还是 CONNACK 报文,亦或是其它类型报文。

可变头(Variable header) : 存在于部分类型的 MQTT 报文中,报文的类型决定了可变头是否存在及其具体的内容。

消息体(Payload): 存在于部分类型的 MQTT 报文中, payload 就是消息载体的意思。

clientId--客户端 id

clientId 是 MQTT 客户端的标识,也就是 MQTT 客户端的名字,MQTT 服务端可通过 clientId 来区分不同的客户端。

keepAlive--心跳时间间隔

有些客户端并不经常发送消息给服务端, 对于这种客户端, MQTT 协议使用了类似心跳检测的方法来判断客户端是否在线 。客户端在没有向服务端发送信息时(空闲时) ,可以定时向服务端发送一个心跳数据包,这个心跳包也被称作心跳请求, 心跳请求的作用正是用于告知服务端,当前客户端依然在线。

譬如 keepAlive=60,表示告诉服务端,客户端将会每隔 60 秒左右向服务端发送心跳包。

cleanSession--清除会话

这是一个布尔值, cleanSession 标志可用于控制客户端与服务端在连接和断开连接时的行为,我们举个例子来进行说明, QQ、微信这些聊天软件大家都用过,假设当前你的 QQ 账号没有登录或者说当前处于离线状态,与服务器断开了连接; 而在离线期间,你的 QQ 好友给你发了几条信息; 由于当前你的 QQ 处于离线状态,自然是接收不到好友发送过来的信息,但是, 当你的 QQ 恢复连接状态时,立马会接收到好友在离线期间所发给你的信息

如果连接服务端时 cleanSession=0, 当 MQTT 客户端由离线(与服务端断开连接)再次上线时,离线期间发给客户端的所有 QoS>0 的消息仍然可以接收到;如果连接服务端时 cleanSession=1, 当 MQTT 客户端由离线(与服务端断开连接)再次上线时,离线期间发

给客户端的所有消息一律接收不到。

说白了,想接收离线消息, 客户端连接服务端时就必须使用 cleanSession=0;除了这个作用之外,如果cleanSession=0,则 MQTT 服务端会在客户端断开连接之后"记住" MQTT 客户端在线期间所订阅的所有"主题";也就是说,服务端会保存、存储客户端所订阅的主题。

如果 cleanSession=1,客户端既无法接收到离线消息、服务端也不会记住该客户端所订阅的主题, 服务端不会保存客户端的会话状态, 每次连接都是一次新的会话

下面再看看 MQTT 服务端接收到客户端发来的连接请求后所回复的 CONNACK 报文详细内容

CONNACK 报文
returnCode--连接返回码

当服务端收到了客户端的连接请求后,会向客户端发送 returnCode(连接返回码), 用来说明连接情况。如果客户端与服务端成功连接,则返回数字"0"。如果未能成功连接,返回码将会是一个非零的数字,具体这个数字的含义,请见下表

sessionPresent

在 cleanSession=0 的情况下, 当客户端连接到服务器之后, 可通过 CONNACK 报文中返回的sessionPresent 来查询服务端是否为客户端保存了会话状态(客户端上一次连接时的会话状态信息) , 如果服务端已为客户端保存了上一次连接时的会话状态,则 sessionPresent=1,如果没有保存会话状态,则sessionPresent=0。

如果 cleanSession=1, 在这种情况下,客户端是不需要服务端保存会话状态的, 那么服务端发送的确认连接 CONNACK 报文中, sessionPresent 肯定是 false(sessionPresent=0) ,也就是说,服务端没有保存客户端的会话状态信息。

简言之, CONNACK 报文的 sessionPresent 与 CONNECT 报文的 cleanSession 相互配合。其作用是客户端发送连接请求时,服务端告知客户端有没有保存会话状态。这个被服务端保存的会话状态是来自于上一次客户端连接时,譬如离线消息以及上一次连接时客户端所订阅的主题。

断开连接

当 MQTT 客户端连接到服务端之后,在后续的通信过程中,如果客户端想要断开与服务端的连接,此时客户端可以主动向服务端发送一个 DISCONNECT 报文来断开与服务端的连接,如下图所示

发布消息、订阅主题与取消订阅主题

PUBLISH-- 发布消息

当客户端连接到服务端之后,就可以向服务端发布消息了, 每条发布的消息必须指定一个"主题",表示向某主题发布消息,MQTT 客户端向服务端发布消息其实就是向服务端发送一个 PUBLISH 报文, 服务端收到客户端发送过来的 PUBLISH 报文之后,会向发送发回复一个报

SUBSCRIBE--订阅主题

客户端要想订阅主题,首先要向服务端发送主题订阅请求。客户端是通过向服务端发送 SUBSCRIBE 报文来实现这一请求的。该报文包含有一系列"订阅主题名"。请留意,一个 SUBSCRIBE 报文可以包含有单个或者多个订阅主题名。也就是说,一个 SUBSCRIBE 报文可以用于订阅一个或者多个主题。

另外每一个 SUBSCRIBE 报文还包含有"报文标识符"。报文标识符可用于对 MQTT 报文进行标识。不同的 MQTT 报文所拥有的标识符不同。 MQTT 设备可以通过该标识符对 MQTT 报文进行甄别和管理。当客户端向服务端发送 SUBSCRIBE 报文,服务端接收到 SUBSCRIBE 报文之后会向客户端回复一个SUBACK 报文(订阅确认报文),如下图所示:

UNSUBSCRIBE--取消订阅主题

客户端订阅了某一主题之后,可以随时取消订阅, MQTT 协议提供了这样的操作。

客户端通过向服务端发送一个 UNSUBSCRIBE 报文来取消订阅主题,当服务端接收到 UNSUBSCRIBE报文后,会向发送发回复一个 UNSUBACK 报文(取消订阅确认报文),如下图所示:

相关推荐
怀旧6665 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
铁锤妹妹头发多1 小时前
新手用docker真**难受
运维·docker·容器
2739920291 小时前
Ubuntu20.04 安装build-essential问题
linux
超栈1 小时前
HCIP(11)-期中综合实验(BGP、Peer、OSPF、VLAN、IP、Route-Policy)
运维·网络·网络协议·计算机网络·web安全·网络安全·信息与通信
Cachel wood1 小时前
Github配置ssh key原理及操作步骤
运维·开发语言·数据库·windows·postgresql·ssh·github
infiniteWei1 小时前
【Lucene】原理学习路线
学习·搜索引擎·全文检索·lucene
编程一生1 小时前
回调数据丢了?
运维·服务器·前端
follycat1 小时前
[极客大挑战 2019]PHP 1
开发语言·学习·网络安全·php
华为云PaaS服务小智1 小时前
华为大咖说 | 浅谈智能运维技术
运维·华为·华为云
zhd15306915625ff3 小时前
化工厂主要涉及的自动化备件有哪些?
运维·自动化·化工厂