SpringBoot集成WebSocket(实时消息推送)

🍓 简介:java系列技术分享(👉持续更新中…🔥)
🍓 初衷:一起学习、一起进步、坚持不懈
🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏
🍓 希望这篇文章对你有所帮助,欢迎点赞 👍 收藏 ⭐留言 📝

🍓 更多文章请点击
在这里插入图片描述在这里插入图片描述

文章目录

  • 一、WebSocket是什么?
    • 1.1 原理解析:
  • 二、客户端开发
  • 三、服务端开发
    • 3.1 引入依赖
    • 3.2 添加配置类
    • 3.3 效验Token(非必选)
    • 3.4 代码实现
    • 3.5 服务端推送消息给客户端
  • 四、常见问题
    • 4.1 在添加有@ServerEndpoint的类中,可以使用@Autowired注入对象?
    • 4.2 为什么使用ConcurrentHashMap?
    • 4.3 项目通过Nginx部署,为什么前端访问不通呢?
    • 4.4 异常The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method
    • 4.5 webSocket java.io.EOFException: null 增加心跳检测

是

一、WebSocket是什么?

调试工具:http://coolaf.com/tool/chattest

WebSocket 是一种在单个 TCP 连接上进行全双工通信的网络协议。它提供了一个持久的连接,允许客户端和服务器之间进行实时数据传输。相比传统的 HTTP 请求-响应模式,WebSocket 允许服务器在没有收到请求的情况下主动向客户端发送数据,从而实现了更高效的实时通信。

全双工:允许谁在两个方向上的同时传输。

半双工: 允许数据在两个方向上传输,但是同一个时间段内只允许一个方向上传输。


1.1 原理解析:

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

  • 客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;
  • 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;
  • 最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。

二、客户端开发

这里简单介绍

    <script>
    		//对象创建  url格式:ws://ip地址/访问罗静
   		 ws = new WebSocket("ws://127.0.0.1:9090/");
   		 //建立连接时触发
			ws.onopen = function () {
				//发送消息给服务端
				ws.send(};
			};
			//连接关闭时触发
			ws.onclose = function () {};
			//收到消息时触发
			ws.onmessage = function (ev) {};
			//发生错误时触发
			ws.onerror = function (event){}

    </script>

三、服务端开发

3.1 引入依赖

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-websocket</artifactId>
       </dependency>

3.2 添加配置类

扫描添加有@ServerEndpoint注解的bean


@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }
 }

3.3 效验Token(非必选)

  1. 添加配置器
    这里可以主动向前端发送特定类型消息,前端接收后抛出异常
    @Slf4j
    public class AuthConfig extends ServerEndpointConfig.Configurator {
        @Override
        public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
            List<String> authorization = request.getHeaders().get("Authorization");
            log.info("这里进行效验逻辑");
            log.info("============Authorization======= :{}",authorization);
            super.modifyHandshake(sec, request, response);
        }
    }
    
  2. 引入配置
    在@ServerEndpoint(value = “/chat/{userId}”, configurator = AuthConfig.class)

3.4 代码实现

  • @ServerEndpoint每个用户对应自己的Endpoint
  • @PathParam获取路径参数
@Slf4j
@Component
@ServerEndpoint(value = "/chat/{userId}", configurator = AuthConfig.class)
public class WebChatServer1 {

    private static final Map<Long, Session> onlineUsers = new ConcurrentHashMap<>();

    /**
     * 连接建立时触发
     */
    @OnOpen
    public void openSession(Session session, @PathParam("userId") Long userId) {
        log.info("用户:{} 上线了,sessionId:{}", userId, session.getId());
        if (onlineUsers.containsKey(userId)) {
            //当前用户可能更换客户端
            onlineUsers.remove(userId);
            onlineUsers.put(userId, session);
        } else {
            onlineUsers.put(userId, session);
        }
    }


    /**
     * 客户端发送消息到服务端,该方法被调用
     * <p>
     * 张三--->李四
     */
    @OnMessage
    public void onMessage(String message, @PathParam("userId") Long userId) {
        log.info("收到的消息为:{}", message);

    }


