SpringBoot中使用WebSocket Demo

news2025/1/24 2:27:49

大概目录结构

 

依赖只引入了JSP 和SpringBoot整合WebSocket   Spring Web

 

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
    <title>SpringBoot+WebSocket+JSP</title>
</head>

<body style="margin: 45px;">
    <h4>在线聊天室</h4>
    <div class="form-group">
        <label for="content"></label>
        <textarea id="content" readonly="readonly" cols="80" rows="15"></textarea>
    </div>
    <div class="form-group" style="margin-top: 8px">
        <textarea id="message" cols="80" rows="5" placeholder="请输入消息"></textarea>
        <div style="margin-top: 10px">
            <button id="toSend" class="btn btn-info">发送</button>
            <button id="user_exit" class="btn btn-danger">离开</button>
            <input id="username" value="${username}" style="display: none">
        </div>
    </div>

    <script type="text/javascript">
        $(function () {
            var ws;
            if ("WebSocket" in window) {
                var baseUrl = 'ws://localhost:8080/websocket/';
                var userName = $('#username').val();
                ws = new WebSocket(baseUrl + userName);

                // 连通之后的回调事件,建立连接
                ws.onopen = function () {
                    console.log("建立 websocket 连接...");
                };

                // 接收后台服务端的消息
                ws.onmessage = function (event) {
                    $('#content').append(event.data + '\n\n');
                    console.log("接收到服务端发送的消息..." + event.data + '\n');
                };

                ws.onerror = function (event) {
                    console.log("websocket发生错误..." + event + '\n');
                }

                // 连接关闭的回调事件
                ws.onclose = function () {
                    $('#content').append('[' + userName + '] 已离开!');
                    console.log("关闭 websocket 连接...");
                };
            } else {
                // 浏览器不支持 WebSocket
                alert("您的浏览器不支持WebSocket!");
            }

            // 客户端发送消息到服务器
            $('#toSend').click(function () {
                sendMsg();
            });

            $(document).keyup(function (event) {
                // 回车键事件
                if (event.keyCode == 13) {
                    sendMsg();
                }
            });

            // 发送消息
            function sendMsg() {
                //websocket发送消息
                ws.send($('#message').val());
                $('#message').val("");
            }

            // 退出
            $('#user_exit').click(function () {
                if (ws) {
                    ws.close();
                }
            });
        });
    </script>
</body>
</html>

控制器

@Controller
public class ChatController {

    private AtomicInteger idProducer = new AtomicInteger();

    @RequestMapping("/")
    public String index(Model model) {
        model.addAttribute("username","user" + idProducer.getAndIncrement());
        return "index";
    }
}

配置类

@EnableWebSocket //启用WebSocket支持
@Configuration //表示配置类
public class WebSocketConfig {

    /**
     * 配置ServerEndpointExporter的bean
     *
     * 该Bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

/**
 * 说明:
 * 1、@ServerEndpoint注解中指定WebSocket协议的地址;
 * 2、@OnOpen、@OnMessage、@OnClose、@OnError注解与WebSocket中监听事件对应
 *
 **/
@Slf4j //lombok jar包,帮我们自动生成一些代码:@Data
@Component
@ServerEndpoint("/websocket/{username}")
public class ChatServerEndpoint {

    /**
     * 连接建立时触发
     */
    @OnOpen
    public void openSession(@PathParam("username") String username, Session session) {
        log.info("用户{}登录", username);
        String message = "用户[" + username + "] 已进入聊天室!";
        // 发送登录消息给其他人
        WebSocketUtils.sendMessageAll(message);

        // 获取当前在线人数,发给自己
        String onlineInfo = WebSocketUtils.getOnlineInfo();

        //发送消息
        WebSocketUtils.sendMessage(session, onlineInfo);

        // 添加自己到map中
        WebSocketUtils.CLIENTS.put(username, session);
    }

    /**
     * 客户端接收服务端数据时触发
     */
    @OnMessage
    public void onMessage(@PathParam("username") String username, String message) {
        log.info("发送消息:{}, {}", username, message);
        //广播,把消息同步给其他客户端
        WebSocketUtils.sendMessageAll("[" + username + "] : " + message);
    }

    /**
     * 连接关闭时触发
     */
    @OnClose
    public void onClose(@PathParam("username") String username, Session session) {
        // 当前的Session移除某个用户
        WebSocketUtils.CLIENTS.remove(username);

        // 离开消息通知所有人
        WebSocketUtils.sendMessageAll("[" + username + "] 已离开!");

        try {
            //关闭WebSocket Session会话
            session.close();
            log.info("{} 已退出, onclose", username);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("onClose error", e);
        }
    }

