Day116 若依融合mqtt

MQTT

1.MQTT协议概述

MQTT是一种基于发布/订阅模式的轻量级消息传输协议,设计用于低带宽、高延迟或不稳定的网络环境,广泛应用于物联网领域

1.1 MQTT协议的应用场景

1.智能家居、车联网、工业物联网:MQTT可以用于连接各种家电设备和传感器,实现设备之间的通信和控制

2.远程监控和控制 : MQTT可以用于将传感器数据发布到云平台,供其他设备或应用程序订阅和使用

3.消息通知:MQTT可以用于提供实时消息通知功能

4.资源监控与管理:MQTT能够提供对物联网设备的实时监控和管理功能

5.数据采集和分析:MQTT也可以用于数据采集和分析

1.2 MQTT协议优势

1.常见的计算机语言(C/C++、Java、Python、Go…)都有支持MQTT协议的客户端

2.MQTT协议是建立在TCP/IP协议基础之上,所以MQTT协议安全可靠

3.服务质量设置,MQTT协议提供了三种服务质量配置分别为:
Qos 0:消息可能丢失
Qos 1:消息不会丢失,但是可能重复
Qos 2:消息不会丢失也不会重复

4.心跳保活:由于网络问题可能造成连接陷于假死状态,为了判断客户端和代理是否出现异常,MQTT定义自己的心跳机制,定期向代理发送报文,以便于快速识别出异常连接,让客户端快速与代理断开连接

5.持久会话: 代理保留客户端发送过来的消息,以便于消息订阅端上线立刻获取消息

1.3 MQTT协议报文

MQTT报文(数据包)由三部分组成:

1.固定报头(Fixed header):所有数据包中都包含此报头,用来表示数据包的类型,以及数据包的分组累标识

2.可变报头(Variable header):存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容

3.有效载荷(Payload):存在于部分MQTT数据包中,表示客户端收到的具体内容

在这里插入图片描述

1.4 MQTT协议的工作原理

在这里插入图片描述

MQTT协议基于TCP/IP协议,TCP/IP协议是一个安全稳定的协议,通信需要服务端和客户端经历三次握手四次挥手,建立一个稳定的通道然后在进行数据传输

MQTT协议建立在TCP/IP协议之上,也是需要编写服务端(上图中的Broker)和客户端(消息发布者和消息订阅者)

2.MQTT代理服务器介绍和搭建

EMQX官网地址: https://www.emqx.io/zh

介绍:

1.开源大规模分布式MQTT代理服务器

2.单台并发连接数可以高达一亿,每秒处理百万级消息

3.安全可靠的消息传递

2.1 EMQX安装: windows

1.下载地址: https://www.emqx.io/zh/get-started

2.安装步骤:

第一步: 下载 emqx-5.3.2-windows-amd64.zip 安装包,版本可能和我这个不同

第二步: 解压

第三步: 打开命令行(以管理员身份运行),切换到解压目录的bin目录下

第四步: 安装,在bin目录下执行EMQX安装命令 emqx.cmd install,完成之后有类似下面的输出,说明安装成功,只需要安装一次(运行命令)

​ D:\app\emqx-5.3.2-windows-amd64\bin>emqx.cmd install
​ EMQX_NODE__DB_ROLE [node.role]: core
​ EMQX_NODE__DB_BACKEND [node.db_backend]: mnesia
​ D:\app\emqx-5.3.2-windows-amd64\erts-13.2.2.4\bin\erlsrv.exe: Service emqx_5.3.2 added to system.
​ [SC] ChangeServiceConfig 成功

第五步(可选择):如果想将EMQX从windows上卸载,可以执行 emqx.cmd uninstall 命令

第六步:去windows服务列表中找到第四步安装的EMQX的服务,鼠标右键启动

第七步:在命令行输入 emqx.cmd console 命令,查看是否启动成功,如果有类似以下日志启动成功

​ D:\app\emqx-5.3.2-windows-amd64\bin>emqx.cmd console
​ EMQX_LOG__CONSOLE_HANDLER__ENABLE [log.console.enable]: true
​ EMQX_NODE__DB_ROLE [node.role]: core
​ EMQX_NODE__DB_BACKEND [node.db_backend]: mnesia

