SpringBoot+WebSocket

news2024/11/15 8:32:25

SpringBoot+WebSocket

1.导入依赖:

-- Spring Boot 2.x 使用 javax.websocket

-- Spring Boot 3.x 使用 jakarta.websocket
		
		
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
		
package com.js.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * websocket配置类中
 */
@Configuration
public class WebSocketConfig {
    /**
     *  第一个配置是因为使用springboot内置容器,自己开发时需要配置,如果有独立的容器需要将其注释掉,
     * 也就意味着,如果将项目打成WAR包,部署到服务器,使用Tomcat启动时,需要注释掉ServerEndpointExporter配置;
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    /**
     * MyEndpointConfigure配置是因为我的需求需要,需要在websocket类中注入service层或者dao层的接口,
     * MyEndpointConfigure配置就是为了解决websocket无法注入的问题,如果没有需要可以不用配置
     * @return
     */
    @Bean
    public MyEndpointConfigure newConfigure() {
        return new MyEndpointConfigure();
    }
}

package com.js.httpclenlient;
import com.hy.core.toolkit.util.CollectionUtil;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author
 * @Description: WebSocket服务端代码,包含接收消息,推送消息等接口
 */
@Component
@ServerEndpoint(value = "/socket/{archivecode}")
public class WebSocketServer {

    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static AtomicInteger online = new AtomicInteger();

    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象的session。
    private static Map<Long, List<Session>> sessionPools = new HashMap<>();


    @Resource
    private RemoteAccessClient remoteAccessClient;

