MQTT协议通信原理

       最近需要学习一些物联网的知识,于是接触到了MQTT协议,跟着网上太极创客团队的资料进行了相关基础知识的学习。为了方便复习和巩固,所以写了下面这篇文章来记录学习过程中的总结

目录

MQTT通讯协议原理

MQTT通讯协议解释

MQTT的主题规范 

MQTT通讯协议的优点

MQTT客户端连接服务器过程

服务器与客户端连接

订阅与发布


MQTT通讯协议原理

MQTT通讯协议解释

       MQTT协议是一个物联网常用的协议,它和一般的网络协议类似,它也需要有服务端客户端,MQTT服务端通常是负责将MQTT客户端发来的消息进行中转和管理,确保客户端之间的通信能够顺畅。而MQTT的客户端是负责向MQTT的服务端发送或者接收服务端消息的。通常情况下,我们将客户端向服务端发送消息称为“发布”,而将客户端从服务端获取消息的行为称为“订阅”。不过在MQTT服务器管理信息的过程中,它会把信息都划分到一个“主题”里面,客户端订阅的就是这个“主题”,而MQTT服务器就会根据客户订阅的主题发送该主题相应的信息给客户端。

       “主题”、“订阅”和“发布”的关系也很好理解,订阅就相当于你(客户端)追番(主题),只要番(主题)更新了,网站(服务器)就会向你发送更新内容(消息)。但是呢,有时候发布信息的客户端也可能称为订阅的客户端,就像读取评论区一样。

MQTT的主题规范 

       其中对于主题的格式也有一定的规范,一般来说,主题的最基本形式就是一个字符串,它可以是myTopic、MyTopic或者my topic等,不同的主题之间是区分大小写的,例如Mytopic和mytopic就不受同一个主题;同时主题里面是可以用空格的,但是主题间最好不要用空格;最后主题是不支持中文的,我们可以用英文字符或者ASCII字符。

       对于不同的主题,我们可以对主题进行分类,将主题分为一级主题、二级主题等多级主题,不同级别的主题之间用“/”来划分等级关系,它们的结构如下图所示,例如下面的就是语文老师主题就是School/Teacher/Chinese Teacher,但是注意不能在最顶层的一级主题前加“/”!!!

       如果我们想订阅多个主题,可以使用一些简便的主题通配符,例如单极通配符+和多级通配符#。单极通配符可以取代一个级别的主题,多级通配符可以取代多个级别的主题。例如School/+/English就可以自动包括Teacher,而School/Teacher/#可以包括Topic所有三个老师的主题。

       我们也应该记住不要向以$开头的主题订阅或者发布消息,它是服务端保留的特殊主题,同时主题最好要简洁明了,易于辨识。 

MQTT通讯协议的优点

       整套通讯流程看下来,最主要的是MQTT的服务端,它负责接收客户端发布的消息或者给订阅的客户端发送消息,同时还负责数据存储、按主题调整和处理。正因为有了服务端,所以可以实现客户端之间通讯相互独立,相当于将客户端与客户端的通信转换为客户端与服务端的对接。并且扩大了通讯的距离,客户端只需要连接到通讯网络(互联网、局域网)上就可以实现和服务器交互,解决了原来客户端和客户端通讯距离较短的问题。最后是通讯时间可调控,当客户端发布数据给服务器,但是订阅该主题的客户端连接断开,此时服务器可以等到该客户端连接上网络后再次传递,使得数据传递可以实现非实时传递。

MQTT客户端连接服务器过程

