MQTT基本应用(Mosquitto+Eclipse Paho)

懵懂的女人 提交于 2019-12-02 11:40:20

本文主要介绍,MQTT 基本概念和实现方式:

1.概述

1.1MQTT协议

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),由IBM在1999年发布,是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,该协议构建于TCP/IP协议上,属于应用层协议,只要是支持TCP/IP协议栈的地方,都可以使用MQTT。

MQTT最大的优点在于可以以极少的代码和有限的带宽,为远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,MQTT在物联网、小型设备、移动应用等方面有广泛的应用。

 

MQTT协议中文文档链接:https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html

 

1.2发布-订阅模式

在“发布者-订阅者”模式中,称为发布者的消息发送者不会将消息编程为直接发送给称为订阅者的特定接收者。这意味着发布者和订阅者不知道彼此的存在。存在第三个组件,称为代理或消息代理或事件总线,它由发布者和订阅者都知道,它过滤所有传入的消息并相应地分发它们。换句话说,pub-sub是用于在不同系统组件之间传递消息的模式,而这些组件不知道关于彼此身份的任何信息。经纪人如何过滤所有消息?实际上,有几个消息过滤过程。最常用的方法有:基于主题和基于内容的。

 

1.3MQTT协议实现

实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。

MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:

(1)Topic,可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload);

(2)payload,可以理解为消息的内容,是指订阅者具体要使用的内容。

 

1.4MQTT相关平台工具

服务端:

(1)支持MQTT的云平台:

BAT三家物联网云平台、中国移动One NET云平台、云巴(Cloud Bus)。

(2)实现MQTT协议的消息中间件:

————————————————

IBM Websphere MQ Telemetry

IBM MessageSight

IBM Integration Bus

Mosquitto

Eclipse Paho

Eurotech Everywhere Device Cloud

emqttd

Xively

m2m.io

webMethods Nirvana Messaging

RabbitMQ

Apache ActiveMQ

Apache Apollo

Moquette

HiveMQ

Mosca

Litmus Automation Loop

JoramMQ

ThingMQ

VerneMQ

————————————————

客户端:

Eclipse Paho

Eclipse Paho是Eclipse 提供的一个访问MQTT服务器的一种开源客户端库。其提供了7种不同语言平台的客户端类库。当然和MQTT服务器进行交互的开源框架还有很多,比如,对于Java语言和平台来说,有下面的框架:

————————————————

Eclipse Paho Java

Xenqtt Includes a client library, mock broker for unit/integration testing, and applications to support enterprise needs like using a cluster of servers as a single client, an HTTP gateway, etc.

MeQanTT

Fusesource mqtt-client

moquette

"MA9B" zip of 1/2 dozen mobile clients source code. Includes Android-optimized Java source that works with Android notifications, based on Paho

IA92 - deprecated IBM IA92 support pack, use Eclipse Paho GUI client instead. A useful MQTT Java swing GUI for publishing & subscribing. The Eclipse Paho GUI is identical but uses newer client code

————————————————

 

2.Mosquitto基本使用

下载地址:  http://mosquitto.org/download/

(1)下一步,默认选项安装即可:C:\Program Files\mosquitto

(2)启动:C:\Users\zlj>cd C:\Program Files\mosquitto

            C:\Program Files\mosquitto>mosquitto  -p 1883 -v

(3)命令补充:

-c 后面跟的是启动mosquitto可以调整的参数,比如是否开启基本认证,端口是什么,SSL单向和双向的认证配置等等。

-d 表示MQTT mosquitto将在后台运行。

-p 代表当前的mosquitto服务实例启动以后,其监听端口号,这个配置的覆盖[-c config file] 指定的配置文件中的端口。

(4)可以启动为本地服务:

 

3.Eclipse Paho基本使用

3.1Eclipse Paho Mqtt客户端工具

下载地址:

