WebSocket实现HTML+SpringBoot聊天功能,小程序+SpringBoot聊天功能

目录

一、认识WebSocket

二、HTML实现聊天

三、微信小程序实现聊天


一、认识WebSocket

1.首先博主在初学Java时自我感觉走了很多弯路,因为以前见识短,在接触聊天功能时根本就没能想到有WebSocket这个聊天框架,就只能用底层的UDP或TCP实现聊天功能,及其繁琐。

1.在入门Java后的朋友学到网络编程会知道UDP和TCP两个知识点,没错WebSocket是一种在单个TCP连接上进行全双工通信的协议。基于TCP协议的一个框架,TCP知识点比较多,具体咱们就不多说了,直接实践怎么使用吧。

二、HTML实现聊天

首先我先贴出完整代码,然后解释

1.html代码,这里我就不单独写js文件了(这个html实现的是一对一聊天,还有一对多,多对多群聊)

<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdn.jsdelivr.net/sockjs/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script><script>var stompClient = null;function connect() {var content = $("#message").val();var sender = 'tbb';var socket = new SockJS('http://127.0.0.1:8080/chat');stompClient = Stomp.over(socket);stompClient.connect({}, function (frame) {console.log('Connected: ' + frame);stompClient.subscribe('/user/tbb/message', function (message) {showMessage(JSON.parse(message.body));});});}function disconnect() {if (stompClient !== null) {stompClient.disconnect();}console.log("Disconnected");}function sendMessage() {var content = $("#message").val();var sender = $("#sender").val();stompClient.send("/app/chat/send/to/user", {}, JSON.stringify({content: content,sender: sender,unionId: sender,}));}function showMessage(message) {$("#chat").append("<div>" + message.sender + ": " + message.content + "</div>");}
</script><div><input type="text" id="sender" placeholder="Enter your name"><input type="text" id="message" placeholder="Type a message..."><button onclick="sendMessage()">Send</button><button onclick="connect()">Connect</button><button onclick="disconnect()">Disconnect</button>
</div><div id="chat">123</div>

 2.SpringBoot完整代码

(1)WebSocketConfig.java配置文件(关键文件)

package com.example.mengchuangyuan.common.chat.config;import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/user","/agent","/topic"); // 定义消息代理,客户端订阅的地址前缀config.setApplicationDestinationPrefixes("/app"); // 定义客户端发送消息的地址前缀config.setUserDestinationPrefix("/user");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/chat").setAllowedOriginPatterns("*").withSockJS(); // 定义WebSocket端点,客户端连接的地址}
}

(2)ChatController.java控制层 

package com.example.mengchuangyuan.common.chat.controller;import com.example.mengchuangyuan.common.chat.entry.ChatMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.stereotype.Controller;@Slf4j
@Controller
public class ChatController {@Autowiredprivate SimpMessagingTemplate simpMessagingTemplate;/*** 发消息(群发)* @param chatMessage 获取用户消息* @return 返回要回复的消息*///第一种方式@MessageMapping("/chat/send") // 定义消息映射路径@SendTo("/agent/public") // 发送消息到指定的代理路径public ChatMessage sendMessage(ChatMessage chatMessage) {log.info(chatMessage.toString());return chatMessage;}//方法二
//    @MessageMapping("/agent/send")
//    public void getAgentInfo (@Payload ChatMessage chatMessage) {
//        System.out.println("发送群发消息");
//        // 使用api进行推送
//        simpMessagingTemplate.convertAndSend("/agent/public2", chatMessage);
//    }/*** 发送给自己?* @param chatMessage* @return*/@MessageMapping("/agent/send/user")
// 这里的路径必须还是以广播的前缀为前缀,否则无法接收@SendToUser("/agent/info")public ChatMessage sendUserMessage(ChatMessage chatMessage) {log.info(chatMessage.toString());return chatMessage;}/*** 发送给指定用户* @param chatMessage* @return*/@MessageMapping("/chat/send/to/user")
// 这里的路径必须还是以广播的前缀为前缀,否则无法接收public void sendToUserMessage(ChatMessage chatMessage) {log.info("发送给指定用户:"+chatMessage.toString());simpMessagingTemplate.convertAndSendToUser(chatMessage.getUnionId(),"/message",chatMessage);}
}
package com.example.mengchuangyuan.common.chat.entry;import lombok.Data;@Data
public class ChatMessage {private String unionId;private String content;private String sender; 
}