    /**
     * 连接关闭时触发
     */
    @OnClose
    public void onClose(Session session, @PathParam("userId") Long userId) {
        try {
            log.info("用户 :{}==============离线", userId);
            //关闭WebSocket Session会话
            onlineUsers.remove(userId);
            session.close();
        } catch (IOException e) {
            log.error("onClose error", e);
        }
    }

    /**
     * 通信发生错误时触发
     */
    @OnError
    public void onError(Session session, @PathParam("userId") Long userId, Throwable throwable) {
        try {
            //关闭WebSocket Session会话
            onlineUsers.remove(userId);
            session.close();
        } catch (Exception e) {
            log.info("捕获到异常:{}", e);
        }
    }
}

3.5 服务端推送消息给客户端

  1. 同步
    session.getBasicRemote().sendText();
    
  2. 异步
    session.getAsyncRemote().sendText();
    

四、常见问题

4.1 在添加有@ServerEndpoint的类中,可以使用@Autowired注入对象?

@Autowired注解通常用于将Spring容器中的bean自动装配到相应的字段中。然而,WebSocket处理程序通常不会通过Spring的依赖注入,因为WebSocket处理程序通常不是由Spring容器管理的bean。

  1. 解决方案一

    SpringContextUtil .getBean(SocketUtils.class);
    

    实现具体的通知类(生命周期)

    @Component
    public class SpringContextUtil implements ApplicationContextAware {
    
        private static ApplicationContext applicationContext;
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            if (SpringContextUtil.applicationContext == null) {
                SpringContextUtil.applicationContext = applicationContext;
            }
        }
    
        //获取applicationContext
        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }
    
        //通过name获取 Bean.
        public static Object getBean(String name) {
            return getApplicationContext().getBean(name);
        }
    
        //通过class获取Bean.
        public static <T> T getBean(Class<T> clazz) {
            return getApplicationContext().getBean(clazz);
        }
    
        //通过name,以及Clazz返回指定的Bean
        public static <T> T getBean(String name, Class<T> clazz) {
            return getApplicationContext().getBean(name, clazz);
        }
    
    }
    
  2. 解决方案二
    在配置类中注入

    @Configuration
    public class WebSocketConfig {
    
        @Bean
        public ServerEndpointExporter serverEndpointExporter(){
            return new ServerEndpointExporter();
        }
    
        @Autowired
        public void socketUserService(SocketUtils socketUtils){
            WebChatServer.socketUtils = socketUtils;
        }
    }
    

4.2 为什么使用ConcurrentHashMap?

本文未使用集群配置

	/**
	 * 在线用户
	 */
	private static final Map<String,Session> onlineUsers = new ConcurrentHashMap<>();

在这里插入图片描述

4.3 项目通过Nginx部署,为什么前端访问不通呢?

在Nginx的对应端口的server块中添加如下配置

		location /ws {
                 proxy_pass http://127.0.0.1:10010/;
                 proxy_http_version 1.1;
                 proxy_set_header Upgrade $http_upgrade;
                 proxy_set_header Connection "upgrade";
                 proxy_set_header Host $host;
           }

4.4 异常The remote endpoint was in state [TEXT_FULL_WRITING] which is an invalid state for called method

原因是多个线程同时使用同一session发送的原因。进行如下更改

	synchronized(session){
	    session.getBasicRemote().sendText(message);
}

4.5 webSocket java.io.EOFException: null 增加心跳检测

webSocket连接,经常自动断开,有如下原因

  • 网络问题: 不稳定的网络连接可能导致 WebSocket 连接断开。这可能是由于网络延迟、断网或者服务器端出现网络问题等引起的。
  • 服务器配置问题: 如果服务器配置不正确,可能会导致 WebSocket 连接断开。

