MQTT抓包分析

03/20 17:42
阅读数 78

#1. 概述 <font size="3.5" >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。</font> #2. MQTT客户端 <font size="3.5" >一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以: (1)发布其他客户端可能会订阅的信息;  //发布消息 (2)订阅其它客户端发布的消息;   //订阅消息 (3)退订或删除应用程序的消息;    //退订消息 (4)断开与服务器连接。        //断开,连接服务器</font> #3. MQTT服务器 <font size="3.5" >MQTT服务器以称为“消息代理”(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以: (1)接受来自客户的网络连接;         //接受客户端连接 (2)接受客户发布的应用信息;        //接收客户端发布的消息 (3)处理来自客户端的订阅和退订请求;    //处理消息的订阅及退订 (4)向订阅的客户转发应用程序消息。     //推送消息</font> #4. MQTT消息格式 <font size="3.5" >每条MQTT命令消息的消息头都包含一个固定的报头,有些消息会携带一个可变报文头和一个负荷。消息格式如下:

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

<center>图4-1固定报头报文结构</center> <font size="3.5" >剩余长度字段单个字节最大值为二进制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。</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320150852163-280938519.png) <center>图4-2数据包类型</center> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320150906186-452226892.png) <center>图4-3数据包类型标识位</center> ##**4.2 可变报文头(Variable Header)** <font size="3.5" >可变报文头主要包含协议名、协议版本、连接标志(Connect Flags)、心跳间隔时间(Keep Alive timer)、连接返回码(Connect Return Code)、主题名(Topic Name)等,后面会针对主要部分进行讲解。</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320150932257-1045974009.png) <center>图4-4可变报头结构</center> ###**4.2.1 消息质量(QoS)** <font size="3.5" >MQTT消息质量有三个等级,QoS 0,QoS 1和 QoS 2。 QoS 0:最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义应答和重试,消息要么只会到达服务端一次,要么根本没有到达。 QoS 1:至少分发一次。服务器的消息接收由PUBACK消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。 QoS 2:只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。</font> ###**4.2.2 遗愿标志(Will Flag)** <font size="3.5" >在可变报文头的连接标志位字段(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字段可以应用于设备掉线后需要通知用户的场景。</font> ###**4.2.3 连接保活心跳机制(Keep Alive Timer)** <font size="3.5" >MQTT客户端可以设置一个心跳间隔时间(Keep Alive Timer),表示在每个心跳间隔时间内发送一条消息。如果在这个时间周期内,没有业务数据相关的消息,客户端会发一个PINGREQ消息,相应的,服务器会返回一个PINGRESP消息进行确认。如果服务器在一个半(1.5)心跳间隔时间周期内没有收到来自客户端的消息,就会断开与客户端的连接。心跳间隔时间最大值大约可以设置为18个小时,0值意味着客户端不断开</font> ##**4.3 有效负荷(Payload)** <font size="3.5" >Payload直译为负荷,可能让人摸不着头脑,实际上可以理解为消息主体(body)。</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320150940228-1162232424.png) <center>图4-5消息结构</center> <font size="3.5" >当MQTT发送的消息类型是CONNECT(连接)、PUBLISH(发布)、SUBSCRIBE(订阅)、SUBACK(订阅确认)、UNSUBSCRIBE(取消订阅)时,则会带有负荷。 (1)CONNECT,消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码。 (2)SUBSCRIBE,消息体内容是一系列的要订阅的主题以及QoS。 (3)SUBACK,消息体内容是服务器对于SUBSCRIBE所申请的主题及QoS进行确认和回复。 (4)UNSUBSCRIBE,消息体内容是要订阅的主题。 (5)PUBLISH,消息体内容是相关主题的数据。</font> #**5. MQTT控制报文** 介绍MQTT协议的报文组成并通过wireshark抓取报文包分析报文内容</font> ##**5.1 连接服务端(CONNECT)** <font size="3.5" >Connect报文在MQTT客户端连接服务器时发出,报文由三部分组成,下面将分别介绍 ###**5.1.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152502881-1101952461.png) <center>图5-1 CONNECT固定报头报文</center> <font size="3.5" >Connect名称的值为1,低四位保留,剩余长度保存可变报头(10字节)加上有效载荷的长度。</font> ###**5.1.2 可变报头(variable header)** <font size="3.5" >Connect报文的可变报头包含协议名,协议等级,连接标志和保持连接</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152527303-1538452794.png) <center>图5-2 CONNECT可变报头报文</center> <font size="3.5" >报文协议名之前有两个字节的报文标识符,唯一标识这条报文。</font>

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

