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

Spring WebSocket 与 STOMP 协议结合实现私聊私信功能

目录

    • 后端
      • pom.xml
      • Config配置类
      • Controller类
      • DTO
    • 前端
      • 安装相关依赖
      • websocketService.js接口
      • javascript
      • html
      • CSS
    • 效果展示
      • 简单测试连接:
    • 报错解决方法
      • 1、vue3 使用SockJS报错 ReferenceError: global is not defined
    • 功能补充拓展
      • 1. 安全性和身份验证
      • 2. 异常处理
      • 3. 消息广播的功能
      • 4. 配置 WebSocket 消息缓存和负载均衡
      • 5. 客户端连接管理
      • 6. WebSocket 消息格式和编码
      • 总结
    • 后面将继续完善,待更新...

后端

pom.xml

		<!-- Spring Boot WebSocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- Spring Boot 数据库支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><!-- MySQL 驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- Thymeleaf(如果你使用了模板) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- Spring Boot Web 支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency>

Config配置类

注意:允许源根据自己项目修改

import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/queue", "/topic","/user");config.setApplicationDestinationPrefixes("/app");}@Overridepublic void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/ws")
//        在 WebSocket 握手时,我们可以通过 URL 参数或者 HTTP headers 传递用户身份信息。
//                .addInterceptors(new MyHandshakeInterceptor())// 添加拦截器.setAllowedOrigins("http://127.0.0.1:8889", "http://localhost:8889", "http://localhost:8888", "http://127.0.0.1:8888", "http://localhost:8000","http://localhost:8890","http://127.0.0.1:8890").withSockJS();  // 添加 SockJS 支持}
}

Controller类

import com.tianwen.mapper.UserMessagesMapper;
import com.tianwen.user.dtos.MessageDTO;
import com.tianwen.user.pojos.Messages;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import java.time.LocalDateTime;@Controller
public class ChatController {@Autowiredprivate UserMessagesMapper userMessagesMapper;@Autowiredprivate SimpMessagingTemplate messagingTemplate; // 注入消息模板,用于发送消息到指定目的地@MessageMapping("/chat.sendMessage")//这个注解用来监听来自前端的 WebSocket 消息,路径是 /app/chat.sendMessage,当前端发送消息到这个路径时,sendMessage 方法会被触发。
//    @SendTo("/topic/messages")//这个注解表明处理完消息后,返回的消息将广播给订阅了 /topic/messages 路径的所有客户端。public MessageDTO sendMessage(MessageDTO messageDTO) throws Exception {System.out.println("接收到的message:"+messageDTO);// 1. 可以在这里进行私信存储到数据库操作Messages messages = new Messages();messages.setSenderId(messageDTO.getSenderId());messages.setReceiverId(messageDTO.getReceiverId());messages.setContent(messageDTO.getContent());messages.setCreateTime(LocalDateTime.now());// 2. 保存私信消息(插入操作)if (userMessagesMapper.insert(messages) <= 0) {// 如果插入失败,可以返回错误或做其他处理return null;}// 3. 实时将消息转发给接收者String receiverIdStr = String.valueOf(messageDTO.getReceiverId());  // 将 receiverId 转换为 StringString receiverDestination = "/user/" + receiverIdStr + "/queue/messages";//通过 SimpMessagingTemplate 的 convertAndSendToUser 方法,将消息实时推送给接收者。//推送的目标是 /user/{receiverId}/queue/messages,该路径是给特定用户的私有消息队列。messagingTemplate.convertAndSendToUser(receiverIdStr, receiverDestination, messageDTO);return messageDTO;}
}

DTO

import lombok.Data;
@Data
public class MessageDTO {private Integer senderId;private Integer receiverId;private String content;
}

前端

安装相关依赖

npm install sockjs-client@latest
npm install @stomp/stompjs sockjs-client
npm install global    
npm i --save-dev @types/sockjs-client 

websocketService.js接口

注意:服务器地址根据自己的修改(application.yml)

