Spring中WebSocket的使用

文章目录

  • 前言
  • 什么是 WebSocket
  • WebSocket 协议和 HTTP 协议的区别
  • WebSocket 原理解析
    • WebSocket 报文格式
  • Spring 中 WebSocket 的使用
    • 前后端发送的数据的数据类型是对象该如何做
    • 使用websocket协议如何获取到HTTP协议中的HttpSession
  • WebSocket使用的完整代码

前言

我们在使用 Spring 做网页开发的时候,我们后端服务器的主要作用就是处理前端、客户端发送来的请求,也就是说我们的服务器是被动接受请求的一方,而在某些时候,需要我们的服务器主动向客户端发送网络数据包,例如购物软件的降价通知,聊天软件别人发送消息时候的通知,这些都需要服务器主动向客户端发送网络数据包。

浏览器像服务器发送网络请求,应用层使用的协议往往是 HTTP 或者 HTTPS 协议,而 HTTP 协议在发展的时候却没有涉及到服务器主动向客户端发送网络数据包的功能,因为 HTTP 的发展初衷就是为了人们通过网络能够看报纸、新闻的,也就没想到服务器能够通过 HTTP 协议主动向客户端发送网络数据包,所以通过 HTTP 协议是无法实现服务器主动向客户端发送网络数据包的功能的。

那么如何实现服务器主动向客户端发送请求的功能呢?这就需要使用到另外一种应用层协议——WebSocket 了。

什么是 WebSocket

来看看百度的解释:

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它允许服务端主动向客户端推送数据,同时也支持客户端向服务端发送数据,实现了真正的双向通信。WebSocket协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。WebSocket API也被W3C定为标准,使得浏览器和服务器之间的数据交换变得更加简单和高效。

传统的应用层使用其他协议的 web 程序,都是属于”一问一答“形式,客户端给服务器发送 HTTP 请求之后,服务器给客户端返回一个 HTTP 响应,在这种情况下,服务器是属于被动的一方,如果客户端不主动发起请求,服务器无法主动给客户端响应。而 WebSocket 则是更接近于 TCP 这种级别的通信方式,一旦建立连接,客户端和服务端都可以主动向对方发送数据。

WebSocket 协议和 HTTP 协议的区别

  1. 协议性质
  • WebSocket:是一种在单个TCP连接上进行全双工通信的协议。它允许服务端主动向客户端推送数据,使得客户端和服务器之间的数据交换变得更加简单和高效。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。(来源:百度百科)
  • HTTP:全称超文本传输协议(HyperText Transfer Protocol),是一种用于分布式、协作式、超媒体信息系统的应用层协议。HTTP是一个基于TCP/IP通信协议来传递数据的协议,客户端发送请求,服务器返回响应。HTTP是无连接的,即每次连接只处理一个请求,服务器处理完客户请求,并收到客户的应答后,就断开连接。(来源:知乎专栏)
  1. 通信方式
  • WebSocket:支持双向通信,即客户端和服务器可以同时向对方发送数据。这种全双工的通信方式使得WebSocket特别适用于需要实时数据交换的场景,如在线聊天、实时数据更新等。
  • HTTP:是单向的、请求-响应模式的协议。客户端发起请求,服务器返回响应,然后连接断开。如果需要再次交换数据,必须重新建立连接。
  1. 连接状态
  • WebSocket:是有状态的协议。一旦建立了WebSocket连接,客户端和服务器之间的通信就可以持续进行,直到连接被关闭。这种持久连接减少了因频繁建立连接而产生的开销。
  • HTTP:是无状态的协议。HTTP协议对事务处理没有记忆能力,即服务器不会记住任何关于客户端请求的信息。如果需要处理多个请求之间的关联,必须在每个请求中携带必要的状态信息。
  1. 数据传输效率
  • WebSocket:由于使用了长连接和较少的控制开销(如头部信息较小),WebSocket在数据传输效率上优于HTTP。特别是在需要频繁交换小量数据的场景中,WebSocket能够显著减少网络带宽的消耗。
  • HTTP:每次请求都需要携带完整的头部信息,这可能导致在传输小量数据时头部信息的开销占比较大。此外,HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,从而浪费了网络带宽。

WebSocket 原理解析

WebSocket 本质上是一个基于 TCP 的协议。为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加的头信息,通过这些附加的头信息来完成握手过程。

那么这些附加的头信息是哪些呢?