<font size="3.5" >报文最后两字节为发送心跳包的间隔时间,当客户端没有数据发给服务器时,须发送心跳包(pingreq)到服务器,保证连接不断开。</font> ###5.1.3 有效载荷(Payload) <font size="3.5" >CONNECT报文的有效载荷(payload)包含一个或多个以长度为前缀的字段,可变报头中的 标志决定是否包含这些字段。如果包含的话,必须按这个顺序出现:客户端标识符,遗嘱主题,遗嘱消息,用户名,密码。</font> <font size="3.5" >客户端标识符(client identifier):服务器通过识别客户标识符,确定客户端,识别两者间的MQTT会话相关状态,服务器允许客户端提供一个零字节的标识符,但clean session必须置1。</font>

<center>图5-3 CONNECT消息报文</center> ##**5.2连接请求确认(CONNACK)** <font size="3.5" >Connack为服务器确认客户端连上服务给出的回应。</font> ###**5.2.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152606537-1440672283.png) <center>图5-4 CONNACK固定报头报文</center> ###**5.2.2 可变报头(variable header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152620341-300622870.png) <center>图5-5 CONNACK可变报头报文</center> <li><font size="3.5" >第一字节1~7位保留,第0位是当前会话标志(session present),若清除会话标志被置1,该位为0,若清除会话标志为0且服务器没有与客户端会话保存,该位置0,有保存置1 第二字节保存连接返回码,若连接成功返回0。</font></li> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152634029-1732163616.png) <center>图5-6 连接返回码</center> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152649991-848414091.png) <center>图5-7 CONNECT响应报文</center> ##**5.3发布消息(PUBLISH)** <font size="3.5" >发布消息可由客户端或服务器发出,被消息订阅者接收。</font> ###**5.3.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152718712-1229234375.png) <center>图5-8 PUBLISH固定报头报文</center> <font size="3.5" >固定报头第一字节的低四位分别保存重发标志(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报文会被服务端当作正常消息处理,它会被发送给订阅主题匹配的客户端。此外,同一个主题下任何现存的保留消息必须被移除,因此这个主题之后的任何订阅者都不会收到一个保留消息。</font> ###**5.3.2 可变报头(variable header)** <font size="3.5" >可变报头按顺序包含主题名和报文标识符。</font> ###**5.3.3 有效载荷(Payload)** <font size="3.5" >若Qos为0,无响应,若Qos为1,返回PUBACK报文,若Qos为2,返回PUBREC报文。</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152740173-156557036.png) <center>图5-9 publish消息报文</center> ##**5.4发布确认(PUBACK)** <font size="3.5" >由服务器或客户端确认已接收到pub消息</font> ###**5.4.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152807457-923204715.png) <center>图5-10 puback固定报头报文</center> ###**5.4.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识(报文标识为pub报文标识)</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152832130-509271420.png) <center>图5-11 puback消息报文</center> ##**5.5发布收到 PUBREC(QoS 2,第一步)** ###**5.5.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152856939-772322678.png) <center>图5-12 pubrec固定报头报文</center> ###**5.5.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识(报文标识为pub报文标识)</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152911010-655005968.png) <center>图5-13 pubrec消息报文</center> ##**5.6发布释放 PUBREL(QoS 2,第二步)** ###**5.6.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320152954172-814314719.png) <center>图5-14 pubrel固定报头报文</center> ###**5.6.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识(报文标识为pub报文标识)</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320153007812-629320514.png) <center>图5-15 pubrel消息报文</center> ##**5.7发布完成 PUBCOMP(QoS 2,第三步)** ###**5.7.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320153025750-118822763.png) <center>图5-16 pubcomp固定报头报文</center> ###**5.7.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识(报文标识为pub报文标识)</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320153034649-1375435819.png) <center>图5-17 pubcomp消息报文</center> ##**5.8订阅主题(SUBSCRIBE)** <font size="3.5" >客户端通过订阅消息的方式来接收服务端下发的消息</font> ###**5.8.1 固定报头(fixed header)** <font size="3.5" >SUBSCRIBE控制固定报头的第3,2,1,0位是保留位,必须分别设置为0,0,1,0。服务端必须将其它的任何值都当做是不合法的并关闭网络连接</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320153101878-1869472025.png) <center>图5-18 subscribe固定报头报文</center> ###**5.8.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识</font> ###**5.8.3 有效载荷(Payload)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320153119443-596637999.png) <center>图5-19 subscribe有效载体报文</center> <font size="3.5" >消息载体包含若干主题过滤器和服务质量等级 PUB时指定的qos是服务器肯定按此规则接收,但是最终订阅者不一定。 SUB时指定的qos表示订阅者可以接收的最高消息等级,也就是可能收到更低等级的消息</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320153136571-1307764657.png) <center>图5-20 subscribe消息报文</center> ##**5.9订阅确认(SUBACK)** <font size="3.5" >SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。</font> ###**5.9.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154334307-1378010234.png) <center>图5-21 suback固定报头报文</center> ###**5.9.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识</font> ###**5.9.3 有效载荷(Payload)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154349990-648069661.png) <center>图5-22 suback有效载体报文</center> <font size="3.5" >允许的返回码为0x00-最大Qos0,0x01-最大Qos1,0x02-最大Qos2,0x80-失败</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154522507-1860735945.png) <center>图5-23 suback消息报文</center> ##**5.10取消订阅 (UNSUBSCRIBE)** ###**5.10.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154543249-1742510210.png) <center>图5-24 unsubscribe固定报头报文</center> ###**5.10.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识</font> ###**5.10.3 有效载荷(Payload)** <font size="3.5" >包含需要取消订阅的主题过滤器的列表.</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154608199-1868739192.png) <center>图5-25 unsubscribe消息报文</center> ##**5.11取消订阅确认 (UNSUBACK)** ###**5.11.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154622466-1917006411.png) <center>图5-26 unsuback固定报头报文</center> ###**5.11.2 可变报头(variable header)** <font size="3.5" >只有两字节的报文标识(报文标识为unsub报文标识)</font> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154633999-970033348.png) <center>图5-27 unsuback消息报文</center> ##**5.12心跳请求 (PINGREQ)** <font size="3.5" >客户端发送PINGREQ报文给服务端的。用于: 1.在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。 2.请求服务端发送 响应确认它还活着。 3.使用网络以确认网络连接没有断开</font> ###**5.12.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320154704660-972406072.png) <center>图5-28 pingreq固定报头报文</center> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320161125403-959049655.png) <center>图5-29 pingreq消息报文</center> ##**5.13心跳响应 (PINGRESP)** ###**5.13.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320161144777-294459415.png) <center>图5-30 pingresp固定报头报文</center> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320161200671-719016214.png) <center>图5-31 pingresp消息报文</center> ##**5.14断开连接 (DISCONNECT)** ###**5.14.1 固定报头(fixed header)** ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320161214234-73571096.png) <center>图5-32 disconnect固定报头报文</center> ![](https://img2020.cnblogs.com/blog/1641952/202003/1641952-20200320161240762-417041719.png) <center>图5-33 disconnect消息报文</center>

<font size="3.5" >抓包注意:用标准不加密MQTT能抓到便于查看的报文,加密报文解析时不便于理解</font>

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

清风 | 文 【原创】

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

原文出处:https://www.cnblogs.com/wdg-blog/p/12532836.html

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部