    /**
     * 通信发生错误时触发
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        try {
            //关闭WebSocket Session会话
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("onError Exception", e);
        }
        log.info("Throwable msg " + throwable.getMessage());
    }
}

public final class WebSocketUtils {

    private static final Logger logger = LoggerFactory.getLogger(WebSocketUtils.class);

    /**
     * 存储WebSocket session
     * <p>
     * 用户名为key,WebSocket Session对象为value
     */
    public static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>();

    /**
     * 使用连接发送数据
     *
     * @param session 用户session
     * @param message 发送内容
     */
    public static void sendMessage(Session session, String message) {
        if (session == null) {
            return;
        }
        final RemoteEndpoint.Basic basic = session.getBasicRemote();
        if (basic == null) {
            return;
        }
        try {
            //发送
            basic.sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("sendMessage IOException ", e);
        }
    }

    /**
     * 发送消息给其他所有人
     *
     * @param message
     */
    public static void sendMessageAll(String message) {
        CLIENTS.forEach((sessionId, session) -> sendMessage(session, message));
    }

    /**
     * 获取所有在线用户
     */
    public static String getOnlineInfo() {
        Set<String> userNames = CLIENTS.keySet();
        if (userNames.size() == 0) {
            return "当前无人在线...";
        }
        return CLIENTS.keySet().toString() + "在线";
    }
}

 

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

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

相关文章

STM32开发(十三)STM32F103 片内资源 —— 外部中断 按键 详解

文章目录一、基础知识点二、开发环境三、STM32CubeMX相关配置四、Vscode代码讲解五、结果演示一、基础知识点 外部中断/事件控制器主要特征&#xff1a; 每个中断/事件都有独立的触发和屏蔽每个中断线都有专用的状态位支持多达20个软件的中断/事件请求检测脉冲宽度低于APB2时…

校园一键报警柱的作用

校园一键报警柱是一种用于校园安全的紧急报警系统&#xff0c;可以随时随地向校园安全管理部门发送紧急警报。这种系统通常采用带有紧急按钮的电缆或无线警报装置&#xff0c;使学生、教师和工作人员可以在出现紧急情况时轻松报告安全问题&#xff0c;迅速地通知校园安全人员&a…

彻底理解java中泛型

一、什么是泛型&#xff1f; 泛型是JDK5引入的一种特性&#xff0c;是一种类型安全检测机制&#xff0c;开发者在编译阶段发现类型相关的报错。 泛型即参数类型化&#xff0c;将操作的数据类型定义为参数&#xff0c;可定义在类、接口、方法中。 可以把类型参数看作是使用参数化…

CorelDRAW2023中文版矢量制图及设计软件更新发布

矢量制图及设计软件&#xff0c;CorelDRAW Graphics Suite 2023中文版&#xff08;以下简称CorelDRAW 2023&#xff09;对新手来说&#xff0c;对于自己多久才能学会cdr软件这个问题是比较关心的。如果你的学习能力比较强&#xff0c;一周时间是有可能完全学会cdr的。但由于每个…

您可以找到的 5 种最佳数据恢复软件

数据恢复软件对很多人来说是一个非常有价值的工具。无论您是否意识到&#xff0c;宝贵的数据都有被删除的风险&#xff0c;而且很多人直到丢失数据才知道数据的价值。 5 种数据恢复软件 如果发生这种情况&#xff0c;您需要最好的软件来恢复数据并确保这种情况不会再次发生。这…

PostgreSQL 函数(一) 数学函数和字符串函数

1.数学函数 1.1.符号函数sign 用于判断正负 1.2.求余函数mod 1.3.圆周率函数pi 1.4.平方根函数sqrt 1.5.向上取整函数ceil和ceiling 1.6.向下取整函数floor 1.7.绝对值函数abs 1.8.四舍五入函数round 第2位参数为保留位数 1.9.其他函数 正弦函数sin, 反正弦函数asin, 余弦…

考研复试确认神操作!

终于进行到了研究生考试的尾声&#xff0c;但让考生感到无力吐槽的事情&#xff0c;却还在继续上演&#xff0c;比如苏科大&#xff0c;再比如中地大、苏大&#xff0c;三所学校的神操作&#xff0c;着实让无数考生忍不住调侃&#xff1a;原来考研不仅拼实力&#xff0c;还得拼…

类ChatGPT代码级解读:如何从零起步实现Transformer、llama/ChatGLM

前言 最近一直在做类ChatGPT项目的部署 微调&#xff0c;关注比较多的是两个&#xff1a;一个LLaMA&#xff0c;一个ChatGLM&#xff0c;会发现有不少模型是基于这两个模型去做微调的&#xff0c;说到微调&#xff0c;那具体怎么微调呢&#xff0c;因此又详细了解了一下微调代…

欧拉回路问题

文章目录 欧拉回路程序设计程序分析欧拉回路 有一条名为Pregel的河流经过Konigsberg城。城中有7座桥,把河中的两个岛与河岸连接起来。当地居民热衷于一个难题:是否存在一条路线,可以不重复地走遍7座桥。这就是著名的七桥问题。它由大数学家欧拉首先提出,并给出了完美的解答…

MapReduce简介

MapReduce是一个编程模型&#xff0c;用于处理和生成大数据。用户通过编写Map函数处理输入键值对生成中间键值对&#xff0c;通过编写Reduce函数来合并所有的中间键值对并生成结果。在我们的日常生活中&#xff0c;大部分的任务都可以被抽象成一个MapReduce模型&#xff0c;并通…

6.1 总体和样本

学习目标&#xff1a; 要学习总体和样本&#xff0c;可以按照以下步骤进行&#xff1a; 了解总体和样本的概念&#xff1a;总体是指研究对象的全体&#xff0c;样本是从总体中随机抽取的一部分。了解它们的概念有助于后续学习。 掌握简单随机抽样的方法&#xff1a;简单随机…

HTML学习(1)

文章目录HTML初识第一个HTML程序HTML元素HTML属性HTML标题HTML段落HTML样式格式化标签HTML引用HTML注释HTMLCSSHTML链接HTML图片HTML表格HTML列表HTML块我的个人博客&#xff1a;欢迎来逛逛 HTML初识 HTML全称是&#xff1a;Hyper Text Markup Language HTML不是一种编程语言…

如何在Windows系统上制作U盘启动盘?

使用U盘重装Windows电脑系统&#xff0c;对于熟悉电脑的人来说十分常见&#xff0c;但是不少电脑小白并不会制作U盘启动盘。U盘启动盘是包含操作系统的外部硬盘驱动器&#xff0c;可用于启动计算机或笔记本电脑。下面我们就来了解一下如何制作U盘启动盘。 注意&#xff1a; U …

在芯片行业,想要达到年薪百万难吗?

近两年芯片行业爆火&#xff0c;受到了方方面面的关注。新的一年新的开始&#xff0c;现在这个行业的真实情况到底如何&#xff1f;从事芯片行业有没有前途&#xff1f;下面就来具体谈一谈&#xff0c;希望能给想入高薪行业的你一些帮助。 芯片工程师为什么这么贵&#xff1f;…

ControlNet 1.1重磅发布,14个模型全部开源!

来源&#xff1a;https://github.com/lllyasviel/ControlNet-v1-1-nightly ControlNet 1.1 与 ControlNet 1.0 具有完全相同的体系结构,ControlNet 1.1 包括所有以前的模型&#xff0c;具有改进的稳健性和结果质量,并添加了几个新模型。 模型命名规则更新 从 ControlNet 1.1 开…

英汉互译在线翻译-英文翻译中文的翻译

您是否曾经遇到需要翻译英语文件&#xff0c;但被繁琐的翻译过程所拖慢了工作进度&#xff1f;或者遭遇了机器翻译的低准确率和翻译错误困扰&#xff1f;如果是这样&#xff0c;那么我们的英语翻译中文转换器将是您在这方面的最佳选择&#xff01; 我们的英语翻译中文转换器是…

CSS学习(2) - 边距与高宽 + 框模型

文章首发于&#xff1a;欢迎大佬们前来逛逛 文章目录CSS边框边框样式边框宽度边框颜色单独设置边框边框属性简写圆角边框CSS边距外边距外边距合并CSS内边距内边距与内容宽度CSS高度与宽度CSS框模型CSS边框 border 属性能够设置边框的属性&#xff0c;包括样式&#xff0c;颜色…

vscode连接Linux开发

vscode远程开发Linux项目 1、首先在vscode中下载安装 Remote SSH。 安装完成后 vscode 左侧就会出现下面的图标。 2、配置远程Linux的信息 # Read more about SSH config files: https://linux.die.net/man/5/ssh_config # Host centos # HostName 192.168.88.10 # …

14. unity粒子特效--速度、粒子环绕、力(重力、阻力、风力等)、噪音

1. 渐变速度&#xff08;Velocity over Time&#xff09; 可以分别指定x,y,z三个方向的速度&#xff0c;最终的速度是三个方向的速度合成&#xff0c;点击右侧的下拉三角&#xff0c;也可以使用曲线进行速度的控制调节&#xff0c;如下图所示&#xff1a; 2. 粒子环绕 粒子…

数据治理:1分钟教你认识和识别主数据

​我们讲元数据是企业数据管理的基石&#xff0c;主数据是企业经营运作的主体对象。一般而言&#xff0c;都是从元数据或主数据切入&#xff0c;再逐步展开数据治理的其他领域。企业数据的范围很广而且在不断的增加和演变&#xff0c;哪些数据应该作为主数据加以合理的管理&…