    /**
     * 发送消息方法
     * @param sessions 客户端与socket建立的会话
     * @param message 消息
     * @throws IOException
     */
    public void sendMessage(List<Session> sessions, String message) throws IOException{
        if(CollectionUtil.isNotEmpty(sessions)){
            sessions.stream().forEach(
                    session-> {
                        try {
                            session.getBasicRemote().sendText(message);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
            );

        }
    }


    /**
     * 连接建立成功调用
     * @param session 客户端与socket建立的会话
     * @param archivecode 客户端的archivecode
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "archivecode") Long archivecode){
        List<Session> sessions = sessionPools.get(archivecode);
        if(CollectionUtil.isNotEmpty(sessions)){
            sessions.add(session);
            sessionPools.put(archivecode, sessions);
        }else {
            List<Session> sessionList = new ArrayList<>();
            sessionList.add(session);
            sessionPools.put(archivecode, sessionList);
        }

        addOnlineCount();
        System.out.println(archivecode + "加入webSocket!当前人数为" + online);
        try {
            sendMessage( sessions, "欢迎" + archivecode + "加入连接!");
        } catch (IOException e) {
            throw new IllegalArgumentException("webSocket建立连接失败");
        }
    }

    /**
     * 关闭连接时调用
     * @param 关闭连接的客户端的session
     */
    @OnClose
    public void onClose(Session session,@PathParam(value = "archivecode") String archivecode){

        Set<Long> sessionkey = sessionPools.keySet();
        for (Long key : sessionkey) {
            List<Session> sessions = sessionPools.get(key);
            boolean contains = sessions.contains(session);
            if(contains){
                sessions.remove(session);
            }

        }

        sessionPools.remove(archivecode);
        subOnlineCount();
        System.out.println(archivecode + "断开webSocket连接!当前人数为" + online);
    }

    /**
     * 收到客户端消息时触发(群发)
     * @param message
     * @throws IOException
     */

    @OnMessage
    public void onMessage(String message) throws IOException{
        Set<Long> companyIds = sessionPools.keySet();
        for (Long companyid : companyIds) {
            List<Session> sessions = sessionPools.get(companyid);
            try {
                sendMessage(sessions, message);
            } catch(Exception e){
                e.printStackTrace();

            }
        }

    }

    /**
     * 发生错误时候
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session, Throwable throwable){
        System.out.println("发生错误");
        throwable.printStackTrace();
    }

    /**
     * 给指定用户发送消息
     * @param archivecode 用户名
     * @param message 消息
     * @throws IOException
     */
    public void sendInfo(Long archivecode, String message){
        List<Session> sessions = sessionPools.get(archivecode);
        try {
            sendMessage(sessions, message);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 根据用户名获取session会话对象
     * @param archivecode
     * @return
     */
    public  List<Session> getSession(Long archivecode){
        List<Session> sessions = sessionPools.get(archivecode);
        return sessions;
    }

    public static void addOnlineCount(){
        online.incrementAndGet();
    }

    public static void subOnlineCount() {
        online.decrementAndGet();
    }


}

package com.js.controller;


import cn.hutool.json.JSONUtil;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
     *  6.密集架报警信息实时更新获取
     */

    @RequestMapping(value = "/denseRack/alarm", method = RequestMethod.POST)
    public void testSocket1(@RequestBody FilealertInformation dto) throws JsonProcessingException {
        Long archiveCompanyId = ThreadLocalUtil.getArchiveCompanyId();
        dto.setPageRefreshType("warning");
        String s = JSONUtil.toJsonStr(dto);
        webSocketServer.sendInfo(archiveCompanyId, s);
    }

测试发送

在这里插入图片描述

结果

在这里插入图片描述

Spring boot接入websocket时,启动报错

Spring Boot 2.x

javax.websocket.server.ServerContainer not available

Spring Boot 3.x

jakarta.websocket.server.ServerContainer not available

问题原因

1. 因为spring boot内带tomcat,tomcat中的websocket会有冲突。
解决方案:

​ spring boot启动时排除tomcat依赖包即可

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>

@SpringBootTest启动时没有启动servlet

解决方案:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

Spring Boot 与WebSocket API版本不对应

Spring Boot 2.x 使用 javax.websocket

Spring Boot 3.x 使用 jakarta.websocket

解决方案:

<!-- SpringBoot3 引入以下依赖,并删除之前的 javax.websocket依赖 -->
<dependency>
    <groupId>jakarta.websocket</groupId>
    <artifactId>jakarta.websocket-api</artifactId>
    <version>2.1.0</version>
</dependency>

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

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

相关文章

C# WPF上位机开发(业务主流程才是核心)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们说了很多的c# wpf编程技术&#xff0c;里面有控件&#xff0c;有绘图&#xff0c;有数据库&#xff0c;有多线程等技术。但是他们都属于实…

宝塔Linux面板计划任务:文件夹改名方式天天切割日志脚本

新手第一次操作&#xff0c;目测成功且完美&#xff0c;供大家参考 current_time$(date %Y%m%d%H%M%S) old_folder_name"/www/wwwlogs" new_folder_name"/www/wwwlogs_${current_time}" mv "$old_folder_name" "$new_folder_name" m…

Inkscape SVG 编辑器 导入 Gazebo

概述 本教程描述了拉伸 SVG 文件的过程&#xff0c;这些文件是 2D 的 图像&#xff0c;用于在 Gazebo 中为您的模型创建 3D 网格。有时是 更容易在 Inkscape 或 Illustrator 等程序中设计模型的一部分。 在开始之前&#xff0c;请确保您熟悉模型编辑器。 本教程将向您展示如…

Backend - Django 项目创建 运行

目录 一、配置环境 二、创建 Django 项目 &#xff08;一&#xff09;新建文件夹 &#xff08;二&#xff09;打开文件夹 &#xff08;三&#xff09;打开运行终端 &#xff08;四&#xff09;创建基础项目 &#xff08;五&#xff09;创建app 1. 安装Django &#xf…

app上架-您的应用在运行时,未同步告知权限申请的使用目的,向用户索取(相机)等权限,不符合华为应用市场审核标准。

上架提示 您的应用在运行时&#xff0c;未同步告知权限申请的使用目的&#xff0c;向用户索取&#xff08;相机&#xff09;等权限&#xff0c;不符合华为应用市场审核标准。 测试步骤&#xff1a;管理-添加-点击二维码&#xff0c;申请相机权限 修改建议&#xff1a;APP在调…

Gazebo GUI模型编辑器

模型编辑器 现在我们将构建我们的简单机器人。我们将制作一个轮式车辆&#xff0c;并添加一个传感器&#xff0c;使我们能够让机器人跟随一个斑点&#xff08;人&#xff09;。 模型编辑器允许我们直接在图形用户界面 &#xff08;GUI&#xff09; 中构建简单的模型。对于更复…

MyBatis ORM映射

MyBatis只能自动维护库表”列名“与”属性名“相同时的对应关系&#xff0c;二者不同时无法自动ORM 因此需要使用到ORM映射。 共有两种解决办法&#xff1a;1.列的别名 2.结果映射 1.列的别名 在SQL中使用 as 为查询字段添加列别名&#xff0c;以匹配属性名 public List<…

VCG 获取某个顶点的邻接顶点

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 与之前的思路相同,这里我们利用VCG为我们提供的拓扑结构,获取某个顶点的邻接顶点,这在我们处理网格数据时往往很有用。 二、实现代码 //VCG #include <vcg/complex/algorithms/create/platonic.h> #inclu…

Java_集合进阶(Collection和List系列)

一、集合概述和分类 1.1 集合的分类 已经学习过了ArrayList集合&#xff0c;但是除了ArrayList集合&#xff0c;Java还提供了很多种其他的集合&#xff0c;如下图所示&#xff1a; 我想你的第一感觉是这些集合好多呀&#xff01;但是&#xff0c;我们学习时会对这些集合进行…

idea 如何使用 JaCoCo 跑覆盖率

背景介绍 什么代码覆盖&#xff1f; 代码覆盖(Code coverage)是软件测试中的一种度量&#xff0c;描述程序中源代码被测试的比例和程度&#xff0c;所得比例称为代码覆盖率。简单来理解&#xff0c;就是单元测试中代码执行量与代码总量之间的比率。 Java常用的单元测试覆盖率…

麒麟V10 ARM 离线生成RabbitMQ docker镜像并上传Harbor私有仓库

第一步在外网主机执行&#xff1a; docker pull arm64v8/rabbitmq:3.8.9-management 将下载的镜像打包给离线主机集群使用 在指定目录下执行打包命令&#xff1a; 执行&#xff1a; docker save -o rabbitmq_arm3.8.9.tar arm64v8/rabbitmq:3.8.9-management 如果懒得打包…

Java开发框架和中间件面试题(1)

1.什么是Spring框架&#xff1f; Spring是一种轻量级框架&#xff0c;旨在提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合&#xff0c;使用这些模块可以很方便的协助我们进行开发。这些模块是核心容器、数据访…

comfyUI + animateDiff video2video AI视频生成工作流介绍及实例

原文&#xff1a;comfyUI animateDiff video2video AI视频生成工作流介绍及实例 - 知乎 目录 收起 前言 准备工作环境 comfyUI相关及介绍 comfyUI安装 生成第一个视频 进一步生成更多视频 注意事项 保存为不同的格式 视频宽高设置 种子值设置 提示词与负向提示词…

【漏洞复现】奥威亚 教学视频应用服务平台任意文件上传漏洞

漏洞描述 AVA 教学视频应用服务平台是由广州市奥威亚电子科技有限公司基于当前教育视频资源建设的背景及用户需求的调研,开发出来能够适应时代发展和满足学校需求,具有实效性、多功能、特点鲜明的平台。 该平台存在任意文件上传漏洞,通过此漏洞攻击者可上传webshell木马,…

【Python-批量修改视频分辨率】

Python-批量修改视频分辨率 1 使用Python修改视频分辨率2 常见的视频编码格式2.1 等效的编码格式表示方式2.2 常见的编码格式 1 使用Python修改视频分辨率 首先拷贝视频文件并修改后缀&#xff0c;然后修改图片的分辨率&#xff0c;实现视频批量修改和转换。 import os impor…

npm安装依赖报错ERESOLVE unable to resolve dependency tree(我是在taro项目中)(node、npm 版本问题)

换了电脑之后新电脑安装包出错 &#x1f447;&#x1f447;&#x1f447; npm install 安装包报错 ERESOLVE unable to resolve dependency tree 百度后尝试使用 npm install --force 还是报错 参考 有人说是 node 版本和 npm 版本的问题 参考 新电脑 node版本&#xff1a;16.1…

在Windows系统平台下部署运行服务端Idea工程的jar服务

前言 目前云原生docker等技术&#xff0c;加上部署流水线大大的简化了各种流程&#xff0c;我们后端开发的人员只需要提交代码后&#xff0c;构建、部署、测试、发布等环节都无需人员接入&#xff0c;完全的自动化交付了。那么你肯定不禁想问&#xff0c;如题的需求不是点击一…

如何用 CleanMyMac 来保护 Mac 隐私

大家早上好&#xff0c;中午好&#xff0c;下午好&#xff0c;晚上好。 在我们使用MacBook上的自带浏览器-Safari&#xff08;或者一些其他浏览器&#xff09;进行网页浏览的时候&#xff0c;往往会留下一些痕迹。如果这些痕迹涉及一些敏感数据信息的话&#xff0c;那么我们肯…

Python 爬虫之下载视频(一)

爬取某平台视频 文章目录 爬取某平台视频前言一、大致内容二、基本思路三、代码编写1.引入库2.前期准备3.获取视频标题和地址3.下载保存视频 总结 前言 今天写个从好K视频平台爬取正在播放的视频&#xff0c;并下载保存到本地。 注意&#xff1a;建议大家先看看我之前的比较简…

【MySQL】mysql执行查询超过30秒之后报错

出现场景&#xff1a; 用workbench&#xff0c;查本地数据库&#xff0c;执行查询超过30秒之后&#xff0c;会报错断开连接&#xff0c;报错信息&#xff1a;Error Code: 2013. Lost connection to MySQL server during query 解决办法&#xff1a; 修改配置设置&#xff0c…