MQTT协议
MQTT(Message Queuing Telemetry Transport Protocol):消息队列遥感传输协议,是一种轻量级代理的发布/订阅模式的消息传输协议,运行在TCP协议栈之上,为其提供有序、可靠、双向连接的网络连接保证。
之所以说是轻量级,是因为Mqtt协议开销非常小,协议头只有2字节。
MQTT协议如何工作
MQTT是基于代理的发布/订阅消息传输模式。因此,在Mqtt中有三种角色:代理服务器(broker)、发布者和订阅者。其中发布者和订阅者互不干扰,通过代理服务器实现解耦,代理服务器会将来自发布者的消息进行存储处理并将这些消息发送到正确的订阅者中。
消息传输模型如下:
代理服务器
代理服务器可以是一个程序或者设备,作为发送消息的客户端和请求订阅的客户端之间的中介。其主要作用是接收发布者客户端发布的应用信息,然后将信息转发给符合条件的订阅者客户端。客户端
客户端指使用MQTT协议的程序或设备。客户端包括发布者客户端和订阅者客户端,同一个客户端可以即是发布者也是订阅者。客户端可以发布消息给其它相关客户端,也可以订阅其它客户端发布的消息。
如何将消息正确送达
MQTT通过“主题”实现将消息从发布者客户端送达至接收者客户端。“主题”是附加在应用消息上的一个标签,发布者客户端将“主题”和“消息”发送至代理服务器,代理服务器将该消息转发至每一个订阅了该“主题”的订阅者客户端。
主题Topic格式
一个主题可以由多个主题层级组成,每一层通过“/”斜杠分隔开。
主题过滤器指客户端在订阅时包含的一个表达式,用于表示相关的一个或多个主题。
如果用户需要一次订阅多个具有类似结构的主题,可以在主题过滤器中包含通配符。通配符只可用在主题过滤器中,在发布应用消息时的主题名不允许包含通配符,主题通配符有两种:
#:表示匹配>=0个层次,比如a/#就匹配a/b,a/b/c(不能匹配a/,后面必须有其它主题)。单独的一个#表示匹配所有,不允许a#或a/#/c等形式。
+:表示匹配一个层次,例如a/+匹配a/b,a/c,不匹配a/b/c。单独的一个+是允许的,但a+为非法形式。
如何确保消息已被送达
Mqtt协议对发布的消息提供了三种服务质量等级,通过设置PUBLISH报文中的QoS标志位实现:
Qos=0:最多一次。此等级的消息不需要回应确认,也没有重发机制,所以这类消息可能会丢失或者重复,取决于TCP/IP提供的尽最大努力交互的数据包服务。
Qos=1:最少一次。确保消息到达,但是消息可能发生重复,发送者如果在指定时间内没有收到PUBACK控制报文,应用消息会被重新发送。
Qos=2:只有一次。最高级别的服务质量,消息丢失和重复都是不可接受的。(发送端有重发机制,在接收端会通过messageid判断是不是重复,如果相等,则丢弃,确保不会重复)
临终遗嘱
MQTT协议利用KeepAlive机制在客户端异常断开时发现问题。当客户端断开时(例如:电量耗尽、系统崩溃或者网络断开),代理服务器会采取相应措施。
客户端设置“临终遗嘱”(LWT)信息后,当代理服务器检测到客户端离线后,就会发送保存在特定主题上的 LWT 信息,让其它订阅该主题的客户端知道该节点已经意外离线。
消息的缓存
如果客户端在连接的时候指定Clean Session=false,那这个会话将成为一个持久化会话。如果客户端异常离线,broker将缓存“QoS=1”的消息,待客户端重新连接后将消息发送至客户端。需要注意以下情况:
消息只能缓存24小时。
客户端重新连接时Clean Session需置为False(flag=0)。
如果Clean Session为True(flag=1),那么当客户端重新接入时,broker会丢弃已经缓存任何会话状态信息,创建一个新的会话连接。
案例
- client_pub1向broker发送消息,client_sub1订阅该topic,client_sub1可以正常收到client_pub1发出的消息。
- client_sub1异常离线,此时broker会缓存client_pub1发出的所有消息。
- 如果此时有其他订阅相同主题的client_sub2接入到broker上,client_sub2是可以正常接收到client_pub1新发出来的消息,但是无法收到之前缓存的消息。
当client_pub1重新接入到broker后,根据状态不同,有以下两种处理方式:
如果clean session设为false,那么broker会将缓存的所有的消息发送给client_pub1。
如果clean session设为true,那么broker会丢弃所有缓存任何会话状态信息,为client_sub1重新创建一个新的连接。
持久化消息
如果PUBLISH消息固定头部RETAIN标记为1,broker会持久保存此消息,直到该消息被新的PUBLISH消息(RETAIN=1)覆盖或用户主动清除该消息。(即会持久化每个topic的最后一条消息)
对于“RETAIN=1”的消息,broker不但会将该消息发送给所有当前的订阅者,同时新的接入设备也会收到该消息。
代码示例
public class Test{
public static void main(String[] args) throws Exception {
String endpoint = "yourhostname"; //输入创建endpoint返回的SSL地址
String username = "yourname"; //输入创建thing返回的username
String password = "yourpwd"; //输入创建principal返回的password
String topic = "topic1"; //订阅的消息主题
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(true);
options.setUserName(username);
options.setPassword(password.toCharArray());
MqttClient client = new MqttClient(endpoint, "java-client"); //java-client为标识设备的ID,用户可自己定义,在同一个实例下,每个实体设备需要有一个唯一的ID
client.connect(options);
client.subscribe(topic);
MqttMessage message = new MqttMessage();
message.setPayload("15".getBytes());
client.publish(topic, message);
client.disconnect();
}
}