对于浏览器发送的请求数据包的头信息中,附加的信息有:

  • Connection: upgrade。表示我需要升级协议
  • Sec-WebSocket-Accept: xxxxxx。服务端与该客户端通讯的钥匙
  • Sec-WebSocket-Version: 13。升级的协议的版本
  • Upgrade: websocket。升级的协议格式

在这里插入图片描述

WebSocket 报文格式

WebSocket 协议的相关信息大家可以去官方文档中查看:https://www.rfc-editor.org/rfc/rfc6455

在这里插入图片描述

  • FIN 表示是否要关闭 WebSocket,为 1 表示断开 WebSocket 连接,这里的 FIN 和 TCP 报文中的 FIN 不是一个概念。
  • RSV1/RSV2/RSV3:保留位,现在先不用,但是不保证后面可能会用到,值一般为 0。
  • opcode:操作代码,决定了如何理解后面的数据载荷。
    • %x0:表示这是一个延续帧。当 opcode 为0,表示本次数据传输采用了数据分片,当前收到的帧为其中一个分片
    • %x1:表示这是文本帧,也就是载荷中的数据是文本类型
    • %x2:表示这是二进制帧,也就是载荷中的数据是二进制类型
    • %x3-7:保留,暂未使用
    • %x8:表示连接断开
    • %x9:表示 ping 帧
    • %xA:表示 pong 帧
    • %xB-F:保留,暂未使用
  • mask:表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作
  • Payload length:数据载荷的长度,单位是字节,能表示的范围是0-127
  • Extended Payload length:扩展的载荷长度,127字节的大小肯定是不够用的,所以就出现了扩展载荷,当Payload length的值0-125的时候表示扩展载荷的长度为0,Payload length的值为126时,表示扩展载荷的长度为16位,值为127时,表示扩展载荷的长度为64位
  • Masking-key:0或者4字节(32位)所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,mask 为 1,且携带了4字节的Masking-key。如果 mask 为0.则没有 Masking-key
  • Payload Data:报文携带的载荷数据

websocket 载荷的长度可以是 6比特位,单位是字节能表示的大小,也可以是 16比特位、或者64比特位能表示的范围大小。

使用掩码算法的目的主要是从安全角度考虑,避免一些缓冲区溢出攻击。

我们可以直接使用 tomcat 提供的 WebSocket 的原生 API,也可以使用 Spring 内置的 WebSocket,其实这两者区别不大。

Spring 中 WebSocket 的使用

首先我们在创建 Spring 项目的时候需要添加进去 WebSocket 依赖。

在这里插入图片描述
在这里插入图片描述
也可以自己手动添加 websockt 依赖。

添加完依赖之后,我们创建一个类,继承TextWebSocketHandler 类,如果你的 websocket 中的载荷的数据类型是二进制类型的话,就继承 BinaryWebSocketHandler 类:

package com.example.websocket.component;import org.springframework.stereotype.Component;
import org.springframework.web.socket.handler.TextWebSocketHandler;@Component
public class TestWebSocketComponent extends TextWebSocketHandler {
}

TextWebSocketHandler 父类中的方法有很多:

在这里插入图片描述

我们需要重写的方法主要就是:afterConnectionEstablishedhandleTextMessagehandleTransportErrorafterConnectionClosed方法。

  • afterConnectionEstablished: 方法表示客户端和服务端建立 websocket 连接之后执行的方法
  • handleTextMessage:方法表示对方传来 websocket 数据帧的时候执行的方法
  • handleTransportError:方法表示 websocket 连接出现异常的时候执行的方法
  • afterConnectionClosed: 方法表示关闭 websocket 连接后执行的方法

重写完成 TextWebSocketHandler 类之后,就需要将这个实例给注册到 spring 中,进行路由的配置:

创建一个类,实现 WebSocketConfigurer 接口,并且实现接口中的 registerWebSocketHandlers 方法:

package com.example.websocket.config;import com.example.websocket.component.TestWebSocketComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Autowiredprivate TestWebSocketComponent testWebSocketComponent;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(testWebSocketComponent,"/test");}
}

@EnableWebSocket 注解用于开启Spring应用程序对WebSocket协议的支持。

registry.addHandler() 方法中的参数分别上我们上面继承并重写了 TextWebSocketHandler 类中的方法的实例,而后面的字符串则是路由配置,当客户端发送的请求的路由定位到这个字符串的时候,就会执行 TextWebSocketHandler 中的对应方法。

当配置完成服务端的代码之后,我们来实现一个简单的页面:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>websocket测试</title>
</head>
<body><input type="text" id="message"><button id="sendBtn">发送</button>
</body>
</html>

在这里插入图片描述
然后编写 js 这边的 websocket 代码:

<script>// 创建一个 websocket 实例// ws 是 websocket 的缩写,然后后面就是我们的服务器的ip地址以及前面服务端配置的路由地址let websocket = new WebSocket("ws://127.0.0.1/test");// 为 websocket 注册一些回调函数websocket.onopen = function() {// websocket 连接建立完成之后,自动执行到console.log('websocket 连接成功');}websocket.onclose = function() {// websocket 连接断开后,自动执行到console.log('websocket 连接断开');}websocket.onerror = function() {// websocket 连接异常时,自动执行到console.log('websocket 连接异常');}websocket.onmessage = function(e) {// 收到对端消息时,自动执行到console.log('websocket 收到消息' + e.data);}
</script>

这是 websocket 相关的 js 代码完成了,让后我们为在这个 button 创建一个事件:

let sendBtn = document.querySelector('#sendBtn');
sendBtn.onclick = function() {let input = document.querySelector('#message');websocket.send(input.value)
}

js 通过 WenSocket 的实例中的 send 方法向对端发送消息,而 spring 则通过类 WebSocketSession 实例中的 sendMessage 方法来向对端发送消息。

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {log.info("websocket 接收到消息:" + message.toString());//sendMessage方法中的参数是类WebSocketMessage及其子类session.sendMessage(message);
}

前后端发送的数据的数据类型是对象该如何做

在网络传输中,不存在什么对象这样的概念,如果传输的数据类型是对象的话,首先需要将对象转换为 json 字符串,然后再传输。

前端发送的消息的数据类型是对象的话,就需要将对象转换为 JSON 字符串,而我们前面使用 Ajax 的时候是因为 Ajax 帮助我们完成了 JSON 的转换,所以才不需要手动转换:

let req = {type: 'message',data: input.value
}
websocket.send(JSON.stringify(req));

然后后端通过 User user = objectMapper.readValue(message.asBytes(),User.class); 来将 json 字符串转换为 Java 对象。

如果后端发送的消息的数据类型是 Java 对象的话,就需要将 Java 对象转换为 json 字符串然后再发送:

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {log.info("websocket 接收到消息:" + message.toString());//向对端发送消息User user = new User();user.setUserId(1);user.setUserName("zhangsan");String respJson = objectMapper.writeValueAsString(user);session.sendMessage(new TextMessage(respJson));
}

然后前端通过 e = JSON.parse(e) 来将 json 字符串转换为对象。

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

使用websocket协议如何获取到HTTP协议中的HttpSession

在很多时候,当我们登录的时候,如果登录成功,往往会将用户信息存放在 session 会话中,而后面升级了 websocket 协议之后,如果我们需要使用到 session 中的信息该怎么办呢?这里 websocket 的开发者也想到了这里。当我们在注册 TextWebSocketHandler 的时候,再注册一个 HttpSession 拦截器就可以了,这样就可以把用户给 HttpSession 中添加的 Attributes 键值对往我们的 WebSocketSession 中也添加一份。

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(testWebSocketComponent,"/test").addInterceptors(new HttpSessionHandshakeInterceptor());
}

在 spring 中,通过 WebSocketSession 的实例中的 getAttributes() 方法得到一个类似哈希表的结构,里面存放 HttpSession 中的设置的所有属性的键值对,然后再从这个哈希表结构中通过 get(key) 方法获取指定 key 的value。

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {log.info("websocket 接收到消息:" + message.toString());session.sendMessage(message);session.getAttributes().get("user");
}

WebSocket使用的完整代码

WebSocketComponent.java 中的代码

package com.example.websocket.component;import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;@Slf4j
@Component
public class TestWebSocketComponent extends TextWebSocketHandler {@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {log.info("websocket 连接成功");}@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {log.info("websocket 接收到消息:" + message.toString());//向对端发送消息session.sendMessage(message);//获取HttpSession中的属性session.getAttributes().get("user");}@Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {log.info("websocket 连接异常:" + exception.toString());}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {log.info("websocket 断开连接");}
}

WebSocketConfig.java 中的代码:

package com.example.websocket.config;import com.example.websocket.component.TestWebSocketComponent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {@Autowiredprivate TestWebSocketComponent testWebSocketComponent;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(testWebSocketComponent,"/test").addInterceptors(new HttpSessionHandshakeInterceptor());}
}