https://repo.eclipse.org/content/repositories/paho-releases/org/eclipse/paho/org.eclipse.paho.ui.app/1.0.2/

(1)下载后解压运行即可:

(2)可以通过客户端工具连接本机的Mosquitto Broker服务(默认1883 端口):

(3)订阅主题:

(4)向Broker发布主题消息:

(5)查看日志:

3.2Eclipse Paho 代码实现客户端

3.2.1实现步骤

(1)添加依赖/或者 http://www.eclipse.org/paho/downloads.php 下载jar包

    <dependency>

      <groupId>org.eclipse.paho</groupId>

      <artifactId>org.eclipse.paho.client.mqttv3</artifactId>

      <version>1.1.1</version>

    </dependency>

(2)完整API可以参考文档:https://www.eclipse.org/paho/files/javadoc/index.html

(3)完整Demo代码及注释

/**
 * 定义一个发布者类:向服务端发布 主题消息
 */
public class ServerMQTT {
	// 定义MQTT代理地址和端口
	public static final String HOST = "tcp://localhost:1883";
	// 定义一个主题:温度
	public static final String TOPIC = "Temperature";
	// 定义客户端ID认证 服务端用于区分不同客户端
	private static final String clientid = "publisher";
	// 定义用户名认证
	private String userName = "root";
	// 定义密码认证
	private String passWord = "123456";

	// MQTT客户端对象
	private MqttClient client;
	// MQTT主题对象:用于发布/订阅消息
	private MqttTopic topic;
	// MQTT消息:用于保存消息主体byte[],以及如何传递消息的选项
	private MqttMessage message;

	public ServerMQTT() throws MqttException {
		// 发布者客户端初始化
		client = new MqttClient(HOST, clientid, new MemoryPersistence());
		connect();
	}