以上就是完整代码

3.接下来我来简单解释一下,因为一对一聊天比其他相对绕一点,所以博主就解释它就好了,且看下面四段被截取的代码

 function connect() {var content = $("#message").val();//发送的消息内容var sender = 'tbb';//接收的人var socket = new SockJS('http://127.0.0.1:8080/chat');//连接后端socket的地址stompClient = Stomp.over(socket);stompClient.connect({}, function (frame) {console.log('Connected: ' + frame);//这里我写死了,接收来自tbb这个人的消息,'/user/tbb/message'可以改成'/user/'+sender+'/message'stompClient.subscribe('/user/tbb/message', function (message) { showMessage(JSON.parse(message.body));});});}

上面的代码可以称之为连接服务器并且实时监听tbb给后端发来的消息 

package com.example.mengchuangyuan.common.chat.config;import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/user","/agent","/topic"); // 定义消息代理,客户端订阅的地址前缀config.setApplicationDestinationPrefixes("/app"); // 定义客户端发送消息的地址前缀config.setUserDestinationPrefix("/user");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/chat").setAllowedOriginPatterns("*").withSockJS(); // 定义WebSocket端点,客户端连接的地址}
}

上面两段是对应的,在html的http://127.0.0.1:8080/chat中chat对应上面Java代码的chat,必须一致,要不然连不上,/user/tbb/message路由中user对应上面Java代码的user,必须一致,要不然发消息时对方收不到消息(博主以前踩过这个坑),而这个tbb则是接收发消息的人的消息

function sendMessage() { //发消息给对方var content = $("#message").val();var sender = $("#sender").val();stompClient.send("/app/chat/send/to/user", {}, JSON.stringify({ //发消息的后端的路由content: content,//消息内容sender: sender,//消息姓名unionId: sender,//消息id,这里我把消息姓名也作为id}));}
 /*** 发送给指定用户* @param chatMessage* @return*/@MessageMapping("/chat/send/to/user")
// 这里的路径必须还是以广播的前缀为前缀,否则无法接收public void sendToUserMessage(ChatMessage chatMessage) {log.info("发送给指定用户:"+chatMessage.toString());//chatMessage.getUnionId()是发送给某人的id,/message对应四段中第一段的/user/tbb/message中的message
simpMessagingTemplate.convertAndSendToUser(chatMessage.getUnionId(),"/message",chatMessage);}

 最后两段是对应的,用于给对方发消息。

三、微信小程序实现聊天

首先,这是博主自己摸索了很久出来的一套小程序聊天体系。

1.聊天数据结构及框架

涉及到了redis缓存,因此需要下载redis的依赖包

聊天数据结构如下(自我感觉存在一定缺陷,懒得改进了):

整体储存结构:

聊天界面结构:{openid1+openid2:{linkType:[info1,info2]}},

如图聊天界面:

聊天列表结构:{linkType:[openid1+openid2,openid3+openid4]}

如图聊天列表:


info的结构:{mid:"",type:"",linkType:"",formUser:fromUser,toUser:toUser,message:"",date:"",nowDate:""}
fromUser和toUser的结构:{openid:"",phone:"",name:"",headImg:""}
openid1和openid2为fromUser和toUser的openid

linkType:属于哪个板块聊天(比如相亲聊天板块或者外卖或商城聊天板块)


type:作用于获取redis缓存的聊天记录和聊天心跳检测(备注:因为获取历史聊天记录和心跳检测是以聊天方式向后端发起请求,因此我用type的聊天要区分是用户发起聊天还是其他请求。由前端自动发动聊天请求,获取历史聊天记录,由前端发起聊天请求检测心跳,检测心跳的目的是为了确保聊天过程不掉线)


date:聊天的时间段(备注:可以设置5分钟显示一个时间段聊天的时间,比如微信隔5分钟后再发一条信息上面会显示时间,这里我设置的date就充当这个角色)


nowDate:每一句话的时间,主要用于计算当前时间是否与上一句聊天记录的时间是否间隔5分钟,如果间隔5分钟那么上面的date记录该时间,如果间隔不到5分钟,则date设置为空。
整体结构上:两个人的openid连接作为获取他们之间所有功能板块历史聊天记录的内容的键(key)。以linkType作为键(key)获取某个功能板块的所有历史聊天记录,这里聊天记录用集合来储存保证了聊天记录顺序。

2.Java SpringBoot代码

