十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
最近公司做的项目中有用到消息推送,经过多方面的筛选之后确定了使用MQTT协议,相对于XMPP,MQTT更加轻量级,并且占用用户很少的带宽。
成都创新互联公司长期为上千多家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为科尔沁左翼企业提供专业的网站设计、成都网站建设,科尔沁左翼网站改版等技术服务。拥有10年丰富建站经验和众多成功案例,为您定制开发。
MQTT是IBM推出的一种针对移动终端设备的基于TCP/IP的发布/预订协议,可以连接大量的远程传感器和控制设备。
MQTT的官网见:。其中里面提供了官方推荐的各种服务器和客户端使用的各种语言版本的API。
下面以服务器Apollo 1.6为例,之前尝试过使用ActiveMQ,效果很不理想,只能实现服务器和客户端一对一的通信,从官网上了解到Apollo属于activemq的一个子工程。先不管这些了,言归正传,以下在windows环境下。
1、在这里下载Apollo服务器,下载后解压,然后运行apache-apollo-1.6\bin\apollo.cmd,输入create mybroker(名字任意取,这里是根据官网介绍的来取的)创建服务器实例,服务器实例包含了所有的配置,运行时数据等,并且和一个服务器进程关联。
2、create mybroker之后会在bin目录下生成mybroker文件夹,里面包含有很多信息,其中etc\apollo.xml文件下是配置服务器信息的文件,etc\users.properties文件包含连接MQTT服务器时用到的用户名和密码,后面会介绍,可以修改原始的admin=password,可以接着换行添加新的用户名密码。
3、打开cmd,运行…apache-apollo-1.6\bin\mybroker\bin\apollo-broker.cmd run 开启服务器,可以在浏览器中输入查看是否安装成功,该界面展示了topic,连接数等很多信息。
经过上面的简单步骤,服务器基本上就已经完成,下一篇将介绍Android客户端的编写和注意事项。
客户端使用的API,开始我使用的是mqtt-client,使用过后发现问题百出,不能很好的满足要求,后来使用了官方推荐的Eclipse Paho,下面开始客户端代码的编写,为了方便测试这里有android和j2se两个工程:
1、新建android工程MQTTClient
2、MainActivity代码如下:
MQTT是一个轻量级的消息发布/订阅协议,它是实现基于手机客户端的消息推送服务器的理想解决方案。 我们可以从这里下载该项目的实例代码,并且可以找到一个采用PHP书写的服务器端实现。 架构如下所示:wmqtt.jar 是IBM提供的MQTT协议的实现。你可以从如下站点下载它。你可以将该jar包加入你自己的Android应用程序中。 Really Small Message Broker (RSMB) ,他是一个简单的MQTT代理,同样由IBM提供。缺省打开1883端口,应用程序当中,它负责接收来自服务器的消息并将其转发给指定的移动设备。 SAM是一个针对MQTT写的PHP库。你可以从这个下载它. send_mqtt.php是一个通过POST接收消息并且通过SAM将消息发送给RSMB的PHP脚本。 实例代码:Ø 采用XMPP协议实现Android推送 这是我在项目中采用的方案。事实上Google官方的C2DM服务器底层也是采用XMPP协议进行的封装。 XMPP(可扩展通讯和表示协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线探测。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息。 androidpn是一个基于XMPP协议的java开源Android push notification实现。它包含了完整的客户端和服务器端。经过源代码研究我发现,该服务器端基本是在另外一个开源工程openfire基础上修改实现的,不过比较郁闷的是androidpn的文档是由韩语写的,所以整个研究过程基本都是读源码。它的实现示意图如下:androidpn客户端需要用到一个基于java的开源XMPP协议包asmack,这个包同样也是基于openfire下的另外一个开源项目smack,不过我们不需要自己编译,可以直接把androidpn客户端里面的asmack.jar拿来使用。客户端利用asmack中提供的XMPPConnection类与服务器建立持久连接,并通过该连接进行用户注册和登录认证,同样也是通过这条连接,接收服务器发送的通知。 androidpn服务器端也是java语言实现的,基于openfire开源工程,不过它的Web部分采用的是spring框架,这一点与openfire是不同的。Androidpn服务器包含两个部分,一个是侦听在5222端口上的XMPP服务,负责与客户端的XMPPConnection类进行通信,作用是用户注册和身份认证,并发送推送通知消息。另外一部分是Web服务器,采用一个轻量级的HTTP服务器,负责接收用户的Web请求。服务器架构如下:最上层包含四个组成部分,分别是SessionManager,Auth Manager,PresenceManager以及Notification Manager。SessionManager负责管理客户端与服务器之间的会话,Auth Manager负责客户端用户认证管理,Presence Manager负责管理客户端用户的登录状态,NotificationManager负责实现服务器向客户端推送消息功能。 服务器端界面如下,分别对应了上述的几个功能模块:发送以后,我们可以在手机端看到接收的消息:
——
[1.MQTT项目工程]()
[2.MQTT API说明文档]()
[3.MQTT协议中文版]()
MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。
MQTT协议通过交换预定义的MQTT控制报文来通信。
报文格式: 固定包头+可变包头+payload。
固定包头: 由两个字节组成
byte1 高四位表示控制报文的类型,低四位表示控制报文类型的标志位。
byte2表示剩余长度,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。
可变包头:
某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标(Packet Identifier)字段存在于在多个类型的报文里。
有效载荷:
对于PUBLISH来说有效载荷就是应用消息。
——1. 网络建立连接后,客户端发送的第一个报文必须是CONNECT报文
——2. 客户端只能发送一次CONNECT 报文,发送两次会当作协议违规处理,并断开连接。
——3. 有效载荷包括:客户端唯一标识符,will主题,will消息,用户名和密码。
——4.可变包头:协议名(protocol name)+协议级别(protocol level)+连接标志(connect flags)+保持连接(keep alive)。
——5. 协议名: MQTT的UTF-8编码的字符串。(MSB+LSB +MQTT 六个字节)
——6. 协议级别:一个字节
——7. 连接标志:一个字节,包含一些用于指定MQTT连接行为的参数。同时还指出有效载荷中的字段是否存在。服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0,如果不为0必须断开客户端连接。
——8. 清理会话:byte8 的bit1位标志。
这个二进制位指定了会话状态的处理方式。客户端和服务端可以保存会话状态,以支持跨网络连接的可靠消息传输。这个标志位用于控
制会话状态的生存时间。
标志设置为0:服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信。
标志设置为1:客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。
——9. 遗嘱标志 WILL FLAG: byte8的bit2位标志。
遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息。
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理 的时间取决于应用的类型和通信基础设施。
CONNACK报文没有有效载荷。
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
固定包头:
注意byte1的bit3为重发标志DUP。
如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。
服务端发送PUBLISH报文给订阅者时,收到(入站)的PUBLISH报文的DUP标志的值不会被传播。发送(出站)的PUBLISH报文与收到(入站)的PUBLISH报文中的DUP标志是独立设置的,它的值必须单独的根据发送(出站)的PUBLISH报文是否是一个重发来确定。
可变包头:
主题名 :topic name
报文标识符 :packet identitfier。
有效载荷 :有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。
响应:PUBLISH报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应。
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
PUBACK报文没有有效载荷。
PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。
PUBREC报文没有有效载荷。
PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。
PUBREL报文没有有效载荷。
PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。
PUBCOMP报文没有有效载荷。
客户端向服务端发送 SUBSCRIBE 报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。
有效载荷:
SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 和 QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的。
响应:
服务端收到客户端发送的一个SUBSCRIBE报文时,必须使用SUBACK报文响应,SUBACK报文必须和等待确认的SUBSCRIBE报文有相同的报文标识符。
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
有效载荷:
有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同。
客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。
有效载荷 :
UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。
UNSUBSCRIBE报文中的主题过滤器必须是连续打包的、按照定义的UTF-8编码字符串
UNSUBSCRIBE报文的有效载荷必须至少包含一个消息过滤器。没有有效载荷的UNSUBSCRIBE报文是违反协议的。
响应:
UNSUBSCRIBE报文提供的主题过滤器(无论是否包含通配符)必须与服务端持有的这个客 户端的当前主题过滤器集合逐个字符比较。如果有任何过滤器完全匹配,那么它(服务端)自己的订阅将被删除,否则不会有进一步的处理。
如果服务端删除了一个订阅:
——它必须停止分发任何新消息给这个客户端 []。
——它必须完成分发任何已经开始往客户端发送的QoS 1和QoS 2的消息 []。
——它可以继续发送任何现存的准备分发给客户端的缓存消息。
服务端必须发送UNSUBACK报文响应客户端的UNSUBSCRIBE请求。UNSUBACK报文必须包含和UNSUBSCRIBE报文相同的报文标识符 。即使没有删除任何主题订阅,服务端也必须发送一个UNSUBACK响应 。
如果服务端收到包含多个主题过滤器的UNSUBSCRIBE报文,它必须如同收到了一系列的多个UNSUBSCRIBE报文一样处理那个报文,除了将它们的响应合并到一个单独的UNSUBACK报文外。
服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。
UNSUBACK报文没有有效载荷。
客户端发送PINGREQ报文给服务端的。用于:
1. 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
2. 请求服务端发送 响应确认它还活着。
3. 使用网络以确认网络连接没有断开。
保持连接(Keep Alive)处理中用到这个报文。
——PINGREQ报文没有可变报头。
——PINGREQ报文没有有效载荷。
响应:
服务端必须发送 PINGRESP报文响应客户端的PINGREQ报文。
服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。
保持连接(Keep Alive)处理中用到这个报文。
PINGRESP报文没有可变报头。
PINGRESP报文没有有效载荷。
DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。
DISCONNECT报文没有可变报头。
DISCONNECT报文没有有效载荷。
响应:
客户端发送DISCONNECT报文之后:
—— 必须关闭网络连接 [MQTT-3.14.4-1]。
——不能通过那个网络连接再发送任何控制报文。
服务端在收到DISCONNECT报文时:
——必须丢弃任何与当前连接关联的未发布的遗嘱消息。
——应该关闭网络连接,如果客户端 还没有这么做。
为了提供服务质量保证,客户端和服务端有必要存储会话状态。在整个会话期间,客户端和服务端都必须存储会话状态 。会话必须至少持续和它的活跃网络连接同样长的时间。服务端的保留消息不是会话状态的组成部分。服务端应该保留那种消息直到客户端删除它。
MQTT协议要求基础传输层能够提供有序的、可靠的、双向传输(从客户端到服务端 和从服务端到客户端)的字节流。
无连接的网络传输协议如UDP是不支持的,因为他们可能会丢失数据包或对数据包重排序。
MQTT按照这里定义的服务质量 (QoS) 等级分发应用消息。分发协议是对称的,在下面的描述中,客户端和服务端既可以是发送者也可以是接收者。分发协议关注的是从单个发送者到单个接收者的应用消息。服务端分发应用消息给多个客户端时,每个客户端独立处理。分发给客户端的出站应用消息和入站应用消息的QoS等级可能是不同的。
qos0:最多分发一次
qos1:至少分发一次
qos2:仅分发一次
客户端设置清理会话(CleanSession)标志为0重连时,客户端和服务端必须使用原始的报文标识符重发任何未确认的PUBLISH报文(如果QoS0)和PUBREL报文 [MQTT-4.4.0-1]。这是唯一要求客户端或服务端重发消息的情况。
服务端接管入站应用消息的所有权时,它必须将消息添加到订阅匹配的客户端的会话状态。正常情况下,客户端收到发送给它的订阅的消息。客户端也可能收到不是与它的订阅精确匹配的消息。如果服务端自动给客户端分配了一个订阅,可能发生这种情况。正在处理UBSUBSCRIBE请求时也可能收到消息。客户端必须按照可用的服务质量(QoS)规则确认它收到的任何PUBLISH报文,不管它选择是否处理报文包含的应用消息 。
实现本章定义的协议流程时,客户端必须遵循下列规则:
重发任何之前的PUBLISH报文时,必须按原始PUBLISH报文的发送顺序重发(适用于QoS 1和QoS 2消息)[MQTT-4.6.0-1]。
——必须按照对应的PUBLISH报文的顺序发送PUBACK报文(QoS 1消息)。
——必须按照对应的PUBLISH报文的顺序发送PUBREC报文(QoS 2消息。
——必须按照对应的PUBREC报文的顺序发送PUBREL报文(QoS 2消息)。
服务端必须默认认为每个主题都是有序的。它可以提供一个管理功能或其它机制,以允许将一个或多个主题当作是无序的 。
服务端处理发送给有序主题的消息时,必须按照上面的规则将消息分发给每个订阅者。此外,它必须按照从客户端收到的顺序发送PUBLISH报文给消费者(对相同的主题和QoS)。
斜杠(‘/’ U+002F)用于分割主题的每个层级,为主题名提供一个分层结构.
数字标志(‘#’ U+0023)是用于匹配主题中任意层级的通配符。
加号 (‘+’ U+002B) 是只能用于单个主题层级匹配的通配符。
服务端不能将 $ 字符开头的主题名匹配通配符 (#或+) 开头的主题过滤器.
$SYS/ 被广泛用作包含服务器特定信息或控制接口的主题的前缀。
应用不能使用 $ 字符开头的主题。
订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息。
订阅 “+/monitor/Clients” 的客户端不会收到任何发布到 “$SYS/monitor/Clients” 的消息。
订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息。
订阅 “$SYS/monitor/+” 的客户端会收到发布到 “$SYS/monitor/Clients” 主题的消息。
如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以 $ 开头主题的消息,它需要同
时订阅 “#” 和 ““$SYS/#”。
除非另有说明,如果服务端或客户端遇到了协议违规的行为,它必须关闭传输这个协议违规控制报文的网络连接.
MQTT方案通常部署在不安全的通信环境中。在这种情况下,协议实现通常需要提供这些机制:
——用户和设备身份认证
——服务端资源访问授权
——MQTT控制报文和内嵌应用数据的完整性校验
——MQTT控制报文和内嵌应用数据的隐私控制
作为传输层协议,MQTT仅关注消息传输,提供合适的安全功能是实现者的责任。使用TLS[RFC5246] 是比较普遍的选择。
广泛采用高级加密标准 [AES] 数据加密标准 [DES] 。
推荐使用为受限的低端设备特别优化过的轻量级加密国际标准 ISO 29192 [ISO29192] 。
如果MQTT在WebSocket [RFC6455] 连接上传输,必须满足下面的条件:
——MQTT控制报文必须使用WebSocket二进制数据帧发送。如果收到任何其它类型的数据帧,接收者必须关闭网络连接 。
——单个WebSocket数据帧可以包含多个或者部分MQTT报文。接收者不能假设MQTT控制报文按WebSocket帧边界对齐 。
——客户端必须将字符串 mqtt 包含在它提供的WebSocket子协议列表里 。
——服务端选择和返回的WebSocket子协议名必须是 。
——用于连接客户端和服务器的WebSocket URI对MQTT协议没有任何影响。
MQTT规范定义了MQTT客户端实现和MQTT服务端实现的一致性要求
MQTT实现可以同时是MQTT客户端和MQTT服务端。接受入站连接和建立到其它服务端的出站连接的服务端必须同时符合MQTT客户端和MQTT服务端的要求 。
为了与任何其它的一致性实现交互操作,一致性实现不能要求使用在本规范之外定义的任何扩展 。
附录:
控制报文类型
byte1:标志位flags: