SpringBoot整合Websocket(Java websocket怎么使用)

目录

    • 1 Websocket是什么
    • 2 Websocket可以做什么
    • 3 Springboot整合Websocket
      • 3.1 服务端
      • 3.2 客户端

1 Websocket是什么

WebSocket 是一种基于 TCP 协议的全双工通信协议,可以在浏览器和服务器之间建立实时、双向的数据通信。可以用于在线聊天、在线游戏、实时数据展示等场景。与传统的 HTTP 协议不同,WebSocket 可以保持长连接,实时传输数据,避免了频繁的 HTTP 请求和响应,节省了网络带宽和服务器资源,提高了应用程序的性能和用户体验。

2 Websocket可以做什么

项目中大部分的请求都是前台主动发送给后台,后台接收后返回数据给前台,返回数据后这个连接就终止了。如果要实现实时通信,通用的方式是采用 HTTP 协议

不断发送请求。但这种方式即浪费带宽(HTTP HEAD 是比较大的),又消耗服务器 CPU 占用(没有信息也要接受请求)。

websocket可以建立长连接实现双向通信,客户端和服务端都可以主动的向对方发送消息

例如:

假设张三今天有个快递快到了,但是张三忍耐不住,就每隔十分钟给快递员或者快递站打电话,询问快递到了没,每次快递员就说还没到,等到下午张三的快递到了,但是,快递员不知道哪个电话是张三的,(可不是只有张三打电话,还有李四,王五),所以只能等张三打电话,才能通知他,你的快递到了。

而最好的情况是,张三给快递员第一次打电话时,说明自己的身份,快递员记录下来,让自己和快递员之间形成一对一的关系可以互相联系到。张三也不用再次给快递员打电话了,快递到了快递员会主动联系张三通知他来取。

后者就是websocket模式,在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实现了“真·长链接”,实时性优势明显。
在这里插入图片描述

在项目中聊天功能也是类似的逻辑,A发送了消息B立刻就要收到,A和B都属于前台客户端,不可能直接从一个前台不走服务器传输给另一个前台,过程一定是前台 —> 服务器 -> 前台。那前台客户端B接收消息是被动的,需要服务器主动发送消息请求,这就用到了WebSocket。大体流程如下图:
在这里插入图片描述

3 Springboot整合Websocket

3.1 服务端

  1. 添加依赖

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    
  2. 添加Websocket配置文件

    @Configuration
    public class WebSocketConfig {/***     注入ServerEndpointExporter,*     这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
    }
    
  3. Webscoket操作类

    import org.springframework.stereotype.Component;
    import javax.websocket.*;
    import javax.websocket.server.PathParam;
    import javax.websocket.server.ServerEndpoint;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.CopyOnWriteArraySet;
    import java.util.logging.Logger;
    import static java.util.logging.Level.WARNING;@Component
    @ServerEndpoint("/websocket/{userId}")  // 接口路径 ws://localhost:9001/webSocket/userId;
    public class WebSocket {private static final Logger log = Logger.getLogger(WebSocket.class.getName());//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 用户ID*/private String userId;//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。//虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。//  注:底下WebSocket是当前类名private static CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();// 用来存在线连接用户信息private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();/*** 链接成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam(value="userId")String userId) {try {this.session = session;this.userId = userId;webSockets.add(this);sessionPool.put(userId, session);log.info("【websocket消息】有新的连接,总数为:"+webSockets.size());} catch (Exception e) {}}/*** 链接关闭调用的方法*/@OnClosepublic void onClose() {try {webSockets.remove(this);sessionPool.remove(this.userId);log.info("【websocket消息】连接断开,总数为:"+webSockets.size());} catch (Exception e) {}}/*** 收到客户端消息后调用的方法** @param message*/@OnMessagepublic void onMessage(String message) {log.info("【websocket消息】收到客户端消息:"+message);}/** 发送错误时的处理* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.log(WARNING,"用户错误,原因:"+error.getMessage());error.printStackTrace();}/***	下面为服务端向客户端发送消息*/// 此为广播消息public void sendAllMessage(String message) {log.info("【websocket消息】广播消息:"+message);for(WebSocket webSocket : webSockets) {try {if(webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {e.printStackTrace();}}}// 此为单点消息public void sendOneMessage(String userId, String message) {Session session = sessionPool.get(userId);if (session != null&&session.isOpen()) {try {log.info("【websocket消息】 单点消息:"+message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}// 此为单点消息(多人)public void sendMoreMessage(String[] userIds, String message) {for(String userId:userIds) {Session session = sessionPool.get(userId);if (session != null&&session.isOpen()) {try {log.info("【websocket消息】 单点消息:"+message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}
    }
    

    上面的操作类中有几个主要的点需要注意

    • @ServerEndpoint注解:注解的value属性为调用时路径。类似于@RequestMapping("")设置的路径。添加该注解,才会被注册。
    • @OnOpen:链接成功调用的方法。
    • @OnMessage:客户端可以主动给服务端发送消息,此方法接受数据并处理。
    • @OnError:发送错误时的处理。
  4. 服务端主动向客户端发送消息

    测试用例

    @Resource
    private WebSocket webSocket;@GetMapping("sendMessage")
    public AjaxResult queryById(@Validated @NotNull Long id){//创建业务消息信息JSONObject obj = new JSONObject();obj.put("msgId", "00000001");//消息idobj.put("msgTxt", "服务端->客户端发送消息");//消息内容//全体发送webSocket.sendAllMessage(obj.toJSONString());//单个用户发送 (userId为用户id)//webSocket.sendOneMessage(userId, obj.toJSONString());//多个用户发送 (userIds为多个用户id,逗号‘,’分隔)//webSocket.sendMoreMessage(userIds, obj.toJSONString());return AjaxResult.success("执行成功");
    }
    