D:\app\emqx-5.3.2-windows-amd64>D:\app\emqx-5.3.2-windows-amd64\erts-13.2.2.4\bin\erl.exe -mode embedded -boot “D:\app\emqx-5.3.2-windows-amd64\releases\5.3.2\start” -config “D:\app\emqx-5.3.2-windows-amd64\data\configs\app.2024.05.06.16.38.19.config” -args_file “D:\app\emqx-5.3.2-windows-amd64\data\configs\vm.2024.05.06.16.38.19.args” -mnesia dir ‘d:/app/emqx-5.3.2-windows-amd64/data/mnesia/emqx@127.0.0.1’
Listener ssl:default on 0.0.0.0:8883 started.
Listener tcp:default on 0.0.0.0:1883 started.
Listener ws:default on 0.0.0.0:8083 started.
Listener wss:default on 0.0.0.0:8084 started.
Listener http:dashboard on :18083 started.
EMQX 5.3.2 is running now!
Eshell V13.2.2.4 (abort with ^G)
v5.3.2(emqx@127.0.0.1)1>

第八步:通过浏览器访问控制台http://127.0.0.1:18083,默认初始化用户名: admin,默认密码: public,进入之后会让你重新修改密码

注意事项: (第六步+第七步)这种启动方式在开发时使用,如果想正式环境使用请遵循官网命令启动介绍: 正式环境启动在bin目录下直接输入 emqx start进行EMQX启动,这时不需要(第六步和第七步)

官网命令详细使用地址: https://www.emqx.io/docs/zh/latest/admin/cli.html

2.2 MQTT客户端工具MQTTX

EMQX官网自带工具MQTTX,官网地址: https://mqttx.app/zh/downloads

傻瓜式安装,无脑下一步

若依框架融合mqtt

仓库地址:https://gitee.com/peng-chuanbin/iot-mqtt.git

实现效果:(初步Demo实现)

点击网页的按钮(发送数据),mqtt能够接收到发送的数据

mqtt发送数据,Java程序能够接收到,并且存储到数据库中

1.下载mqttx

在这里插入图片描述

2.运行项目

1.新建一个mqtt数据库,然后运行sql文件,修改yaml中的数据库配置

在这里插入图片描述

2.pom.xml添加mqtt的依赖

        <!--mqtt--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration</artifactId></dependency><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-stream</artifactId></dependency><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-mqtt</artifactId></dependency>

3.utils包下新建一个mqtt包,添加三个文件

