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

news2025/1/24 2:19:43

目录

    • 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
         */
        @Bean
        public 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>();
    
        /**
         * 链接成功调用的方法
         */
        @OnOpen
        public 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) {
            }
        }
    
        /**
         * 链接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            try {
                webSockets.remove(this);
                sessionPool.remove(this.userId);
                log.info("【websocket消息】连接断开,总数为:"+webSockets.size());
            } catch (Exception e) {
            }
        }
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message
         */
        @OnMessage
        public void onMessage(String message) {
            log.info("【websocket消息】收到客户端消息:"+message);
        }
    
        /** 发送错误时的处理
         * @param session
         * @param error
         */
        @OnError
        public 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");//消息id
    	obj.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() {
    //初始化websocket
    this.initWebSocket()
  },
  destroyed: function () {
    //关闭连接
    this.websocketclose();
  },
  methods: {
    initWebSocket: function () { // 建立连接
      // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
      var 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.coloradmin.cn/o/965786.html

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

相关文章

【Stable Diffusion安装】支持python3.11 window版

前言 主要的安装步骤是参考B站播放量第一的视频&#xff0c;但是那位阿婆主应该是没有编程经验&#xff0c;只强调使用3.10&#xff0c;而python最新版本是3.11。 理论上来说&#xff0c;只是一个小版本的不同&#xff0c;应该是可以安装成功了。自己摸索了下&#xff0c;挺费…

springboot使用logback配置彩色日志

springboot使用logback配置彩色日志 前言一、logback文件二、效果 前言 应该有很多同学发现&#xff0c;使用了logback以后&#xff0c;我们的控制台日志都变成灰色了&#xff0c;网络上搜到的logback配置大多数没有进行配色&#xff0c;所以会把springboot的默认配色方案给覆盖…

Unity中Shader的帧缓存区Clear(color+Z+stencil)

文章目录 前言一、什么是帧缓冲区二、片段运算三、随机扫描显示器&#xff08;可以按照自定义路径绘制帧&#xff09;四、光栅扫描显示器&#xff08;从左到右&#xff0c;从上到下&#xff0c;依次绘制&#xff09;五、缓冲的方式&#xff1a;单缓冲 和 双缓冲1、单缓冲2、双缓…

认识JVM的内存模型

从上一节了解到整个JVM大的内存区域&#xff0c;分为线程共享的heap&#xff08;堆&#xff09;&#xff0c;MethodArea&#xff08;方法区&#xff09;&#xff0c;和线程独享的 The pc Register&#xff08;程序计数器&#xff09;、Java Virtual Machine Stacks&#xff08;…

2. postgresql并行扫描(1)——pg强制走并行扫描建表及参数配置

转载自&#xff1a;https://developer.aliyun.com/article/700370 1. 参数设置 1.1 postgresql.conf中修改 # 1、总的可开启的WORKER足够大 max_worker_processes 128# 2、所有会话同时执行并行计算的并行度足够大 max_parallel_workers64# 3、单个QUERY中并行计算NODE开…

测试验证平台

测试验证平台 1.功能说明&#xff1a; 模拟智能终端车端数据采集及上报的功能&#xff0c;提供数据管理平台的模拟和验证功能。 2.系统组成&#xff1a; 系统示意图 功能要求&#xff1a; 本地电脑实现Imx6配置功能&#xff0c;能够通过运行不同的脚本&#xff0c;模拟不…

C语言记录程序日志

我们写程序&#xff0c;不可能一次就写的一个bug都没有&#xff0c;必须要不停地修改&#xff0c;有可能自己调试已经没有问题了&#xff0c;发给客户后还是问题很多&#xff0c;这个时候跑到客户处解决问题就不现实了&#xff0c;自己不在还要找到问题的所在&#xff0c;最好的…

质谱技术对蛋白质进行鉴定

参考B站教学视频: 质谱如何鉴定蛋白质_哔哩哔哩_bilibili 针对该视频&#xff0c;别人的 笔记 质谱是一台体重秤&#xff0c;称的不是人&#xff0c;而是分子、原子的体重 不同分子有不同分子量是质谱仪工作的底层逻辑 图片来自&#xff1a;【蛋白组】蛋白质组定量技术的原理和…

如何设计一个好的游戏剧情(Part 1:主题的设定)

提醒&#xff1a;此教程仅仅为作者的一些经验和感悟&#xff0c;非专业教程&#xff0c;若介意请前往网上搜集或者书本查阅相关资料&#xff01; 前言&#xff1a;游戏为什么要有剧情——游戏剧情的重要性 游戏剧情的重要性难以低估。一个精彩的剧情可以让玩家感受到强烈的情感…

ZigBee案例笔记 -- RFID卡片读写(模拟饭卡)

RFID模拟饭卡应用 RFID&#xff08;射频识别技术&#xff09;RFID通讯协议RFID发展历史RFID操作流程说明RFID卡片读写流程RFID寻卡RFID防碰撞RFID选卡RFID卡密验证RFID读卡RFID写卡读写数据流程 RFID饭卡模拟案例驱动代码串口协议饭卡操作案例结果优化建议 RFID&#xff08;射频…

QUdpSocket Class

继承自 QAbstractSocket 类 QUdpSocket类提供UDP套接字。 UDP(用户数据报协议)是一种轻量级、不可靠、面向数据报、无连接的协议。它可以在可靠性不重要的情况下使用。QUdpSocket是QAbstractSocket的一个子类&#xff0c;它允许您发送和接收UDP数据报。 使用这个类最常见的方法…

Laravel 模型1对1关联 1对多关联 多对多关联 ⑩①

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; THINK PHP &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f44…

说说CDN和负载均衡具体是怎么实现的

分析&回答 什么是 CDN CDN (全称 Content Delivery Network)&#xff0c;即内容分发网络。 构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需…

如何让你的jupyter notebook 排版得像Word(Markdown和网页文件写法)

案例背景 很多时候我们在jupyter notebook里面的写代码&#xff0c;画图&#xff0c;但是文字分析什么的写在里面纯文本不好看&#xff0c;需要进行排版&#xff0c;那么就得用markdown的写法&#xff0c;如何还想居中或者更花里胡哨的字体&#xff0c;那就得要网页文件的一些…

【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型(全网首发)

【MATLAB第70期】基于MATLAB的LightGbm(LGBM)梯度增强决策树多输入单输出回归预测及多分类预测模型&#xff08;全网首发&#xff09; 一、学习资料 (LGBM)是一种基于梯度增强决策树(GBDT)算法。 本次研究三个内容&#xff0c;分别是回归预测&#xff0c;二分类预测和多分类预…

终端安全与端点保护:讨论保护终端设备免受恶意软件、恶意链接和其他威胁的方法,包括终端保护工具和实践

第一章&#xff1a;引言 在当今数字化世界中&#xff0c;终端设备如电脑、手机和平板成为我们生活与工作的不可或缺的一部分。然而&#xff0c;随着技术的进步&#xff0c;恶意软件、网络攻击和数据泄露等威胁也不断增加&#xff0c;对终端设备的安全提出了更高的要求。本文将…

面试官:说一下 MyBatis 的一级缓存和二级缓存 ?

目录 1. MyBatis 的缓存机制 2. 为什么不默认开启 MyBatis 的二级缓存 3. MyBatis 如何开启二级缓存 1. MyBatis 的缓存机制 MyBayis 中包含两级缓存&#xff1a;一级缓存和二级缓存 1. 一级缓存是 SqlSession 级别的&#xff0c;是 MyBatis 自带的缓存功能&#xff0c;默认…

用正则清除标记符号

定义方法 clearHtml(str){return str.replace(/<[^>]>/g,) }

【阅读笔记】Graph of Thoughts: Solving Elaborate Problems with Large Language Models

Graph of Thoughts: Solving Elaborate Problems with Large Language Models Website & code: https://github.com/spcl/graph-of-thoughts 作者介绍了Graph of Thought (GoT)&#xff1a;一个具备提高LLM提示能力&#xff0c;超越了思维链或思维树 (ToT) 等范式提供的能…

Docker部署pyspider webui显示页面太小的解决方法

进入docker容器&#xff0c;输入以下指令来获取pyspider的位置 python -c "import pyspider;print(pyspider)"如图所示 然后进入到 /opt/pyspider/pyspider/webui/static 修改debug.min.css vi debug.min.css使用vi的查找命令&#xff0c;然后回车。即可找到该样…