服务器与客户端连接

       MQTT客户端连接服务器主要分为两步:

       1、客户端发送含有连接请求信息的数据包CONNECT

       CONNECT报文的发送格式要符合MQTT的规范,否则服务器会拒绝客户端的连接请求,通常情况下,MQTT规范中对于CONNECT数据包中需要包含的信息要求如下:

       其中我们来仔细看看一个CONNECT报文里面每一个信息的意义:

       clientId:这个是客户端id,MQTT服务器就是通过该标识来识别客户端的,因此每一个人的ClientId必须是不同且独立的,不然就会被MQTT服务器误认。

       cleanSession:这个是清除会话的意思,当某次服务端发送的消息客户端没有确定接收到时,服务端会默认该次发送没有成功,那么如果我们将cleansession设置为true时,服务端就只负责发送数据,发完就把刚才的数据删掉,至于你返回给我的是接收到还是没有接收到我都不管,我发了就行了。而当我们把cleansession设置为false时,如果服务器没有接收到客户端返回的接收成功的信息,那么客户端会把那些信息保存此来,以后尝试再次发送。

       一般情况下,如果信息非常重要,那么该处应该设置为False,否则则为True。并且,如果我们需要QOS大于0的服务质量,这里就必须设置为False

       keepAlive:这个指的是心跳时间间隔,如果服务器和客户端断开连接,那么MQTT服务器就通过keepAlive这个信息来了解客户端是否与服务端保持联系状态。为什么会有这种奇怪的标志位呢?因为与服务器连接的客户端不一定是经常发消息的,这种情况下如果太久了,我们就不知道这个客户端就是是断了呢?还是没有发消息,所以就需要引入这个心跳时间间隔来判断客户端是否断开的标志位。

       当一个客户端在未来一段时间内不会传递消息给服务端时,它需要定期向服务端发送一个报文PINGREQ来证明自己还连接着,没有断开,这个报文也被称为心脏请求。当服务端接收到报文后,会回复一个PINGRESP报文来告诉客户端我知道了,你还活着~,这个回复报文就被称为心脏响应

       由于客户端是按照一定时间间隔来发送报文PINGREQ的,如果服务器发送客户端在一段时间内都没有发送心脏请求了,那么服务端就可以判断客户端断开连接了。而客户端向服务端发送连接请求报文CONNECT时,往往就会告知服务端心跳时间间隔keepAlive,这个数值就是发送报文PINGREQ的时间间隔!通常情况下,如果服务器没有在1.5倍keepAlive的时间间隔内受到客户端发送的PINGREQ心跳请求报文或者PUBLISH报文,那么就会默认该客户端已经断开。

       username:这个表示用户名,某些服务器可能会对客户端开启认证,只有客户端提供正确的用户名和密码才能连接,这样可以方便服务器对客户端进行管理和分配,让每一个客户都有自己的私人主题,并且服务器可以根据CONNECT报文所提供的账号和密码来判断该客户端是否可以订阅某个发布客户端的主题。

       password:这个表示密码,是与用户名相互配套使用的。

       lastWillTopiclastWillQoslastWillMessagelastWillRetain:这几个通常是发布话题的客户端发送连接报文CONNECT给服务端时提前准备好的信息,当客户端异常断线的情况下作为遗嘱使用的可选项。

       为了更好保护客户端的数据信息,MQTT协议允许客户端在备好信息,在意外断线的情况下作为客户端的遗嘱。这种遗嘱信息是只有在客户端异常断开连接时才会发送的,如果客户端是正常断开连接,它会往服务器发送DISCONNECT报文,表名自己是正常断开的。如果服务器在1.5倍KeepAlive时间内没有受到信息或者PINGREQ报文和DISCONNECT报文,那么将默认客户端意外断线,当客户端意外断线后

       lastWillTopic:这个表示遗嘱主题,这个主题在于告诉服务端,只有订阅了lastWillTopic主题的客户端才会受到本发布客户端挂了后的遗嘱。

       lastWillMessage:这个表示遗嘱消息,当发布主题的客户端突然挂了,那么那些订阅了主题的客户端就会收到由服务器发送的lastWillTopic的消息。

       lastWillQos:这个表示服务端发送阈值给接收端时采用的质量等级。

       lastWillRetain:这个表示遗嘱保留,这个与后面将要提及的保留消息类似,可以理解为是否需要对遗嘱进行消息保留,如果选择保留的话,那么下一个订阅的客户端将会立马受到该信息,不保留的话即使订阅了的客户端也只会在下一次消息发布的时候才得到遗嘱的消息。

       2、服务端发送连接确定的数据包CONNACK

       相较于CONNECT报文,CONNACK报文比较简单,主要包含两个信息:sessionPresent和returnCode。

       sessionPresent:这个是当前会话的意思,这个与前面的CONNECT相结合应用,它表示服务端是否保存有未发送成功的信息。当CONNECT设置为True时,那么服务端肯定不会保存有信息,此时sessionPresent肯定是false,同时如果我们CONNECT设置为False但是上次结束前服务端发送的信息客户端都成功接受了,那么此时sessionPresent也会是False,但是如果我们的CONNECT是false,但是我们上一次没有成功接收到信息,服务器保存有我们接收失败的信息,那么此时sessionPresent会返回true 

       returnCode:连接返回码,当服务端接收到客户端的请求后,其会返回连接信息表是否连接成功,而连接失败也会返回一些提示信息。比如返回“o”表示连接成功;返回“1”表示因为不支持MQTT客户端协议版本被服务器拒绝;返回“2”表示因为不支持客户端标识符的编码被拒绝(常因为客户端用的是UTF8编码,服务端不允许);返回“3”则表示因为服务端不可用被拒绝;返回“4”则表示用户名或者密码无效被拒绝;返回“5”则表示客户端未授权该服务被拒绝。

