MQTT协议分析

Publish: November 3, 2016 Category: 小玩具,编程 2 Comments

比较全面的MQTT协议分析文档

目录

使用研究

协议分析

连接和心跳

发布流程

消息流

订阅流程

使用研究:

服务端:http://mosquitto.org/download/<;;/span>

编译安装:http://blog.csdn.net/xukai871105/article/details/39252653<;;/span>

配置说明

http://cswei.blog.51cto.com/3443978/1225617<;;/span>

PHP实现

https://github.com/mgdm/Mosquitto-PHP<;;/span>

LUA实现

https://github.com/flukso/lua-mosquitto<;;/span>

译文

http://my.oschina.net/scholer/blog/296402<;;/span>

PHP环境相关

http://www.hivemq.com/blog/mqtt-client-library-encyclopedia-mosquitto-php<;;/span>

http://wingsquare.com/blog/developing-php-client-for-mqtt-using-mosquitto-php-library/<;;/span>

mqtt 遇到的错误收集:

Error: Invalid user 'mosquitto' 没有mosquitto的用户

两种解决办法:

(1)修改配置文件:mosquitto.conf ,增加登录的用户,例如当前登录用户为root

60444674196.jpeg

(1)执行命令增加当前用户:

adduser mosquitto

如下图:

75166231759.jpeg

•PINGREQ/PINGRESP 心跳

功能测试:

1) Connect

2) Disconnect

3) Subscribe

4) UnSubscribe

5) Publish QOS0,QOS1, QOS2

6) Retain 【为新来的订阅者推送那些已经存在的持久化消息】

7) Will 特性【发布遗愿】

协议分析:

先说一下整个协议的构造,整体上协议可拆分为:

固定头部+可变头部+消息体

固定头部

002110437924.png

协议头占两个字节

BYTE1

【RETAIN】0 : 保持;针对PUBLISH消息。

1 表示发送的消息一直持久保存,不受服务端重启影响,不但要发送给当前订阅者,并且以后新来的订阅者订阅了此topic name的订阅者会马上得到推送,新订阅者只会接受一次flag为1的消息。

0 仅为当前订阅者推送此消息,假如收到一个空消息体(zero_length_payload)RETAIN=1,已存在topic name的publish消息,服务器可以删除对应已被持久化的PUBLISH消息。

【Qos level】1、2 : Quality of Service服务质量,使用两个二进制表示的publish类型消息

QoS value

bit 2

bit 1

Description

0

0

0

至多一次

发完即丢弃

<=1

1

0

1

至少一次

需要确认回复

=1

2

1

0

只有一次

需要确认回复

=1

3

1

1

待用,保留位置

【DUP flag】3 打开标志

保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重发送等。只适用于客户端或者服务端尝试重发PUBLISH、PUBREL、SUBCRIBE或UNSUBSCRIBE消息注意需要满足以下条件:当QOS>0 消息需要回复确认 此时,在可变头部需要包含消息ID。当值为1时,表示当前消息先前已经被传送过。

【Message Type】4、5、6、7 消息类型x

Mnemonic

Enumeration

Description

Reserved

0

Reserved //保留待用

CONNECT

1

Client request to connect to Server //客户端请求连接到服务端

CONNACK

2

Connect Acknowledgment //请求应答,请求连接确认

PUBLISH

3

Publish message //发布消息

PUBACK

4

Publish Acknowledgment //发布应答,发布确认

PUBREC

5

Publish Received (assured delivery part 1) //发布已接收,保证传递1

PUBREL

6

Publish Release (assured delivery part 2) //发布释放,保证传递2

PUBCOMP

7

Publish Complete (assured delivery part 3) //发布完成,保证传递3

SUBSCRIBE

8

Client Subscribe request //客户端订阅请求

SUBACK

9

Subscribe Acknowledgment //订阅应答,订阅请求

UNSUBSCRIBE

10

Client Unsubscribe request //客户端取消订阅请求

UNSUBACK

11

Unsubscribe Acknowledgment //取消订阅应答,取消订阅确认

PINGREQ

12

PING Request //ping请求

PINGRESP

13

PING Response //ping响应

DISCONNECT

14

Client is Disconnecting //客户端断开连接

Reserved

15

Reserved //保留待用

BYTE2

是用来保存接下去的变长头部+消息体的总大小的。

但是不是并不是直接保存的,同样也是可以扩展的,其机制是,前7位用于保存长度,后一部用做标识。举个例了,即如果计算出后面的大小为0的,正常保存,如果是127的,则需要二个字节保存了,将第一个字节的最大的一位置1,表示未完。然后第二个字节继续存。拿130来说,第一个字节存10000011,第二个字节存000000001,也就是0x83,0x01,把两个字节连起来看,第二个字节权重从2的8次开始。同起可以加第3个字节,最多可以加至第4个字节。故MQTT协议最多可以实现268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)将近256M的数据。可谓能伸能缩。

单个字节最大值,01111111=>0x7f=>127

时MQTT协议最多允许4个字节表示剩余长度。那么最大长度为:0xFF,0xFF,0xFF,0x7F,二进制表示为:11111111,11111111,11111111,01111111,十进制:268435455 byte=261120KB=256MB=0.25GB 四个字节之间值的范围:

