转载MQTT

转载地址:https://www.cnblogs.com/wdg-blog/p/12532836.html

1. 概述

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布,目前最新版本为v3.1.1。MQTT最大的优点在于可以以极少的代码和有限的带宽,为远程设备提供实时可靠的消息服务。做为一种低开销、低带宽占用的即时通讯协议,MQTT在物联网、小型设备、移动应用等方面有广泛的应用。众所周知,TCP/IP参考模型可以分为四层:应用层、传输层、网络层、链路层。TCP和UDP位于传输层,应用层常见的协议有HTTP、FTP、SSH等。MQTT协议运行于TCP之上,属于应用层协议,因此只要是支持TCP/IP协议栈的地方,都可以使用MQTT。

2. MQTT客户端

一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以:
(1)发布其他客户端可能会订阅的信息;  //发布消息
(2)订阅其它客户端发布的消息;   //订阅消息
(3)退订或删除应用程序的消息;    //退订消息
(4)断开与服务器连接。        //断开,连接服务器

3. MQTT服务器

MQTT服务器以称为“消息代理”(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以:
(1)接受来自客户的网络连接;         //接受客户端连接
(2)接受客户发布的应用信息;        //接收客户端发布的消息
(3)处理来自客户端的订阅和退订请求;    //处理消息的订阅及退订
(4)向订阅的客户转发应用程序消息。     //推送消息

4. MQTT消息格式

每条MQTT命令消息的消息头都包含一个固定的报头,有些消息会携带一个可变报文头和一个负荷。消息格式如下:

固定报文头 | 可变报文头 | 负荷

4.1 固定报文头(Fixed Header)

MQTT固定报文头最少有两个字节,第一字节包含消息类型(Message Type)和QoS级别等标志位。第二字节开始是剩余长度字段,该长度是后面的可变报文头加消息负载的总长度,不包括用于编码剩余长度字段本身的字节数,该字段最多允许四个字节。

图4-1固定报头报文结构


剩余长度字段单个字节最大值为二进制0b0111 1111,16进制0x7F。也就是说,单个字节可以描述的最大长度是127字节。为什么不是256字节呢?因为MQTT协议规定,单个字节第八位(最高位)若为1,则表示后续还有字节存在,第八位起“延续位”的作用。
例如,数字64,编码为一个字节,十进制表示为64,十六进制表示为0×40。数字321(65+2*128)编码为两个字节,重要性最低的放在前面,第一个字节为65+128=193(0xC1),第二个字节是2(0x02),表示2×128。
由于MQTT协议最多只允许使用四个字节表示剩余长度(如表1),并且最后一字节最大值只能是0x7F不能是0xFF,所以能发送的最大消息长度是256MB,而不是512MB。

图4-2数据包类型


图4-3数据包类型标识位

 

4.2 可变报文头(Variable Header)

可变报文头主要包含协议名、协议版本、连接标志(Connect Flags)、心跳间隔时间(Keep Alive timer)、连接返回码(Connect Return Code)、主题名(Topic Name)等,后面会针对主要部分进行讲解。

图4-4可变报头结构

 

4.2.1 消息质量(QoS)

MQTT消息质量有三个等级,QoS 0,QoS 1和 QoS 2。
QoS 0:最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义应答和重试,消息要么只会到达服务端一次,要么根本没有到达。
QoS 1:至少分发一次。服务器的消息接收由PUBACK消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。
QoS 2:只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。

4.2.2 遗愿标志(Will Flag)

在可变报文头的连接标志位字段(Connect Flags)里有三个Will标志位:Will Flag、Will QoS和Will Retain Flag,这些Will字段用于监控客户端与服务器之间的连接状况。如果设置了Will Flag,就必须设置Will QoS和Will Retain标志位,消息主体中也必须有Will Topic和Will Message字段。
那遗愿消息是怎么回事呢?服务器与客户端通信时,当遇到异常或客户端心跳超时的情况,MQTT服务器会替客户端发布一个Will消息。当然如果服务器收到来自客户端的DISCONNECT消息,则不会触发Will消息的发送。
因此,Will字段可以应用于设备掉线后需要通知用户的场景。

4.2.3 连接保活心跳机制(Keep Alive Timer)

MQTT客户端可以设置一个心跳间隔时间(Keep Alive Timer),表示在每个心跳间隔时间内发送一条消息。如果在这个时间周期内,没有业务数据相关的消息,客户端会发一个PINGREQ消息,相应的,服务器会返回一个PINGRESP消息进行确认。如果服务器在一个半(1.5)心跳间隔时间周期内没有收到来自客户端的消息,就会断开与客户端的连接。心跳间隔时间最大值大约可以设置为18个小时,0值意味着客户端不断开

4.3 有效负荷(Payload)

Payload直译为负荷,可能让人摸不着头脑,实际上可以理解为消息主体(body)。

图4-5消息结构


当MQTT发送的消息类型是CONNECT(连接)、PUBLISH(发布)、SUBSCRIBE(订阅)、SUBACK(订阅确认)、UNSUBSCRIBE(取消订阅)时,则会带有负荷。
(1)CONNECT,消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码。
(2)SUBSCRIBE,消息体内容是一系列的要订阅的主题以及QoS。
(3)SUBACK,消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。
(4)UNSUBSCRIBE,消息体内容是要订阅的主题。
(5)PUBLISH,消息体内容是相关主题的数据。

 

5. MQTT控制报文

介绍MQTT协议的报文组成并通过wireshark抓取报文包分析报文内容

5.1 连接服务端(CONNECT)

Connect报文在MQTT客户端连接服务器时发出,报文由三部分组成,下面将分别介绍

5.1.1 固定报头(fixed header)

图5-1 CONNECT固定报头报文


Connect名称的值为1,低四位保留,剩余长度保存可变报头(10字节)加上有效载荷的长度。

 

5.1.2 可变报头(variable header)

Connect报文的可变报头包含协议名,协议等级,连接标志和保持连接

图5-2 CONNECT可变报头报文


报文协议名之前有两个字节的报文标识符,唯一标识这条报文。

 

连接标志包含用户名标志(username flag)、密码标志(password flag)、遗嘱标志(will flag)、遗嘱服务指令(will Qos)、遗嘱保留标志(will retain)、清除会话标志(clean session)、保留位(reserved)。
用户名标志(username flag):若用户名标志被置为1,有效载荷中必须包含用户名字段。
密码标志(password flag):若密码标志被置为1,有效载荷中必须包含密码字段,当用户标志被置为0时,密码标志必须被置0.。
遗嘱标志(will flag):若遗嘱标志被置1,遗嘱服务指令(will Qos)与遗嘱保留标志(will retain)会被服务器用到,遗嘱消息中必须包含will topic和will message。
遗嘱服务指令(will Qos):如果遗嘱标志被设置为0,遗嘱QoS也必须设置为0(0x00),如果遗嘱标志被设置为1,遗嘱QoS的值可以等于0(0x00),1(0x01),2(0x02)。它的值不能等 于3。
遗嘱保留标志(will retain):若遗嘱保留标志位被置位,服务器将保留遗嘱消息(保留发布),当客户端异常断开连接时将遗嘱发给订阅遗嘱主题的客户。
清除会话标志(clean session):标志被设置为1,客户端和服务端必须丢弃之前的任何会话并开始一个新的会话(之前的订阅与发布消息被删除),若标志为0,恢复与服务器会话连接,若没有连接 新建一个会话连接(不删除之前与客户端的会话信息并保存断开本次会话之后的Qos1与Qos2消息)。
保留位(reserved):如果不为0必须断开客 户端连接。

报文最后两字节为发送心跳包的间隔时间,当客户端没有数据发给服务器时,须发送心跳包(pingreq)到服务器,保证连接不断开。

5.1.3 有效载荷(Payload)

CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的 标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码。
客户端标识符(client identifier):服务器通过识别客户标识符,确定客户端,识别两者间的MQTT会话相关状态,服务器允许客户端提供一个零字节的标识符,但clean session必须置1。

图5-3 CONNECT消息报文

 

5.2连接请求确认(CONNACK)

Connack为服务器确认客户端连上服务给出的回应。

5.2.1 固定报头(fixed header)

图5-4 CONNACK固定报头报文

 

5.2.2 可变报头(variable header)

图5-5 CONNACK可变报头报文

 

  • 第一字节1~7位保留,第0位是当前会话标志(session present),若清除会话标志被置1,该位为0,若清除会话标志为0且服务器没有与客户端会话保存,该位置0,有保存置1
    第二字节保存连接返回码,若连接成功返回0。



图5-6 连接返回码



图5-7 CONNECT响应报文

 

5.3发布消息(PUBLISH)

发布消息可由客户端或服务器发出,被消息订阅者接收。

5.3.1 固定报头(fixed header)

图5-8 PUBLISH固定报头报文


固定报头第一字节的低四位分别保存重发标志(DUP),服务质量(Qos),保留标志(RETAIN)。
重发标志(DUP):当该位被置1,表示该条报文为重发报文,客户端或服务端请求重发一个PUBLISH报文时,必须将DUP标志设置为1。对于QoS 0的消息,DUP标志必须设置为0。
服务质量(Qos):表示发送质量,取值有00 01 10。
保留标志(RETAIN):若该位被置1,表示服务器必须保存其应用消息和质量等级,若为0,表示该消息不须保存,如果服务端收到一条保留(RETAIN)标志为1的QoS 0消息,它必须丢弃之前为那个主题保留的任何消息。它应该将这个新的QoS0消息当作那个主题的新保留消息,但是任何时候都可以选择丢弃它,保留标志为1且有效载荷为零字节的PUBLISH报文会被服务端当作正常消息处理,它会被发送给订阅主题匹配的客户端。此外,同一个主题下任何现存的保留消息必须被移除,因此这个主题之后的任何订阅者都不会收到一个保留消息。

 

5.3.2 可变报头(variable header)

可变报头按顺序包含主题名和报文标识符。

5.3.3 有效载荷(Payload)

若Qos为0,无响应,若Qos为1,返回PUBACK报文,若Qos为2,返回PUBREC报文。

图5-9 publish消息报文

 

5.4发布确认(PUBACK)

由服务器或客户端确认已接收到pub消息

5.4.1 固定报头(fixed header)

图5-10 puback固定报头报文

 

5.4.2 可变报头(variable header)

只有两字节的报文标识(报文标识为pub报文标识)

图5-11 puback消息报文

 

5.5发布收到 PUBREC(QoS 2,第一步)

5.5.1 固定报头(fixed header)

图5-12 pubrec固定报头报文

 

5.5.2 可变报头(variable header)

只有两字节的报文标识(报文标识为pub报文标识)

图5-13 pubrec消息报文

 

5.6发布释放 PUBREL(QoS 2,第二步)

5.6.1 固定报头(fixed header)

图5-14 pubrel固定报头报文

 

5.6.2 可变报头(variable header)

只有两字节的报文标识(报文标识为pub报文标识)

图5-15 pubrel消息报文

 

5.7发布完成 PUBCOMP(QoS 2,第三步)

5.7.1 固定报头(fixed header)

图5-16 pubcomp固定报头报文

 

5.7.2 可变报头(variable header)

只有两字节的报文标识(报文标识为pub报文标识)

图5-17 pubcomp消息报文

 

5.8订阅主题(SUBSCRIBE)

客户端通过订阅消息的方式来接收服务端下发的消息

5.8.1 固定报头(fixed header)

SUBSCRIBE控制固定报头的第3,2,1,0位是保留位,必须分别设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接

图5-18 subscribe固定报头报文

 

5.8.2 可变报头(variable header)

只有两字节的报文标识

5.8.3 有效载荷(Payload)

图5-19 subscribe有效载体报文


消息载体包含若干主题过滤器和服务质量等级
PUB时指定的qos是服务器肯定按此规则接收,但是最终订阅者不一定。
SUB时指定的qos表示订阅者可以接收的最高消息等级,也就是可能收到更低等级的消息


图5-20 subscribe消息报文

 

5.9订阅确认(SUBACK)

SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。

5.9.1 固定报头(fixed header)

图5-21 suback固定报头报文

 

5.9.2 可变报头(variable header)

只有两字节的报文标识

5.9.3 有效载荷(Payload)

图5-22 suback有效载体报文


允许的返回码为0x00-最大Qos0,0x01-最大Qos1,0x02-最大Qos2,0x80-失败

图5-23 suback消息报文

 

5.10取消订阅 (UNSUBSCRIBE)

5.10.1 固定报头(fixed header)

图5-24 unsubscribe固定报头报文

 

5.10.2 可变报头(variable header)

只有两字节的报文标识

5.10.3 有效载荷(Payload)

包含需要取消订阅的主题过滤器的列表.

图5-25 unsubscribe消息报文

 

5.11取消订阅确认 (UNSUBACK)

5.11.1 固定报头(fixed header)

图5-26 unsuback固定报头报文

 

5.11.2 可变报头(variable header)

只有两字节的报文标识(报文标识为unsub报文标识)

图5-27 unsuback消息报文

 

5.12心跳请求 (PINGREQ)

客户端发送PINGREQ报文给服务端的。用于:
1.在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
2.请求服务端发送 响应确认它还活着。
3.使用网络以确认网络连接没有断开

5.12.1 固定报头(fixed header)

图5-28 pingreq固定报头报文



图5-29 pingreq消息报文

 

5.13心跳响应 (PINGRESP)

5.13.1 固定报头(fixed header)

图5-30 pingresp固定报头报文



图5-31 pingresp消息报文

 

5.14断开连接 (DISCONNECT)

5.14.1 固定报头(fixed header)

图5-32 disconnect固定报头报文



图5-33 disconnect消息报文

 

抓包注意:用标准不加密MQTT能抓到便于查看的报文,加密报文解析时不便于理解

创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!

清风 | 文 【原创】

如果本篇博客有任何错误,请批评指教,不胜感激 !

posted @ 2020-03-20 19:31  maanshancss  阅读(281)  评论(0编辑  收藏  举报