MQTT协议是一个面向物联网应用的即时通信协议,使用TCP/IP提供网络连接,能够对负载内容实现消息屏蔽传输,开销小,可以有效降低网络流量。MQTT协议适用于设备和平台需要保持长连接的使用场景,MQTT特点在于可以实现设备间的消息单播以及组播,可以不依赖于其他服务(下发命令服务,推送服务等)实现让设备以应用服务器的方式对真实设备进行管理和控制。
正因为MQTT协议拥有这些特点,现在成文了各个物联网云平台支持的最广泛的协议,百度、阿里、亚马逊、OneNet等国内外物联网云服务提供商均支持该协议,所以在做物联网开发的过程中,有必要学习和了解一下该协议。接下来我们就以OneNET的MQTT接入协议为例,学习一下该协议个通讯。
硬件连接环境:麒麟座迷你开发板用STlink连接到PC的USB口
软件开发环境:Keil MDK5.25编辑麒麟座mini开发板官方例程”6.ESP8266-MQTT_TYPE3-LED”
网络环境:PC机以太网卡连接路由器接入互联网,Windows10无线网卡建立热点,麒麟座开发板的ESP8266经过热点接入互联网。
抓包工具:Wireshart抓取以太网卡的数据包,设置过滤条件为”ip.addr == 183.230.40.39”,只显示与OneNET MQTT服务器通讯的数据包。
模拟器:simulate-device.exe,在PC上可以模拟嵌入式设备通讯。
参考文档:OneNET官方MQTT文档:”MQTT.docx”,MQTT中文文档MQTT.PDF
交互过程:连接权鉴,数据上报,命令下发,断开连接。
一、连接权鉴
首先在修改官方例程中的参数信息,把WiFi名称和密码改成使用PC无线网卡模拟的热点网络,OneNET服务器的IP地址和端口号确保为”183.230.40.39”和”6002”,onenet.c中的PROID、DEVID、AUTH_INFO修改为项目中的真实值。
编译并下载程序到麒麟座迷你开发板,在PC上使用Wireshark开始抓取以太网卡的数据包,设置过滤条件为”ip.addr == 183.230.40.39”,只显示与OneNET MQTT服务器通讯的数据包。给麒麟座开发板上电,等待几秒钟后,就可以看到开发板与OneNET服务器通讯的数据包了。
数据包中前三帧为开发板与OneNET服务器建立TCP连接的三次握手信息,这个是开发板给ESP8266发送建立TCP连接指令后,ESP8266与服务器之间自动建立的。
数据包的第四帧至第五帧为麒麟座开发板项OneNET发送的鉴权信息和服务区应答。第六帧和第七帧是OneNET服务器返回的鉴权结果信息和ESP8266的应答。
在以上过程中,我们作为设备端开发人员,只需要了解第四帧的鉴权信息发送和第六帧的服务器鉴权结果返回就可以了。
接下来重点分析第四帧数据,次帧数据总计有114字节,去除以太网头14字节,IP头20字节,TCP头20字节,剩余的TCP有效载荷共计60字节。
根据MQTT报文协议中规定,每一个MQTT包总共包含三部分:
1、Fixed Header部分定义如下:
根据抓包的数据,TCP负载的第一个字节是0x10,对应表格可以得知,MQTT Packet Type值为1,名称为CONNECT,其功能是客户端请求与服务器建立连接。其第二个字节的0x3a为表格中的Remaining Length字段,为数据包的长度。
根据MQTT协议规定,剩余长度(Remaining Length)表示当前报文剩余部分的字节数,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。0x3a为十进制的58,这个数正好是TCP负载的60字节减去固定报头的两个字节长度。至于如何判断剩余长度占用的字节数,MQTT协议是这么规定的:
剩余长度字段使用一个变长度编码方案,对小于128的值它使用单字节编码。更大的值按下面的方式处理。低7位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码128个数值和一个延续位(conTInuaTIon bit)。剩余长度字段最大4个字节。
根据以上定义,0x3a的二进制最高位为0,可以判定数据长度为1字节。
固定报头的部分分析完成后,根据下表进行判断:
2、CONNECT类型的消息是有可变报头和负载的。对于可变报头部分,按照以下格式编码:
对照抓包数据:
其中byte1-byte6是固定值,表格与数据完全对应。Byte7表示MQTT协议版本,这个必须固定为4,即3.1.1版,OneNET只支持这一版本协议,不支持更早版本的协议。
在Byte8中,user flag与password flag平台不允许匿名登陆,因此这两个标志位在连接时必须设置为1,否则认为协议错误,平台将会断开连接。所以该字节数据为0xC0。
Byte9-10为保持连接(Keep Alive),是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。如果保持连接的值非零,并且服务端在一点五倍的保持连接时间内没有收到客户端的控制报文,它必须断开客户端的网络连接,认为网络连接已断开。OneNET规定最短120秒,最长65535秒,这里设置的事0x0100,也就是256秒。
3、负载部分
负载部分的数据是按照以下格式编码
对照数据:
0x0008为域一的字符串长度,这里是8字节,内容为ASCII码的“31421353”,正好是源码中DEVID,也就是设备ID(DeviceID);0x0006为域二的字符串长度,这里是6字节,内容为ASCII码的“141215”,正好是源码中PROID,也就是产品ID(ProduceID);0x001C为域三的字符串长度,这里是28字节,内容为ASCII码的”dpsO9ruH0aTZublG9g5SvBtFSEQ=”,正好是源码中AUTH_INFO,也就是产品ID(AuthInfo);
至此,上传的鉴权信息就分析完毕了,服务器接收到鉴权信息后首先会有一个应答包,同时进行鉴权,鉴权完毕后会下发结果给客户端:
鉴权结果同样采用TCP传输,总共60个字节,除去以太网头、IP头、TCP头共计54字节,还剩余60字节,其中有效TCP负载为4字节,其后面的两个字节为TCP数据包需要四字节对齐所补充的无效数据。
服务器返回的鉴权结果同样遵循MQTT包规则,首先是固定报头,根据上文表格0x20表示服务器确认连接,0x02表示后面跟随两字节有效数据,这里就是可变报头了。
可变报头规则如下:
根据返回的数据为0x0000,表示鉴权成功了。
二、数据上报
数据上报过程其实就是TCP通讯过程,每一次上报数据需要三帧,分别是数据上报,服务器确认,客户端确认,其中只需要了解数据上报帧就可以了。
在数据上报帧中,总计有121字节,除去以太网头、IP头、TCP头共计54字节,TCP有效字节数为67字节。
1、固定报头
根据MQTT协议规定的上传报文中的固定报头格式如下:
对照抓取的数据,TCP负载第一个字节0x32中的“3”表示上传数据报文,其中的“2”表示QoS值为1。
根据MQTT协议中服务质量定义表格如下:
对照表格,表示QoS值为1,即至少分发一次。
TCP负载的第二字节的0x41表示后面数据长度为65字节。
2、可变报头
其报文格式如下:
对照抓取数据:
0x0003为域一的两字节字符串长度,这里为3个字节,内容为主题名,这里为ASCII码的”$dp”,OneNET规定,”$dp”为系统上传数据点的指令。
接下来的0x000a为报文标识符(PackeTIdenTIfier),因为之前QoS值选用的1,所以这两个字节在这里是必须的,固定为10,也就是0x000a。
3、负载
Payload包含真正的数据点内容,支持的格式如下:
对照抓取的数据:
负载的第一个字节为0x03,即Type=3,根据类型3的说明:
类型3说明后面个数据是JSON格式2的字符串,后面两个字节0x0037表示字符串的长度为55。
最后的55个字节就是上传的数据了,内容为55个ASCII码:
{"Red_Led":1,"Green_Led":1,"Yellow_Led":1,"Blue_Led":1}
这里表示上传了代表Led灯状态的四个数据流以及对应的值,OneNET服务器就会解析数据流并保存数据了。
三、命令下发
通过抓取数据包,命令下发过程需要四帧完成。
根据MQTT协议,四帧分别是服务器命令下发,客户端应答,客户端命令回复,服务器端应答。所以我们只需要了解服务器命令下发帧和客户端命令回复帧即可。
根据抓取到的数据,服务器命令下发帧总计108字节,其中TCP负载为54字节。
固定报头中的0x30表示发布消息,0x34表示后续内容有52字节。
可变报头部分数据格式如下:
根据抓取数据,0002a表示字符串长度为42字节,字符串内容为ASCII码的”$creq/e8b6c9b6-225b-57dc-abaa-246ba58761d8”。其中”$creq”为系统下发指令标记,”/”为分隔符,后续的”e8b6c9b6-225b-57dc-abaa-246ba58761d8”为该条指令的uuid,uuid是通用唯一识别码,用于识别该指令的唯一性。
最后的八个字节为MQTT数据报的负载部分,为真正的指令内容,这里是”redled:0”,客户端接收到该指令后控制led灯的亮灭。
四、断开连接
MQTT协议的断开连接没有特殊的规定,只是遵循了TCP的断开连接过程,主要就是双方各自发送FIN标记的TCP包,并给对方确认,总共四帧数据完成。
五、总结
至此OneNET官方例程中的主要MQTT协议就分析完成了,其实除了分析过的外,还有其他交易存在,比如订阅、取消订阅、创建Topic、推送Topic、离线Topic等等,但是在例程中没有用到,而且在也不是最常用的,所以这里没有分析。通过分析发现,MQTT协议非常的精炼,很适合作为物联网的控制协议,而且通过分析,基本了解了MQTT协议的主要内容,这对于下一步在各个不同平台(Arduino,STM32,树莓派,windows)通过MQTT协议接入OneNET云做了充分的准备。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)