js 中的代码:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>websocket测试</title>
</head>
<body><input type="text" id="message"><button id="sendBtn">发送</button><script>// 创建一个 websocket 实例// ws 是 websocket 的缩写,然后后面就是我们的服务器的ip地址以及前面服务端配置的路由地址let websocket = new WebSocket("ws://127.0.0.1:8080/test");// 为 websocket 注册一些回调函数websocket.onopen = function() {// websocket 连接建立完成之后,自动执行到console.log('websocket 连接成功');}websocket.onclose = function() {// websocket 连接断开后,自动执行到console.log('websocket 连接断开');}websocket.onerror = function() {// websocket 连接异常时,自动执行到console.log('websocket 连接异常');}websocket.onmessage = function(e) {// 收到对端消息时,自动执行到console.log('websocket 收到消息' + e.data);}let sendBtn = document.querySelector('#sendBtn');sendBtn.onclick = function() {let input = document.querySelector('#message');websocket.send(input.value)}</script>
</body>
</html>

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

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

相关文章

Pixel Adventure Unity2D开发完整指南

本文参考&#xff1a;2-2. Get and Setup Assets_哔哩哔哩_bilibili 1、下载资源 在Asset Store中下载Pix Adventure1 2的资源&#xff1a; 在import的时候&#xff0c;不用到Scene import进来&#xff0c;如下图所示&#xff0c;Scenes目录反勾选一下。 两个资源都下载完成后…

Unity 使用 NewtonSoft Json插件报错

JsonReaderException: Unexpected character encountered while parsing value: . Path , line 0, position 0. 通过断点发现&#xff0c;头有一串ZWNBSP&#xff0c;这个是BOM格式的JSON。在文件下看不到。 解决方法&#xff1a;改编码格式&#xff0c;Remove BOM.

(回溯) LeetCode 51. N 皇后

原题链接 一. 题目描述 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后…

腾讯云AI代码助手:智能AI代码助手 ,新一代的高效代码开发辅助工具

前言 近些年是一个科技大爆发的时代&#xff0c;自从大模型发布以来越来越多的科技产品出现。例如去年的智能编码助手自出现以来&#xff0c;各大老牌大厂腾讯&#xff0c;百度 阿里也都紧随其后&#xff0c;智能编码助手的出现可以说大大的节省了我们写一些冗余代码的时间成本…

十七、访问者模式

文章目录 1 基本介绍2 案例2.1 Element 接口2.2 Vehicle 抽象类2.3 Car 类2.4 Jeep 类2.5 VehicleCollection 类2.6 Action 抽象类2.7 Repair 类2.8 Drive 类2.9 Client 类2.10 Client 类的运行结果2.11 总结 3 各角色之间的关系3.1 角色3.1.1 Element ( 元素 )3.1.2 ConcreteE…

靓图!多点创新!CEEMDAN-Kmeans-VMD-CNN-LSTM-Attention双重分解+卷积长短期+注意力多元时间序列预测

靓图&#xff01;多点创新&#xff01;CEEMDAN-Kmeans-VMD-CNN-LSTM-Attention双重分解卷积长短期注意力多元时间序列预测 目录 靓图&#xff01;多点创新&#xff01;CEEMDAN-Kmeans-VMD-CNN-LSTM-Attention双重分解卷积长短期注意力多元时间序列预测效果一览基本介绍程序设计…

LVS 调度器 nat和DR模式

lvs-nat 修改请求报文的目标IP,多目标IP的DNAT 配置网络 LVS主机 注意网卡的顺序 &#xff08;nat和主机模式&#xff09; [rootlvs ~]# cat /etc/NetworkManager/system-connections/ens160.nmconnection [connection] idens160 typeethernet interface-nameens160 ​ [ip…

Linux使用学习笔记3 系统运维监控基础

系统运维监控类命令 查询每个进程的线程数 for pid in $(ps -ef | grep -v grep|grep "systemd" |awk {print $2});do echo ${pid} > /tmp/a.txt;cat /proc/${pid}/status|grep Threads > /tmp/b.txt;paste /tmp/a.txt /tmp/b.txt;done|sort -k3 -rn for pid…

