概述
核心:mqtt服务器------>mqtt客户端--------->数据库----------->web服务器------------>web页面
详情:mqtt服务器发布主题之后,客户端订阅主题,通过回调类拿到数据保存到数据库,接着web服务器读取数据库中的数据显示在页面。
大部分内容复制粘贴而来,加了一些自己的见解,如有理解不正确的地方请指教,谢谢!
一 mqtt协议(Broker代理服务器)
正如我们所了解的,MQTT解耦了发布者和订阅者,所以任何客户端都只与中间人(broker)建立连接。在深入了解连接细节之前,让我们先搞清楚客户端和中间人的概念。
1.MQTT协议是什么?
MQTT(Message Queuing Telemetry Transport Protocol)的全称是消息队列遥感传输协议的缩写,是一种基于轻量级代理的发布/订阅模式的消息传输协议,运行在TCP协议栈之上,为其提供有序、可靠、双向连接的网络连接保证。
2.MQTT协议如何工作?
MQTT采用代理的发布/订阅模式实现了发布者和订阅者的解耦(decouple),因此,在MQTT协议中有三种角色:代理服务器、发布者客户端以及订阅者客户端,其中发布者和订阅者互不干扰,也就是说发布者和订阅者互不知道对方的存在,它们只知道代理服务器,代理服务器负责将来自发布者的消息进行存储处理并将这些消息发送到正确的订阅者中去。
3.客户端
我们所说的客户端泛指MQTT的客户端,包含发布者和订阅者,分别负责发布消息和订阅消息。(通常情况下,一个MQTT实体同时具备发布者和订阅者两重功能)。任何包含了MQTT运行库并且通过任意网路类型连接到MQTT broker的且具备微控制器的设备都称为MQTT客户端。它可以是一个用于测试的小型设备,包含一个小型计算机系统,同时可以接入无线网络,最重要的是其支持TCP/IP协议从而允许MQTT在其上运行。在客户端上实现MQTT协议非常直观方便,基于此,可以说MQTT非常适合小型设备。MQTT客户端运行库支持大部分编程语言和平台,例如,Android,Arduino,C,C++,C#,Go,iOS,Java,Javascript,.Net。完整的支持列表可以参考MQTT wiki
4.中间人(Broker)
和MQTT客户端协作的另一部分是MQTT broker,其被称为发布/订阅协议的心脏部分,根据具体的实现不同,一个broker可以支持数以千计的客户端并发连接。broker的主要职责是接受所有消息,并将其过滤后分发给不同的消息订阅者。它也可以根据订阅内容和未送达的消息来保持持久的会话。broker的另一个职责是验证和授权客户端。在大多数时候,broker是可扩展的,我们可以将其整合进后台系统,整合进系统显得尤为重要,因为大多数时候,broker只是一个网络通信系统的组件。我们之前的一篇文章提到订阅所有消息并不是很好的选择。总而言之,broker是一个中心交换机,交换所有数据。因此高扩展性,可集成到后台系统,易于监控当然还包括不出错误对broker来说尤为重要。HiveMQ的插件系统提供了良好的解决方案。
5.MQTT连接
MQTT协议基于TCP/IP,并工作在其上层,所有的客户端和broker都需要支持TCP/IP协议。
MQTT连接只发生在客户端和broker之间,客户端并不直接与客户端连接。连接开始于一个客户端给broker发送一条CONNECT消息。broker回复一条CONNACK和状态码。一旦连接建立,broker将保持连接开放,直至客户端发送断开连接命令或者连接本身丢失。
6.MQTT通过NAT建立连接
通常情况下,MQTT客户端都在一个NAT路由器下,为了能够使一个局域网ip(例如192.168.x.x)与一个公网ip通信。正如前文提到的,MQTT客户端第一步是发送一个CONNECT消息,所以客户端在路由器下没有任何问题,因为broker有一个公网ip地址并且连接将会保持开放状态以允许在连接初始化后进行双向的发送和接受消息。
7.客户端以一个CONNECT消息初始化连接
让我们来看一下MQTT连接消息,正如前面提到的,客户端发送消息给broker以初始化连接。如果CONNET消息是畸形的,或者由建立socket连接到发送消息中间等待的时间过长,broker都会关闭连接。这是一个较好的避免恶意客户端攻击服务器的处理方式。一个正常的客户端将会按照下面的内容发送连接消息。
此外,CONNECT消息还包含了一些其他信息,这些信息与MQTT库的制定者有更多关系,实际使用者则不必关心,如果你感兴趣,请参考官方MQTT 3.1.1 说明
下面让我们逐个了解一下这些信息的含义。
ClientId
ClientId是连接到broker的每个MQTT客户端的唯一标识符。根据场景不同,broker制定的ID规则也可以不同。broker使用此标识符来识别客户端以及客户端的当前状态。如果你不需要broker记录客户端的状态,也可以发送一个空的ClientId,这样将会创建一个无状态的连接,此功能适用于MQTT 3.1.1版本。这样做的一个前提条件是cleanSession字段需要置为true,否则连接将会被拒绝。
Clean Session
Clean session 字段表明客户端是否想与broker建立持久的会话。一个持久的会话(cleanSession为false)意味着,当使用QoS级别为1或2时,broker将会存储所有的客户端订阅的消息,和尚未送达的消息。如果cleanSession为true时,broker不会存储任何客户端订阅的消息,并会将之前所存的内容清空。
Username/Password
MQTT允许发送用户名和密码来鉴定和授权客户端身份。然而,如果未使用TLS加密,用户名和密码将会以明文的方式传输。我们强烈建议使用安全传输协议来传输用户名和密码。HiveMQ broker也支持使用SSL验证客户端身份,此时用户名和密码不再必须。
Will Message(遗嘱)
遗嘱是MQTT的一大特色,它允许broker在发现一台设备意外断开时发送通告给其他相关设备。客户端在建立CONNECT连接时会将遗嘱打包在消息体里。如果这个客户端在没有通知的情况下意外断开连接,broker将会发送遗嘱消息给其他关联设备。我们将会在单独一章讨论此话题。
KeepAlive(心跳)
心跳是指客户端周期性地发送PING请求给broker,broker也会应答此心跳,这种机制可以保证双方知道对方是否还在线。我们将会在单独一章讨论此话题。
最主要的是所有消息都由MQTT客户端向broker建立连接,有一些定制化的库文件还会附加其他选项,例如规定消息如何排序和存储等。
8.Broker以CONNACK消息应答
当broker收到一个CONNECT消息时,broker有义务应答一个CONNACK消息,CONNACK只包含两个数据字段,一个是Session present flag(当前会话标志),另一个是Return code(返回码)。
Session Present Flag(当前会话标志)
当前会话标识可以表明broker是否在之前已经和客户端建立过持久会话。如果客户端连上来并且将cleanSession字段置为true,那么当前会话标志将始终为false,因为会话都已经被清空了。如果客户端在连上来时将cleanSession置为false,那么flag的状态决定于当前针对此客户端是否有可用的会话。如果有已有存储的会话消息,那么false将会为true,否则为false。这个flag标志在MQTT 3.1.1中被添加,以帮助客户端来确定是否需要订阅主题或判断当前是否有待处理的消息。
Return Code(返回码)
第二个字段是连接告知标志,它通知客户端,连接是否正常或出现了什么问题。
二 搭建自己的mqtt服务器
1.自己的服务器和阿里云平台mqtt服务器比较
阿里云MQTT的优点:方便,稳定,专业,安全。缺点:专业版收费,数据不在自己服务器上。
自建的优点:数据在自己服务器上。缺点:麻烦,如果涉及到高并发,得有专业人员支持,自己得做MQTT连接加密部分。
2.mqtt服务器种类
(1)Apache-Apollo:一个代理服务器,在ActiveMQ基础上发展而来,可以支持STOMP、AMQP、MQTT、Openwire、SSL和WebSockets等多种协议,并且Apollo提供后台管理页面,方便开发者管理和调试。
(2)EMQ:EMQ 2.0,号称百万级开源MQTT消息服务器,基于Erlang/OTP语言平台开发,支持大规模连接和分布式集群,发布订阅模式的开源MQTT消息服务器。
(3)HiveMQ:一个企业级的MQTT代理,主要用于企业和新兴的机器到机器M2M通讯和内部传输,最大程度的满足可伸缩性、易管理和安全特性,提供免费的个人版。HiveMQ提供了开源的插件开发包。
(4)Mosquitto:一款实现了消息推送协议MQTT v3.1的开源消息代理软件,提供轻量级的、支持可发布/可订阅的消息推送模式。
3.具体搭建百度
4.比较
Server | QoS 0 | QoS 1 | QoS 2 | auth | bridge | $SYS | SSL | dynamic topics | cluster | websockets | plugin system |
---|---|---|---|---|---|---|---|---|---|---|---|
Trafero Tstack | ✔ | ✔ | ✔ | ✔ | ✘ | ✘ | ✔ | ✔ | ✘ | ✘ | ✘ |
mosquitto | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | § | ✔ | ✔ |
RSMB | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✘ | ✘ | ? |
WebSphere MQ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ? | ? | ? |
HiveMQ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
Apache Apollo | ✔ | ✔ | ✔ | ✔ | ✘ | ✘ | ✔ | ✔ | ? | ✔ | ? |
Apache ActiveMQ | ✔ | ✔ | ✔ | ✔ | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✔ |
Software AG Universal Messaging | ✔ | ✔ | ✔ | ✔ | ✘ | ✘ | ✔ | ✔ | ✔ | rm | ✘ |
RabbitMQ | ✔ | ✔ | ✘ | ✔ | ✘ | ✘ | ✔ | ✔ | ? | ? | ? |
Solace | ✔ | ✔ | ✘ | ✔ | § | ✔ | ✔ | ✔ | ✔ | ✔ | ✘ |
MQTT.js | ✔ | ✔ | ✔ | § | ✘ | ✘ | ✔ | ✔ | ✘ | ✔ | ✘ |
moquette | ✔ | ✔ | ✔ | ✔ | ? | ? | ✔ | ? | rm | ✔ | ✘ |
mosca | ✔ | ✔ | ✘ | ✔ | ? | ? | ? | ? | ✘ | ✔ | ✘ |
IBM MessageSight | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ | ✔ | § | ✔ | ✘ |
2lemetry | ✔ | ✔ | ✔ | ✔ | ✔ | § | ✔ | ✔ | ✔ | ✔ | ✘ |
GnatMQ | ✔ | ✔ | ✔ | ✔ | ✘ | ✘ | ✘ | ✔ | ✘ | ✘ | ✘ |
JoramMQ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
VerneMQ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
emqttd | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
HBMQTT | ✔ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ | ✔ | ✘ | ✔ | ✔ |
Mongoose | ✔ | ✔ | ? | ? | ? | ? | ? | ? | ? | ? | ? |
emitter | ✔ | § | ✘ | ✔ | ✘ | ✘ | ✔ | ✔ | ✔ | ✔ | ✘ |
Bevywise IoT Platform | ✔ | ✔ | ✔ | ✔ | rm | ✔ | ✔ | ✔ | ✔ | ✔ |
三 java代码
1.服务器端发布主题代码
package bsit.mqtt.demo.one_way;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
/**
*
* Title:Server
* Description: 服务器向多个客户端推送主题,即不同客户端可向服务器订阅相同主题
* @author chenrl
*/
public class Server {
public static final String HOST = "tcp://127.0.0.1:61613";
public static final String TOPIC = "toclient/124";
public static final String TOPIC125 = "toclient/125";
private static final String clientid = "server";
private MqttClient client;
private MqttTopic topic;
private MqttTopic topic125;
private String userName = "admin";
private String passWord = "password";
private MqttMessage message;
public Server() throws MqttException {
// MemoryPersistence设置clientid的保存形式,默认为以内存保存
client = new MqttClient(HOST, clientid, new MemoryPersistence());
connect();
}
private void connect() {
MqttConnectOptions options = new MqttConnectOptions();
options.setCleanSession(false);
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);
topic125 = client.getTopic(TOPIC125);
} catch (Exception e) {
e.printStackTrace();
}
}
public void publish(MqttTopic topic , MqttMessage message) throws MqttPersistenceException,
MqttException {
MqttDeliveryToken token = topic.publish(message);//将请求传递到 MQTT 客户机后,立即返回到应用程序线程。
token.waitForCompletion();//等待返回传递令牌。
System.out.println("message is published completely! "
+ token.isComplete());
}
public static void main(String[] args) throws MqttException {
Server server = new Server();
server.message = new MqttMessage();
server.message.setQos(2);
server.message.setRetained(true);
server.message.setPayload("给客户端124推送的信息".getBytes());
server.publish(server.topic , server.message);
server.message = new MqttMessage();
server.message.setQos(2);
server.message.setRetained(true);
server.message.setPayload("给客户端125推送的信息".getBytes());
server.publish(server.topic125 , server.message);
System.out.println(server.message.isRetained() + "------ratained状态");
}
}
2.客户端订阅主题代码
package bsit.mqtt.demo.one_way;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttTopic;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class Client {
public static final String HOST = "tcp://127.0.0.1:61613";
public static final String TOPIC = "toclient/124";
private static final String clientid = "client124";
private MqttClient client;
private MqttConnectOptions options;
private String userName = "admin";
private String passWord = "password";
public void start() {
try {
// host为主机名,clientid即连接MQTT的客户端ID,一般以唯一标识符表示,MemoryPersistence设置clientid的保存形式,默认为以内存保存
client = new MqttClient(HOST, clientid, new MemoryPersistence());
// MQTT的连接设置
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);
// 设置回调
client.setCallback(new PushCallback());
MqttTopic topic = client.getTopic(TOPIC);
//setWill方法,如果项目中需要知道客户端是否掉线可以调用该方法。设置最终端口的通知消息
options.setWill(topic, "close".getBytes(), 2, true);
client.connect(options);
//订阅消息
int[] Qos = {1};
String[] topic1 = {TOPIC};
client.subscribe(topic1, Qos);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.回调类即调用数据库接口代码
package bsit.mqtt.demo.one_way;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import mqtt.demo.domain.Receive;
/**
* 发布消息的回调类
*
* 必须实现MqttCallback的接口并实现对应的相关接口方法CallBack 类将实现 MqttCallBack。
* 每个客户机标识都需要一个回调实例。在此示例中,构造函数传递客户机标识以另存为实例数据。
* 在回调中,将它用来标识已经启动了该回调的哪个实例。
* 必须在回调类中实现三个方法:
*
* public void messageArrived(MqttTopic topic, MqttMessage message)接收已经预订的发布。
*
* public void connectionLost(Throwable cause)在断开连接时调用。
*
* public void deliveryComplete(MqttDeliveryToken token))
* 接收到已经发布的 QoS 1 或 QoS 2 消息的传递令牌时调用。
* 由 MqttClient.connect 激活此回调。
*
*/
public class PushCallback implements MqttCallback {
public void connectionLost(Throwable cause) {
// 连接丢失后,一般在这里面进行重连
System.out.println("连接断开,可以做重连");
}
public void deliveryComplete(IMqttDeliveryToken token) {
System.out.println("deliveryComplete---------" + token.isComplete());
}
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()));
String str=new String(message.getPayload());
// setThreadId("sdf");
// test te=test.getInstance();
// te.put(str);
// System.out.println(te.get());
if(str.equals("close"))
System.out.println("客户端掉线!");
else {
SaveMysql save=new SaveMysql();
save.savedate(topic,message.getQos(),str);}
}
}
package bsit.mqtt.demo.one_way;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class SaveMysql {
private static String driver="com.mysql.jdbc.Driver";
private static String url="jdbc:mysql://localhost/mqtt_receive?useUnicode=true&characterEncoding=utf-8&useSSL=false";
private static String user="root";
private static String password="123456";
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
public void savedate(String topic,int Qos,String payload) {
String sql = "insert into m_receive(TOPIC,Qos,message)"+"values('"+topic+"','"+Qos+"','"+payload+"');";
try {
Class.forName(driver);
conn=DriverManager.getConnection(url,user,password);
stmt=conn.createStatement();
stmt.executeUpdate(sql);
}
catch(Exception e) {
e.printStackTrace();
}
finally {
try {
if(stmt!=null) stmt.close();
if(conn!=null) conn.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
四.如何使用通配符订阅多个主题?
如果用户需要一次订阅多个具有类似结构的主题,可以在主题过滤器中包含通配符。通配符只可用在主题过滤器中,在发布应用消息时的主题名不允许包含通配符,主题通配符有两种:
1.#:表示匹配>=0个层次,比如a/#就匹配a/b,a/b/c(不能匹配a/,后面必须有其它主题)。单独的一个#表示匹配所有,不允许a#或a/#/c等形式。
2.+:表示匹配一个层次,例如a/+匹配a/b,a/c,不匹配a/b/c。单独的一个+是允许的,但a+为非法形式。
五. 名词解释
资源类
Instance
用户创建购买 MQTT 服务的实体单元,每个 MQTT 实例都对应一个全局唯一的服务接入点 URL。使用 MQTT 前都需要在对应的 Region 创建一个实例,并使用对应的接入点来访问服务。关于如何创建 MQTT 实例,请参见快速入门指导。
Parent Topic
MQTT 协议基于 Pub/Sub 模型,因此任何消息都属于一个 Topic。根据 MQTT 协议,Topic 存在多级,定义第一级 Topic 为父 Topic(Parent Topic),使用 MQTT 前,该 Parent Topic 需要先在控制台创建,可以在 MQTT 控制台创建,或者直接在消息队列 RocketMQ 的控制台创建。
Subtopic
MQTT 的二级 Topic,甚至三级 Topic 都是父 Topic 下的子类。使用时,直接在代码里设置,无需创建。需要注意的是 MQTT 限制 Parent Topic 和 Subtopic 的总长度为64个字符,如果超出长度限制将会导致客户端异常。
Client ID
MQTT 的 Client ID 是每个客户端的唯一标识,要求全局唯一,使用相同的 Client ID 连接 MQTT 服务会被拒绝。
Client ID 由两部分组成,组织形式为 GroupID@@@DeviceID。Client ID 的长度限制为64个字符,不允许使用不可见字符,具体限制参考使用限制。
- Group ID: 用于指定一组逻辑功能完全一致的节点共用的组名,代表一类相同功能的设备。Group ID 需要在消息队列 RocketMQ 的控制台创建方可使用。关于如何创建,请参见快速入门指导。
- Device ID: 每个设备独一无二的标识,由业务方自己指定。需要保证全局唯一,例如每个传感器设备的序列号。
网络类
ServerUrl
MQTT 推荐移动终端使用公网接入点,也支持内网接入点。目前 MQTT 的接入除了支持标准协议的1883端口,同时还支持加密 SSL,WebSocket,Flash 等方式。接入点 URL 是在创建实例之后自动分配,请妥善保管。关于如何创建实例,请参见快速入门指导。
协议相关
QoS
QoS (Quality of Service)指代消息传输的服务质量。它包括 QoS0(最多分发一次)、QoS1(至少达到一次)和 QoS2(仅分发一次)三种级别。
cleanSession
cleanSession 标志是 MQTT 协议中对一个客户端建立 TCP 连接后是否关心之前状态的定义。具体语义如下:
- cleanSession=true:客户端再次上线时,将不再关心之前所有的订阅关系以及离线消息。
- cleanSession=false:客户端再次上线时,还需要处理之前的离线消息,而之前的订阅关系也会持续生效。
注意:
- MQTT 要求每个客户端每次连接时的 cleanSession 标志必须固定,不允许动态变化,否则会导致离线消息的判断有误。
- MQTT 目前对外 QoS2 消息不支持非 cleanSession ,如果客户端以 QoS2 方式订阅消息,即使设置 cleanSession=false 也不会生效。
- P2P 消息的 cleanSession 判断以发送方客户端的配置为准。
- QoS 和 cleanSession 的不同组合产生的结果如下表所示:
QoS 级别 | cleanSession=true | cleanSession=false |
---|---|---|
QoS0 | 无离线消息,在线消息只尝试推一次 | 无离线消息,在线消息只尝试推一次 |
QoS1 | 无离线消息,在线消息保证可达 | 有离线消息,所有消息保证可达 |
QoS2 | 无离线消息,在线消息保证只推一次 | 暂不支持 |
六.MQTT协议之使用Future模式订阅及发布(使用fusesource mqtt-client实现)
fusesource版本:mqtt-client-1.10.jar
下载地址:https://github.com/fusesource/mqtt-client
fusesource提供三种方式实现发布消息的方式:
1.采用阻塞式的连接的(BlockingConnection)
2.采用回调式的连接 (CallbackConnection)
3.采用Future样式的连接(FutureConnection)
其中,回调API是最复杂的也是性能最好的,
另外两种均是对回调API的封装。 我们下面就简单介绍一下回调API的使用方法。
1.服务器实现
package com.ctfo.mqtt.client.demo.fusesource;
import java.net.URISyntaxException;
import org.fusesource.hawtbuf.Buffer;
import org.fusesource.hawtbuf.UTF8Buffer;
import org.fusesource.hawtdispatch.Dispatch;
import org.fusesource.mqtt.client.Callback;
import org.fusesource.mqtt.client.CallbackConnection;
import org.fusesource.mqtt.client.FutureConnection;
import org.fusesource.mqtt.client.Listener;
import org.fusesource.mqtt.client.MQTT;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.mqtt.client.Topic;
import org.fusesource.mqtt.client.Tracer;
import org.fusesource.mqtt.codec.MQTTFrame;
/**
* 采用Future模式 发布主题
*
*/
public class MQTTFutureServer {
private final static String CONNECTION_STRING = "tcp://192.168.13.240:1883";
private final static boolean CLEAN_START = true;
private final static String CLIENT_ID = "server";
private final static short KEEP_ALIVE = 30;// 低耗网络,但是又需要及时获取数据,心跳30s
public static Topic[] topics = {
new Topic("mqtt/aaa", QoS.EXACTLY_ONCE),
new Topic("mqtt/bbb", QoS.AT_LEAST_ONCE),
new Topic("mqtt/ccc", QoS.AT_MOST_ONCE)};
public final static long RECONNECTION_ATTEMPT_MAX=6;
public final static long RECONNECTION_DELAY=2000;
public final static int SEND_BUFFER_SIZE=2*1024*1024;//发送最大缓冲为2M
public static void main(String[] args) {
MQTT mqtt = new MQTT();
try {
//==MQTT设置说明
//设置服务端的ip
mqtt.setHost(CONNECTION_STRING);
//连接前清空会话信息 ,若设为false,MQTT服务器将持久化客户端会话的主体订阅和ACK位置,默认为true
mqtt.setCleanSession(CLEAN_START);
//设置心跳时间 ,定义客户端传来消息的最大时间间隔秒数,服务器可以据此判断与客户端的连接是否已经断开,从而避免TCP/IP超时的长时间等待
mqtt.setKeepAlive(KEEP_ALIVE);
//设置客户端id,用于设置客户端会话的ID。在setCleanSession(false);被调用时,MQTT服务器利用该ID获得相应的会话。
//此ID应少于23个字符,默认根据本机地址、端口和时间自动生成
mqtt.setClientId(CLIENT_ID);
//服务器认证用户名
//mqtt.setUserName("admin");
//服务器认证密码
//mqtt.setPassword("admin");
/*
//设置“遗嘱”消息的内容,默认是长度为零的消息
mqtt.setWillMessage("willMessage");
//设置“遗嘱”消息的QoS,默认为QoS.ATMOSTONCE
mqtt.setWillQos(QoS.AT_LEAST_ONCE);
//若想要在发布“遗嘱”消息时拥有retain选项,则为true
mqtt.setWillRetain(true);
//设置“遗嘱”消息的话题,若客户端与服务器之间的连接意外中断,服务器将发布客户端的“遗嘱”消息
mqtt.setWillTopic("willTopic");
*/
//==失败重连接设置说明
//设置重新连接的次数 ,客户端已经连接到服务器,但因某种原因连接断开时的最大重试次数,超出该次数客户端将返回错误。-1意为无重试上限,默认为-1
mqtt.setReconnectAttemptsMax(RECONNECTION_ATTEMPT_MAX);
//设置重连的间隔时间 ,首次重连接间隔毫秒数,默认为10ms
mqtt.setReconnectDelay(RECONNECTION_DELAY);
//客户端首次连接到服务器时,连接的最大重试次数,超出该次数客户端将返回错误。-1意为无重试上限,默认为-1
//mqtt.setConnectAttemptsMax(10L);
//重连接间隔毫秒数,默认为30000ms
//mqtt.setReconnectDelayMax(30000L);
//设置重连接指数回归。设置为1则停用指数回归,默认为2
//mqtt.setReconnectBackOffMultiplier(2);
//== Socket设置说明
//设置socket接收缓冲区大小,默认为65536(64k)
//mqtt.setReceiveBufferSize(65536);
//设置socket发送缓冲区大小,默认为65536(64k)
mqtt.setSendBufferSize(SEND_BUFFER_SIZE);
设置发送数据包头的流量类型或服务类型字段,默认为8,意为吞吐量最大化传输
mqtt.setTrafficClass(8);
//==带宽限制设置说明
mqtt.setMaxReadRate(0);//设置连接的最大接收速率,单位为bytes/s。默认为0,即无限制
mqtt.setMaxWriteRate(0);//设置连接的最大发送速率,单位为bytes/s。默认为0,即无限制
//==选择消息分发队列
//若没有调用方法setDispatchQueue,客户端将为连接新建一个队列。如果想实现多个连接使用公用的队列,显式地指定队列是一个非常方便的实现方法
//mqtt.setDispatchQueue(Dispatch.createQueue("mqtt/aaa"));
//==设置跟踪器
/* mqtt.setTracer(new Tracer(){
@Override
public void onReceive(MQTTFrame frame) {
System.out.println("recv: "+frame);
}
@Override
public void onSend(MQTTFrame frame) {
System.out.println("send: "+frame);
}
@Override
public void debug(String message, Object... args) {
System.out.println(String.format("debug: "+message, args));
}
});*/
//使用Future创建连接
final FutureConnection connection= mqtt.futureConnection();
connection.connect();
int count=1;
while(true){
count++;
// 用于发布消息,目前手机段不需要向服务端发送消息
//主题的内容
String message="Hello "+count+" MQTT...";
String topic = "mqtt/bbb";
connection.publish(topic, message.getBytes(), QoS.AT_LEAST_ONCE,
false);
System.out.println("MQTTFutureServer.publish Message "+"Topic Title :"+topic+" context :"+message);
}
//使用回调式API
/* final CallbackConnection callbackConnection=mqtt.callbackConnection();
//连接监听
callbackConnection.listener(new Listener() {
//接收订阅话题发布的消息
@Override
public void onPublish(UTF8Buffer topic, Buffer body, Runnable ack) {
System.out.println("=============receive msg================"+new String(body.toByteArray()));
ack.run();
}
//连接失败
@Override
public void onFailure(Throwable value) {
System.out.println("===========connect failure===========");
callbackConnection.disconnect(null);
}
//连接断开
@Override
public void onDisconnected() {
System.out.println("====mqtt disconnected=====");
}
//连接成功
@Override
public void onConnected() {
System.out.println("====mqtt connected=====");
}
});
//连接
callbackConnection.connect(new Callback<Void>() {
//连接失败
public void onFailure(Throwable value) {
System.out.println("============连接失败:"+value.getLocalizedMessage()+"============");
}
// 连接成功
public void onSuccess(Void v) {
//订阅主题
Topic[] topics = {new Topic("mqtt/bbb", QoS.AT_LEAST_ONCE)};
callbackConnection.subscribe(topics, new Callback<byte[]>() {
//订阅主题成功
public void onSuccess(byte[] qoses) {
System.out.println("========订阅成功=======");
}
//订阅主题失败
public void onFailure(Throwable value) {
System.out.println("========订阅失败=======");
callbackConnection.disconnect(null);
}
});
//发布消息
callbackConnection.publish("mqtt/bbb", ("Hello ").getBytes(), QoS.AT_LEAST_ONCE, true, new Callback<Void>() {
public void onSuccess(Void v) {
System.out.println("===========消息发布成功============");
}
public void onFailure(Throwable value) {
System.out.println("========消息发布失败=======");
callbackConnection.disconnect(null);
}
});
}
});
*/
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.客户端实现:
package com.ctfo.mqtt.client.demo.fusesource;
import java.net.URISyntaxException;
import org.fusesource.mqtt.client.Future;
import org.fusesource.mqtt.client.FutureConnection;
import org.fusesource.mqtt.client.MQTT;
import org.fusesource.mqtt.client.Message;
import org.fusesource.mqtt.client.QoS;
import org.fusesource.mqtt.client.Topic;
/**
* 采用Future模式 订阅主题
*/
public class MQTTFutureClient {
private final static String CONNECTION_STRING = "tcp://192.168.13.240:1883";
private final static boolean CLEAN_START = true;
private final static short KEEP_ALIVE = 30;// 低耗网络,但是又需要及时获取数据,心跳30s
private final static String CLIENT_ID = "client";
public static Topic[] topics = {
new Topic("mqtt/aaa", QoS.EXACTLY_ONCE),
new Topic("mqtt/bbb", QoS.AT_LEAST_ONCE),
new Topic("mqtt/ccc", QoS.AT_MOST_ONCE) };
public final static long RECONNECTION_ATTEMPT_MAX = 6;
public final static long RECONNECTION_DELAY = 2000;
public final static int SEND_BUFFER_SIZE = 2 * 1024 * 1024;// 发送最大缓冲为2M
public static void main(String[] args) {
// 创建MQTT对象
MQTT mqtt = new MQTT();
try {
// 设置mqtt broker的ip和端口
mqtt.setHost(CONNECTION_STRING);
// 连接前清空会话信息
mqtt.setCleanSession(CLEAN_START);
// 设置重新连接的次数
mqtt.setReconnectAttemptsMax(RECONNECTION_ATTEMPT_MAX);
// 设置重连的间隔时间
mqtt.setReconnectDelay(RECONNECTION_DELAY);
// 设置心跳时间
mqtt.setKeepAlive(KEEP_ALIVE);
// 设置缓冲的大小
mqtt.setSendBufferSize(SEND_BUFFER_SIZE);
//设置客户端id
mqtt.setClientId(CLIENT_ID);
// 获取mqtt的连接对象BlockingConnection
final FutureConnection connection = mqtt.futureConnection();
connection.connect();
connection.subscribe(topics);
while (true) {
Future<Message> futrueMessage = connection.receive();
Message message = futrueMessage.await();
System.out.println("MQTTFutureClient.Receive Message " + "Topic Title :" + message.getTopic() + " context :"
+ String.valueOf(message.getPayloadBuffer()));
}
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
}
}
}
最后
以上就是酷酷水杯为你收集整理的mqtt服务器—web服务器——web页面的全部内容,希望文章能够帮你解决mqtt服务器—web服务器——web页面所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复