3.2 客户端

<script>
export default {name: "index",data() {return {websock:null};},mounted() {//初始化websocketthis.initWebSocket()},destroyed: function () {//关闭连接this.websocketclose();},methods: {initWebSocket: function () { // 建立连接// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于httpsvar userId = "user-001";var url = "ws://localhost:9001/websocket/" + userId;this.websock = new WebSocket(url);this.websock.onopen = this.websocketonopen;this.websock.onerror = this.websocketonerror;this.websock.onmessage = this.websocketonmessage;this.websock.onclose = this.websocketclose;},// 连接成功后调用websocketonopen: function () {console.log("WebSocket连接成功");},// 发生错误时调用websocketonerror: function (e) {console.log("WebSocket连接发生错误");},// 接收后端消息websocketonmessage: function (e) {console.log("eee",e)var data = eval("(" + e.data + ")"); },// 关闭连接时调用websocketclose: function (e) {console.log("connection closed (" + e.code + ")");},//向后台发送消息sendMessage(){let params = {id:"00000",msg:"前端消息测试"}let a = JSON.stringify(params);this.websock.send(a)},}
</script>

websockke中内置了连接、错误、接收消息、接收消息、关闭的回调,下面自己定义的websocketonopen、websocketonmessage等方法的名字可以随便起,但需要在初始化时赋值给websocket对应的属性。

属性事件处理回调函数描述
onopenwebsocketonopen建立连接时触发
onerrorwebsocketonerror通信发生错误时触发
onmessagewebsocketonmessage客户端接收服务端消息时触发
onclosewebsocketclose连接关闭触发
send不需要回调函数,建议直接调用websocket的send方法

下面测试下完整流程

  • 创建连接

    ws同http,wss同https,后面路径为服务段@ServerEndpoint注解的值,以此选择连接不同连接端。
    在这里插入图片描述

  • 前台客户端主动发送消息给服务端

    调用websock.send()方法,但消息的类型需要注意,socket本质是传输字节流,所以不能把任意类型的数据直接传入send方法,限制类型如下:
    在这里插入图片描述

    等接收到数据以后通过IO包装类都可以把数据还原。

    服务端成功接收到消息:
    在这里插入图片描述

  • 服务端主动向客户端发送消息

    客户端成功接收
    在这里插入图片描述
    在这里插入图片描述

    可以看到是一个请求长连接:

    • 绿色向上的箭头是客户端发送给服务端
    • 红色向下的箭头是服务端发送给客户端

上述测试的流程如下:
在这里插入图片描述

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

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

相关文章

算法通关村第十七关:青铜挑战-贪心其实很简单

青铜挑战-贪心其实很简单 1. 难以解释的贪心算法 贪心学习法则&#xff1a;直接做题&#xff0c;不考虑贪不贪心 贪心(贪婪)算法 是指在问题尽心求解时&#xff0c;在每一步选择中都采取最好或者最优&#xff08;最有利&#xff09;的选择&#xff0c;从而希望能够导致结果最…

【爬虫笔记】Python爬虫简单运用爬取代理IP

一、前言 近些年来&#xff0c;网络上的爬虫越来越多&#xff0c;很多网站都针对爬虫进行了限制&#xff0c;封禁了一些不规则的请求。为了实现正常的网络爬虫任务&#xff0c;爬虫常用代理IP来隐藏自己的真实IP&#xff0c;避免被服务器封禁。本文将介绍如何使用Python爬虫来…

百度智能云千帆大模型丨未来人手必备的代码助手

文章目录 1. 前言2. 千帆大模型平台3. 十分友好的功能4. comate代码助手5. 总结 1. 前言 我之前给大家推荐过Poe这个网站&#xff0c;它用的人比较少&#xff0c;但一旦接触后会发现它其实挺强大的。 因为它是一个可以同时支持好几个大模型的在线聚合平台。常用的GPT4&#x…

基于阻塞队列的生产消费模型

目录 一、线程同步 1.生产消费模型&#xff08;或生产者消费者模型&#xff09; 2.认识同步 &#xff08;1&#xff09;生产消费模型中的同步 &#xff08;2&#xff09;生产者消费者模型的特点 二、条件变量 1.认识条件变量 2.条件变量的使用 3.代码改造 三、基于阻…

uniapp移动端h5设计稿还原

思路 动态设置html的font-size大小 实现步骤 先创建一个public.css文件&#xff0c;设置初始的font-size大小 /* 注意这样写 只能使用css文件, scss 是不支持的, setProperty 只适用于原生css上 */ html {--gobal-font-size: 0.45px; } .gobal-font-size {font-size: var(--g…

leetcode 655. 输出二叉树(java)

输出二叉树 题目描述代码演示 题目描述 难度 - 中等 leetcode 655. 输出二叉树 给你一棵二叉树的根节点 root &#xff0c;请你构造一个下标从 0 开始、大小为 m x n 的字符串矩阵 res &#xff0c;用以表示树的 格式化布局 。构造此格式化布局矩阵需要遵循以下规则&#xff1a…

Python接口自动化封装导出excel方法和读写excel数据

一、首先需要思考&#xff0c;我们在页面导出excel&#xff0c;用python导出如何写入文件的 封装前需要确认python导出excel接口返回的是一个什么样的数据类型 如下&#xff1a;我们先看下不对返回结果做处理&#xff0c;直接接收数据类型是一个对象&#xff0c;无法获取返回值…

IOC和注解

想要学好spring&#xff0c;必须时时刻刻想着&#xff0c;spring的本质就是一个容器&#xff0c;放java对象的容器&#xff0c;java对象在spring容器中也叫做bean对象。 文章目录 一、spring介绍1、什么是框架2、框架的作用![在这里插入图片描述](https://img-blog.csdnimg.cn…

这几招真管用!找回丢失的iPhone的好方法!

你昂贵的iPhone不见了。它丢了吗?它被偷了吗?如果你把iPhone弄丢了,你可以从各种其他来源找到它,包括iPad、Mac、iCloud和Apple Watch。 你可以使用iCloud网站上的苹果“查找我的”应用程序、你的任何其他苹果设备或你家人注册的设备来追踪它。或者从“查找我的”应用程序…

Java基础知识点汇总

一、Java基础知识点整体框架 详细知识点见链接资源&#xff0c;注&#xff1a;框架是用Xmind App完成&#xff0c;查看需下载。 二、基础知识各部分概况 2.1 认识Java 2.2 数据类型和变量 2.3 运算符 2.4 程序逻辑控制 2.5 方法的使用 2.6 数组的定义和使用 2.7 类和对象 2.8 …

移植STM32官方加密库STM32Cryptographic

感谢这位博主&#xff0c;文章具有很高的参考价值&#xff1a; STM32F1做RSA&#xff0c;AES数据加解密&#xff0c;MD5信息摘要处理_我以为我爱了的博客-CSDN博客 概述 ST官方在很多年前就推出了自己的加密库&#xff0c;配合ST芯片用起来非常方便&#xff0c;支持ST的所有…

借助CIFAR10模型结构理解卷积神经网络及Sequential的使用

CIFAR10模型搭建 CIFAR10模型结构 0. input : 332x32&#xff0c;3通道32x32的图片 --> 特征图(Feature maps) : 3232x32即经过32个35x5的卷积层&#xff0c;输出尺寸没有变化&#xff08;有x个特征图即有x个卷积核。卷积核的通道数与输入的通道数相等&#xff0c;即35x5&am…

SpringCloud(十)——ElasticSearch简单了解(一)初识ElasticSearch和RestClient

文章目录 1. 初始ElasticSearch1.1 ElasticSearch介绍1.2 安装并运行ElasticSearch1.3 运行kibana1.4 安装IK分词器 2. 操作索引库和文档2.1 mapping属性2.2 创建索引库2.3 对索引库的查、删、改2.4 操作文档 3. RestClient3.1 初始化RestClient3.2 操作索引库3.3 操作文档 1. …

网络技术二十二:NATPPP

NAT 转换流程 产生背景 定义 分类 常用命令 PPP PPP会话建立过程 认证 PPP会话流程

第 3 章 栈和队列(循环队列的顺序存储结构实现)

1. 背景说明 和顺序栈相类似&#xff0c;在队列的顺序存储结构中&#xff0c;除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外&#xff0c; 尚需附设两个指针 front 和 rear 分别指示队列头元素及队列尾元素的位置。约定&#xff1a;初始化建空队列时&#x…

qt nodeeditor编译安装

目录 1. 下载源码 2. Qt creator编译源码 2.1 编译debug模式 &#xff08;MinGW&#xff09; 2.2 编译release模式 &#xff08;MinGW&#xff09; 1. 下载源码 https://github.com/paceholder/nodeeditor/archive/refs/tags/3.0.10.zip 2. Qt creator编译源码 解压文件…

Java 数据库改了一个字段, 前端传值后端接收为null问题解决

前端传值后端为null的原因可能有很多种&#xff0c;我遇到一个问题是&#xff0c;数据库修改了一个字段&#xff0c;前端传值了&#xff0c;但是后台一直接收为null值&#xff0c; 原因排查&#xff1a; 1、字段没有匹配上&#xff0c;数据库字段和前端字段传值不一致 2、大…

c语言 4.0

&#x1f482; 个人主页: 程序员爱摸鱼&#x1f91f; 版权: 本文由【程序员爱摸鱼】原创、在CSDN首发、需要转载请联系博主&#x1f4ac; 如果文章对你有帮助、欢迎关注点赞收藏(一键三连)哦&#x1f485; 想寻找共同成长的小伙伴&#xff0c;可以互粉哦 &#x1f4ac;文章目录…

软件架构设计(六) 软件架构风格-MDA(模型驱动架构)

概念 模型驱动架构MDA, 全称叫做Model Driven Architecture。 Model:表示客观事物的抽象表示Architecture:表示构成系统的部件,连接件及其约束的规约Model Driven: 使用模型完成软件的分析,设计,构建,部署和维护等 开发活动MDA起源于分离系统规约和平台实现的思想。之前…

stable diffusion实践操作-复制-清空-保存提示词

系列文章目录 stable diffusion实践操作 stable diffusion实践操作-webUI教程 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、右上生成图标附近按钮介绍1. 箭头介绍&#xff08;复现别人的…