package com.ruoyi.common.utils.mqtt;@Component
@ConfigurationProperties("spring.mqtt")
public class MqttConfig {@Autowiredprivate MqttPushClient mqttPushClient;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 连接地址*/private String hostUrl;/*** 客户Id*/private String clientId;/*** 默认连接话题*/private String defaultTopic;/*** 超时时间*/private int timeout;/*** 保持连接数*/private int keepalive;/*** mqtt功能使能*/private boolean enabled;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getHostUrl() {return hostUrl;}public void setHostUrl(String hostUrl) {this.hostUrl = hostUrl;}public String getClientId() {return clientId;}public void setClientId(String clientId) {this.clientId = clientId;}public String getDefaultTopic() {return defaultTopic;}public void setDefaultTopic(String defaultTopic) {this.defaultTopic = defaultTopic;}public int getTimeout() {return timeout;}public void setTimeout(int timeout) {this.timeout = timeout;}public int getKeepalive() {return keepalive;}public void setKeepalive(int keepalive) {this.keepalive = keepalive;}public boolean isEnabled() {return enabled;}public void setEnabled(boolean enabled) {this.enabled = enabled;}@Beanpublic MqttPushClient getMqttPushClient() {if (enabled == true) {mqttPushClient.connect(hostUrl, clientId, username, password, timeout, keepalive);//订阅java主题   #重要,这里确定主题是哪一个mqttPushClient.subscribe("java",0);}return mqttPushClient;}
}

PushCallback

package com.ruoyi.common.utils.mqtt;@Component
public class PushCallback implements MqttCallback {private static final Logger logger = LoggerFactory.getLogger(MqttPushClient.class);@Autowiredprivate MqttConfig mqttConfig;private static MqttClient client;private static String _topic;private static String _qos;private static String _msg;@Overridepublic void connectionLost(Throwable throwable) {// 连接丢失后,一般在这里面进行重连logger.info("连接断开,可以做重连");if (client == null || !client.isConnected()) {mqttConfig.getMqttPushClient();}}@Overridepublic void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {// subscribe后得到的消息会执行到这里面logger.info("接收消息主题 : " + topic);logger.info("接收消息Qos : " + mqttMessage.getQos());logger.info("接收消息内容 : " + new String(mqttMessage.getPayload()));}@Overridepublic void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {logger.info("deliveryComplete---------" + iMqttDeliveryToken.isComplete());}}

MqttPushClient

package com.ruoyi.common.utils.mqtt;@Component
public class MqttPushClient {private static final Logger logger = LoggerFactory.getLogger(MqttPushClient.class);@Autowiredprivate PushCallback pushCallback;private static MqttClient client;private static MqttClient getClient() {return client;}private static void setClient(MqttClient client) {MqttPushClient.client = client;}/*** 客户端连接** host  ip+端口* clientID  客户端Id* username  用户名* password  密码* timeout 超时时间* keepalive 保留数*/public void connect(String host, String clientID, String username, String password, int timeout, int keepalive) {MqttClient client;try {client = new MqttClient(host, clientID, new MemoryPersistence());MqttConnectOptions options = new MqttConnectOptions();options.setCleanSession(true);options.setUserName(username);options.setPassword(password.toCharArray());options.setConnectionTimeout(timeout);options.setKeepAliveInterval(keepalive);MqttPushClient.setClient(client);try {client.setCallback(pushCallback);client.connect(options);} catch (Exception e) {e.printStackTrace();}} catch (Exception e) {e.printStackTrace();}}/*** 发布* qos 连接方式* retained 是否保留* topic 主题* pushMessage 消息体*/public AjaxResult publish(int qos, boolean retained, String topic, String pushMessage) {MqttMessage message = new MqttMessage();message.setQos(qos);message.setRetained(retained);message.setPayload(pushMessage.getBytes());MqttTopic mTopic = MqttPushClient.getClient().getTopic(topic);if (null == mTopic) {logger.error("topic not exist");}MqttDeliveryToken token;try {token = mTopic.publish(message);token.waitForCompletion();return success();} catch (MqttPersistenceException e) {e.printStackTrace();return error();} catch (MqttException e) {e.printStackTrace();return error();}}/*** 订阅某个主题* topic 主题* qos 连接方式*/public void subscribe(String topic, int qos) {logger.info("开始订阅主题" + topic);try {MqttPushClient.getClient().subscribe(topic, qos);} catch (MqttException e) {e.printStackTrace();}}}

4.application.yml

如果有服务器了,直接修改broker.emqx.io这个就可以了,其他的都无所谓

broker.emqx.io:官方测试的

  # mqttmqtt:username: pcb # 用户名password: 123456 # 密码hostUrl: tcp://broker.emqx.io:1883 # tcp://ip:端口   #重要clientId: clientIdBamBam # 客户端iddefaultTopic: topic,topic1 # 订阅主题   #重要timeout: 100 # 超时时间 (单位:秒)keepalive: 60 # 心跳 (单位:秒)enabled: true # 是否使能mqtt功能

5.启动mqttx,运行项目,访问 http://localhost/ruoyi

在这里插入图片描述

mqttx发送数据,Java程序接收数据

在这里插入图片描述

3.设计前端界面

新建一个WlwController

package com.ruoyi.project.system.wlw.controller;@Controller
@RequestMapping("/system/wlw")
public class WlwController {private String prefix = "system/wlw";@GetMapping()public String wlw(){return prefix + "/w";}}

在resources包下的templates包中system包新建一个w.html界面,用来发送数据和显示数据

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>wlw</title>
</head>
<body>1
</body>
</html>

ShiroConfig放开拦截

