SpringBoot+WebSocket

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>

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

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

相关文章

git基础命令(小白适合看)

作者&#xff1a;爱塔居 欢迎大佬指正 1.git add 跟上文件地址 要注意斜杆&#xff0c;然后文件地址就是我们修改文件的地址。 就比如git add xx/xx/xx.x&#xff0c;记得加后缀&#xff0c;然后如果是几个文件&#xff0c;就加空格 例如 git add xx/xx/xx.x yy/yy/yy.y 2.…

机器翻译:跨越语言边界的智能大使

导言 机器翻译作为人工智能领域的瑰宝&#xff0c;正在以前所未有的速度和精度&#xff0c;为全球沟通拓展新的可能性。本文将深入研究机器翻译的技术原理、应用场景以及对语言交流未来的影响。 1. 简介 机器翻译是一项致力于通过计算机自动将一种语言的文本翻译成另一种语言的…

BearPi Std 板从入门到放弃 - 先天神魂篇(3)(RT-Thread I2C设备 读取光照强度BH1750)

简介 使用BearPi IOT Std开发板及其扩展板E53_SC1&#xff0c; SC1上有I2C1 的光照强度传感器BH1750 和 EEPROM AT24C02&#xff0c; 本次主要就是读取光照强度; 主板: 主芯片: STM32L431RCT6LED : PC13 \ 推挽输出\ 高电平点亮串口: Usart1I2C使用 : I2C1E53_SC1扩展板 : LE…

Linux服务器性能优化小结

文章目录 生产环境监测常见专业名词扫盲服务器平均负载服务器平均负载的定义如何判断平均负载值以及好坏情况如果依据平均负载来判断服务器当前状况系统平均负载和CPU使用率的区别 CPU上下文切换基本概念3种上下文切换进程上下文切换线程上下文切换中断上下文切换 查看上下文切…

视频号链接提取器详细使用指南,教你轻松下载号视频!

视频号下载提取器的使用方法会因不同工具而略有差异&#xff0c;但大体上可以按照以下步骤进行操作&#xff1a; 1. 找到一个适合的视频号下载提取器&#xff1a;可以在微信搜一搜中输入关键词“超级短视频去水印解析助手”&#xff0c;选择进入公众号、获取在线视频下载提取工…

12.18_黑马数据结构与算法笔记Java

目录 thinking:orElse? thinking:map.computerifabsent? thinking&#xff1a;subString&#xff1f; 184 哈希表 问2 解释拆分 185 哈希算法 概述 186 哈希算法 Object.hashCode 187 哈希算法 String.hashCode 188 哈希算法 冲突测试 189 哈希算法 MurmurHash 190…

【轮式移动机器人课程笔记3】移动机器人运动学简介

文章目录 写在前面L3 移动机器人运动学简介3.1 运动学概述3.2 研究机器人运动学的意义3.3 机器人运动的描述3.4 机器人正微分运动学3.5 机器人逆微分运动学3.6 总结 写在前面 前两节课介绍了移动机器人、机械手、类型&#xff0c;本节课重点讲解移动机器人运动学相关知识&…

hive的分区表和分桶表详解

分区表 Hive中的分区就是把一张大表的数据按照业务需要分散的存储到多个目录&#xff0c;每个目录就称为该表的一个分区。在查询时通过where子句中的表达式选择查询所需要的分区&#xff0c;这样的查询效率会提高很多。 静态分区表基本语法 创建分区表 create table dept_p…

数据安全无阻,轻松远程工作!迅软DSE出差加密指南,让你出差更放心!

文件加密软件是确保内网文件安全使用的重要工具&#xff0c;但在终端脱离内部网络、面对外出或居家办公等情境时&#xff0c;文件加密的挑战也相应增加。为解决这一问题&#xff0c;迅软DSE文件加密软件提供了离线授权功能&#xff0c;确保在终端脱离公司网络后的设定时间内&am…

使用Docker运行Nacos并安装cpolar内网穿透工具实现远程访问

