当前位置: 首页 > news >正文

MQTT从入门到精通之 MQTT 客户端编程

MQTT 客户端编程

1 在VUE中使用MQTT

具体步骤如下所示:

1、初始化vue项目

// 创建一个使用vite构建的前端项目
npm create vite@latest// 进入到项目中,执行如下命令安装项目依赖
npm install 

2、安装element plus

// 安装element plus
npm install element-plus --save// 安装mqtt.js依赖
npm install mqtt --save// 在main.js中添加如下代码
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

3、导入课程资料中的MqttDemo.vue页面到components文件夹下

4、更改App.vue页面代码如下所示

<script setup>
import MqttDemo from "./components/MqttDemo.vue";
</script><template><MqttDemo/>
</template><style>
</style>

5、建立和关闭连接

<script setup>
import mqtt from "mqtt";// 定义链接信息对象
const connectionInfo = ref({protocol: 'ws',host: "192.168.136.147",port: 8083,clientId: "emqx_vue3_" + Math.random().toString(16).substring(2, 8),username: "zhangsan",password: "123",clean: true,connectTimeout: 10 * 1000, // msreconnectPeriod: 4000, // ms
})// 创建链接对象
const client = ref({})
const clientInitData = ref({      // 链接初始化相关数据connnected: false
})// 建立连接事件处理函数
const createConnection = () => {const { protocol, host, port , ...options } = connectionInfo.value;const connectUrl = `${protocol}://${host}:${port}/mqtt`;console.log(connectUrl)client.value = mqtt.connect(connectUrl , options);   // 建立连接clientInitData.value.connnected = true ;console.info("createConnection successful...")}// 端口链接事件处理函数
const closeConnection = () => {// 关闭链接client.value.end(false , () => {   // 如果设置为true,将会立即关闭套接字,并且不发送MQTT DISCONNECT包。如果设置为false(默认值),则会发送MQTT DISCONNECT包给代理,然后关闭套接字。clientInitData.value.connnected = false;console.info("closeConnection successful...")})}
</script><template><div class="mqtt-demo"><el-card><h1>配置信息</h1><el-form label-position="top" ><el-row :gutter="20"><el-col :span="8"><el-form-item prop="protocol" label="选择协议"><el-select v-model="connectionInfo.protocol"><el-option label="ws://" value="ws"></el-option><el-option label="wss://" value="wss"></el-option></el-select></el-form-item></el-col><el-col :span="8"><el-form-item prop="host" label="主机地址"><el-input v-model="connectionInfo.host" ></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="port" label="端口号"><el-input type="number" v-model="connectionInfo.port" placeholder="8083/8084"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="clientId" label="客户端ID"><el-input v-model="connectionInfo.clientId"> </el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="username" label="用户名"><el-input v-model="connectionInfo.username"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="password" label="密码"><el-input v-model="connectionInfo.password"></el-input></el-form-item></el-col><el-col :span="24"><el-button type="primary" :disabled="clientInitData.connnected" @click="createConnection">建立连接</el-button><el-button type="danger" :disabled="!clientInitData.connnected" @click="closeConnection">断开连接</el-button></el-col></el-row></el-form></el-card></div>
</template>

6、订阅和取消订阅

<script setup>// 消息质量取值数组
const qosList = [0, 1, 2];
const receivedMessages = ref(null)
const subscriptionInfo = ref({     // 订阅参数数据模型topic: '' ,qos: 0
})
const subscriptionInitData = ref({	// 订阅初始化数据subscription: false
})// 定义定义主题的事件处理函数
const subscriptionTopicHandler = () => {const { topic, qos } = subscriptionInfo.valueconsole.info(qos)client.value.subscribe(topic, { qos } , (error , res) => {if(error) {console.info("subscriptionTopic Error:", error);return ;}subscriptionInitData.value.subscription = true ;console.info("subscriptionTopic successful.... ");// 订阅成功以后,监听发送消息事件client.value.on('message' , (topic , message) => {console.info("topic -----> " + topic + ", message -----> " + message)receivedMessages.value = "topic -----> " + topic + ", message -----> " + message ;})})}// 定义取消订阅的事件处理函数
const unSubscriptionTopicHandler = () => {const {topic, qos } = subscriptionInfo.valueclient.value.unsubscribe(topic, { qos } , (error , res) => {if(error) {console.info("unSubscriptionTopic Error:", error);return ;}subscriptionInitData.value.subscription = false ;console.info("unSubscriptionTopic successful.... ");})
}</script><template><el-card><h1>订阅主题</h1><el-form label-position="top" ><el-row :gutter="20"><el-col :span="8"><el-form-item prop="topic" label="Topic"><el-input v-model="subscriptionInfo.topic"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="qos" label="QoS"><el-select v-model="subscriptionInfo.qos"><el-optionv-for="qos in qosList":key="qos":label="qos":value="qos"></el-option></el-select></el-form-item></el-col><el-col :span="8"><el-button  type="primary" class="sub-btn" :disabled="subscriptionInitData.subscription" @click="subscriptionTopicHandler">订阅主题</el-button><el-button type="primary" class="sub-btn" :disabled="!subscriptionInitData.subscription" @click="unSubscriptionTopicHandler">取消订阅</el-button></el-col></el-row></el-form></el-card></template>

7、发布消息

<script setup>// 发布消息参数
const publishInfo = ref({topic: '' ,qos: 0,payLoad: ''
})// 定义发布消息的事件处理函数
const doPublish = () => {const {topic , payLoad , qos } = publishInfo.value ;client.value.publish(topic , payLoad , { qos } , (error , res) => {if(error) {console.info("publish msg info error...." , error)return ;}console.info("publish msg info successful....")}) ;}</script>
<template><el-card><h1>发布消息</h1><el-form label-position="top" ><el-row :gutter="20"><el-col :span="8"><el-form-item prop="topic" label="Topic"><el-input v-model="publishInfo.topic"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="payload" label="Payload"><el-input v-model="publishInfo.payLoad"></el-input></el-form-item></el-col><el-col :span="8"><el-form-item prop="qos" label="QoS"><el-select v-model="publishInfo.qos"><el-optionv-for="qos in qosList":key="qos":label="qos":value="qos"></el-option></el-select></el-form-item></el-col></el-row></el-form><el-col :span="24" class="text-right"><el-button type="primary" @click="doPublish">发布消息</el-button></el-col></el-card></template>

2 在Java中使用MQTT

2.1 Eclipse Paho Java Client

具体步骤:

1、创建一个Spring Boot项目,添加如下依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.5</version>
</parent><dependencies><!-- spring boot整合junit单元测试的起步依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><!-- mqtt java客户端依赖 --><dependency><groupId>org.eclipse.paho</groupId><artifactId>org.eclipse.paho.client.mqttv3</artifactId><version>1.2.5</version></dependency></dependencies>

2、建立连接代码实现

@Test
public void createConnection() throws MqttException {// 定义链接相关参数String broker = "tcp://192.168.136.147:1883";String username = "zhangsan";String password = "123";String clientid = "mqtt_java_client_01";// 创建MqttJava客户端对象// MqttClientPersistence: 代表一个持久的数据存储,用于在传输过程中存储出站和入站的信息MqttClient client = new MqttClient(broker, clientid , new MemoryPersistence());   MqttConnectOptions options = new MqttConnectOptions();options.setUserName(username);options.setPassword(password.toCharArray());client.connect(options);// 阻塞当前线程while (true) ;
}

3、发布消息代码演示

@Test
public void sendMessage() throws MqttException {// 定义链接相关参数String broker = "tcp://192.168.136.147:1883";String username = "zhangsan";String password = "123";String clientid = "mqtt_java_client_01";// 创建MqttJava客户端对象MqttClient client = new MqttClient(broker, clientid , new MemoryPersistence());MqttConnectOptions options = new MqttConnectOptions();options.setUserName(username);options.setPassword(password.toCharArray());client.connect(options);// 创建消息对象QoSString content = "hello mqtt";MqttMessage message = new MqttMessage(content.getBytes());message.setQos(2);message.setRetained(true);// 发送消息client.publish("a/c" , message);// 关闭链接释放资源client.disconnect();client.close();}

4、订阅主题获取消息

@Test
public void receiveMessage() throws MqttException {// 定义链接相关参数String broker = "tcp://192.168.136.147:1883";String username = "zhangsan";String password = "123";String clientid = "mqtt_java_client_02";// 创建MqttJava客户端对象MqttClient client = new MqttClient(broker, clientid , new MemoryPersistence());MqttConnectOptions options = new MqttConnectOptions();options.setUserName(username);options.setPassword(password.toCharArray());// 添加回调函数获取主题消息client.setCallback(new MqttCallback() {@Overridepublic void connectionLost(Throwable cause) {  // 连接丢失时被调用System.out.println("connectionLost: " + cause.getMessage());}@Overridepublic void messageArrived(String topic, MqttMessage message) throws Exception {  // 接收到消息时被调用System.out.println("topic: " + topic);System.out.println("Qos: " + message.getQos());System.out.println("message content: " + new String(message.getPayload()));}@Overridepublic void deliveryComplete(IMqttDeliveryToken token) {  // 消息接收完成时被调用System.out.println("deliveryComplete---------" + token.isComplete());}});// 订阅主题client.connect(options);client.subscribe("a/d" , 2);while(true) ;}

2.2 spring-integration-mqtt

2.2.1 基础环境搭建

1、创建一个Spring Boot项目,并加入如下依赖:

<dependencies><!-- spring boot项目web开发的起步依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- spring boot项目集成消息中间件基础依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration</artifactId></dependency><!-- spring boot项目和mqtt客户端集成起步依赖 --><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-mqtt</artifactId><version>5.4.3</version></dependency><!-- lombok依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- fastjson依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency></dependencies>

2、编写启动类

@EnableConfigurationProperties(value = MqttConfigurationProperties.class)
@SpringBootApplication
public class MqttDemoApplication {public static void main(String[] args) {SpringApplication.run(MqttDemoApplication.class , args) ;}}

3、在application.yml文件中添加如下配置

spring:mqtt:username: zhangsanpassword: 123url: tcp://192.168.136.147:1883subClientId: sub_client_id_123subTopic: liujh/iot/lamp/linepubClientId: pub_client_id_123

4、创建实体类读取自定义配置

@Data
@ConfigurationProperties(prefix = "spring.mqtt")
public class MqttConfigurationProperties {private String username;private String password;private String url;private String subClientId ;private String subTopic ;private String pubClientId ;}

5、创建配置类配置链接工厂

@Configuration
public class MqttConfiguration {@Autowiredprivate MqttConfigurationProperties mqttConfigurationProperties ;@Beanpublic MqttPahoClientFactory mqttClientFactory(){// 创建客户端工厂DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();// 创建MqttConnectOptions对象MqttConnectOptions options = new MqttConnectOptions();options.setCleanSession(true);options.setUserName(mqttConfigurationProperties.getUsername());options.setPassword(mqttConfigurationProperties.getPassword().toCharArray());options.setServerURIs(new String[]{mqttConfigurationProperties.getUrl()});factory.setConnectionOptions(options);// 返回return factory;}}

2.2.2 订阅主题获取消息

具体步骤:

1、配置入站适配器

@Configuration
public class MqttInboundConfiguration {@Autowiredprivate MqttConfigurationProperties mqttConfigurationProperties ;@Autowiredprivate ReceiverMessageHandler receiverMessageHandler;/*** 配置消息传输通道* @return*/@Beanpublic MessageChannel mqttInputChannel() {return new DirectChannel();}/*** 配置入站适配器*/@Beanpublic MessageProducer messageProducer(MqttPahoClientFactory mqttPahoClientFactory) {MqttPahoMessageDrivenChannelAdapter adapter  =new MqttPahoMessageDrivenChannelAdapter(mqttConfigurationProperties.getUrl() ,mqttConfigurationProperties.getSubClientId() ,mqttPahoClientFactory , mqttConfigurationProperties.getSubTopic().split(",")) ;adapter.setConverter(new DefaultPahoMessageConverter());adapter.setQos(1);adapter.setOutputChannel(mqttInputChannel());return adapter ;}/*** 配置入站消息处理器* @return*/@Bean@ServiceActivator(inputChannel = "mqttInputChannel")public MessageHandler messageHandler() {return this.receiverMessageHandler ;}}

2、定义监听主题消息的处理器

@Component
public class ReceiverMessageHandler implements MessageHandler {@Overridepublic void handleMessage(Message<?> message) throws MessagingException {MessageHeaders headers = message.getHeaders();String receivedTopicName = (String) headers.get("mqtt_receivedTopic");if("liujh/iot/lamp/line".equals(receivedTopicName)) {System.out.println("接收到消息:" + message.getPayload());}}}

测试:通过MQTTX向liujh/iot/lamp/line主题发送消息

2.2.3 向指定主题发送消息

具体步骤:

1、配置出站消息处理器

@Configuration
public class MqttOutboundConfiguration {@Autowiredprivate MqttConfigurationProperties mqttConfigurationProperties ;@Autowiredprivate MqttPahoClientFactory pahoClientFactory ;@Beanpublic MessageChannel mqttOutputChannel() {return new DirectChannel();}@Bean@ServiceActivator(inputChannel = "mqttOutputChannel")public MessageHandler mqttOutboundMassageHandler() {MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(mqttConfigurationProperties.getUrl() ,mqttConfigurationProperties.getPubClientId() , pahoClientFactory ) ;messageHandler.setAsync(true);messageHandler.setDefaultQos(0);messageHandler.setDefaultTopic("default");return messageHandler ;}}

2、定义发送消息的网关接口

@MessagingGateway(defaultRequestChannel = "mqttOutputChannel")
public interface MqttGateway {/*** 发送mqtt消息* @param topic 主题* @param payload 内容*/void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, String payload);/*** 发送包含qos的消息* @param topic 主题* @param qos 对消息处理的几种机制。*          * 0 表示的是订阅者没收到消息不会再次发送,消息会丢失。<br>*          * 1 表示的是会尝试重试,一直到接收到消息,但这种情况可能导致订阅者收到多次重复消息。<br>*          * 2 多了一次去重的动作,确保订阅者收到的消息有一次。* @param payload 消息体*/void sendToMqtt(@Header(MqttHeaders.TOPIC) String topic, @Header(MqttHeaders.QOS) int qos, String payload);}

3、定义发送消息的服务类

@Component
@AllArgsConstructor
public class MqttMessageSender {private MqttGateway mqttGateway;/*** 发送mqtt消息* @param topic 主题* @param message 内容*/public void send(String topic, String message) {mqttGateway.sendToMqtt(topic, message);}/*** 发送包含qos的消息* @param topic 主题* @param qos 质量* @param message 消息体*/public void send(String topic, int qos, byte[] message){mqttGateway.sendToMqtt(topic, qos, message);}
}

3 智能灯泡案例

需求:

1、智能灯泡设备上线以后向MQTT服务端发送消息,后端服务从MQTT中获取消息记录设备信息到数据库中

2、后端微服务向MQTT服务端发送开灯或者关灯消息,设备端从MQTT中获取消息控制灯泡的开和关

3、设备端对灯泡进行开和关操作的时候向MQTT中发送消息,后端服务获取MQTT消息记录灯泡的开关状态

3.1 服务端获取设备上线消息

3.1.1 环境准备

具体步骤:

1、创建对应的数据库表

-- 智能灯泡设备表
CREATE TABLE `tb_lamp` (`id` bigint NOT NULL AUTO_INCREMENT,`deviceId` varchar(50) DEFAULT NULL,`status` int DEFAULT NULL COMMENT '1:上线  0:下线',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ,`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;-- 智能灯泡设备状态表
CREATE TABLE `tb_lamp_status` (`id` int NOT NULL AUTO_INCREMENT,`deviceId` varchar(50) DEFAULT NULL,`status` int DEFAULT NULL COMMENT '0: 关灯   1:开灯',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2、在spring-integration-mqtt案例中加入如下依赖

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.32</version>
</dependency>

3、在application.yml文件中加入如下依赖

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.136.147:3306/lamp_test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: 1234mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: truemapper-locations: classpath*:mapper/*Mapper.xml

4、通过mybatis的逆向工程生成tb_lamp和tb_lamp_status表对应的基础代码

5、在启动类上添加@MapperScan注解指定Mapper接口的包路径

3.1.2 接口说明

接口一:设备上线

当终端设备连接上EMQX以后,发送上线消息到EMQX服务端,说明如下:

主题: liujh/iot/lamp/line
消息内容:{"deviceId": "xxxxxx","online": 1}
数据说明:deviceId: 设备idonline:   上线状态,1表示上线,0表示离线

3.1.3 业务代码

ReceiverMessageHandler类的代码进行如下改造:

@Component
public class ReceiverMessageHandler implements MessageHandler {@Autowiredprivate TbLampService tbLampService ;@Overridepublic void handleMessage(Message<?> message) throws MessagingException {MessageHeaders headers = message.getHeaders();String receivedTopicName = (String) headers.get("mqtt_receivedTopic");if("liujh/iot/lamp/line".equals(receivedTopicName)) {tbLampService.updateLampOnlineStatus(message.getPayload().toString()) ;        // 更新智能灯泡的上线状态}}}

TbLampServiceImpl类的代码进行如下改造:

@Service
public class TbLampServiceImpl extends ServiceImpl<TbLampMapper, TbLamp> implements TbLampService {@Overridepublic void updateLampOnlineStatus(String jsonInfo) {// 解析消息获取设备id和上线状态Map<String ,  Object> map = JSON.parseObject(jsonInfo, Map.class);String deviceId = map.get("deviceId").toString();Integer status = Integer.parseInt(map.get("online").toString());// 根据设备的id查询设备数据LambdaQueryWrapper<TbLamp> lambdaQueryWrapper = new LambdaQueryWrapper<>() ;lambdaQueryWrapper.eq(TbLamp::getDeviceid , deviceId) ;TbLamp tbLamp = this.getOne(lambdaQueryWrapper);if(tbLamp == null) {        // 设备不存在,新增设备tbLamp = new TbLamp() ;tbLamp.setDeviceid(deviceId);tbLamp.setStatus(status);this.save(tbLamp) ;}else {     // 设备已经存在,修改设备的状态tbLamp.setStatus(status);tbLamp.setUpdateTime(new Date());this.updateById(tbLamp) ;}}}

3.2 服务端发送关灯开灯消息到MQTT

3.2.1 接口说明

接口三:后端发送消息控制智能灯泡开关

后端可以发送控制灯泡状态消息到EMQX中,设备端监听指定主题获取消息,控制灯泡的开关状态,说明如下:

主题: liujh/iot/lamp/server/status
消息内容:{"deviceId": "xxxxxx","status": 0}
数据说明:		status:	0:关灯   , 1:开灯

3.2.2 业务代码

在spring-integration-mqtt案例中添加如下controller接口方法

@RestController
@RequestMapping(value = "/api/lamp")
public class LampApiController {@Autowiredprivate MqttMessageSender mqttMessageSender;@GetMapping(value = "/{deviceId}/{status}")public String sendStatusLampMsg(@PathVariable(value = "deviceId") String deviceId , @PathVariable(value = "status") Integer status) {Map<String , Object> map = new HashMap<>() ;map.put("deviceId" , deviceId) ;map.put("status" , status) ;String json = JSON.toJSONString(map);mqttMessageSender.send("liujh/iot/lamp/server/status" , json);return "ok" ;}}

3.3 服务端获取设备开灯关灯消息

3.3.1 接口说明

接口四:设备端改变智能灯泡开关的状态,状态发给给后端,后端记录状态

主题:liujh/iot/lamp/device/status
消息内容:{"deviceId": "xxxxx"  "status": 0}
数据说明:	deviceId:设备idstatus:0:关灯   , 1:开灯

3.3.2 业务代码

ReceiverMessageHandler类的代码进行如下改造:

// com.liujh.mqtt.receiver.ReceiverMessageHandler
@Override
public void handleMessage(Message<?> message) throws MessagingException {MessageHeaders headers = message.getHeaders();String receivedTopicName = (String) headers.get("mqtt_receivedTopic");if("liujh/iot/lamp/line".equals(receivedTopicName)) {tbLampService.updateLampOnlineStatus(message.getPayload().toString()) ;        // 更新智能灯泡的上线状态}else if("liujh/iot/lamp/device/status".equals(receivedTopicName)) {tbLampStatusService.saveDeviceStatus(message.getPayload().toString()) ;}
}

TbLampStatusServiceImpl类的代码进行如下改造:

@Service
public class TbLampStatusServiceImpl extends ServiceImpl<TbLampStatusMapper, TbLampStatus> implements TbLampStatusService {@Overridepublic void saveDeviceStatus(String json) {// 获取消息内容Map<String , Object> map = JSON.parseObject(json, Map.class);String deviceId = map.get("deviceId").toString();Integer status = Integer.parseInt(map.get("status").toString());// 创建对象封装消息TbLampStatus tbLampStatus = new TbLampStatus() ;tbLampStatus.setDeviceid(deviceId);tbLampStatus.setStatus(status);this.save(tbLampStatus) ;}}

相关文章:

MQTT从入门到精通之 MQTT 客户端编程

MQTT 客户端编程 1 在VUE中使用MQTT 具体步骤如下所示&#xff1a; 1、初始化vue项目 // 创建一个使用vite构建的前端项目 npm create vitelatest// 进入到项目中&#xff0c;执行如下命令安装项目依赖 npm install 2、安装element plus // 安装element plus npm install …...

数据结构-集合

一.集合的表示 一个重要的操作是查某个元素属于哪个集合&#xff0c;另一个操作是合并操作 从这个树的节点去找树根也就是从下往上找,要把树并起来只需把两个根并在一起就可以了 不存在已知一个节点去找孩子节点&#xff0c;根重要的是已知一个节点找它的父亲节点,与之前的二…...

前端 JS面向对象 原型 prototype

目录 一、问题引出 二、prototype原型对象 三、小结 四、constructor 五、__proto__对象原型 六、原型链 一、问题引出 由于JS的构造函数存在内存浪费问题&#xff1a; function Star(name,age){this.namenamethis.ageagethis.singfunction () {console.log("唱歌&…...

Java中的不可变集合:性能与安全并重的最佳实践

Java中的不可变集合&#xff1a;性能与安全并重的最佳实践 在现代软件开发中&#xff0c;集合类&#xff08;如List、Set和Map&#xff09;是Java开发者的日常工具。它们用于存储和操作数据&#xff0c;能极大地简化开发工作。但随着并发编程和大规模应用的广泛使用&#xff0…...

RandomWords随机生成单词

from random_words import RandomWords rw RandomWords() r rw.random_word() print(r) 更多How to use — random_words documentation (randomwords.readthedocs.io) li LoremIpsum()# 这行代码创建了一个 LoremIpsum 类的实例。li.get_sentence()# 这个方法返回一个随机…...

从零开始使用Intel的AIPC使用xpu加速comfyui

Intel的AIPC使用xpu加速跑comfyui 环境安装python环境搭建驱动及oneAPI安装创建python环境验证环境是否生效 ComfyUI的安装下载、汉化comfyui下载checkpoint 测试使用xpu加速测试使用cpu执行测试 环境安装 python环境搭建 直接下载Anaconda 下载地址 安装好后&#xff0c;通…...

PyQt入门指南五十二 版本控制与协作开发

在开发PyQt应用程序时&#xff0c;版本控制和协作开发是提高开发效率和项目可维护性的重要手段。本指南将介绍如何使用Git进行版本控制&#xff0c;以及如何使用GitHub进行协作开发。 版本控制基础 Git简介&#xff1a;Git是一种分布式版本控制系统&#xff0c;用于跟踪代码变…...

思考:linux Vi Vim 编辑器的简明原理,与快速用法之《 7 字真言 》@ “鱼爱返 说 温泉啊“ (**)

Linux vi/vim | 菜鸟教程 https://zhuanlan.zhihu.com/p/602675406 Linux Vim编辑器的基本使用_vim文本编辑器-CSDN博客 这里提出使用 vi / vim 进行简单的编辑操作的原因&#xff0c;主要是在容器镜像中&#xff0c;普遍都是使用这个。 在 linux 服务器应用场景&#x…...

共筑开源技术新篇章 | 2024 CCF中国开源大会盛大开幕

在这个技术革新日新月异的时代&#xff0c;开源精神如同点燃创新火焰的火种&#xff0c;照亮了无数技术探索者的征途。2024年11月9日&#xff0c;备受瞩目的2024 CCF中国开源大会在深圳这座充满活力的创新之城盛大开幕。这场开源领域的顶级盛事&#xff0c;以“湾区聚力 开源启…...

SpringBoot(十八)SpringBoot集成Minio

项目上传文件集成一下Minio,下面是我在项目中集成Minio的全过程。 首先介绍一下Minio:MinIO是高性能的对象存储,单个对象最大可达5TB。适合存储图片、视频、文档、备份数据、安装包等一系列文件。是一款主要采用Golang语言实现发开的高性能、分布式的对象存储系统。客户端支…...

ODOO学习笔记(3):Odoo和Django的区别是什么?

Odoo和Django都是基于Python的开源框架&#xff0c;但它们的设计目标和用途有所不同&#xff1a; 设计目标和用途&#xff1a; Odoo&#xff1a;Odoo是一个企业资源规划&#xff08;ERP&#xff09;系统&#xff0c;它提供了一套完整的商业管理软件&#xff0c;包括会计、库存…...

持续收集解决VCcode各种报错的方法

在学习中我们经常会发生各种各样的报错&#xff0c; 1、pip 安装失败的报错 类似下面的 我们有时候纠结在上面会纠结好久&#xff0c;浪费很多时间。&#xff08;什么轮子我不知道&#xff09; 常见的解决方法: s-1:先uninstall packing&#xff0c;再重新装一次(有时候会重…...

Windows下使用adb实现在模拟器中ping

文章目录 前言安装adb执行adb命令查找模拟器设备链接模拟器命令行执行ping命令 总结 前言 有时在模拟器中测试应用不像在Windows这种开发环境中那么方便&#xff0c;毕竟Windows或者Linux下的工具五花八门&#xff0c;可以满足各种测试需求&#xff0c;比如应用在模拟器中无法…...

c++之deque和priority_queue

Deque 文档&#xff1a;https://legacy.cplusplus.com/reference/deque/deque/?kwdeque 相关接口&#xff1a; push_back():在尾部插入 #include <iostream> #include <deque>int main () {std::deque<int> mydeque;int myint;std::cout << "…...

SDL渲染器和纹理

文章目录 渲染器 (SDL_Renderer)纹理 (SDL_Texture)代码 渲染器 (SDL_Renderer) &#xff1a;它是渲染内容的接口&#xff0c;负责将内容绘制到窗口中。通过SDL_CreateRenderer创建&#xff0c;可以设置渲染器的背景颜色、绘图颜色、透明度等。所有绘图操作&#xff08;如绘制…...

基于Matlab 火焰识别技术

课题介绍 森林承担着为人类提供氧气以及回收二氧化碳等废弃气体的作用&#xff0c;森林保护显得尤其重要。但是每年由于火灾引起的事故不计其数&#xff0c;造成重大的损失。如果有一款监测软件&#xff0c;从硬件处获得的图像中监测是否有火焰&#xff0c;从而报警&#xff0…...

Qt 监控USB设备的插入和移除

Qt 监控USB设备的插入和移除 flyfish Ubuntu22.04 Qt 6.2.4 CMakeLists.txt 内容 # 指定 CMake 的最低版本要求 cmake_minimum_required(VERSION 3.16)# 定义项目的名称和使用的编程语言 project(USBMonitor LANGUAGES CXX)# 开启自动 UIC&#xff0c;MOC 和 RCC 工具 set(…...

终于弄懂了Python自定义模块与代码复用

自定义模块与代码复用 在编写Python代码时&#xff0c;很多时候我们会遇到需要多次使用相同功能的情况。这时候&#xff0c;模块化编程就显得尤为重要。通过将常用的功能代码放入单独的模块中&#xff0c;我们可以轻松地进行代码复用&#xff0c;避免重复编写相同的代码&#…...

从无音响Windows 端到 有音响macOS 端实时音频传输播放

以下是从 Windows 端到 macOS 端传输音频的优化方案&#xff0c;基于上述链接中的思路进行调整&#xff1a; Windows 端操作 安装必要软件 安装 Python&#xff08;确保版本兼容且已正确配置环境变量&#xff09;。安装 PyAudio 库&#xff0c;可通过 pip install pyaudio 命令…...

直方图均衡化及Matlab实现

文章目录 直方图均衡化关键点及思路Matlab实现 直方图均衡化 直方图均衡化是一种图像增强技术&#xff0c;主要用于增强图像的对比度&#xff0c;特别是当图像的有用数据的对比度接近时效果显著。通过改变图像的直方图分布&#xff0c;直方图均衡化能够使图像的灰度值更加接近…...

设备接入到NVR管理平台EasyNVR多品牌NVR管理工具/设备的音视频配置参考

NVR管理平台EasyNVR是一款功能强大的安防视频监控平台&#xff0c;能够轻松实现视频流的导入、录像、存储和回放等功能。在将设备接入到海康NVR管理平台EasyNVR时&#xff0c;视音频配置是确保视频监控效果的重要步骤。本文将详细介绍如何将设备接入到EasyNVR平台&#xff0c;并…...

后端:Aop 面向切面编程

文章目录 1. Aop 初步学习面向切面编程&#xff0c;EnableAspectJAutoProxy2. AOP的核心概念3. 前置通知&#xff08;Before&#xff09;4. 后置通知&#xff08;After&#xff09;5. 返回通知&#xff08;AfterReturning&#xff09;6. 异常通知&#xff08;AfterThrowing&…...

大数据机器学习算法与计算机视觉应用02:线性规划

Linear Programming Definition of linear programmingmax and min-cost max flowlinear program to solve minimax optimal strategies in gamesAlgoithms for linear programmingl1 regressionSeidel’s 2-dimensional linear programming algorithm linear program 线性规…...

godot——主题、Theme、StyleBox

我刚开始被这些术语吓到了&#xff0c;一直不敢去接触它们&#xff0c;都用的默认样式。现在好不容易有点思路了&#xff0c;记录下来。 下面看看怎么自定义样式。 1.先新建一个Theme 2.再次点击创建好的Theme 得到 图1 这样一个面板。&#xff08;看不懂没事&#xff0c;继…...

深入理解接口测试:实用指南与最佳实践5.0(一)

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…...

SQL面试题——飞猪SQL面试 重点用户

飞猪SQL面试题—重点用户 在一些场景中我们经常听到这样的一些描述&#xff0c;例如20%的用户贡献了80%的销售额&#xff0c;或者是20%的人拥有着80%的财富&#xff0c;你知道这样的数据是怎么算出来的吗 数据如下,uid 是用户的id ,amount是用户的消费金额 |uid|amount| ---…...

Angular 和 Vue2.0 对比

前言 &#xff1a;“业精于勤&#xff0c;荒于嬉&#xff1b;行成于思&#xff0c;毁于随” 很久没写博客了&#xff0c;大多记录少进一步探查。 Angular 和 Vue2.0 对比&#xff1a; 一.概念 1.1 Angular 框架&#xff1a; 是一款由谷歌开发的开源web前端框架&#xff08;核…...

websocket服务器(协程风格)--swoole进阶篇

swoole的websocket服务器(协程风格)示例真不算友善,从头了解到尾,那还好,但是谁有那么多时间从头到尾了解。示例不够针对性,写websocket就该单独写websocket的东西,偏偏又加上http的东西。这里我来解读一下websocket服务器(协程风格)示例 <?php use Swoole\Http\…...

Windows C/C++ Socket 编程

承接上文&#xff1a;socket 编程 本文目录 Windows Client 端WSADATA 结构体WSAStartup() 函数SOCKET 以及 socket() 函数sockaddr_ininet_pton() 函数in_addr structmemcpy()connect() 函数send() 函数recv() 函数 Windows Server 端 在进行 socket 编程之前&#xff0c;你要…...

计算两个结构的乘法

在行列可自由变换的平面上&#xff0c;2点结构有3个 3点结构有6个 计算2*2 2a1*2a14a6 2a1*2a24a8 2a1*2a34a12 显然2a1*2a14a6因为这3个结构都分布在同一列上&#xff0c;就是整数乘法。2a1*2a2的结果有2种写法&#xff0c;一种外形像2a1细节为2a2&#xff0c;一种外形为2…...