学习MQTT协议。若是只是看了相关文档就认为能够了。那是一个错误的观念。笔者为了能更好的去理解MQTT协议。看了很多相关的开源Broker的项目。惋惜这些项目通常都是不彻底的。不过从这些项目中笔者至少发现他们大部都是经过Netty这个通讯框架来完成的。哪怕是大型项目ActiveMQ也脱不了俗。特别是商用HiveMQ更是列为重要的一部分。因此笔者接下来会用Netty框架来实现一些代码。这样子有助于咱们去理解MQTT协议。html
本节笔者会来说链接报文(CONNECT)。能够说他是全部报文的基础。全部的动做都必须在链接之上操做。咱们都知道MQTT是基于TCP/IP网络协议的。并以字节流传输的。他的行为动做更为简单。以下web
咱们能够知道链接会用到俩个报文类型类——CONNECT报文和CONNACK报文。其中CONNECT报文比较复杂一点。能够说是全部报文中信息种类最多的。CONNACK报文的最大特色就是没有有效载荷部分。网络
接下笔者就会讲解一下链接的相关行为。同时也但愿读者们记住笔者这里讲的通常是MQTT 3.1 和MQTT 3.1.1的协议。框架
CONNECT报文就是至关链接请求同样子。因此当客户端和服务端创建以后,服务端接受的第一份报文就必须是链接报文。相信这个不用笔者说明也知道为何。同时客户端这边要保证链接报文只能发送一次。同时在服务端也要有验证来自客户端的链接报文只有一次。若是发送俩次以上的链接报文的话,很差意思请看成违反了协议断开当前链接。从前面的图片里面咱们就知道若是客户端发送一个链接报文(CONNECT)以后,服务端就会返回一个链接肯定报文(CONNACK)。若是客户端在一段时间以后,尚未接收到来自服务端的链接肯定报文(CONNACK)的话,客户端必定要断开链接。同时要重起一个新的网络链接。在发一次链接报文(CONNECT)。学习
咱们都知道控制报文分为固定报头+可变报头+有效载荷部分。其中可变报头和有效载荷并非必需要拥有的。而CONNECT报文倒是全部中笔者认为最为复杂的报文。不论是可变报头仍是有效载荷部分他都拥有。spa
固定报头htm
固定报头的结构上一单已讲过了。占八个字节。从上一单的报文类型列表咱们知道他的值为1。报文类型占四个字节。因此他的二进制就是0001。在MQTT 3.1里面有讲到DUP、QoS、 RETAIN 都没有被用到。这些加起来占四个字节。这样子的话笔者觉得固定报头最终的二进制是0001。可是在MQTT 3.1.1里面却又说到以0保留了。因此最终二进制是00010000。简单的讲DUP、QoS、 RETAIN虽然没有被用到,因此设置为0。blog
可变报头图片
链接报文(CONNECT)的可变报头的结果构分为四个部分:协议名(Protocol Name)、协议等级(Protocol Level)、链接标志(Connect Flags)、保持链接(Keep Alive)。其实协议名(Protocol Name)和协议等级(Protocol Level)笔者也不是很明白有什么用。只是知道MQTT 3.1 的协议名是MQIsdp,协议等级是3。而MQTT 3.1.1的协议名倒是MQTT,等级是4。好吧。至少说明不一样版本的MQTT协议存在不一样的协议名和协议等级。这里面在文档也有一个动做存在。就是若是服务端发现协议名不正确的话,就必须断开链接。若是是协议等级不对话,就是必须回返一个码。开发
要问可变报头里面最重要的部分是哪一部分的话,笔者认为是链接标志(Connect Flags)。链接标志里面包括用户名标志(User Name Flag)、密码标志(Password Flag)、遗嘱保留(Will Retain)、清理会话(Clean Session)、保留(Reserved)等。他们的位置如图下。
清理会话(CleanSession)
客户端和服务端之间的通讯时间,笔者认为是一次会话。也就是在链接成功的时候为会话开始。当断开链接的时候表示一次会话结束了。即然是会话,天然就有会话状态。这些状态当前就是指通讯之间的信息。以下
1、服务端的会话状态:
1)客户端的订阅信息。
2)已经发送给客户端,可是尚未完成确认的QoS 1和QoS 2级别的消息。
3)即将传输给客户端的QoS 1和QoS 2级别的消息
4)已从客户端接收,可是尚未完成确认的QoS 2级别的消息
5)准备发送给客户端的QoS 0级别的消息(可选)
2、客户端的会话状态:
1)已经发送给服务端,可是尚未完成确认的QoS 1和QoS 2级别的消息
2)已从服务端接收,可是尚未完成确认的QoS 2级别的消息
知道了会话状态,就能够明白清理会话就用来表示是否清除或是保存会话状态。若是清理会话为0的话,表示要保存这里会话状态。若是链接断开了,在次链接的时候,服务要以什么来得到已存在的会话状态呢?客户ID。这是在有效载荷分部分的下面会讲到。根据客户ID来找查有没有相关的会话状态存在。若是存在,就必须以存在的会话状态来创建链接。若是没有就建立一个新会话状态。固然断开以后,不论是客户端仍是服务端都要保存当前会话状态。这个举动笔者认为是为了数据重用吧。也能够说是保证数据不会丢失。若是清理会话为1的话,事情就变的很简单。给我清除掉就是行了。没有一次链接都是一个新的会话。
遗嘱标志(Will Flag)
关看到遗嘱俩个字就应该明白。用于表示在网络链接突关闭的时候,要不要发送遗嘱。遗嘱标志可关系遗嘱QoS(Will QoS),遗嘱保留(Will Retain),还有有效载荷里面的遗嘱主题(Will Topic)和遗嘱消息(Will Message)。能够说遗嘱标志是遗嘱功能总开关。只当遗嘱标志为1的时候。说明要用到遗嘱功能。那遗嘱QoS(Will QoS),遗嘱保留(Will Retain)就能够被启用。也就是说遗嘱主题(Will Topic)和遗嘱消息(Will Message)必需要在有效载荷部分里面出现。
遗嘱QoS(Will QoS)
他的值主要要看遗嘱标志(Will Flag)是的值。若是遗嘱标志(Will Flag)是1的话,遗嘱QoS能够是0,1,2。这些值表示跟服务质量是同样子。若是遗嘱标志(Will Flag)是0的话,那么遗嘱QoS必须也是0;
遗嘱保留(Will Retain)
他的值也是要看遗嘱标志(Will Flag)是的值。若是遗嘱标志(Will Flag)是1的话,遗嘱保留能够是0或1。用于表示有没有作于保留消息发布;
用户名标志 User Name Flag 和 密码标志 Password Flag
笔者为何把这俩个放在一块儿呢?相信作过不少业务开发的人都知道用户名和密码。上面这俩个就是用于标示在有效载荷里有没有相关的部分。好比。当就用户名标示为1的时候,那就是说明有效载荷部分里面有用户名的信息。同理密码标示为1 就是表示有效载荷部分有密码的信息。这里有一点要注意就是若是用户名标志为0的话,那密码标志就必须为0。有用户名和密码的话,咱们就能够在服务端作一些身分验证的业务。同时还能够加入一些权限。
上面笔者讲过可变报头分为四个部分。剩下一个保持链接(Keep Alive)。保持链接(Keep Alive)表示在客户端一个报文发送结束以后到下一次报文发送以前的空闲时间。单位为秒。记住是在客户端而不是服务端。若是在保持链接的时间内没有发送任何报文的话。文档里面是要求发送一个PINGREQ报文。经过他判断服务端和客户端之间的链接状态。若是在一时间段接受不到没有收到服务端发来PINGRESP报文,那么就应该关闭于服务端的链接。前面讲都是在客户端这边要作的事情。服务端这固然也不能少。若是服务端判断保持链接不为0的时候,就要以保持链接值的1.5陪时间来判断是否有接到报文。若是没有接受报文的话,就要关闭跟客户端之间的链接。值得注意的事。服务端要断开客户端不是根据保持链接来处理。而是查看这个客户端是否是闲了。若是是的话,何时均可以断开链接的。
有效载荷
有效载荷事实上就是通讯里面的用户要存放的信息。只是这里面又不能全是用户信息。还包括了一些MQTT须要的信息。这跟可变报头的一些标志有关系。可是无论什么样子。客户ID是必须在第一位的。后面就是遗嘱主题和遗嘱消息。最后才是用户和密码。
有效载荷 = 客户ID + 遗嘱主题 + 遗嘱消息 + 用户 + 密码
为了方便学习,笔者用软件把包搞下来。上一章也讲到过什么样子搞下。以下图下
上面的图片算是比较全的链接报文。笔者也根据相应的标示出他们所在的位置。图片下方是报文相应的二进制流。红线标出的数据是报文真时的数据对应。而绿色线表示是接下来红线相关数据的长度。相信若是你看过MQTT相关的文档就应该知道MSB和LSB关键字的做用。那么图片1红线就是固定报头,2红线就是可变报头。从3红线开始后面都是有效载荷部分。
通上面咱们大至能了解MQTT的链接过程要作些什么。在MQTT文档里面有一点笔者有一点吃惊。笔者觉得若是一个客户端发送链接报文(CONNECT)以后,并接受到了服务端的链接报文肯定(CONNACK)以后,才能够有进行发布相关信息。但是MQTT文档指出客户端在发送链接报文(CONNECT)以后就能够进行发布了。而不须要等待链接报文肯定(CONNACK)。若是服务端由于客户端不合适,能够彻底不须要去处理和反应以前送来的消息。
链接报文肯定(CONNACK)的特色就是没有有效载荷的部分。客户端想要知道本身有没有链接成功服务端。就必须去查看可变报头里面的回返码。在CONNACK报文的可变报头里面还有一叫Session Present。他用于表示服务端没有保存会话状态。这个笔者就很少讲。各位本身看下图吧。
注意:红线为Session Present,绿线为返回码。