(1)MiniWebSocketConfig.java文件
package com.example.mengchuangyuan.common.chat.mini.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class MiniWebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

(2)MiniWebSocketController.java文件

package com.example.mengchuangyuan.common.chat.mini.controller;import com.example.mengchuangyuan.common.redis.tool.SufferVariable;
import com.example.mengchuangyuan.common.tool.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;
import java.util.*;@Slf4j
@RestController
@RequestMapping("/mini/socket")
public class MiniWebSocketController {@Value("${upload.messageChatUrl}")private String messageChatUrl;@Value("${upload.messageChatPath}")private String messageChatPath;private final List<Map<String,Object>> mapList = new ArrayList<>();@GetMapping("/get/openid/history")private Result getOpenidHistory(@RequestParam("openid") String openid,@RequestParam("linkType") String linkType){
//        log.info(openid);List<String> openids = (List<String>) SufferVariable.openidKeys.get(linkType);
//        if (keyMap==null){
//            return Result.error();
//        }
//        List<String> openids = (List<String>) keyMap.get(openid);
//        log.info("所以openid"+String.valueOf(openids));if (openids==null){return Result.error();}mapList.clear();for (String openid1: openids) {if (openid1.contains(openid)) {
//            if (SufferVariable.messageMap.get(openid1)!=null){mapList.add((Map<String, Object>) SufferVariable.messageMap.get(openid1));
//                log.info("聊天记录缓存"+openid);
//                log.info(String.valueOf(SufferVariable.messageMap.get(openid1)));//            }}}return Result.success(mapList);}//上传聊天图片@PostMapping("/img/upload")public Result imgUpDown(@RequestParam("file") MultipartFile file,@RequestParam("filename") String filename) throws IOException {System.out.println(filename);File file1 = new File(messageChatPath,filename);if(!file1.exists()) {if(!file1.mkdirs()){ //创建目录return Result.error();}}//获取文件名String fileName = file.getOriginalFilename();//获取文件后缀名。也可以在这里添加判断语句,规定特定格式的图片才能上传,否则拒绝保存。String suffixName = fileName.substring(fileName.lastIndexOf("."));//为了避免发生图片替换,这里使用了文件名重新生成fileName = UUID.randomUUID()+suffixName;file.transferTo(new File(file1,fileName));return Result.success(messageChatUrl+filename+"/"+fileName);}
}

(3)WebSocketEndPoint.java文件

package com.example.mengchuangyuan.common.chat.mini.mapper;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.mengchuangyuan.common.redis.mapper.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;//对外公布的一个后端站点
//ws://localhost:8080/websocket/用户id
@ServerEndpoint(value = "/websocket/{userId}")
@Component
@Slf4j
public class WebSocketEndPoint {//与某个客户端的连接会话,需要他来给客户端发送数据private Session session;@Autowiredprivate SessionPool sessionPool;
//    @Autowired
//    private RedisUtils redisUtils;private static WebSocketEndPoint webSocketEndPoint;//初始化 ②@PostConstructpublic void init() {webSocketEndPoint = this;webSocketEndPoint.sessionPool = this.sessionPool;}//连接建立成功调用的方法@OnOpenpublic void onOpen(Session session, @PathParam("userId") String userId) {//把会话加入连接池中//userId通过用户传入,session是系统自动产生SessionPool.sessions.put(userId, session);//TODO 可以添加日志操作}//关闭会话的时候@OnClosepublic void onClose(Session session) throws IOException {webSocketEndPoint.sessionPool.close(session.getId());session.close();}//接收客户端的消息后调用的方法,在这里可以进行各种业务逻辑的操作@OnMessagepublic void onMessage(String message, Session session) {System.out.println(message);
//        log.info("redisUtils:"+redisUtils);//心跳检测if (message.equalsIgnoreCase("ping")) {try {Map<String, Object> params = new HashMap<>();params.put("type", "pong");session.getBasicRemote().sendText(JSON.toJSONString(params));} catch (Exception e) {e.printStackTrace();}return;}//将Json字符串转为键值对
//        HashMap params = JSON.parseObject(message, HashMap.class);JSONObject params =  JSON.parseObject(message);webSocketEndPoint.sessionPool.sendMessage(params);//这里的业务逻辑仅仅是把收到的消息返回给前端
//        SessionPool.sendMessage(message);}
}

(4)SessionPool.java文件

package com.example.mengchuangyuan.common.chat.mini.mapper;import com.alibaba.fastjson.JSON;
import com.example.mengchuangyuan.common.redis.mapper.RedisUtils;
import com.example.mengchuangyuan.common.redis.tool.SufferVariable;
import com.example.mengchuangyuan.common.tool.DateYMDms;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;import javax.websocket.Session;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;@Component
@Slf4j
public class SessionPool {@Autowiredprivate RedisUtils redisUtils;//key-value : userId - 会话(系统创建)public static Map<String, Session> sessions = new ConcurrentHashMap<>();//避免多线程问题public void close(String sessionId) {//sessionId是在session中添加了一个标识,准确定位某条sessionfor (String userId : SessionPool.sessions.keySet()) {Session session = SessionPool.sessions.get(userId);if (session.getId().equals(sessionId)) {sessions.remove(userId);break;}}}public void sendMessage(String userId, String message) {sessions.get(userId).getAsyncRemote().sendText(message);}//消息的群发,业务逻辑的群发public void sendMessage(String message) {
//        redisUtils.cacheValue("chatMessage","String.valueOf(SufferVariable.messageMap)");for (String sessionId : SessionPool.sessions.keySet()) {SessionPool.sessions.get(sessionId).getAsyncRemote().sendText(message);}}//点对点的消息推送public void sendMessage(Map<String, Object> params) {log.info("消息内容:"+String.valueOf(params));long mid = System.currentTimeMillis();Map<String,Object> formUser = (Map<String, Object>) params.get("formUser");Map<String,Object> toUser = (Map<String, Object>) params.get("toUser");String userId = formUser.get("openid").toString();String toUserId = toUser.get("openid").toString();
//        String msg = params.get("message").toString();String type  = params.get("type").toString();String linkType = params.get("linkType").toString();//获取用户sessionSession session = sessions.get(toUserId);Map<String,Object> keyMap;List<String> setOpenid;Map<String,Object> map;List<Object> list;String uid = userId+toUserId;params.put("mid",mid);if (SufferVariable.messageMap.get(userId+toUserId)==null&&SufferVariable.messageMap.get(toUserId+userId)==null){
//            messageMap.put(userId+toUserId,userId+toUserId);map = new HashMap<>();list = new ArrayList<>();map.put(linkType,list);SufferVariable.messageMap.put(uid,map);//            list.add(params);}else {if (SufferVariable.messageMap.get(userId+toUserId)!=null){uid = userId+toUserId;}else {uid = toUserId+userId;}map = (Map<String, Object>) SufferVariable.messageMap.get(uid);list = (List<Object>) map.get(linkType);}//获取历史记录if (type.equalsIgnoreCase("history")){if (sessions.get(userId) != null) {Map<String, Object> map2 = new HashMap<>();
//                map = (Map<String, Object>) SufferVariable.messageMap.get(uid);map2.put("type", "isHistory");map2.put("message",list);sessions.get(userId).getAsyncRemote().sendText(JSON.toJSONString(map2));}return;}String nowDate = DateYMDms.getUtilDate();if (list.size()!=0) {Map<String, Object> lastMap = (Map<String, Object>) list.get(list.size() - 1);String date = DateYMDms.getYMDms(5, lastMap.get("nowDate").toString());params.put("date", date);}else {params.put("date", nowDate);}
//        if (SufferVariable.openidKeys.get(linkType)==null){
//            keyMap = new HashMap<>();
//        }else {
//            keyMap = (Map<String, Object>) SufferVariable.openidKeys.get(linkType);
//        }
//        if (keyMap.get(userId)==null){
//            setOpenid = new ArrayList<>();
//        }else {
//            log.info(String.valueOf(SufferVariable.openidKeys));
//            setOpenid = (List<String>) keyMap.get(userId);
//        }if (SufferVariable.openidKeys.get(linkType) == null){setOpenid = new ArrayList<>();}else {setOpenid = (List<String>) SufferVariable.openidKeys.get(linkType);}setOpenid.add(uid);Set<String> set = new LinkedHashSet<>(setOpenid);setOpenid.clear();setOpenid.addAll(set);
//        keyMap.put(userId,setOpenid);SufferVariable.openidKeys.put(linkType,setOpenid);params.put("nowDate",nowDate);list.add(params);map.put(linkType,list);SufferVariable.messageMap.put(uid,map);redisUtils.cacheValue("chatMessage",SufferVariable.messageMap);redisUtils.cacheValue("chatOpenidKeys",SufferVariable.openidKeys);if (session != null) {log.info(uid+":"+String.valueOf(SufferVariable.messageMap.get(uid)));session.getAsyncRemote().sendText(JSON.toJSONString(params));}
//        log.info(String.valueOf(messageMap));
//        params.remove("formUserId");//session不为空的情况下进行点对点推送}
}

以上小程序相关代码存在一些缺陷,并且未完整,

需要小程序及SpringBoot完整代码的朋友可以私信博主,

好了本次分享就到此结束。

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

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

相关文章

2.Kubernetes基础-1

Kubernetes基础-1 掌握Kubernetes&#xff0c;需要我们有扎实的docker基础。 深入了解pods之前&#xff0c;我们需要&#xff1a; 应用程序已经开发并打包成Docker镜像&#xff0c;并且在Docker存储库&#xff08;如Docker Hub&#xff09;中可用&#xff0c;可下载Kubernet…

【GPU】深入理解GPU硬件架构及运行机制

深入理解GPU硬件架构及运行机制 作者&#xff1a;Tim在路上​ 曾看到有一篇名为《The evolution of a GPU: from gaming to computing》的文章。 这篇文章非常热烈的讨论了这些年GPU的进步&#xff0c;这引发了我们的一些思考: 为什么我们总说GPU比CPU要强大&#xff0c;既然…

unity学习笔记----游戏练习07

一、僵尸攻击和植物的掉血和销毁 当僵尸接触到植物开始攻击时会持续削减植物的血量&#xff0c;当植物血量为零时就销毁当前植物。 在plantManager中&#xff0c; 为植物添加一个血量HP 100&#xff0c; public int HP 100; 在写一个减少血量的方法&#xff0c;来减少血…

心理学大纲

简介 psychology&#xff0c;“psyche”(ψυχή):意为"soul"(灵魂)&#xff0c;即对我们灵魂的研究 我的学习的目的 了解人精神世界的模型&#xff0c;人格的形成]&#xff0c;作为观察分析他人内心的理论指导&#xff0c;便于我实践了解情绪的机理&#xff0c;…

Java面试题(6)

28.创建线程池有哪几种方式 newFixedThreadPool(int nThreads) &#xff1a;创建一个固定长度的线程池&#xff0c;如果有线程发生错误而结束&#xff0c; 线程池会补充一个新线程。 newCachedThreadPool() &#xff1a;创建一个可缓存的线程池&#xff0c;会自动回收和创建空…

OpenHarmony—TypeScript到ArkTS约束说明

对象的属性名必须是合法的标识符 规则&#xff1a;arkts-identifiers-as-prop-names 级别&#xff1a;错误 在ArkTS中&#xff0c;对象的属性名不能为数字或字符串。通过属性名访问类的属性&#xff0c;通过数值索引访问数组元素。 TypeScript var x { name: x, 2: 3 };c…

WWDG喂狗

3F 是0111111 40 是1000000 0X7F 127 0X5F 95 127-9532 注意:中断是在0x40,在0x40喂狗则程序不会复位 在0x5F之前喂狗会复位,减小到63以下也会复位 在0x5F与0x3F之间喂狗会继续执行,不会复位 WWDG_HandleTypeDef WWDG_Handler; //窗口看门狗句柄//初始化窗口看门狗…

项目解决方案:非执法视频监控系统项目设计方案

目 录 一、概述 &#xff08;一&#xff09;前言 &#xff08;二&#xff09;设计思路 &#xff08;三&#xff09;设计原则 1、实用性 2、可靠性 3、安全性 4、先进性 5、开放性 6、易管理、易维护 &#xff08;四&#xff09;设计依据 二、方案总…

【MySQL】阿里云服务器卸载MySQL流程

使用该命令查询当前安装的MySQL rpm -qa | grep mysql使用rpm -ev接上相应名称删除这些项目&#xff0c;推荐从下往上逐个卸载 接下来使用find / -name mysql查询系统内剩余的MySQL文件 最后使用rm -rf接对应文件目录删除 收尾再次使用find / -name mysql查询&#xff0c;No…

关于鸿蒙系统开源和技术细节的一些探讨

1月18日在深圳举办了“鸿蒙生态千帆启航仪式”&#xff0c;这也是华为鸿蒙开启生态进阶的信号。在政策的叠加下&#xff0c;鸿蒙未来必定是势不可挡的。我们这些程序员也得与时俱进&#xff0c;熟悉鸿蒙的技术和细节&#xff0c;别在经济寒冬里被淘汰了。 官方称 Harmony OS N…

HarmonyOS鸿蒙学习笔记(23)监听Wifi状态变化

监听Wifi状态变化 前言创建接收状态变化的Bean对象创建订阅者和订阅事件参考资料&#xff1a; 前言 本篇博文通过动态订阅公共事件来说明怎么使用HarmonyOS监听Wifi状态的变化。关于动态订阅公共事件的概念&#xff0c;官网有详细说明&#xff0c;再次就不在赘述。博文相关项目…

[GDMEC-无人机遥感研究小组]无人机遥感小组-000-数据集制备

基于labelme的无人机语义分割数据集制备 文章目录 基于labelme的无人机语义分割数据集制备1. 数据获取2. 安装labelme3.利用labelme进行标注 1. 数据获取 数据集制备需要利用无人机飞行并采集标注。使用录制模式&#xff0c;镜头垂直向下进行拍摄&#xff0c;得到DJI_XXXX.MP4…

研发日记,Matlab/Simulink避坑指南(四)——transpose()转置函数Bug

文章目录 前言 背景介绍 问题描述 分析排查 解决方案 总结 前言 见《研发日记&#xff0c;Matlab/Simulink避坑指南&#xff08;一&#xff09;——Data Store Memory模块执行时序Bug》 见《研发日记&#xff0c;Matlab/Simulink避坑指南(二)——非对称数据溢出Bug》 见《…

32人联机自建服务器攻略【幻兽帕鲁多人游玩】

创建幻兽帕鲁服务器1分钟部署教程&#xff0c;阿里云和腾讯云均推出幻兽帕鲁服务器服务器和部署教程&#xff0c;4核16G和4核32G配置可选&#xff0c;阿腾云atengyun.com分享1分钟自建幻兽帕鲁Palworld服务器教程&#xff1a; 幻兽帕鲁服务器创建教程 幻兽帕鲁服务器官方推荐…

使用 Redis 的 List 数据结构实现分页查询的思路

假设有一个存储数据的 List&#xff0c;每个元素代表一个记录&#xff0c;例如 recordsList。 按页存储数据&#xff1a; 每页存储一定数量的记录。例如&#xff0c;第一页存储索引 0 到 N-1 的记录&#xff0c;第二页存储索引 N 到 2N-1 的记录&#xff0c;以此类推。 分页查…

书生·浦语大模型--第六节课笔记作业--OpenCompass大模型评测

文章目录 OpenCompass评测框架实战&基础作业安装查看支持的数据集和模型启动评测 为什么需要评测&#xff1f; 统一的模型评测 如何评测&#xff1f; 基座模型需要加上额外的提示 客观评测&#xff1a;只要回答包括北京就可以认为正确 主观评测&#xff1a;创作类问题。人工…

设计模式_访问者模式_Visitor

案例引入 要求 测评系统需求&#xff1a;将观众分为男人和女人&#xff0c;对歌手进行测评&#xff0c;当看完某个歌手表演后&#xff0c;得到他们对该歌手不同的评价(比如 成功、失败 等) 传统方案 Man和Woman里面都有“成功”、“失败”的方法 【分析】 如果系统比较小&…

springboot优雅停机

import org.springframework.context.annotation.Configuration;import javax.annotation.PreDestroy;Configuration public class DataBackupConfig {PreDestroypublic void backData(){System.out.println("开始备份..."System.currentTimeMillis());System.out.pr…

6.Toast(Android)

愿你出走半生,归来仍是少年&#xff01; 环境&#xff1a;.NET 7、MAUI 在Maui开发中使用的Toast太丑了&#xff0c;在android项目中使用时不够看。通过Maui的安卓绑定库可实现将android中已有的包导入到C#项目中使用&#xff0c;借助这个方法就可以使用以前在android原生开发…

Python_NumPy——入门学习(概述,数据类型,创建数组)

作者&#xff1a;初次知晓 邮箱&#xff1a;lr_1052107892outlook.com 资料参考 [菜鸟教程](https://www.runoob.com/)NumPy概述 NumPy(Numerical Python)是python的一个扩展程序库&#xff0c;支持大量的维度数据与矩阵运算&#xff0c;针对数据运算提供大量的数学函数库,包…