这里为了webSocket连接正常,增加心跳检测逻辑

  1. 客户端定时发送指定字符串.例:"ping"
  2. 服务端收到后回复"pong"
  3. 当客户端在指定时间没有收到"pong"时,重新连接
    @OnMessage
    public void onMessage(String message, @PathParam("userId") Long userId) {
        log.info("收到的消息为:{}", message);
        if(Objects.equals("ping",message)){
            //心跳
            socketUtils.sendTextTo(userId,"pong");
        }else{
        	log.info("业务逻辑")
        }

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习 - 神经网络分类

什么叫做分类问题&#xff1f; A classification problem involves predicting whether something is one thing or another. Problem typeWhat is it?ExampleBinary classificationTarget can be one of two options, e.g. yes or noPredict whether or not someone has hea…

UDP实现聊天室

现象&#xff1a; 源码&#xff1a; 服务器&#xff1a; #include<myhead.h>struct sockaddr_in serveraddr,caddr; enum type_t//枚举 {Login,Chat,Quit, }; typedef struct MSG {char type;//L C Qchar name[32];//char text[128];// }msg_t;typedef struct NODE//链…

【C语言基础】:内存操作函数

文章目录 一、memcpy函数的使用和模拟实现1.1 memcpy函数的使用1.2 memcpy函数的模拟实现 二、memmove函数的使用和模拟实现2.1 memmove函数的使用2.2 memmove函数的模拟实现 三、memset函数的使用3.1 menset函数的使用 四、memcmp函数的使用4.1 memcmp函数的使用 学海无涯苦作…

HarmonyOS模拟器调试

1 、设置 -> 系统设置 -> 关于手机 快速点击 5 次 HarmonyOS 版本开启开发者模式。 2 、设置 -> 系统和更新 -> 开发人员选项 到开发人员选项后往下拉有 USB 调试 &#xff0c;把 USB 调试开关打开。 源自&#xff1a;HarmonyOS HarmonyOS Next 仿小米商城App入门…

Prompt提示工程上手指南:基础原理及实践(四)-检索增强生成(RAG)策略下的Prompt

前言 此篇文章已经是本系列的第四篇文章&#xff0c;意味着我们已经进入了Prompt工程的深水区&#xff0c;掌握的知识和技术都在不断提高&#xff0c;对于Prompt的技巧策略也不能只局限于局部运用而要适应LLM大模型的整体框架去进行改进休整。较为主流的LLM模型框架设计可以基…

iOS - Runtime-isa详解(位域、union(共用体)、位运算)

文章目录 iOS - Runtime-isa详解&#xff08;位域、union&#xff08;共用体&#xff09;、位运算&#xff09;前言1. 位域介绍1.1 思路1.2 示例 - 结构体1.3 示例 - union&#xff08;共用体&#xff09;1.3.1 说明 1.4 结构体 对比 union&#xff08;共用体&#xff09; 2. a…

Selenium 自动化 —— 切换浏览器窗口

更多内容请关注我的 Selenium 自动化 专栏&#xff1a; 入门和 Hello World 实例使用WebDriverManager自动下载驱动Selenium IDE录制、回放、导出Java源码浏览器窗口操作 平时我们在使用浏览器时&#xff0c;通常会打开多个窗口&#xff0c;然后再多个窗口中来回切换&#xf…

[STM32] 使用 STM32CubeMX 创建 STM32 工程模板

STM32CubeMX 创建工程模板 跟着100ASK_STM32F103_MINI用户手册V1.1.pdf第7章的步骤&#xff0c;使用STM32CubeMX为STM32F103C8T6创建工程模板。 点击“ACCESS TO MCU SELECTOR”通过选择芯片创建工程&#xff0c;如下图所示 等待下载完成&#xff0c;如下图所示 在搜索框输入“…

矩阵螺旋输出

问题描述&#xff1a; 所谓螺旋矩阵&#xff0c;顾名思义&#xff0c;就是将矩阵元素以螺旋顺序输出&#xff0c;如图&#xff1a; 解决思路&#xff1a; 由图不难发现&#xff0c;整个螺旋输出过程是一个个左下右上遍历的循环&#xff0c;只是遍历的规模在越变越小&#xff…

【vscode打开多文件夹】

1)将文件夹添加到工作空间中 2)文件夹方式展开 3)最终效果 小技巧&#xff1a; 文件夹的位置不对的话&#xff0c;可以拖动进行调整。

