MQTT协议中一共有14个报文,如下图
从上图看,我们要特别要注意以下3个点:
(1)值,14个报文的排列,不是随意的,每个报文都有自己的值,而值在报文中是要用到的。后面例子会介绍到。
(2)流向,C指客户端,S指服务器,比如CONNECT报文的流向是C->S,表示客户端向服务器发送报文,意思是客户端连接服务器,比如第2个CONNACK报文的流向是S->C,表示服务器向客户端发送的报文,意思是服务器确认连接,也就是连接成功啦。
(3)报文成员,MQTT的报文可以分成3个组成部分,分别是固定报头、可变报头以及负载。从上图可以看到,固定报头是必备成员,14个报文都必须包含固定报头。而可变报头和负载是非必备的,有的报文有,有的报文没有。
本节来讲连接报文(CONNECT)。可以说他是所有报文的基础,“所有报文的基础”这句话的意思就是说不管你做什么操作,订阅也好,发布也好,都必须先连接,你说对不对,电话都没有接通还谈什么爱情谈什么生意?俗话说,要致富先通路。
所有的动作都必须在连接之上操作,MQTT是基于TCP/IP网络协议的,并且以字节流传输的。那这句话说明了2个意思:
1)MQTT通信时客户和服务器都必须有IP地址
2)通信是以字节流,在网络通信中IO流中按类型有字节流和字符流,字节流是以byte方式,并且是16进制的表现形式
Connect和Connack示意如下,其中CONNECT报文是客户端发送服务器,它比较复杂一点。可以说是所有报文中信息种类最多的。CONNACK报文是服务器发送客户机,最大特点就是没有有效载荷部分,如图:
?从前面的图片里面我们就知道如果客户端发送一个连接报文(CONNECT)之后,服务端就会返回一个连接确定报文(CONNACK)。如果客户端在一段时间之后,还没有接收到来自服务端的连接确定报文(CONNACK)的话,客户端一定要断开连接。同时要重起一个新的网络连接。在发一次连接报文(CONNECT)。并且客户端这边要保证连接报文只能发送一次,同时在服务端也要有验证来自客户端的连接报文只有一次。如果发送俩次以上的连接报文的话,不好意思请当作违反了协议断开当前连接。
客户端发送给服务端的第一个报文必须是CONNECT报文。在一个网络连接上,客户端只能发送一次CONNECT报文。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接 。此话意思就表明了连接报文只能发一次。报文结构如下
第1个字节报文类型,固定值为1,从上面的14种报文类型看到是固定的1.
第2个字节剩余字节长度,注意这是指剩余字节的长度,意思是指从它开始到最后一共有多少个字节,包括自己在内。
10个字节分别是:协议名称长度2+协议名称4+协议级别1+连接标志1+保持连接2=10
--1~6个字节为协议名长度和协议名
MQTT协议名是预先规定好的,这个后续不会变。
--第7个字节为协议级别
其实就是mqtt协议的修订版本。对于3.1.1版协议,协议级别字段的值是4(0x04)。
--第8个字节为连接标志
连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。有效荷载里有啥全看这个字节的8位字符是0还是1了,这8个位的意义如下所述:
|--第0位:服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0,如果不为0必须断开客户端连接。
|--第1位:清理会话 Clean Session
这个二进制位指定了会话状态的处理方式。客户端和服务端可以保存会话状态,以支持跨网络连接的可靠消息传输。这个标志位用于控制会话状态的生存时间。
这个值是0:服务端可以根据报文里的客户端标识符,来找之前这个客户端有没有连过,连过的话就恢复之前的会话状态,没有的话就新建一个会话。就算是这个连接断开了,会话也要保存在服务端,针对这个客户端订阅的消息,会保存为会话的一部分,在这个客户端重新连接后,把这些 消息再推送给这个客户端。
这个值是1:服务端丢弃之前所有的和这个客户端建立的会话,并开始一个新的会话,新的会话仅持续和网络连接同样长的时间。与这个会话关联的状态数据不能被任何之后的会话重用。为了确保在发生故障时状态的一致性,客户端应该使用会话状态标志1重复请求连接,直到连接成功。
一般来说,客户端连接时总是将清理会话标志设置为0或1,并且不交替使用两种值。这个选择取决于具体的应用。清理会话标志设置为1的客户端不会收到旧的应用消息,而且在每次连接成功后都需要重新订阅任何相关的主题。清理会话标志设置为0的客户端会收到所有在它连接断开期间发布的QoS 1和QoS 2级别的消息。因此,要确保不丢失连接断开期间的消息,需要使用QoS 1或 QoS 2级别,同时将清理会话标志设置为0。清理会话标志0的客户端连接时,它请求服务端在连接断开后保留它的MQTT会话状态。如果打算在之后的某个时间点重连到这个服务端,客户端连接应该只使用清理会话标志0。当客户端决定之后不再使用这个会话时,应该将清理会话标志设置为1最后再连接一次,然后断开连接。
|--第2位:遗嘱标志 Will Flag
遗嘱比较有用,比如说你手机app订阅了你家扫地机器人的在线状态topic,这个在线状态扫地机器人可以通过遗嘱来实现,一旦它断开了与mqttbroker连接,mqttboker就可以发送机器人在连接时就设置好的遗嘱,通知手机机器人掉线了。
如果被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。也就是确认使用遗嘱。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息。如果决定启用遗嘱,那么连接标志中的Will QoS和Will Retain字段会被服务端用到,同时有效载荷中必须包含Will Topic和Will Message字段。因为你得组织服务端遗嘱的publish的报文。
如果被设置成了0,连接标志中的Will QoS和Will Retain字段必须设置为0,并且有效载荷中不能包含Will Topic和Will Message字段。
|--第3位、第4位:遗嘱QoS Will QoS
没啥说的,遗嘱也是一个publish的消息,需要确定的消息等级。
|--第5位:遗嘱保留 Will Retain
如果遗嘱消息被发布时需要保留,需要指定这一位的值。
? ? ? ?|--第6位:用户名标志 User Name Flag
这设置0,有效载荷就不能有了,设置1就必须有这个字段。
|--第7位:密码标志 Password Flag
同上
? ? ?--第9、10个字节:保持连接 Keep Alive
保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个。保持连接的实际值是由应用指定的,一般是几分钟。允许的最大值是18小时12分15秒。
? ? ? ? 这个为什么是占N个字节,意思是指字节数是不确定的,因为连接时的用户名密码或客户端Id长度不同,那所占的字节数就不同啦,所以说是占X个字节。????????
CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端id字符长度,客户端id字符,用户名字符长度,用户名字符,密码字符长度,密码字符。比如下列的一个软件界面?
用户名字符长度是5,用户名字符内容是admin。跟前面讲的协议名称长度和协议名称一样,意思是指两个东西,不要混乱了。
有个概念必须要了解--客户端标识符id,也就是客户端的唯一标识。服务端使用客户端标识符 (ClientId) 识别客户端。连接服务端的每个客户端都有唯一的客户端标识符(ClientId)。客户端和服务端都必须使用ClientId识别两者之间的MQTT会话相关的状态。
客户端标识符 (ClientId)?必须存在而且必须是CONNECT报文有效载荷的第一个字段 ,客户端标识符必须是1.5.3节定义的UTF-8编码字符串。
?
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK。如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理?的时间取决于应用的类型和通信基础设施。
固定头没啥可说的的,报文类型是2,占1个字节
?如图所示:
占两个字节:
--第一个字节是连接确认标志,8位里面就第0位有用,其他都是0不用管。
第0位标识的是当前会话,也就是服务端告诉客户端你在我这的session是我新启动的(值是0),还是原来就有的,我只是重用(值是1)
如果之前客户端发的连接报文,让服务端清理clientsession,那服务端的session肯定是新建的,那这个标志就是0,或者是之前客户端就没连过,那这个值也是0 。
如果连接报文没有让服务端清理clientsession,并且之前客户端连接过,服务器用的是原来的会话,那这个值就是1 。
--第二个字节是连接返回码
连接返回码字段使用一个字节的无符号值【见下表】。如果服务端收到一个合法的CONNECT报文,但出于某些原因无法处理它,服务端应该尝试发送一个包含非零返回码(表格中的某一个)的CONNACK报文。
如果服务端发送了一个包含非零返回码的CONNACK报文,那么它必须关闭网络连接 。
如果认为上表中的所有连接返回码都不太合适,那么服务端必须关闭网络连接,不需要发送CONNACK报文。
CONNACK报文没有有效载荷。
这报文结构有点晕啊,不要慌,正常现象,遇到带刺的玫瑰,真香啊。