    /*** Shiro过滤器配置*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){// Shiro连接约束配置,即过滤链的定义LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 对静态资源设置匿名访问filterChainDefinitionMap.put("/favicon.ico**", "anon");filterChainDefinitionMap.put("/ruoyi.png**", "anon");filterChainDefinitionMap.put("/html/**", "anon");filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/docs/**", "anon");filterChainDefinitionMap.put("/fonts/**", "anon");filterChainDefinitionMap.put("/img/**", "anon");filterChainDefinitionMap.put("/ajax/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/ruoyi/**", "anon");filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");// 退出 logout地址,shiro去清除sessionfilterChainDefinitionMap.put("/logout", "logout");// 不需要拦截的访问filterChainDefinitionMap.put("/login", "anon,captchaValidate");// 不需要拦截的访问 wlw   添加这句话filterChainDefinitionMap.put("/system/wlw", "anon,captchaValidate");}

访问 http:/localhost:80/ruoyi/system/wlw

在这里插入图片描述

4.设计假数据

新建数据库表w

在这里插入图片描述

使用若依自动生成代码

在这里插入图片描述

将生成的代码放到指定位置

在这里插入图片描述

xml文件,并且添加一句话:在执行插入(INSERT)操作之前,先生成一个主键值id,并将其设置到要插入的对象中

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.project.system.w.mapper.WMapper"><resultMap type="W" id="WResult"><result property="id"    column="id"    /><result property="topic"    column="topic"    /><result property="data"    column="data"    /></resultMap><sql id="selectWVo">select id, topic, data from w</sql><insert id="insertW" parameterType="W">//添加这句话<selectKey keyProperty="id" resultType="String" order="BEFORE" >SELECT REPLACE(UUID(),'-','') from dual</selectKey>insert into w<trim prefix="(" suffix=")" suffixOverrides=","><if test="id != null">id,</if><if test="topic != null">topic,</if><if test="data != null">data,</if></trim><trim prefix="values (" suffix=")" suffixOverrides=","><if test="id != null">#{id},</if><if test="topic != null">#{topic},</if><if test="data != null">#{data},</if></trim></insert></mapper>

前端文件

在这里插入图片描述

启动项目,新建菜单

在这里插入图片描述

新增加一个Java订阅,新增加了之后数据库就会显示一条数据:id=uuid,dtopic=java,data=null

这里要和MqttConfig中,选择订阅的主题一样

在这里插入图片描述

注意:代码写的位置
package com.ruoyi.common.utils.mqtt;@Component
public class PushCallback implements MqttCallback {private static final Logger logger = LoggerFactory.getLogger(MqttPushClient.class);@Autowiredprivate MqttConfig mqttConfig;private static MqttClient client;private static String _topic;private static String _qos;private static String _msg;@Overridepublic void connectionLost(Throwable throwable) {// 连接丢失后,一般在这里面进行重连logger.info("连接断开,可以做重连");if (client == null || !client.isConnected()) {mqttConfig.getMqttPushClient();}}@Overridepublic void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {// subscribe后得到的消息会执行到这里面logger.info("接收消息主题 : " + topic);logger.info("接收消息Qos : " + mqttMessage.getQos());logger.info("接收消息内容 : " + new String(mqttMessage.getPayload()));//todo 代码一般写在这里......}@Overridepublic void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {logger.info("deliveryComplete---------" + iMqttDeliveryToken.isComplete());}}

注意:在MqttConfig中,选择订阅的主题

	@Beanpublic MqttPushClient getMqttPushClient() {if (enabled == true) {mqttPushClient.connect(hostUrl, clientId, username, password, timeout, keepalive);//订阅java主题   mqttPushClient.subscribe("java",0);}return mqttPushClient;}

5.接收数据

编写代码,硬件传来的数据更新保存在数据库中

先获取topic=Java的数据,然后可存不存在,存在就j更新,将硬件传过来的数据更新到data中

package com.ruoyi.common.utils.mqtt;@Component
public class PushCallback implements MqttCallback {private static final Logger logger = LoggerFactory.getLogger(MqttPushClient.class);@Autowiredprivate MqttConfig mqttConfig;@Autowiredprivate IWService wService;private static MqttClient client;private static String _topic;private static String _qos;private static String _msg;@Overridepublic void connectionLost(Throwable throwable) {// 连接丢失后,一般在这里面进行重连logger.info("连接断开,可以做重连");if (client == null || !client.isConnected()) {mqttConfig.getMqttPushClient();}}@Overridepublic void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {// subscribe后得到的消息会执行到这里面logger.info("接收消息主题 : " + topic);logger.info("接收消息Qos : " + mqttMessage.getQos());logger.info("接收消息内容 : " + new String(mqttMessage.getPayload()));//硬件传来的数据update在数据库中
//        @Log(title = "物联网接收数据", businessType = BusinessType.UPDATE)
//        @PostMapping("/edit")
//        @ResponseBody
//        public AjaxResult editSave (W w){
//            return toAjax(wService.updateW(w));
//        }//查询更新操作,查询topic=Java的数据,如果有,就把数据更新到数据库中(data)W w = new W();w.setTopic("java");//查询List<W> list = wService.selectWList(w);if (list.size() > 0) {//根据id去查询w.setId(list.get(0).getId());w.setTopic(null);w.setData(new String(mqttMessage.getPayload()));try {wService.updateW(w);//alt+ctrl+t:抛异常} catch (Exception e) {throw new RuntimeException(e);}}}@Overridepublic void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {logger.info("deliveryComplete---------" + iMqttDeliveryToken.isComplete());}}

运行系统,此时数据库的data为空(新增加一个Java订阅的数据)

在这里插入图片描述

使用mqttx,模拟硬件发Java发消息

在这里插入图片描述

发送111

在这里插入图片描述

在这里插入图片描述

5.1 前端显示接收的数据

wlw包的w.html编写前端,编写一个定时器localhost/ruoyi/system/wlw

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>wlw</title><script th:src="@{/js/jquery.min.js}"></script><script th:inline="javascript">var ctx = [[@{/}]];//定时器,每五秒定时接收数据setInterval(function(){$.ajax({type: "post",url: ctx + "system/w/list",dataType: "json",success: function (result) {console.log(result);}})}, 1000);</script>
</head>
<body><button></button> 
</body>
</html>

shiroConfig,放开拦截

// 不需要拦截的访问  接收
filterChainDefinitionMap.put("/system/w/list", "anon,captchaValidate");

控制台接收数据

在这里插入图片描述

把查询的数据显示到界面上

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>wlw</title><script th:src="@{/js/jquery.min.js}"></script><script th:inline="javascript">var ctx = [[@{/}]];//定时器,每五秒定时接收数据setInterval(function () {$.ajax({type: "post",url: ctx + "system/w/list",data: {//根据topic为Java的查topic: "java",},dataType: "json",success: function (result) {if (result.total > 0) {//显示数据$("div").text(result.rows[0].data);}}})}, 1000);</script>
</head>
<body>
<button></button>
<div></div>
</body>
</html>

使用mqttx模拟硬件发送数据,Java程序接收并显示在界面上

在这里插入图片描述

6.发送消息

修改主题(三个地方)

PushCallback,修改为test

       W w = new W();w.setTopic("test");  //test//查询List<W> list = wService.selectWList(w);if (list.size() > 0) {//根据id去查询w.setId(list.get(0).getId());w.setTopic(null);w.setData(new String(mqttMessage.getPayload()));try {wService.updateW(w);//alt+ctrl+t:抛异常} catch (Exception e) {throw new RuntimeException(e);}}

MqttConfig

    @Beanpublic MqttPushClient getMqttPushClient() {if (enabled == true) {mqttPushClient.connect(hostUrl, clientId, username, password, timeout, keepalive);//订阅java主题mqttPushClient.subscribe("test",0);}
//        if(enabled == true){
//            String mqtt_topic[] = StringUtils.split(defaultTopic, ",");
//            mqttPushClient.connect(hostUrl, clientId, username, password, timeout, keepalive);//连接
//            for(int i=0; i<mqtt_topic.length; i++){
//                mqttPushClient.subscribe(mqtt_topic[i], 0);//订阅主题
//            }
//        }return mqttPushClient;}
}

数据库中的topic修改为test

前端代码

<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>wlw</title><script th:src="@{/js/jquery.min.js}"></script><script th:inline="javascript">var ctx = [[@{/}]];$(function () {$("button").click(function () {$.ajax({type: "post",url: ctx + "system/wlw/open",//添加这里的代码data: {//向java主题发送open数据topic: "java",msg: "open"},dataType: "json",success: function (result) {console.log(result);}// data: {//     topic: "java1",//     msg: "open"// },// dataType: "json",// success: function(result) {//     console.log(result);// }})})})</script>
</head>
<body>
<button></button>
<div></div>
</body>
</html>

运行项目,点击开按钮,查看mqttx是否接收到了数据

注意:发送的订阅主题不能和接收的主题一样,所以前面我们要修改主题为test

在这里插入图片描述

7.iot-mqtt

半成品项目https://gitee.com/peng-chuanbin/iot-mqtt.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/92145.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/92145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch + PaddlePaddle 语音识别

PyTorch PaddlePaddle 语音识别 目录 概述环境配置基础理论数据预处理模型架构设计完整实现案例模型训练与评估推理与部署性能优化技巧总结 语音识别&#xff08;ASR, Automatic Speech Recognition&#xff09;是将音频信号转换为文本的技术。结合PyTorch和PaddlePaddle的…

施耐德 Easy Altivar ATV310 变频器:高效电机控制的理想选择(含快速调试步骤及常见故障代码)

施耐德 Easy Altivar ATV310 变频器&#xff1a;高效电机控制的理想选择&#xff08;含快速调试步骤&#xff09;在工业自动化领域&#xff0c;变频器作为电机控制的核心设备&#xff0c;其性能与可靠性直接影响整个生产系统的效率。施耐德电气推出的 Easy Altivar ATV310 变频…

搭建邮件服务器概述

一、电子邮件应用解析标准邮件服务器&#xff08;qq邮箱&#xff09;&#xff1a;1&#xff09;提供电子邮箱&#xff08;lvbuqq.com&#xff09;及存储空间2&#xff09;为客户端向外发送邮件给其他邮箱&#xff08;diaochan163.com&#xff09;3&#xff09;接收/投递其他邮箱…

day28-NFS

1.每日复盘与今日内容1.1复盘Rsync:本地模式、远程模式&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;、远程守护模式&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;&#x1f35f;安装、配置Rsync启动、测试服务备份案例1.2今日内容NFS优缺点NFS服…

二叉搜索树--通往高阶数据结构的基石

目录 前言&#xff1a; 1、二叉搜索树的概念 2、二叉搜索树性能分析 3、二叉搜索树的实现 BinarySelectTree.h test.cpp 4、key 和 key / value&#xff08; map 和 set 的铺垫 &#xff09; 前言&#xff1a; 又回到数据结构了&#xff0c;这次我们将要学习一些复杂的…

Profinet转Ethernet IP网关接入五轴车床上下料机械手控制系统的配置实例

本案例为西门子1200PLC借助PROFINET转EtherNet/IP网关与搬运机器人进行连接的配置案例。所需设备包括&#xff1a;西门子1200PLC、Profinet转EtherNet/IP网关以及发那科&#xff08;Fanuc&#xff09;机器人。开启在工业自动化控制领域广泛应用、功能强大且专业的西门子博图配置…

专题二_滑动窗口_长度最小的子数组

引入&#xff1a;滑动窗口首先&#xff0c;这是滑动窗口的第一道题&#xff0c;所以简短的说一下滑动窗口的思路&#xff1a;当我们题目要求找一个满足要求的区间的时候&#xff0c;且这个区间的left和right指针&#xff0c;都只需要同向移动的时候&#xff0c;就可以使用滑动窗…

解锁高效开发:AWS 前端 Web 与移动应用解决方案详解

告别繁杂的部署与运维&#xff0c;AWS 让前端开发者的精力真正聚焦于创造卓越用户体验。在当今快速迭代的数字环境中&#xff0c;Web 与移动应用已成为企业与用户交互的核心。然而&#xff0c;前端开发者常常面临诸多挑战&#xff1a;用户认证的复杂性、后端 API 的集成难题、跨…

北京JAVA基础面试30天打卡04

1. 单例模式的实现方式及线程安全 单例模式&#xff08;Singleton Pattern&#xff09;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。以下是常见的单例模式实现方式&#xff0c;以及如何保证线程安全&#xff1a; 单例模式的实现方式饿汉式&#xff08;Eager Init…

Redis 缓存三大核心问题:穿透、击穿与雪崩的深度解析

引言在现代互联网架构中&#xff0c;缓存是提升系统性能、降低数据库压力的核心手段之一。而 Redis 作为高性能的内存数据库&#xff0c;凭借其丰富的数据结构、灵活的配置选项以及高效的网络模型&#xff0c;已经成为缓存领域的首选工具。本文将从 Redis 的基本原理出发&#…

耘瞳科技国产化点云处理软件,开启智能化三维测量新时代

在现代工业制造领域&#xff0c;三维点云数据已成为推动生产效率提升、质量控制优化以及智能制造转型的关键技术之一。三维点云数据能够提供高精度的物体表面信息&#xff0c;广泛应用于制造零件的质量检测&#xff1b;通过点云数据与CAD模型的对比分析&#xff0c;可以快速检测…

RabbitMQ面试精讲 Day 8:死信队列与延迟队列实现

【RabbitMQ面试精讲 Day 8】死信队列与延迟队列实现 文章标签 RabbitMQ,消息队列,死信队列,延迟队列,面试技巧,分布式系统 文章简述 本文是"RabbitMQ面试精讲"系列第8天&#xff0c;深入讲解死信队列与延迟队列的实现原理与实战应用。文章详细解析死信队列的触发…

团结引擎 1.5.0 版本发布:Android App View 功能详解

核心亮点 原生安卓应用支持 2D & 3D 双形态呈现 编辑器全流程集成 灵活调控功能 多应用并行展示 智能座舱应用示例 快速入门指南 开发说明 功能支持 实验性功能 资源链接 团结引擎 1.5.0 版本已于 4 月 14 日正式上线。本次更新中&#xff0c;车机版引入了一项突…

基于SpringBoot的OA办公系统的设计与实现

文章目录前言详细视频演示具体实现截图后端框架SpringBoot持久层框架MyBaits成功系统案例&#xff1a;代码参考数据库源码获取前言 博主介绍:CSDN特邀作者、985高校计算机专业毕业、现任某互联网大厂高级全栈开发工程师、Gitee/掘金/华为云/阿里云/GitHub等平台持续输出高质量…

知识随记-----用 Qt 打造优雅的密码输入框:添加右侧眼睛图标切换显示

Qt 技巧&#xff1a;通过 QLineEdit 右侧眼睛图标实现密码可见性切换 文章目录Qt 技巧&#xff1a;通过 QLineEdit 右侧眼睛图标实现密码可见性切换概要整体架构流程技术名词解释技术细节实现效果展示概要 本文介绍如何使用 Qt 框架为 QLineEdit 控件添加一个右侧的眼睛图标&a…

Unity里的对象旋转数值跳转问题的原理与解决方案

文章目录1. 问题描述2. 问题原因3. 解决方案3.1通过多个父子关系从而控制旋转&#xff08;推荐&#xff09;3.2 使用四元数进行旋转1. 问题描述 我们现在写一个3D的Unity程序&#xff0c;我们现在设置了一个物体后&#xff0c;我们想旋转使其改为我们想要的情况。但是我们如果…

为什么现代 C++ (C++11 及以后) 推荐使用 constexpr和模板 (Templates) 作为宏 (#define) 的替代品?​

我们用现实世界的比喻来深入理解​​为什么 C 中的宏 (#define) 要谨慎使用&#xff0c;以及为什么现代 C (C11 及以后) 推荐使用 constexpr 和模板 (Templates) 作为替代品。​​&#x1f9e9; ​​核心问题&#xff1a;宏 (#define) 是文本替换​​想象宏是一个 ​​“无脑的…

PyCharm vs. VSCode 到底哪个更好用

在 Python 开发者中&#xff0c;关于 PyCharm 和 VSCode 的讨论从未停止。一个是功能齐备的集成开发环境&#xff08;IDE&#xff09;&#xff0c;另一个是轻快灵活的代码编辑器。它们代表了两种不同的开发哲学&#xff0c;选择哪个&#xff0c;往往取决于你的项目需求、个人习…

FPGA学习笔记——VGA彩条显示

目录 一、任务 二、分析 三、代码 四、实验现象 五、更新 一、任务 使用VGA实现彩条显示&#xff0c;模式是640x48060。 二、分析 首先&#xff0c;模式是640x48060&#xff0c;那么对照以下图标&#xff0c;知道其它信息&#xff0c;不清楚时序和VGA扫描方式的可以看看这…

ES-301A :让 Modbus 设备无缝接入工业以太网的高效桥梁

在工业自动化领域&#xff0c;串口设备与以太网的互联互通是提升系统效率的关键。ES-301A 工业以太网串口网关作为上海泗博自动化精心打造的专业解决方案&#xff0c;以强大的协议转换能力、工业级可靠性和灵活配置特性&#xff0c;成为连接 Modbus RTU/ASCII 设备与 Modbus TC…