	/**
	 * 连接服务器
	 */
	private void connect() {
		// MQTT连接选项
		MqttConnectOptions options = new MqttConnectOptions();
		// 持久订阅(设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接)
		options.setCleanSession(true);
		// 设置用户名
		options.setUserName(userName);
		// 设置密码
		options.setPassword(passWord.toCharArray());
		// 设置超时时间
		options.setConnectionTimeout(10);
		// 设置会话心跳时间
		options.setKeepAliveInterval(20);
		try {
			// 设置客户端的事件监听 回调
			client.setCallback(new PushCallback());
			// 连接服务端
			client.connect(options);
			// 获取可用于发布消息的主题对象
			topic = client.getTopic(TOPIC);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 发布主题消息两种方式
	 */
	public void publish(MqttTopic topic, MqttMessage message) throws MqttPersistenceException, MqttException {
		// 1.使用客户端发布(API文档 建议优先使用这种方法)
		// client.publish(TOPIC, message);

		// 2.使用主题对象发布(其中token 提供了发布行为的追踪机制)
		MqttDeliveryToken token = topic.publish(message);
		// 等待 直到发布完成
		token.waitForCompletion();
		System.out.println("[" + topic.getName() + "]主题发布信息:[" + new String(message.getPayload()).toString()
				+ "] 发布状态:[" + token.isComplete() + "]");
	}

	public static void main(String[] args) throws MqttException {
		// 初始化发布 客户端
		ServerMQTT publisher = new ServerMQTT();

		// 初始化MQTT消息对象
		publisher.message = new MqttMessage();
		// 设置消息传递质量
		publisher.message.setQos(1);
		// 告知代理保留该主题的最后一条消息
		publisher.message.setRetained(true);
		// 传入消息实体
		publisher.message.setPayload("The current temperature is 16 ℃".getBytes());

		// 保留消息为true时,可以通过发送new byte[0],清空服务器中保留的消息
		//publisher.message.setPayload(new byte[0]);

		// 发布消息
		publisher.publish(publisher.topic, publisher.message);
		// 订阅 订阅者掉线主题信息
		publisher.client.subscribe("SubcriberWill");

		// 服务器是否保留了最新的消息
		System.out.println("[" + publisher.topic.getName() + "]主题消息服务端  保存状态:[" + publisher.message.isRetained() + "]");
	}
}
/**
 * 定义一个订阅者类:订阅服务端的主题
 */
public class ClientMQTT {
	public static final String HOST = "tcp://localhost:1883";
	public static final String TOPIC = "Temperature";
	private static final String clientid = "Subscriber";
	private MqttClient client;
	private MqttConnectOptions options;
	private String userName = "root";
	private String passWord = "123456";

	private void start() {
		try {
			// 订阅者对象初始化
			client = new MqttClient(HOST, clientid, new MemoryPersistence());
			// 订阅者客户端初始化选项对象
			options = new MqttConnectOptions();
			// 设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接
			options.setCleanSession(true);
			// 设置连接的用户名
			options.setUserName(userName);
			// 设置连接的密码
			options.setPassword(passWord.toCharArray());
			// 设置超时时间 单位为秒
			options.setConnectionTimeout(10);
			// 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制
			options.setKeepAliveInterval(20);
			
			// setWill方法,应用于设备掉线后需要通知用户的场景
			options.setWill("SubcriberWill", "订阅者掉线,发布最后信息!".getBytes(), 2, false);
			// 设置回调
			client.setCallback(new PushCallback());
			
			// 连接服务器
			client.connect(options);
			
			// 订阅消息
			int[] Qos = { 1 };
			String[] topics = { TOPIC };
			client.subscribe(topics, Qos);
			// 订阅可以使用 + # / ,具体查看文档主题部分
		    //client.subscribe("#");

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws MqttException {
		ClientMQTT Subscriber = new ClientMQTT();
		Subscriber.start();
	}
}
/**
 * 客户端事件监听 回调类
 */
public class PushCallback implements MqttCallback {
	/**
	 * 在与服务器连接断开后调用connectionLost()
	 */
	public void connectionLost(Throwable cause) {
		System.out.println("连接断开!可以进行重连!");
	}

	/**
	 * 在完成 信息传递 并收到所有确认后调用deliveryComplete() 
	 * 对于QoS0的消息:一旦将消息传递到网络就会调用它 ;
	 * 对于QoS1的消息:当接收到PUBACK时调用它; 
	 * 对于QoS2消息:当接收到PUBCOMP时调用它;
	 */
	public void deliveryComplete(IMqttDeliveryToken token) {
		System.out.println("本次信息传递完成,回调执行:[" + token.isComplete() + "]");
	}

	/**
	 * 在接收信息后调用messageArrived() 在该方法完全返回之前,不会将确认发送回服务器。 可以在该方法内完成对 信息数据的持久化。
	 * 如果此方法的实现引发异常,则客户端将关闭。
	 */
	public void messageArrived(String topic, MqttMessage message) throws Exception {
		System.out.println("接收消息主题 : " + topic);
		System.out.println("接收消息Qos : " + message.getQos());
		System.out.println("接收消息内容 : " + new String(message.getPayload()));
	}
}

3.2.2细节补充

3.2.2.1消息主题(Topic)

(1)命名规则

  1. 所有的主题名和主题过滤器必须至少包含一个字符
  2. 主题名或主题过滤器以前置或后置斜杠 “/” 区分
  3. 只包含斜杠 “/” 的主题名或主题过滤器是合法的
  4. 主题名和主题过滤器是 UTF-8 编码字符串, 它们不能超过 65535 字节
  5. 主题名和主题过滤器是区分大小写的

(2)作用

主题层级分隔符 / : 用于分割主题层级,/分割后的主题,这是消息主题层级设计中很重要的符号。 比方说: aaa/bbb和 aaa/bbb/ccc 和aaa/bbb/ccc/ddd ,这样的消息主题格式,是一个层层递进的关系,可通过多层通配符同时匹配两者,或者单层通配符只匹配一个。 这在现实场景中,可以应用到:公司的部门层级推送、国家城市层级推送等包含层级关系的场景。

单层通配符 +: 单层通配符只能匹配一层主题。比如: aaa/+ 可以匹配 aaa/bbb ,但是不能匹配aaa/bbb/ccc。单独的+号可以匹配单层的所有推送

多层通配符 #:多层通配符可以匹配于多层主题。比如: aaa/# 不但可以匹配aaa/bbb,还可以匹配aaa/bbb/ccc/ddd。 也就是说,多层通配符可以匹配符合通配符之前主题层级的所有子集主题。单独的#匹配所有的消息主题.

注意: 单层通配符和多层通配符只能用于订阅(subscribe)消息而不能用于发布(publish)消息(否则会报错),层级分隔符两种情况下均可使用。

 

3.2.2.2消息质量(QOS)

MQTT消息质量有三个等级,QoS 0,QoS 1和 QoS 2。

    QoS 0:最多分发一次。消息的传递完全依赖底层的TCP/IP网络,协议里没有定义应答和重试,消息要么只会到达服务端一次,要么根本没有到达。

    QoS 1:至少分发一次。服务器的消息接收由PUBACK消息进行确认,如果通信链路或发送设备异常,或者指定时间内没有收到确认消息,发送端会重发这条在消息头中设置了DUP位的消息。

    QoS 2:只分发一次。这是最高级别的消息传递,消息丢失和重复都是不可接受的,使用这个服务质量等级会有额外的开销。

    通过下面的例子可以更深刻的理解上面三个传输质量等级。

比如目前流行的共享单车智能锁,智能锁可以定时使用QoS level 0质量消息请求服务器,发送单车的当前位置,如果服务器没收到也没关系,反正过一段时间又会再发送一次。之后用户可以通过App查询周围单车位置,找到单车后需要进行解锁,这时候可以使用QoS level 1质量消息,手机App不断的发送解锁消息给单车锁,确保有一次消息能达到以解锁单车。最后用户用完单车后,需要提交付款表单,可以使用QoS level 2质量消息,这样确保只传递一次数据,否则用户就会多付钱了。

 

3.2.2.3消息持久化机制(Persistence)

在构造Mqtt客户端时:

MqttClient(java.lang.String serverURI, java.lang.String clientId, MqttClientPersistence persistence)

 

构造参数中,MqttClientPersistence接口,定义了持久化机制:

Interface MqttClientPersistence

All Known Implementing Classes:
MemoryPersistence, MqttDefaultFilePersistence

分别用于:内存存储、使用文件存储。

API说明

To enable messages to be delivered even across network and client restarts messages need to be safely stored until the message has been delivered at the requested quality of service. A pluggable persistence mechanism is provided to store the messages.

要使消息能够通过网络传递,并且客户端重新启动,则需要安全地存储消息,直到以请求的服务质量传递消息为止。提供了可插入的持久性机制来存储消息。

 

By default MqttDefaultFilePersistence is used to store messages to a file. If persistence is set to null then messages are stored in memory and hence can be lost if the client, Java runtime or device shuts down.

默认情况下,mqttdefaultfilepersistence

用于将消息存储到文件。如果persistence设置为null,那么消息将存储在内存中,因此如果客户端、java运行时或设备关闭,消息可能会丢失。

 

A persistence mechanism is used to enable reliable messaging. For messages sent at qualities of service (QoS) 1 or 2 to be reliably delivered, messages must be stored (on both the client and server) until the delivery of the message is complete. If messages are not safely stored when being delivered then a failure in the client or server can result in lost messages. A pluggable persistence mechanism is supported via the MqttClientPersistence interface. An implementer of this interface that safely stores messages must be specified in order for delivery of messages to be reliable. In addition MqttConnectOptions.setCleanSession(boolean) must be set to false. In the event that only QoS 0 messages are sent or received or cleanSession is set to true then a safe store is not needed.

持久性机制用于启用可靠的消息传递。要使以服务质量(QoS)1或2发送的消息可靠地传递,必须存储消息(在客户端和服务器上),直到消息传递完成。如果消息在传递时未安全存储,则客户端或服务器中的故障可能会导致消息丢失。通过mqttclientpersistence接口支持可插入的持久性机制。必须指定此接口的安全存储消息的实现者,才能可靠地传递消息。此外,mqttconnectoptions.setcleansession(boolean)必须设置为false。如果只发送或接收QoS 0消息,或者cleansession设置为true,则不需要安全存储。

 

An implementation of file-based persistence is provided in class MqttDefaultFilePersistence which will work in all Java SE based systems. If no persistence is needed, the persistence parameter can be explicitly set to null.

MqttDefaultFilePersistence类提供了一个基于文件的持久性实现,它将在所有基于java se的系统中工作。如果不需要持久性,则可以将持久性参数显式设置为空。

 

3.2.2.4连接选项(Connect Options)

(1)客户端连接服务端的选项设置

MqttConnectOptions Holds the set of options that control how the client connects to a server.

(2)默认构造API说明

Constructs a new MqttConnectOptions object using the default values. The defaults are:

使用默认值构造新的mqttconnectoptions对象。默认值为:

   The keepalive interval is 60 seconds

   保持间隔是60秒

   Clean Session is true

   清除会话为真

   The message delivery retry interval is 15 seconds

   邮件传递重试间隔为15秒

   The connection timeout period is 30 seconds

   连接超时时间为30秒

   No Will message is set

   未设置遗嘱信息(连接意外中断,发布客户端“遗嘱”消息)

   A standard SocketFactory is used

   使用标准的socketfactory

More information about these values can be found in the setter methods.

有关这些值的更多信息可以在setter方法中找到。

(3)关于记住状态选项

public void setcleansession(Boolean cleansession)

设置客户端和服务器是否应在重新启动和重新连接时记住状态。

 

如果设置为false,则客户端和服务器将在重新启动客户端、服务器和连接时保持状态。当状态保持时:

即使重新启动客户端、服务器或连接,消息传递也将可靠地满足指定的QoS。

服务器将把订阅视为持久订阅。

 

如果设置为true,则客户端和服务器将不会在重新启动客户端、服务器或连接时保持状态。这意味着

如果重新启动客户端、服务器或连接,则无法维护到指定QoS的消息传递

服务器将把订阅视为非持久订阅。

 

3.2.2.5保留最后消息(Retained Messages)

(1)保留消息特点

发布消息时把retain设置为true,该条消息即为保留信息。

1个Topic只有唯一的retain消息,Broker会保存每个Topic的最后一条retain消息。
每个Client订阅Topic后会立即读取到retain消息,不必要等待发送。
订阅Topic时可以使用通配符,就会收到匹配的每个Topic的retain消息。

(2)作用

首先,retain设置为false时,如果发布者向主题发布消息,并且没有人订阅该主题,则该消息将被代理放弃。

在一些场景,比如检测在线状态的传感器发布的主题中,如果没有保留最后一条的消息,订户将不得不在收到消息之前等待发布者的状态改变。然而,对于保留的消息,订户将看到传感器的当前状态。

 

3.2.2.6遗愿标志(Will)

在可变报文头的连接标志位字段(Connect Flags)里有三个Will标志位:Will Flag、Will QoS和Will Retain Flag,这些Will字段用于监控客户端与服务器之间的连接状况。如果设置了Will Flag,就必须设置Will QoS和Will Retain标志位,消息主体中也必须有Will Topic和Will Message字段。

服务器与客户端通信时,当遇到异常或客户端心跳超时的情况,MQTT服务器会替客户端发布一个Will消息。当然如果服务器收到来自客户端的DISCONNECT消息,则不会触发Will消息的发送。

Will字段可以应用于设备掉线后需要通知用户的场景。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!