文章目录 1. Docker 运行Nacos2. 本地访问Nacos3. Linux安装Cpolar4. 配置Nacos UI界面公网地址5. 远程访问 Nacos UI界面6. 固定Nacos UI界面公网地址7. 固定地址访问Plik Nacos是阿里开放的一款中间件,也是一款服务注册中心&#xff0c;它主要提供三种功能&#xff1a;持久化…

openwrt 搭建web

折腾 软路由 有几年了&#xff0c;最近试了下 移动的 IPV6, 既然可以拿到 公网的 IPV6&#xff0c; 所以想折腾下, 经过不懈努力 实现了&#xff1a;通过 ipv4/ipv6 地址访问我的 web站点 (白飘不花钱的方式) 1 动态DNS 折腾 DDNS 无非是想 白飘 公网IP&#xff0c;但是 仅仅…

一个企业为什么要数字化转型?答案在这里!

一个企业为什么要数字化转型&#xff1f; 先简单说说原因。 因为很多行业现在存在大量的产能过剩、产品过剩、服务过剩&#xff0c;经营维度低、行业竞争激烈......企业生存困难&#xff0c;必须改变经营维度才能活下来&#xff0c;才能变现。 单方面举个例子&#xff0c;可…

windows如何环境搭建属于自己的Zblog博客并发布上线公网访问?

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

LibreNMS:从docker出发

引言 LibreNMS 是一个免费开源的网络监控和自动化工具&#xff0c;用于监视网络设备、服务器和应用程序的性能和状态。它提供了一个集中的管理平台&#xff0c;帮助管理员实时监控和管理整个网络基础设施。 以下是 LibreNMS 的一些主要特点和功能&#xff1a; 自动发现&#…

JavaSE 排序

目录 1 概念1.1 排序1.2 稳定性 2 常见基于比较排序算法总览3 插入排序3.1 直接插入排序3.1.1 思想3.1.2 实现3.1.3 性能分析 3.2 折半插入排序3.2.1 思想3.2.2 实现3.2.3 性能分析 3.3 希尔排序3.3.1 思想3.3.2 实现3.3.3 性能分析 4 选择排序4.1 选择排序4.1.1 思想4.1.2 实现…

实验4.1 静态路由的配置

实验4.1 静态路由的配置 一、任务描述二、任务分析三、具体要求四、实验拓扑五、任务实施1.设置交换机和路由器的基本配置。2.使用display ip interface brief命令查看接口配置信息。3.配置静态路由&#xff0c;实现全网互通。 六、任务验收七、任务小结 一、任务描述 某公司刚…

整合SSH(Spring+Struts+Hibernate)

0.前言, 由于工作需要故来学习SSH的整合,structs其实相当于(把view和controller结合起来,没有像现在的前后端分离,请求会发送给Action处理,在structs.xml映射地址和类) Hibernate(就是处理数据库的,几乎自动化,也可以写sql语句) Struts&#xff1a;Struts 是一个基于 MVC&#…

大数据服务与低代码开发:赋能创新与效率的双剑合璧

在科技飞速发展的当下&#xff0c;数据已经渗透到了企业的方方面面&#xff0c;成为了企业决策和业务发展的重要依据。海量数据的处理和分析&#xff0c;对于企业来说既是机遇&#xff0c;也是挑战。传统的数据处理方式往往无法满足快速变化的市场需求&#xff0c;而繁琐的开发…

AI摄影绘画与PS优化:重塑数字艺术的未来

文章目录 《AI摄影绘画与PS优化从入门到精通》内容简介作者简介楚天 目录前言/序言 在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的各个领域&#xff0c;包括艺术创作。AI摄影绘画和Photoshop&#xff08;PS&#xff09;优化是这个领…

Numpy、Pandas常用函数

目录 Numpy 核心功能 常用函数 运算实例 Pandas 核心功能 常用函数 统计汇总函数 数据清洗函数 数据筛选 绘图与元素级函数 时间序列函数 其他函数 Numpy 官方文档&#xff1a;NumPy: the absolute basics for beginners — NumPy v1.26 Manual NumPy&#xff0…