写在前面:
MQTT协议内容的基本概述以及入门使用
Spring-intergration整合Eclipse.paho作为MQTTClient实现消费订阅消息功能
Eclipse.paho内部消息流转的执行流程及源码解析
使用过程中遇到的问题汇总


一. 简述

MQTT消息模型
二、协议原理
2.1 协议的实现方式
实现MQTT协议需要客户端和服务器端通讯完成。在通讯过程中,MQTT协议中有三种身份
发布者(Publish)
代理(Broker)(服务器)
订阅者(Subscribe)
其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为两个部分
主题(Topic)
负载(payload)
2.2 MQTT客户端
一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以实现下面的功能
发布其他客户端可能会订阅的信息
订阅其它客户端发布的消息
退订或删除应用程序的消息
断开与服务器连接
2.3 MQTT服务器
MQTT服务器也称为"消息代理"(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,可以实现下面的功能
接受来自客户的网络连接
接受客户发布的应用信息
处理来自客户端的订阅和退订请求
向订阅的客户转发应用程序消息
2.4 MQTT协议中的订阅、主题、会话
订阅(Subscription)
2. 会话(Session)
3. 主题名(Topic Name)
4. 主题筛选器(Topic Filter)
5. 负载
2.5 MQTT协议中的方法
Connect:等待与服务器建立连接。
Disconnect:等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
Subscribe:等待完成订阅。
UnSubscribe:等待服务器取消客户端的一个或多个topics订阅。
Publish:MQTT客户端发送消息请求,发送完成后返回应用程序线程。
三、MQTT协议数据包结构
在MQTT协议中,一个MQTT数据包由以下三部分内容组成
固定头(Fixed header):存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。
可变头(Variable header):存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
消息体(Payload):存在于部分MQTT数据包中,表示客户端收到的具体内容。
3. 1 MQTT固定头
每个MQTT控制报文都包含一个固定报头,固定头部占用两个字节。其内存结构大概如下图所示
固定头内存结构
3.1.1 控制报文类型
用来描述报文的类型,通常用于客户端和服务端进行双向通信时对报文进行分类,不同的报文类型处理的方式也不同。

3.1.2 重发表示 DUP FLAG:
3.1.3 消息质量QOS(Quality of Service)
发送者和接收者之间,对于消息传递的可靠程度的协商。目前有三种消息发布的服务质量
QOS= 0:"至多一次",对于client而言,有且仅发一次publish包,对于broker而言,有且仅发一次publish。简而言之,就是仅发一次包,是否收到完全不管,适合那些不是很重要的数据。

QOS= 1:确保消息到达,但消息重复可能会发生。当broker收到client的publisher或者subscriber收到broker的publisher时,都会产生一条puback用来确认到达。当client没收到broker的puback或者broker没有收到client的puback,name就会一直发送publisher

QOS= 2:确保消息到达一次。publisher和broker分别进行了缓存,其中publisher缓存了message和messageId,而broker缓存了messageId,两方都做了记录。所以可以保证消息不重复,但同时因为增加了两次通信交互,对网络带宽和服务器负载也会产生一些压力

注意:对于QOS2的情况,增加了一个PUBREL及PUBCOM的过程,同时broker端做了如下的特殊处理。
message存在本地,先不给publish和subscriber
保存messageId,把message publish给他的subscriber
3.1.4 retain(保留消息)
发布保留标识,表示服务器要保留这次推送的信息,如果有新的订阅者出现,就把这消息推送给它,如果不设那么推送至当前订阅的就释放了。这里需要和消息积压处理区分开,通常如果publisher生产一条消息到broker,如果订阅的消费者此时与broker断开连接,在cleanSession设置为true的情况下(这个属性后面会详细讲),消费者重新连接后是无法收到这些在自己断开时被生产的消息的。这与我们经常使用的AMQP有很大的不同。而保留消息的意思是生产端如果生产一条保留消息,这条消息会一直缓存在broker端,每次消费端断开重连,都会收到这条消息。但是保留消息在broker有且仅有一条。也就是说,后发送的保留消息会覆盖掉先发送的保留消息。消费端连接成功后收到的保留消息,永远是最后一条发送给broker的保留消息。
3.2 可变报头
3.2.1 Clean Session
3.2.2 遗嘱消息
服务端检测到了一个I/O错误或者网络故障。
客户端在保持连接(Keep Alive)的时间内未能通讯。
客户端没有先发送DISCONNECT报文直接关闭了网络连接。
由于协议错误服务端关闭了网络连接。
3.2.3 主题名(Topic Name)
sport/tennis/player1sport/tennis/player1/rankingsport/tennis/player1/score/wimbledon
sport/tennis#sport/tennis/#/ranking
上面两种监听方式都是无效的 !!
对应AMQP的单层通配符 “*”,MQTT协议使用的是 “+”,并且+只能用于单层通配符。在主题过滤器的任意层级都可以使用单层通配符,包括第一个和最后一个层级。然而它必须占据过滤器的整个层级。可以在主题过滤器中的多个层级中使用它,也可以和多层通配符一起使用。
再举个栗子
“+” 是有效的。
“+/tennis/#” 是有效的。
“sport+” 是无效的。
“sport/+/player1” 也是有效的。
“/finance” 匹配 “+/+” 和 “/+” ,但是不匹配 “+”。
另外:服务端不能将 $ 字符开头的主题名匹配通配符 (#或+) 开头的主题过滤器
订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息。
订阅 “+/monitor/Clients” 的客户端不会收到任何发布到 “$SYS/monitor/Clients” 的消息。
订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息。
订阅 “$SYS/monitor/+” 的客户端会收到发布到 “$SYS/monitor/Clients” 主题的消息。
如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以 $ 开头主题的消息,它需要同时订阅 “#” 和 ““$SYS/#”。
除此之外:主题名和主题过滤器还必须符合下列规则
所有的主题名和主题过滤器必须至少包含一个字符 [MQTT-4.7.3-1]。
主题名和主题过滤器是区分大小写的。
主题名和主题过滤器可以包含空格。
主题名或主题过滤器以前置或后置斜杠 “/” 区分。
只包含斜杠 “/” 的主题名或主题过滤器是合法的。
主题名和主题过滤器不能包含空字符
主题名和主题过滤器是UTF-8编码字符串,它们不能超过65535字节
3.2.4 报文标识符 Packet Identifier
3.3 有效负载(payload)
Payload消息体是MQTT数据包的第三部分。包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息。也就是说,这四种控制报文的消息体是必须要有的
CONNECT:消息体内容主要是:客户端的ClientID、订阅的Topic、Message以及用户名和密码。
SUBSCRIBE:消息体内容是一系列的要订阅的主题以及QOS。
SUBACK:消息体内容是服务器对于SUBSCRIBE所申请的主题及QOS进行确认和回复。
UNSUBSCRIBE:消息体内容是要订阅的主题。
而publish是消息体中则保存推送的消息,以二进制形式,当然这里的编辑可自定义。
以下是各个控制报文对消息体的规则

传送门:http://mqtt.org/
结束语