订阅与发布

       客户端连接到服务端后,我们之后就会进行消息发布或者订阅,下面我们来看看这两个过程:

       1、发布消息PUBLISH

       发布消息时必须要包含一个主题,MQTT可以通过主题来确定消息会被发送到哪个客户端,通常情况下一份符合规范的PUBLISH报文包含以下的部分:

       packetId:这个是标文标识符的意思,这是区分不同的MQTT报文的符号,可以便于使用MQTT协议的设备对报文进行处理,通常情况下,只有当QOS级别大于0时这个标识符才会说非零数值,如果QOS是0,那么标识符也会是0。

       topicName:这个就是主题名的意思是了,它是区分我们的消息放置在哪个主题下的重要标志。 

       QOS:这个是服务质量等级,总共有三个级别:0,1,2,它将决定MQTT的服务可靠程度,一般情况下,对于比较重要的消息,我们通常会选择Qos>0的服务等级。

       Qos0表示最多发送一次消息,当Qos等级为0时,服务端一旦发送了消息就不再管了,如果此时发生了网络波动,客户端没有接收到,那也没有办法了~

       Qos1表示最少发送一次消息,当Qos等级为1时,此时如果发送端发送了一份报文给接收端,接收端成功接收后会返回一份确定报文给发送端,这份报文只含有一个信息那就是packetId,用来确定报文的标识符。如果接收端接收到了该报文,证明成功发送了,对方也接收到了,反之,发送端会重复发送信息直至接收端返回PUBACK报文(同时发送端重复发布时,其第二份开始的报文的dupFlag信息会被置为True,这个信息下面会提到)。

       Qos2表示保证收到一次消息 ,这是Qos的最高等级,可以确保接收端肯定会接收到一次,它总共需要确定两次,所以它的效率是最低的。一次完整的发送总共分为以下几个过程:

       ①首先发送端发送报文给接收端,该报文的服务质量等级设置为QOS2

       ②接收端接收到后会返回PUBREC报文给发送端回应

       ③发送端会回应PUBREL报文给接收端,表示我收到你发送的确定受到报文了

       ④接收端会返回一条PUBCOMP报文给发送端,表示我接到你接到我的确定请求回复报文了。

       通过这四次传输,可以确定接收端肯定接收到一次发送端的报文。注意的,如果发送的客户端和接收的客户端之间QOS质量等级不同,那么默认按最低等级处理。例如如果发布端的QOS等级为2,订阅端的QOS等级为0,那么服务器将数据发送给订阅端是按照QOS0等级发的。如果发布端的QOS等级为0,订阅端的QOS等级为2,那么服务器发送消息给订阅端也为QOS0

       retainFlag:这个是保留消息标志,它将决定我们的服务器在接收到某个主题的新信息时是否第一时间将接收到该主题的新消息发送给订阅该主题的客户端。这个功能对于那些定时发布消息的客户端相对来说比较重要。

       举个例子,我们设计了一款有害气体检测程序,检测端每隔10min就会发布一次消息,然后我们的客户端显示屏订阅了该消息,那么每个十分钟上面就会显示当前的警报情况,如果某一瞬间停电了2分钟,然后重新来电后客户端重新启动,但是此时距离检测端发布消息还有8分钟,那么此时客户端将无法显示信息。为了解决这个问题,这个保留信息标注就很有用了。它会在每次我们开机订阅后服务端发送一次当前检测端的保留信息。

       当检测端再一次发送更新消息后,保留消息就会被覆盖啦,同时如果我们检测端向主题发送一条空的保留消息(也就是payload有效载荷为0字节),那么保留消息就会被删除了。 

       payload:有效荷载,这个就是我们发布的内容啦

       dupFlag:重发标志,如果QOS级别大于0,并且MQTT协议的接收方没有回应确认接收的消息时,发送方就会重复发送MQTT报文。在发送方第二次发送该报文时,就会将重发标志置为True。

       2、发送订阅消息SUBSCRIBE

       当客户端需要订阅某个主题时,可以通过向服务器发送SUBSCRIBE报文来实现。往往一个SUBSCRIBE报文可以订阅多个主题,而客户端发送的订阅报文也含有一个与PUBLISH报文信息里的packetid信息类似的标识符和Qos等级信息。

       packetid:这个就是报文标识符,用于给MQTT协议的设备确定每一份报文的身份。 

       topicName:我们需要订阅的主题名,可以在一份SUBSCRIBE报文里面申请订阅多个主题。

       Qos:这个就是质量等级设置的信息

       3、订阅确定SUBACK

       服务端接收到客户端的订阅SUBSCRIBE报文请求后,会向客户端发送SUBACK的订阅确定报文,该报文的规范格式如下图所示:

       returnCode:这个就是订阅返回码,当服务端接收到客户端的订阅请求后,会根据订阅的主题数目以此返回对应数量的订阅返回码。单个订阅返回码会有4种状态:”0“表示订阅成功,级别是QOS0;”1“表示订阅成功,级别是QOS1;”2“表示订阅成功,级别是QOS2;”128“表示订阅失败。

        packetid:就是报文识别符,它的作用与我们发送订阅请求SUBSRCIBE时发送的那个报文里面的报文标识符的类似,都是用于识别的。

       4、发送订阅取消UNSUBSCRIBE

       该报文是客户端发送给服务端的报文,订阅取消报文UNSUBSRCIBE的规范结构如下图所示:

       packetid:这个没什么好说的,和前面的类似;

       topicName:这个是我们取消订阅的主题的名称,与订阅确定的报文类似,当我们取消多个主题的订阅时,我们可以在单个订阅取消报文里面发送多个主题。 

       5、订阅取消UNSUBACK

       该报文是当客户端发送取消订阅的报文给服务器后,服务器成功接收发送的确定报文,可以知道它的结构与订阅确定SUBACK报文类似。

       下面就是一次订阅并且取消的过程,发布的过程与订阅的过程相类似,就不再多赘述了:


       上面是我根据太极创客发布的《零基础入门学用物联网-MQTT基础篇》所总结的学习内容,原学习内容参考下面的网站,感谢他们团队的付出:零基础入门学用物联网 – MQTT基础篇 – 目录 – 太极创客

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值