// websocketService.js
// Stomp.js:用于处理 STOMP 协议,它在 WebSocket 基础上实现了消息订阅、发送等功能。
import { Stomp } from "@stomp/stompjs";
//SockJS:是一个用于实现 WebSocket 的库,它为 WebSocket 提供了回退机制(例如 HTTP 长轮询等),确保在不同浏览器和网络环境下的兼容性。
import SockJS from "sockjs-client/dist/sockjs.min.js";
export default {connect(onMessageReceived) {//使用 SockJS 和 Stomp 创建一个 WebSocket 客户端,连接到后端的 WebSocket 服务 http://localhost:8000/ws。const socket = new SockJS("http://localhost:8000/ws"); // 使用 SockJS 连接const stompClient = Stomp.over(socket);const userId = JSON.parse(localStorage.getItem("userId"));stompClient.connect({}, () => {console.log("本人消息队列ID:", userId);//在连接成功后,通过 stompClient.subscribe 订阅 /topic/messages,接收从后端广播过来的消息。// stompClient.subscribe("/topic/messages", (messageOutput) => {//   onMessageReceived(JSON.parse(messageOutput.body));// });// 订阅当前用户的私有消息队列stompClient.subscribe("/user/" + userId + "/queue/messages",(messageOutput) => {onMessageReceived(JSON.parse(messageOutput.body)); // 处理接收到的私聊消息});// 订阅当前用户的私有消息队列;// stompClient.subscribe(//   "/user/" + userId + "/queue/messages",//   function (messageOutput) {//     const message = JSON.parse(messageOutput.body);//     console.log("接收到私信:", message);//   }// );});// 连接到 WebSocket 后,订阅用户消息// stompClient.connect({}, function (frame) {//   // 获取当前用户ID//   const userId = getCurrentUserId(); // 假设这个方法能够获取当前用户的ID//   // 订阅接收者的消息队列// stompClient.subscribe(//   "/user/" + userId + "/queue/messages",//   function (messageOutput) {//     const message = JSON.parse(messageOutput.body);//     console.log("接收到私信:", message);//   }// );// });},sendMessage(message) {console.log("发送消息接口1:", message);const socket = new SockJS("http://localhost:8000/ws"); // 使用 SockJS 连接const stompClient = Stomp.over(socket);stompClient.connect({}, () => {//当用户输入消息时,通过 stompClient.send 方法将消息发送到 /app/chat.sendMessage,这个路径会将消息推送到后端进行处理。stompClient.send("/app/chat.sendMessage", {}, JSON.stringify(message));});},
};

javascript

<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import type { IToolbarConfig, IEditorConfig } from "@wangeditor/editor";
const editorRef = shallowRef();
import websocketService from "@/api/websocketService.js";
import {ref,onMounted,
} from "vue";
import websocketService from "@/api/websocketService.js";const receiverIdAnswer = ref();
const sendPrivateMessage = () => {dialogVisible.value = true;
};
const sendPrivateMessage = async (userId) => {receiverIdAnswer.value = userId;dialogVisible.value = true;const response = await getAuthorDetailsByUserId(userId);console.log("response", userId);privateMessagesUser.value = response.data;console.log("privateMessagesUser", privateMessagesUser.value);
};const dialogVisible = ref(false);interface Message {id: string;senderId: string;receiverId: string;content: string;
}const messages = ref<Message[]>([]); // 明确指定消息数组的类型const newMessage = ref("");onMounted(() => {websocketService.connect((message: Message) => {// 明确指定回调函数的参数类型messages.value.push(message);});
});const sendMessage = () => {if (newMessage.value.trim()) {const message: Message = {// 明确声明消息类型id: Date.now().toString(), // 使用当前时间戳作为唯一 IDsenderId: userInfo.value.id, // Example senderreceiverId: receiverIdAnswer.value, // Example receivercontent: newMessage.value,};websocketService.sendMessage(message);newMessage.value = "";}
};
const editorConfig: Partial<IEditorConfig> = {placeholder: "请输入...",MENU_CONF: {},
};
const handleCreated = (editor) => {editorRef.value = editor;
};
// 排除富文本的菜单项
const toolbarConfigPrivateMessages: Partial<IToolbarConfig> = {// toolbar 配置excludeKeys: ["headerSelect","blockquote","|","bold","underline","italic","group-more-style", // 排除菜单组,写菜单组 key 的值即可"color","bgColor","|","fontSize","fontFamily","lineHeight","bulletedList","numberedList","todo","group-justify","group-indent","insertLink","group-video","insertTable","codeBlock","divider","undo","redo","fullScreen",],
};
</script>

html

 <!-- 私信聊天框 --><el-dialog v-model="dialogVisible"><template #title><div style="text-align: center; font-weight: bold">{{ privateMessagesUser.username }}</div><hr class="line" /></template><div class="chat-container"><div class="messages" ref="messagesContainer"><divv-for="message in messages":key="message.id":class="{'my-message': message.senderId === userInfo.id,'other-message': message.senderId !== userInfo.id,}"><div style="display: flex; flex-direction: row"><div><el-image:src="userInfo.avatarUrl"style="width: 45px; border-radius: 50%"></el-image></div><div style="margin-top: 5px; margin-left: 10px"><div><strong>{{ userInfo.username }}</strong></div><divclass="message-bubble message-green"v-html="message.content"></div></div></div></div></div></div><div style="border: 1px solid #ccc"><Toolbarstyle="border-bottom: 1px solid #ccc":editor="editorRef":defaultConfig="toolbarConfigPrivateMessages"mode="default"/><Editorstyle="height: 200px; overflow-y: hidden"v-model="newMessage"@keyup.enter="sendMessage":defaultConfig="editorConfig"mode="default"@onCreated="handleCreated"/></div><template #footer><el-button @click="dialogVisible = false">取消</el-button><el-button type="primary" @click="sendMessage">发送</el-button></template></el-dialog>

CSS

<style scoped>
/* 私信样式 */
/* 标题居中 */
/* .private-message-dialog {
} */
.line {border-top: 1px solid #ccc; /* 直线的样式,可以修改颜色 */
}/* 聊天框滚动 */
.chat-container {display: flex;flex-direction: column;height: 300px;overflow-y: auto;box-sizing: border-box; /* 让 padding 和 border 包含在宽度和高度内 */
}.chat-container > * {width: 100%; /* 确保所有子元素不会超出容器宽度 */box-sizing: border-box; /* 确保子元素的宽度计算不受 padding 和 border 影响 */
}/* 消息容器 */
.messages {display: flex;flex-direction: column;gap: 10px;padding: 10px;max-height: 250px;overflow-y: auto;
}/* 发送方和接收方的消息样式 */
.my-message {/* text-align: right; */border-radius: 10px;height: auto;/* padding: 5px 10px; */
}.other-message {/* text-align: left; */border-radius: 10px;/* padding: 5px 10px; */
}.message-bubble {/* max-width: 70%; */padding: 0px 10px;border-radius: 10px;/* height: 30px; *//* margin: 0px 0px 0px 0px; *//* word-wrap: break-word; *//* line-height: 1.4; */font-size: 14px;display: flex;align-items: center;justify-content: center;
}.message-green {background-color: #57c457; /* 微信消息绿色 */color: white;align-self: flex-end; /* 让气泡靠右显示 */
}
</style>

效果展示

在这里插入图片描述

简单测试连接:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

报错解决方法

1、vue3 使用SockJS报错 ReferenceError: global is not defined

解:

import SockJS from “sockjs-client”;

修改为:

import SockJS from “sockjs-client/dist/sockjs.min.js”;

并安装依赖

npm i --save-dev @types/sockjs-client

功能补充拓展

以下是一些可能的补充和优化,确保 WebSocket 能够顺利运行并且高效处理消息。

1. 安全性和身份验证

如果你的 WebSocket 服务需要进行身份验证(如用户登录),你可以考虑在 WebSocket 握手时验证用户身份。你可以在 WebSocketConfig 中添加一个 HandshakeInterceptor 来拦截握手请求,获取 HTTP header 或 URL 参数中的用户信息,确保只有经过身份验证的用户能够连接。

例如:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {registry.addEndpoint("/ws").addInterceptors(new MyHandshakeInterceptor()) // 添加拦截器.setAllowedOrigins("http://localhost:8889").withSockJS();
}

其中 MyHandshakeInterceptor 可以用来在 WebSocket 握手期间传递用户信息。

2. 异常处理

在 WebSocket 消息处理过程中,你可能会遇到一些异常,如数据库操作失败或消息传输失败等。在控制器中,你可以捕获这些异常并返回相应的错误消息,确保系统更加健壮。

例如:

@MessageMapping("/chat.sendMessage")
public MessageDTO sendMessage(MessageDTO messageDTO) {try {// 消息处理逻辑} catch (Exception e) {log.error("发送消息失败", e);return new MessageDTO("error", "消息发送失败");}
}

3. 消息广播的功能

目前,你的代码实现了将消息发送到指定用户的功能(私信)。如果你希望实现群聊功能或全局广播,可以进一步扩展 @SendTo 注解。这个注解的使用使得你可以将处理后的消息发送给所有订阅某个特定主题的客户端。

例如,广播消息给所有订阅 /topic/messages 的客户端:

@MessageMapping("/chat.sendMessage")
@SendTo("/topic/messages")
public MessageDTO sendMessage(MessageDTO messageDTO) {// 处理消息逻辑return messageDTO;  // 返回的消息会广播给所有订阅 /topic/messages 的客户端
}

4. 配置 WebSocket 消息缓存和负载均衡

如果你的系统需要处理大量的 WebSocket 连接,可能会面临性能和可扩展性的问题。在这种情况下,考虑使用 Redis 等消息队列作为消息代理,可以通过 @EnableWebSocketMessageBroker 配置远程消息代理。

例如,通过 Redis 实现消息广播:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {@Overridepublic void configureMessageBroker(MessageBrokerRegistry config) {config.enableSimpleBroker("/queue", "/topic", "/user");config.setApplicationDestinationPrefixes("/app");// 使用 Redis 消息代理config.setBrokerRegistry().setApplicationDestinationPrefixes("/app").enableStompBrokerRelay("/topic", "/queue").setRelayHost("localhost").setRelayPort(61613);  // 配置Redis(如ActiveMQ)等消息中间件}
}

5. 客户端连接管理

如果你希望在某个用户断开连接时进行一些清理工作(例如清理会话、推送通知等),你可以使用 @OnDisconnect 注解来捕获断开连接事件。

@MessageMapping("/chat.disconnect")
public void handleDisconnect(SessionDisconnectEvent event) {// 用户断开连接时执行的逻辑log.info("用户断开连接,sessionId: " + event.getSessionId());
}

6. WebSocket 消息格式和编码

确保你的客户端和服务端使用相同的消息格式。你可能需要为 WebSocket 消息提供适当的序列化和反序列化器,以确保消息能够正确地从客户端传输到服务端,以及从服务端传输回客户端。

例如,使用 Jackson 或其他库将 Java 对象序列化为 JSON 格式:

@MessageMapping("/chat.sendMessage")
@SendTo("/topic/messages")
public MessageDTO sendMessage(MessageDTO messageDTO) throws Exception {// 消息传输过程中确保使用适当的 JSON 格式return messageDTO;
}

总结

大体上,已经完成了 WebSocket 的配置和处理消息的核心部分,剩下的步骤主要是根据具体业务需求做扩展,如身份验证、消息缓存、广播支持等。如果你的应用规模较大,可能还需要考虑负载均衡和消息队列等高可用性的设计。

后面将继续完善,待更新…

相关文章:

Spring WebSocket 与 STOMP 协议结合实现私聊私信功能

目录 后端pom.xmlConfig配置类Controller类DTO 前端安装相关依赖websocketService.js接口javascripthtmlCSS 效果展示简单测试连接&#xff1a; 报错解决方法1、vue3 使用SockJS报错 ReferenceError: global is not defined 功能补充拓展1. 安全性和身份验证2. 异常处理3. 消息…...

从0到1:C++ 开启游戏开发奇幻之旅(一)

目录 为什么选择 C 进行游戏开发 性能卓越 内存管理精细 跨平台兼容性强 搭建 C 游戏开发环境 集成开发环境&#xff08;IDE&#xff09; Visual Studio CLion 图形库 SDL&#xff08;Simple DirectMedia Layer&#xff09; SFML&#xff08;Simple and Fast Multim…...

基于Flask的哔哩哔哩综合指数UP榜单数据分析系统的设计与实现

【Flask】基于Flask的哔哩哔哩综合指数UP榜单数据分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统旨在通过大数据分析和数据挖掘技术&#xff0c;结合Flask轻量级We…...

在php中怎么打开OpenSSL

&#xff08;点击即可进入聊天助手&#xff09; 背景 在使用php做一些项目时,有用到用户邮箱注册等,需要开启openssl的能力 在php系统中openssl默认是关闭状态的,在一些低版本php系统中,有的甚至需要在服务器终端后台,手动安装 要打开OpenSSL扩展&#xff0c;需要进行以下步骤 …...

oracle 分区表介绍

oracle 分区表介绍 Oracle 分区表是一个非常强大的数据库功能&#xff0c;可以将一个大的表分割成多个更小、更易管理的块&#xff08;分区&#xff09;。这种分区结构在处理大规模数据时非常有用&#xff0c;因为它能改善性能、简化维护和管理&#xff0c;并支持高效的数据存取…...

wxwidgets直接获取系统图标,效果类似QFileIconProvider

目前只做了windows版本&#xff0c;用法类似QFileIconProvider // 头文件 #ifndef WXFILEICONPROVIDER_H #define WXFILEICONPROVIDER_H#include <wx/wx.h> #include <wx/icon.h> #include <wx/image.h> #include <wx/bmpcbox.h> // Include for wxB…...

Arduino大师练成手册 -- 控制 PN532 NFC 模块

要在 Arduino 上控制 PN532 NFC 模块&#xff0c;你可以按照以下步骤进行&#xff1a; 硬件连接 VCC&#xff1a;连接到 Arduino 的 3.3V 引脚。 GND&#xff1a;连接到 Arduino 的 GND 引脚。 SDA&#xff1a;连接到 Arduino 的 SDA 引脚&#xff08;通常是 A4&#xff09…...

解决日志中 `NOT NULL constraint failed` 异常的完整指南

在开发和运维过程中,日志是我们排查问题的重要工具。然而,当日志中出现类似 NOT NULL constraint failed 的异常时,往往意味着数据库约束与代码逻辑不匹配。本文将详细分析此类问题的原因,并提供完整的解决方案。 © ivwdcwso (ID: u012172506) 问题描述 在同步 AWS …...

C动态库的生成与在Python和QT中的调用方法

目录 一、动态库生成 1&#xff09;C语言生成动态库 2&#xff09;c类生成动态库 二、动态库调用 1&#xff09;Python调用DLL 2&#xff09;QT调用DLL 三、存在的一些问题 1&#xff09;python调用封装了类的DLL可能调用不成功 2&#xff09;DLL格式不匹配的问题 四、…...

UE求职Demo开发日志#7 强化属性完善

1 实现思路设计 定义一个结构体记录技能树一个单元的信息&#xff0c;命名为FStrengthenCellInfo&#xff0c;一个TArray记录技能树整体信息&#xff0c;需要以下信息&#xff1a; 1.TArray前置技能index 2.FString 描述文本 3.TArray<FMyItemInfo>激活需要的物品ID和…...

Day35:字符串的大小写转换

在 Python 中&#xff0c;字符串的大小写转换是一个常见的操作&#xff0c;它可以帮助我们快速地将字符串中的字母从大写转换为小写&#xff0c;或者从小写转换为大写。Python 提供了多种方法来进行字符串大小写的转换&#xff0c;包括 upper()、lower()、capitalize()、title(…...

喜报丨迪捷软件入选2025年浙江省“重点省专”

根据《浙江省经济和信息化厅 浙江省财政厅关于进一步支持专精特新中小企业高质量发展的通知》&#xff08;浙经信企业〔2024〕232号&#xff09;有关要求&#xff0c;经企业自主申报、地方推荐、材料初审以及专家评审等程序&#xff0c;浙江省经济和信息化厅发布了2025年浙江省…...

深度剖析 PyTorch框架:从基础概念到高级应用的深度学习之旅!

目录 一、引言 二、PyTorch 简介 &#xff08;一&#xff09;诞生背景与发展历程 &#xff08;二&#xff09;核心特点 三、PyTorch 基础概念 &#xff08;一&#xff09;张量&#xff08;Tensor&#xff09;&#xff1a;数据的基石 &#xff08;二&#xff09;自动微分&…...

基于C++的DPU医疗领域编程初探

一、大型医院数据处理困境与 DPU 的崛起 在数字化浪潮的席卷下,医疗行业正经历着深刻变革,大型医院作为医疗服务的核心枢纽,积累了海量的数据,涵盖患者的基本信息、诊断记录、检验报告、影像资料等多个维度。这些数据不仅规模庞大,而且增长速度迅猛,传统的中央处理器(C…...

Linux 执行 fdisk -l 出现 GPT PMBR 大小不符 解决方法

目录 前言1. 问题所示2. 原理分析3. 解决方法前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 1. 问题所示 执行fdisk -l的时候出现如下提示: [root@VMS-Centos-test1 ~]# fdisk -l GPT PMBR 大小不符(419430399 != 4294967295),将用写入予以更正…...

图漾相机搭配VisionPro使用简易教程

1.下载并安装VisionPro软件 请自行下载VisonPro软件。 VisionPro 9.0 /9.5/9.6版本经测试&#xff0c;可正常打开图漾相机&#xff0c;建议使用图漾测试过的版本。 2.下载PercipioCameraForVisionPro软件包 使用浏览器下载&#xff1a;https://gitee.com/percipioxyz/camport…...

第一届“启航杯”网络安全挑战赛WP

misc PvzHE 去这个文件夹 有一张图片 QHCTF{300cef31-68d9-4b72-b49d-a7802da481a5} QHCTF For Year 2025 攻防世界有一样的 080714212829302316092230 对应Q 以此类推 QHCTF{FUN} 请找出拍摄地所在位置 柳城 顺丰 forensics win01 这个软件 云沙盒分析一下 md5 ad4…...

大模型训练策略与架构优化实践指南

标题&#xff1a;大模型训练策略与架构优化实践指南 文章信息摘要&#xff1a; 该分析全面探讨了大语言模型训练、架构选择、部署维护等关键环节的优化策略。在训练方面&#xff0c;强调了pre-training、mid-training和post-training的不同定位与目标&#xff1b;在架构选择上…...

新电脑安装系统找不到硬盘原因和解决方法来了

有不少网友反馈新电脑采用官方u盘方式装win10或win100出现找不到硬盘是怎么回事&#xff1f;后来研究半天发现是bios中开启了rst(vmd)模式。如果关闭rst模式肯定是可以安装的&#xff0c;但这会影响硬盘性能&#xff0c;有没有办法解决开启rst模式的情况安装win10或win11呢&…...

【Linux】21.基础IO(3)

文章目录 3. 动态库和静态库3.1 静态库与动态库3.2 静态库的制作和使用原理3.3 动态库的制作和使用原理3.3.1 动态库是怎么被加载的 3.4 关于地址 3. 动态库和静态库 3.1 静态库与动态库 静态库&#xff08;.a&#xff09;&#xff1a;程序在编译链接的时候把库的代码链接到可…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后&#xff0c;迭代器会失效&#xff0c;因为顺序迭代器在内存中是连续存储的&#xff0c;元素删除后&#xff0c;后续元素会前移。 但一些场景中&#xff0c;我们又需要在执行删除操作…...