数据结构与算法-16高级数据结构_图论(图论基础)

图论基础 1 什么是图 1.1 基础定义 图&#xff08;Graph&#xff09;是一个用于描述一组对象之间关系的数学结构。这些对象被称为顶点&#xff08;Vertex&#xff09;&#xff0c;也称为节点&#xff08;Node&#xff09;或点&#xff08;Point&#xff09;&#xff0c;而对…

2024国赛Word论文模板【一键生成式操作】

一、比赛介绍 该竞赛创办于1992年&#xff0c;每年一届&#xff0c;是首批列入“高校学科竞赛排行榜”的19项竞赛之一。2023年&#xff0c;来自全国及美国、澳大利亚、马来西亚的1685所院校/校区、59611队(本科54158队、专科5453队)、近18万人报名参赛。 而今年的国赛马上就要…

【CTF | WEB】001、攻防世界WEB题目之backup

文章目录 backup题目描述:解题思路&#xff1a;解题过程&#xff1a; backup 题目描述: X老师忘记删除备份文件&#xff0c;他派小宁同学去把备份文件找出来,一起来帮小宁同学吧&#xff01; 进入题目后显示&#xff1a; 解题思路&#xff1a; 在进行网站安全检查时&#xf…

网络协议四 物理层,数据链路层

从这一节开始学习 五层模型。学习方法是从最底层物理层开始学习 七层模型 五层模型 各个层用的协议&#xff0c;以及加上协议后的称谓 各个层的作用 应用层&#xff1a;可以认为是原始数据&#xff0c;该数据称为 报文&#xff0c;用户数据。 运输层&#xff1a;也叫传输层&am…

全网超详细攻略——LVS原理详解及部署

目录 一、LVS原理 1.LVS简介 2.LVS结构 3.IP负载均衡技术 4.LVS相关术语 二、LVS负载均衡四种工作模式 1.LVS-DR模式 2.LVS-NAT模式 3.LVS-TUN模式&#xff08;了解&#xff09; 4.FULL-NAT模式&#xff08;了解&#xff09; 三、LVS负载均衡十种调度算法 四、LVS部…

米思奇安装——Mac版本

米思奇安装——Mac版本 1.下载 访问米思奇官网https://mixly.org/bnu-maker/mixl2.0rc 打开官网后在首页点击导航栏的软件平台&#xff0c;选择Mixly离线版 点击Mixly2.0RC4发布下载。 进入百度网盘分享的文件&#xff0c;选择Mac一键更新版本&#xff0c;等待下载完成。 …

尚品汇-ES(三十一)

目录&#xff1a; &#xff08;1&#xff09;封装搜索相关实体对象 &#xff08;2&#xff09;搜索接口封装 &#xff08;3&#xff09;在service-list-client模块添加远程接口 &#xff08;1&#xff09;封装搜索相关实体对象 搜索参数实体&#xff1a;SearchParam 搜索参…

第七节 流编辑器sed(stream editor)(7.1)

一,sed简介 sed是一种流编辑器,处理时,把当前处理的行存储在临时缓冲区中,称为模式空间,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,接着处理下一行,这样不断重复,直到文件末尾,文件内容并没有改变 二,sed的语法 2,1,基本语法 sed options ... […

AI学习记录 - gpt如何进行token化,理论知识,以GPT2为举例

AI学习记录已经发了十几篇&#xff0c;大佬们可以看看&#xff0c;如果有帮助动动小手点赞 token入门版&#xff0c;有空会更新具体代码操作 GPT4当中&#xff0c;我们提问问题是按照token进行扣费的&#xff0c;那到底什么是token&#xff1f; 在不同的语言模型当中&#x…

gradio之进度条

输出控件显示进度&#xff0c;进度结束显示控件结果 import gradio as gr import timedef slowly_reverse(word, progressgr.Progress()):progress(0, desc"Starting")time.sleep(1)progress(0.05)new_string ""for letter in progress.tqdm(word, desc&…

C++ 特性之vector详解 + 联合opencv使用

C 特性之vector详解 联合opencv使用 在C中&#xff0c;遍历vector并删除元素需要小心处理迭代器失效的问题。通常推荐的方法是使用迭代器进行遍历&#xff0c;并在需要删除元素时使用erase函数。这里给出一个示例代码&#xff0c;演示如何安全地遍历vector并删除特定条件的元…

计算机毕业设计 家电销售展示平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…