【NLP笔记】预训练+Prompt Tuning新范式之LLM时代(GPT3...)

文章目录 概述GPT3 【参考链接】 一张图总结大语言模型的技术分类、现状和开源情况 大语言模型LLM微调技术&#xff1a;Prompt Tuning A Survey of Large Language ModelsThe Practical Guides for Large Language ModelsGPT3&#xff1a;Language Models are Few-Shot Learner…

鸿蒙开发案例:【图像加载缓存库ImageKnife】

专门为OpenHarmony打造的一款图像加载缓存库&#xff0c;致力于更高效、更轻便、更简单。 简介 OpenHarmony的自研版本&#xff1a; 支持内存缓存&#xff0c;使用LRUCache算法&#xff0c;对图片数据进行内存缓存。支持磁盘缓存&#xff0c;对于下载图片会保存一份至磁盘当…

视频汇聚平台EasyCVR启用图形验证码之后调用login接口的操作方法

视频综合管理平台EasyCVR视频监控系统支持多协议接入、兼容多类型设备&#xff0c;平台可以将区域内所有部署的监控设备进行统一接入与集中汇聚管理&#xff0c;实现对监控区域的实时高清视频监控、录像与存储、设备管理、云台控制、语音对讲、级联共享等&#xff0c;在监控中心…

Oracle 19C RAC集群补丁升级

文章目录 一、补丁包概述二、OPatch检查和更新Grid用户更新OPatchOracle用户更新OPatch 三、验证Oracle Inventory的有效性四、运行 OPatch 冲突检查五、运行opatch命令检查GI HOME下是否有足够的空间六、补丁冲突检测与解决&#xff08;修补程序&#xff09;七、使用root用户应…

聚观早报 | 滴滴2023年Q4营收;微软推广Copilot

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 3月25日消息 滴滴2023年Q4营收 微软推广Copilot 极狐汽车将出口西班牙 华为公开智能驾驶新专利 华为P70系列发布…

数据挖掘终篇!一文学习模型融合!从加权融合到stacking, boosting

模型融合&#xff1a;通过融合多个不同的模型&#xff0c;可能提升机器学习的性能。这一方法在各种机器学习比赛中广泛应用&#xff0c; 也是在比赛的攻坚时刻冲刺Top的关键。而融合模型往往又可以从模型结果&#xff0c;模型自身&#xff0c;样本集等不同的角度进行融合。 数据…

辅助功能IOU(交并比)_3.2

实现两个目标框的交并比候选框在多目标跟踪中的表达方式及相应转换方法 IOU(Intersection over Union)&#xff0c;“交并比”&#xff0c;是计算机视觉和图像处理中常用的一个评价指标&#xff0c;尤其在目标检测任务中用来衡量模型预测的目标框与真实目标框的重合程度。 具体…

GuLi商城-商品服务-API-三级分类-查询-树形展示三级分类数据

1、网关服务配置路由 2、商品服务 3、启动本地nacos&#xff0c;打开nacos地址看nacos服务列表 4、编写VUE <template> <el-tree :data"menus" :props"defaultProps" node-click"handleNodeClick"></el-tree> </template…

计算机网络:物理层 - 传输媒体

计算机网络&#xff1a;物理层 - 传输媒体 物理层基本概念导引型传输媒体同轴电缆双绞线光纤 非引导型传输媒体无线电波微波红外线 物理层基本概念 在计算机网络中用来连接各种网络设备的传输媒体&#xff0c;种类众多&#xff0c;大致可以分为两类&#xff0c;一类是导引型传…

零基础入门数据挖掘系列之「特征工程」

摘要&#xff1a;对于数据挖掘项目&#xff0c;本文将学习应该从哪些角度做特征工程&#xff1f;从哪些角度做数据清洗&#xff0c;如何对特征进行增删&#xff0c;如何使用PCA降维技术等。 特征工程&#xff08;Feature Engineering&#xff09;对特征进行进一步分析&#xf…