文章目录
- 1. 简介
- 使用场景
- 2. 搭建MQTT测试环境服务
- 1. 先创建映射目录
- 2. 创建两个文件
- 2.1. mosquitto.conf
- 3. 启动 MQTT服务 Docker 容器
- 3.1. 配置用户名和密码
- 3.1.1. 创建密码文件
- 3.1.2. 修改配置文件,追加密码文件
- 3.2. 重启mosquitto 容器服务
- 3. 编写测试程序
- 3.1.pom依赖
- 3.2.配置文件
- 3.3.配置连接信息初始化client
- MqttService
- 使用Controller来直观的测试验证
- 4. 常见报错
1. 简介
MQTT,全称Message Queuing Telemetry Transport,即消息队列遥测传输。它是一种基于发布/订阅模式的"轻量级"消息协议,有助于低带宽、不可靠或高延迟的网络环境中的远程传感器和控制设备消息通信。
让我们以一个生动的例子来揭开MQTT的神秘面纱。假设你是一名热衷于植物种植的园丁,你有一个自动灌溉系统,它可以根据植物的需要,如土壤湿度、天气预报等,自动浇水。在这个系统中,MQTT就像是你和这个自动灌溉系统之间的通信者。
-
发布/订阅模式: 这就像你告诉自动灌溉系统:“嘿,如果土壤湿度低于某个阈值,就告诉我一声。”(即订阅),然后,当土壤湿度确实低于阈值时,系统就会通知你:“嘿,土壤湿度低了,需要浇水了。”(即发布)。
-
轻量级: MQTT设计得非常简洁轻巧,就像是你和自动灌溉系统的对话中只包含必要的信息,例如“土壤湿度”、“浇水”,而不包含任何复杂的语法或者冗余的信息。
-
QoS(Quality of Service): MQTT定义了三种消息质量等级:QoS 0(最多一次)、QoS 1(至少一次)和QoS 2(只有一次)。这就像是你可以告诉自动灌溉系统:“对于浇水这件事,我希望你只通知我一次。”或者“对于土壤湿度的问题,我希望你一直通知我,直到我处理完为止。”
-
保留消息和遗嘱消息: MQTT可以设置保留消息,让新订阅者立即得到最新的更新。此外,还可以设置遗嘱消息,当客户端异常断开连接时,服务器会发布这个遗嘱消息,通知其他客户端。这就像是你可以对自动灌溉系统说:“如果我突然失去联系,你就自动启动浇水程序。”
使用场景
-
物联网(IoT)通信:MQTT是物联网通信的重要协议之一。它提供了一种可靠、高效的方式来连接和交换传感器、设备和应用程序之间的数据。MQTT的轻量级特性使其在资源受限的设备上运行效率高,并支持发布/订阅模型,使设备能够通过订阅感兴趣的主题(Topic)来接收数据,或者通过发布数据到特定主题来向其他设备传递信息。
-
实时数据传输:MQTT可以用于实时数据传输,例如传感器数据、监测数据、实时位置数据等。它支持低延迟、高可靠性的消息传输,并具有良好的扩展性,可以应对大规模的数据传输需求。
-
远程监控与控制:MQTT可以用于远程监控和控制各种设备和系统。通过将设备连接到MQTT Broker,可以实现对设备状态的实时监控,并通过MQTT消息发送指令来控制设备操作。
-
传感器网络:MQTT适用于传感器网络,其中大量的传感器设备需要将数据发送到中心或云平台进行处理和分析。MQTT的低网络开销、可靠的消息传输和易于实现的特性使其成为传感器网络中常用的通信协议。
-
聊天和即时通信:MQTT的轻量级和高效性使其成为构建聊天和即时通信应用程序的理想选择。通过MQTT,可以实现实时的消息传递和订阅机制,支持点对点通信、多用户聊天和群组通信等功能。
-
跨平台集成:MQTT是一种跨平台的协议,可以在各种操作系统和设备上运行,包括嵌入式设备、移动设备和服务器。这使得MQTT成为不同设备和应用程序之间进行通信和集成的理想选择。
2. 搭建MQTT测试环境服务
我是用docker
搭建了一个简单 的mosquitto 服务端
1. 先创建映射目录
$ mkdir -p /data/mosquitto/config
$ mkdir -p /data/mosquitto/data
$ mkdir -p /data/mosquitto/log
2. 创建两个文件
mosquitto.conf
和 passwd
root@ip /data/mosquitto/config# ll
total 4
-rw-r--r-- 1 1883 1883 168 Sep 8 18:05 mosquitto.conf
-rw-r--r-- 1 root root 0 Sep 8 18:02 passwd
2.1. mosquitto.conf
新建 /data/mosquitto/config/mosquitto.conf
追加以下内容
persistence true
persistence_location /mosquitto/data/
allow_anonymous false
log_dest file /mosquitto/log/mosquitto.log
listener 1883
3. 启动 MQTT服务 Docker 容器
Docker中创建并运行一个名为"mosquitto"的容器,该容器包含了Eclipse Mosquitto MQTT Broker。该容器将监听主机的1883端口和9003端口,同时使用映射的配置文件、数据目录和日志目录来进行配置和持久化存储。
docker run -it --name=mosquitto --privileged -p 1883:1883 -p 9003:9001 -v /data/mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf -v /data/mosquitto/data:/mosquitto/data -v /data/mosquitto/log:/mosquitto/log -d eclipse-mosquitto
--privileged
: 这个选项授予容器特权访问,以便执行某些特定的操作和功能。
-p 1883:1883
: 这个选项将主机的端口1883映射到容器内部的端口1883,用于MQTT协议的通信。
-p 9003:9001
: 这个选项将主机的端口9003映射到容器内部的端口9001,用于WebSocket通信。
-v /data/mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf
: 这个选项将主机上的Mosquitto配置文件mosquitto.conf
映射到容器内部的相应路径,以便在容器中加载自定义配置。
-v /data/mosquitto/data:/mosquitto/data
: 这个选项将主机上的Mosquitto数据目录映射到容器内部的相应路径,用于持久化存储Mosquitto的数据。
-v /data/mosquitto/log:/mosquitto/log
: 这个选项将主机上的Mosquitto日志目录映射到容器内部的相应路径,用于存储Mosquitto的日志文件。
3.1. 配置用户名和密码
3.1.1. 创建密码文件
# 进入容器执行
$ docker exec -it b495e3d42429 sh
# 容器内执行
$ mosquitto_passwd -c /data/mosquitto/passwd root
输入两次密码后
3.1.2. 修改配置文件,追加密码文件
mosquitto.conf
persistence true
persistence_location /mosquitto/data/
allow_anonymous false
log_dest file /mosquitto/log/mosquitto.log
listener 1883
password_file /mosquitto/passwd
3.2. 重启mosquitto 容器服务
docker restart mosquitto
程序即可正常连接,这个版本如果没有配置密码是不能远程访问的额
3. 编写测试程序
3.1.pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3.2.配置文件
# 服务器的端口号
server.port=8080
# MQTT 代理服务器的连接URI
mqtt.broker=tcp://127.0.0.1:1883
# MQTT 客户端ID,应保证在同一MQTT服务器上唯一
mqtt.clientId=1
# MQTT 默认的订阅话题
mqtt.topic=testTopic
# MQTT 代理服务器的用户名
mqtt.username=root
# MQTT 代理服务器的密码
mqtt.password=123456
# MQTT 链接是否启用清理会话,true 表示客户端与服务器断开连接后,会话信息将被清除,false 表示信息将会保留,以便客户端重新连接
mqtt.cleanSession=true
# MQTT 设置的心跳时间,即每隔多久时间(秒)客户端需要向服务端发送一个PINGREQ报文
mqtt.keepAlive=60
# MQTT 连接超时设置,指的是客户端连接到服务器时,等待CONNACK报文回应的最大时间间隔。
mqtt.timeout=30
3.3.配置连接信息初始化client
package com.icepip.project.mqtt.config;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* MQTT配置
*
* @author 冰点
* @version 1.0.0
* @date 2023/9/8 16:14
*/
@Configuration
public class MqttConfig {
@Value("${mqtt.broker}")
private String mqttBroker;
@Value("${mqtt.clientId}")
private String mqttClientId;
@Value("${mqtt.username}")
private String username;
@Value("${mqtt.password}")
private String password;
@Value("${mqtt.cleanSession}")
private Boolean cleanSession;
@Value("${mqtt.keepAlive}")
private Integer keepAlive;
@Value("${mqtt.timeout}")
private Integer timeout;
@Bean
public MqttConnectOptions mqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(new String[]{mqttBroker});
options.setCleanSession(true);
// 其他设置,如用户名和密码等
options.setUserName(username);
options.setPassword(password.toCharArray());
options.setCleanSession(cleanSession);
options.setKeepAliveInterval(keepAlive);
options.setConnectionTimeout(timeout);
return options;
}
@Bean
public MqttClient mqttClient() throws MqttException {
MqttClient mqttClient = new MqttClient(mqttBroker, MqttClient.generateClientId());
mqttClient.connect(mqttConnectOptions());
return mqttClient;
}
}
MqttService
package com.icepip.project.mqtt.service;
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author 冰点(icepip.blog.csdn.com)
* @version 1.0.0
* @date 2023/9/8 16:15
*/
@Service
public class MqttService {
@Autowired
private MqttClient mqttClient;
public void publishMessage(String topic, String message) throws MqttException {
MqttMessage mqttMessage = new MqttMessage(message.getBytes());
mqttClient.publish(topic, mqttMessage);
}
public void subscribeToTopic(String topic, IMqttMessageListener listener) throws MqttException {
mqttClient.subscribe(topic, listener);
}
}
使用Controller来直观的测试验证
package com.icepip.project.mqtt.controller;
import com.icepip.project.mqtt.service.MqttService;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
/**
* 使用Controller来直观的测试验证
* @author 冰点(icepip.blog.csdn.com)
* @version 1.0.0
* @date 2023/9/8 16:15
*/
@RestController
@RequestMapping("/mqtt")
public class MqttController {
private final MqttService mqttService;
@Value("${mqtt.topic:testTopic}")
public String DEFAULT_TOPIC;
@Autowired
public MqttController(MqttService mqttService) {
this.mqttService = mqttService;
}
/**
* 模拟发布消息
*
* @param topic
* @param message
* @throws MqttException
*/
@PostMapping("/publish")
public void publishMessage(@RequestParam String topic, @RequestParam String message) throws MqttException {
if (StringUtils.isEmpty(topic)) {
topic=this.DEFAULT_TOPIC;
}
mqttService.publishMessage(topic, message);
}
/**
* 模拟订阅消息
*
* @param topic
* @throws MqttException
*/
@PostMapping("/subscribe")
public void subscribeToTopic(@RequestParam String topic) throws MqttException {
mqttService.subscribeToTopic(topic, (topic1, message) ->
System.out.println("Received topic:"+topic1+",message: " + new String(message.getPayload())));
}
/**
* 监听默认消息
* @throws MqttException
*/
@PostConstruct
public void subscribeToTopic() throws MqttException {
mqttService.subscribeToTopic(DEFAULT_TOPIC, (topic1, message) ->
System.out.println("Received topic:"+topic1+",message: " + new String(message.getPayload())));
}
}
测试
输出结果
4. 常见报错
1.org.eclipse.paho.client.mqttv3.MqttSecurityException: 无权连接 此错误需要配置账号和密码
2.connect timeout 是端口没有开启端口有两个,一个是数据端口 一个websocket 端口。