MQTT协议
MQTT是轻量级基于代理的发布/订阅的消息传输协议,它可以通过很少的代码和带宽和远程设备连接。例如通过卫星和代理连接,通过拨号和医疗保健提供者连接,以及在一些自动化或小型设备上,而且由于小巧,省电,协议开销小和能高效的向一和多个接收者传递信息,故同样适用于称动应用设备上。
Mqtt协议特点:
1.使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合。
2.协议简单,最小的头部只需2个字节,特别适合于嵌入式中。
3.对负载内容屏蔽的消息传输。
4.使用 TCP/IP 提供网络连接。
5.有三种消息发布服务质量:
(1) “至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
(2) “至少一次”,确保消息到达,但消息重复可能会发生。
(3) “只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
6. 使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。
PUB/SUB 模型
Mqtt结构
固定头部,使用两个字节,共16位:
bit |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
byte 1 |
Message Type |
DUP flag |
QoS level |
RETAIN |
||||
byte 2 |
Remaining Length |
消息类型
使用4位二进制表示,可代表16种消息类型
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)----发布消息被接收 |
PUBREL |
6 |
Publish Release (assured delivery part 2) |
PUBCOMP |
7 |
Publish Complete (assured delivery part 3)----发布消息完成 |
SUBSCRIBE |
8 |
Client Subscribe request----客户端订阅 |
SUBACK |
9 |
Subscribe Acknowledgment----订阅反馈 |
UNSUBSCRIBE |
10 |
Client Unsubscribe request----客户端解除订阅 |
UNSUBACK |
11 |
Unsubscribe Acknowledgment----接触订阅反馈 |
PINGREQ |
12 |
PING Request-------心跳检测 |
PINGRESP |
13 |
PING Response----心跳反馈 |
DISCONNECT |
14 |
Client is Disconnecting----客户端断开连接 |
Reserved |
15 |
Reserved----保留待用 |
消息类型使用的场景这里我使用的是wireshark 抓包工具
首先我先来测试demo1也就是消息类型QOS
代码中我没有设置QOS类型所以他会默认选0;
(至多发送一次,发送即丢弃。没有确认消息,也不知道对方是否收到)
我本机ip是192.168.101.248 向192.168.56.3服务器发送订阅请求如上图mqtt使用的是TCP/IP网络传输所以也要经过三次握手原则。在客户机和服务器之间建立正常的TCP网络连接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递,也就是上面mqtt的特点:使用 TCP/IP 提供网络连接。
名词解释:SYN表示建立连接, FIN表示关闭连接, ACK表示响应, PSH表示有DATA数据传输, RST表示连接重置。
在Connect 中可以看到我的发布者ID为 demo也就是我代码中所写的:
第二个Connect Ack 内容
可以看到是0;因为我没有设置QOS消息方法所以默认是0;0代表最多1次,发完就丢弃,不保证消息是否收到。下面可以参考QOS服务质量资料。
Publish message 也就是我们发送的消息了
因为这里汉字是采用8进制表示的,如果转码需要经过转为16进制每两位代表一个汉字共四位,从中间的topic就可以看出那个就是我们发送的消息了。
PUBACK
服务器向我们返回的信息也就是这个信息的ID号了。
DISCONNECT
-客户端断开连接,因为我在代码中最后执行了断开连接的方法所以会通知服务器。代码如图:
QOS 1
(所有QoS level 1都要在可变头部中附加一个16位的消息ID提供SUBSCRIBE(订阅)和UNSUBSCRIBE(退订)消息使用。针对消息的发布,Qos level 1,意味着消息至少被传输一次。发送者若在一段时间内接收不到PUBACK消息,发送者需要打开DUB标记为1,然后重新发送PUBLISH消息。因此会导致接收方可能会收到两次PUBLISH消息)
因为连接的是时候可以接受到mqtt服务器的回复,在送出去消息的时候要让服务器回执发送不到客户端,这个测试环境不好测试所以就没例子。
QOS 2
从上图可以看出首先发布者连接mqtt服务器,然后得到服务器的回复,发布者就开始发送消息,服务器这边回复 publish received也就是告诉发布者你的消息我收到了,接着发布告诉服务器,我收到你的回复了,那条消息你可以删除了,然后服务器又回复发布者我做完了,最后发布者断开连接。
QOS 3 (mqtt 预留的信息类型待用。这里就没测试的必要了)
DUP flag(打开标志)
保证消息可靠传输,默认为0,只占用一个字节,表示第一次发送。不能用于检测消息重复发送等。只适用于客户端或服务器端尝试重发PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要满足以下条件:当QoS > 0 消息需要回复确认此时,在可变头部需要包含消息ID。当值为1时,表示当前消息先前已经被传送过。
QoS(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 |
待用,保留位置 |
RETAIN(保持)
仅针对PUBLISH消息。不同值,不同含义:
1:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。
这里我设置为true 也就是1,测试的topic为testRetain
这里我先让一个订阅者jieshou订阅一个testRetain,现在来测试下发布者发布一条消息的结果。这里发布者已经发布了一条消息
已经订阅的订阅者已经接受到了消息
现在再来启动第二个订阅者订阅者叫jieshou2
新来的订阅者也接受到了该信息。备注:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送。
0:仅仅为当前订阅者推送此消息。
假如服务器收到一个空消息体(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服务器可以删除掉对应的已被持久化的PUBLISH消息。
发送者发送一条消息给订阅方。
开启第二个新订阅者
新的订阅者没有接收到消息
Remaining Length(剩余长度)
在当前消息中剩余的byte(字节)数,包含可变头部和负荷(称之为内容/body,更为合适)。单个字节最大值:01111111,16进制:0x7F,10进制为127。单个字节为什么不能是11111111(0xFF)呢?因为MQTT协议规定,第八位(最高位)若为1,则表示还有后续字节存在。同时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可变头部User name标记若为1则需要在消息体中附加用户名称字符串)而存在。
CONNECT/SUBSCRIBE/SUBACK/PUBLISH等消息有消息体。PUBLISH的消息体以二进制形式对待。
我这里使用的是paho mqtt框架所以消息体中二进制转义等一系列功夫就不需要关心太多,只需要记住mqtt协议消息体最大容量和消息的表现形式就可以了。具体转义实现可以参考:http://www.blogjava.net/yongboy/archive/2014/02/07/409587.html
当我强制关闭客户端程序的时候
也就是mqtt协议特点 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。 ·
其他问题参考文献:http://www.blogjava.net/yongboy/archive/2014/02/07/409587.html,
http://blog.csdn.net/u013944791/article/details/44218471