Digits

From

To

1

0 (0x00)

127 (0x7F)

2

128 (0x80, 0x01)

16 383 (0xFF, 0x7F)

3

16 384 (0x80, 0x80, 0x01)

2 097 151 (0xFF, 0xFF, 0x7F)

4

2 097 152 (0x80, 0x80, 0x80, 0x01)

268 435 455 (0xFF, 0xFF, 0xFF, 0x7F)

可变头部

固定头部仅定义了消息类型和一些标志位,一些消息的元数据,需要放入可变头部中。可变头部内容字节长度 + Playload/负荷字节长度 = 剩余长度,这个是需要牢记的。可变头部,包含了协议名称,版本号,连接标志,用户授权,心跳时间等内容,这部分和后面要讲到的CONNECT消息类型,有重复,暂时略过。

218212825500.png

这个是可变头部的全貌。

1、首先最上面的8个字节是Protocol Name(编码名),UTF编码的字符“MQIsdp”,头两个是编码名提长为6。

这里多说一些,接下去的协议多采用这种方式组合,即头两个字节表示下一部分的长,然后后面跟上内容。这里头两个字节长为6,下面跟6个字符“MQIsdp”。

2、Protocol Version,协议版本号,v3 也是固定的。

3、Connect Flag,连接标识,有点像固定头部的。8位分别代表不同的标志。第1个字节保留。

Clean Session,Will flag,Will Qos, Will Retain都是相对于CONNECT消息来说的。

Clean Session:0表示如果订阅的客户机断线了,那么要保存其要推送的消息,如果其重新连接时,则将这些消息推送。

1表示消除,表示客户机是第一次连接,消息所以以前的连接信息。

Will Flag,表示如果客户机在不是在发送DISCONNECT消息中断,比如IO错误等,将些置为1,要求重传。并且下且的WillQos和WillRetain也要设置,消息体中的Topic和MessageID也要设置,就是表示发生了错误,要重传。

Will Qos,在CONNECT非正常情况下设置,一般如果标识了WillFlag,那么这个位置也要标识。

Will RETAIN:同样在CONNECT中,如果标识了WillFlag,那么些位也一定要标识

usename flag和passwordflag,用来标识是否在消息体中传递用户和密码,只有标识了,消息体中的用户名和密码才用效,只标记密码而不标记用户名是不合法的。

4、Keep Alive,表示响应时间,如果这个时间内,连接或发送操作未完成,则断开tcp连接,表示离线。

5、Connect Return Code即通常于CONNACK消息中,表示返回的连接情况,我可以通过此检验连接情况。

bVyoew.png

6、Topic Name,订阅消息标识,MQTT是基于订阅/发布的消息,那么这个就是消息订阅的标识,像新闻客户端里的订阅不同的栏目一样。用于区别消息的推送类别。

主要用于PUBLISH和SUBSCRIBE中。最大可支持32767个字符,即4个字节。

消息体(Playload/消息体/负荷)

消息体主要是为配合固定/可变头部命令(比如CONNECT可变头部User name标记若为1则需要在消息体中附加用户名称字符串)而存在。

CONNECT/SUBSCRIBE/SUBACK/PUBLISH等消息有消息体。PUBLISH的消息体以二进制形式对待。

请记住,MQTT协议只允许在PUBLISH类型消息体中使用自定义特性,在固定/可变头部想加入自定义私有特性,就免了吧。这也是为了协议免于流于形式,变得很分裂也为了兼顾现有客户端等。比如支持压缩等,那就可以在Playload中定义数据支持,在应用中进行读取处理。

这部分会在后面详细论述。

消息标识符/消息ID

固定头中的QoS level标志值为1或2时才会在:PUBLISH,PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE,SUBACK,UNSUBSCRIBE,UNSUBACK等消息的可变头中出现。

一个16位无符号位的short类型值(值不能为 0,0做保留作为无效的消息ID),仅仅要求在一个特定方向(服务器发往客户端为一个方向,客户端发送到服务器端为另一个方向)的通信消息中必须唯一。比如客户端发往服务器,有可能存在服务器发往客户端会同时存在重复,但不碍事。

可变头部中,需要两个字节的顺序是MSB(Most Significant Bit) LSB(Last/Least Significant Bit),翻译成中文就是,最高有效位,最低有效位。最高有效位在最低有效位左边/上面,表示这是一个大端字节/网络字节序,符合人的阅读习惯,高位在最左边。

bit

7

6

5

4

3

2

1

0

Message Identifier MSB

Message Identifier LSB

但凡如此表示的,都可以视为一个16位无符号short类型整数,两个字节表示。在JAVA中处理比较简单:

DataInputStream.readUnsignedShort

或者

in.read() * 0xFF + in.read();

最大长度可为: 65535

基于workmen的客户端实现:

MQTT协议--连接和心跳

截图.png

可变头部

协议名称和协议版本都是固定的。

连接标志(Connect Flags)

一个字节表示,除了第1位是保留未使用,其它7位都具有不同含义。

业务上很重要,对消息总体流程影响很大,需要牢记。

Clean Session

0

Tags: mqtt, 协议分析

Related Posts:

2 Comments »

  1. lidashuang

    现在用这个吗

Leave a Comment