Springboot整合WebSocket实现浏览器和服务器交互

news2025/2/25 2:34:52

Websocket定义

代码实现

引入maven依赖

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

配置类

  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 {
  
      @Bean
      public ServerEndpointExporter serverEndpointExporter() {
          return new ServerEndpointExporter();
      }
  
  }

WebSocketServer 服务端

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.hc.dialer_server.service.SendMessageService;
import com.hc.dialer_server.utils.SpringContextUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocket的操作类
 */
@Component
@Slf4j
@ServerEndpoint("/websocket/{number}")
@DependsOn("springContext")
public class WebSocketServer {

    SendMessageService sendMessageServiceImpl = SpringContextUtils.getBean("sendMessageServiceImpl");

    // 记录当前在线连接数
    private static AtomicInteger onlineSessionClientCount = new AtomicInteger(0);

    // 记录number和 session的对应关系
    public static Map<String, Session> connectionMap = new ConcurrentHashMap<>();

    private String number;


    @OnOpen
    public void onOpen(@PathParam("number") String number, Session session) throws IOException {
        log.info("连接建立中 ==> session_id = {}, number = {}", session.getId(), number);
        // 在线人数+1
        onlineSessionClientCount.incrementAndGet();
        this.number = number;

        connectionMap.put(number, session);
        session.getBasicRemote().sendText("连接建立成功");
        log.info("连接建立成功, 当前在线数为: {}, session_id = {}, number = {}", onlineSessionClientCount, session.getId(), number);
    }


    @OnClose
    public void onClose(@PathParam("number") String number, Session session) {
        connectionMap.remove(number);
        onlineSessionClientCount.decrementAndGet();
        log.info("连接关闭成功, 当前在线数为: {} ==> 关闭该连接信息: session_id = {}, number = {}", onlineSessionClientCount, session.getId(), number);
    }


    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        if (StringUtils.isBlank(message)) {
            return;
        }
        JSONObject jsonObject = JSON.parseObject(message);
        String eventType = jsonObject.getString("eventType");
        String msg = jsonObject.getString("message");
        log.info("服务端收到客户端消息 ==> number = {}, eventType = {}, message = {}", number, eventType, msg);
        session.getBasicRemote().sendText("服务端给客户端发送消息");
    }


    @OnDisconnect
    public void onDisconnect(Session session) {
        connectionMap.remove(number);
        onlineSessionClientCount.decrementAndGet();
        log.info("disconnect event ==> number: {}, session_id: {}, 当前在线数: {}", number, session.getId(), onlineSessionClientCount);
    }


    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误, session id = {}, 错误信息为:{}", session.getId(), error.getMessage());
    }

}

工具类


import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component("springContext")
public class SpringContextUtils implements ApplicationContextAware {
    /**
     * Spring应用上下文环境
     */
    private static ApplicationContext applicationContext;

    /**
     * 获取对象
     *
     * @return Object 一个以所给名字注册的bean的实例
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        return (T) applicationContext.getBean(name);
    }

    /**
     * 获取类型为requiredType的对象
     */
    public static <T> T getBean(Class<T> clz) {
        return applicationContext.getBean(clz);
    }

    /**
     * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
     *
     * @return boolean
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    /**
     * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
     *
     * @return boolean
     */
    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    /**
     * @return Class 注册对象的类型
     */
    public static Class<?> getType(String name) {
        return applicationContext.getType(name);
    }


    @Override
    public synchronized void setApplicationContext(ApplicationContext applicationContext) {
        if (SpringContextUtils.applicationContext == null) {
            SpringContextUtils.applicationContext = applicationContext;
        }
    }
}

前端

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>测试webSocket</title>
</head>
<body>
Client传来的number:
<!--<input type="text" th:value="${number}" id="number"/>-->
<p>【eventType】:
<div><input id="toUserId" name="toUserId" type="text" value="message"></div>
<p>【messageText】:
<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
<p>【操作】:
<div>
    <button type="button" onclick="sendMessage()">发送消息</button>
    <button type="button" onclick="sendMessage1()">1626</button>

</div>
</body>

<script type="text/javascript">
    var socket;
    if (typeof (WebSocket) == "undefined") {
        console.log("您的浏览器不支持WebSocket");
    } else {
        console.log("您的浏览器支持WebSocket");
        //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接

        var number = '1626';
        console.log("number-->" + number);
        // socket服务器端的ip和端口
        var reqUrl = "http://localhost:8080/websocket/" + number;
        socket = new WebSocket(reqUrl.replace("http", "ws"));
        //打开事件
        socket.onopen = function () {
            console.log("Socket 已打开");
            //socket.send("这是来自客户端的消息" + location.href + new Date());
        };
        //获得消息事件
        socket.onmessage = function (msg) {
            console.log("onmessage--" + msg.data);
            //发现消息进入,开始处理前端触发逻辑
        };
        //关闭事件
        socket.onclose = function () {
            console.log("Socket已关闭");
        };
        //发生了错误事件
        socket.onerror = function () {
            alert("Socket发生了错误");
            //此时可以尝试刷新页面
        }
    }

    function sendMessage() {
        if (typeof (WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        } else {
            // console.log("您的浏览器支持WebSocket");
            var toUserId = document.getElementById('toUserId').value;
            var contentText = document.getElementById('contentText').value;
            var msg = '{"eventType":"' + toUserId + '","message":"' + contentText + '"}';
            console.log(msg);
            socket.send(msg);
        }
    }

    //发送json串
    function sendMessage1() {
        if (typeof (WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        } else {
            // console.log("您的浏览器支持WebSocket");
            var toUserId = document.getElementById('toUserId').value;
            var contentText = document.getElementById('contentText').value;
            // var msg = '{"eventType":"' + toUserId + '","message":"' + contentText + '"}';

            var msg = '{"eventType":"' + toUserId + '","message":{"number":"1626","serviceNumber":"60600","req":"event01"}}';
            console.log(msg);
            socket.send(msg);
        }
    }

    
</script>
</html>

测试

在这里插入图片描述

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

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

相关文章

Supervisor监控Go程序

目的&#xff1a; 线上服务有时候会由于一些bug导致painc&#xff0c;程序终止&#xff0c;这个时候需要自动重启项目&#xff0c;让项目能继续提供服务。 容器启动Golang项目 vscode安装golang插件,让vscode可以运行go代码 提示没有go环境&#xff0c;服务器本地确实是没有…

AMEYA360:君正低功耗AIoT图像识别处理器—X1600/X1600E

• 高性能 XBurst 1 CPU&#xff0c;主频1.0GHz • 超低功耗 • 内置LPDDR2(X1600&#xff1a;32MB&#xff0c;X1600E&#xff1a;64MB) • 实时控制核XBurst 0&#xff0c;面向安全管理和实时控制 • 丰富的外设接口 应用领域 • 基于二维码的智能商业 • 智能物联网 • 高端…

Leetcode刷题详解——有效三角形的个数

1.题目链接&#xff1a;有效三角形的个数 2.题目描述&#xff1a; 给定一个包含非负整数的数组 nums &#xff0c;返回其中可以组成三角形三条边的三元组个数。 示例 1: 输入: nums [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3示…

【数据可视化】—大屏数据可视化展示

【数据可视化】—大屏数据可视化展示 一、数据可视化 数据可视化的目的&#xff1a;借助于图形化工具&#xff0c;清晰有效的传达与沟通信息。 数据可视化可以把数据从冰冷的数字转换成图形&#xff0c;揭示蕴含在数据中的规律和道理。 二、 免费数据可视化库 Echarts 百度…

7-k8s-helm管理

文章目录 一、为什么需要Helm二、Helm相关概念介绍三、Helm安装四、Helm指令介绍五、Helm创建tomcat六、Helm创建tomcat其他方式七、Helm创建redis 一、为什么需要Helm k8s部署&#xff1a;k8s平台部署的服务都是由资源文件描述组成&#xff0c;传统的k8s部署应用需要手工编排…

过拟合与过拟合的经典例子

本站原创文章&#xff0c;转载请说明来自 《老饼讲解-机器学习》 ml.bbbdata.com 过拟合是机器学习中最常遇到的问题&#xff0c;本文解析什么叫过拟合 01. 什么是过拟合 本节通过概念解析与可视化例子&#xff0c;讲述什么是过拟合 什么是过拟合 过拟合是指在由于过度拟…

16 - 多线程调优(下):如何优化多线程上下文切换?

通过上一讲的讲解&#xff0c;相信你对上下文切换已经有了一定的了解了。如果是单个线程&#xff0c;在 CPU 调用之后&#xff0c;那么它基本上是不会被调度出去的。如果可运行的线程数远大于 CPU 数量&#xff0c;那么操作系统最终会将某个正在运行的线程调度出来&#xff0c;…

4.3 划分子网和构造超网

思维导图&#xff1a; 4.3.1 划分子网 **4.3 划分子网和构造超网笔记&#xff1a;** --- **4.3.1 划分子网** **1. 两级IP地址到三级IP地址的转变&#xff1a;** **关键点&#xff1a;** - **问题背景&#xff1a;** 早期的ARPANET对IP地址的设计存在不足&#xff1a; 1…

python 之f-strings 来格式化字符串

文章目录 当使用 f-strings 来格式化字符串时&#xff0c;可以在字符串中嵌入变量的值以及其他表达式的结果。以下是一些示例&#xff1a; 基本用法&#xff1a; name "Alice" age 30print(f"My name is {name} and I am {age} years old.")输出&#…

树莓派和arduino的恩恩怨怨

一、负责与树莓派互动的putty的会话经常断&#xff0c;不爽&#xff1a; 参考&#xff1a;【SSH】SSH自动断开连接的原因和解决办法|SSH保持长连接方法_ssh连上几秒就断开了_bandaoyu的博客-CSDN博客 用nano 1、修改 /etc/profile中改MOUT的值&#xff1a;export TMOUT0 ec…

Linux常见的指令合集

Linux指令合集 认识linuxlinux基础指令1.pwd 命令2. ls 命令3.cd 命令4. man 命令5. grep 命令6. ps 命令7. kill 命令8. netstat 命令9. date 查看当前系统时间10. echo 打印选项 -e linux文件操作指令1. mkdir 命令2. rmdir 命令3. touch 命令4. rm 命令5. mv 命令6. cp 命令…

【文件IO】文件系统的操作 流对象 字节流(Reader/Writer)和字符流 (InputStream/OutputStream)的用法

目录 1.文件系统的操作 (File类) 2.文件内容的读写 (Stream流对象) 2.1 字节流 2.2 字符流 2.3 如何判断输入输出&#xff1f; 2.4 reader读操作 (字符流) 2.5 文件描述符表 2.6 Writer写操作 (字符流) 2.7 InputStream (字节流) 2.8 OutputStream (字节流) 2.9 字节…

使用html2canvas将html转pdf,由于table表的水平和竖直有滚动条导致显示不全(或者有空白)

结果&#xff1a; 业务&#xff1a;将页面右侧的table打印成想要的格式的pdf&#xff0c;首先遇到的问题是table表上下左右都有滚轮而html2canvas相当于屏幕截图&#xff0c;那滚动区域如何显示出来是个问题&#xff1f; gif有点模糊&#xff0c;但是大致功能可以看出 可复制…

基于双级阈值及过零率的语音激活检测(VAD)

语音激活检测&#xff08;Voice Activity Detection, VAD&#xff09;:也称为端点检测&#xff0c;目的就是要找到音频信号的开始和结束位置。 时域方法&#xff1a; 音量&#xff1a;只用音量来进行端点检测&#xff0c;是最简单的方法&#xff0c;但是会对清音造成误判。音…

C#,数值计算——分类与推理Gaumixmod的计算方法与源程序

1 文本格式 using System; using System.Collections.Generic; namespace Legalsoft.Truffer { public class Gaumixmod { private int nn { get; set; } private int kk { get; set; } private int mm { get; set; } private double…

力扣第17题 电话号码的字母组合 c++ 回溯 经典提升题

题目 17. 电话号码的字母组合 中等 相关标签 哈希表 字符串 回溯 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。…

Elasticsearch系列组件:Logstash强大的日志管理和数据分析工具

Elasticsearch 是一个开源的、基于 Lucene 的分布式搜索和分析引擎&#xff0c;设计用于云计算环境中&#xff0c;能够实现实时的、可扩展的搜索、分析和探索全文和结构化数据。它具有高度的可扩展性&#xff0c;可以在短时间内搜索和分析大量数据。 Elasticsearch 不仅仅是一个…

sqlserver系统存储过程添加用户学习

sqlserver有一个系统存储过程sp_adduser&#xff1b;从名字看是添加用户的&#xff1b;操作一下&#xff0c; 从错误提示看还需要先添加一个登录名&#xff0c;再执行一个系统过程sp_addlogin看一下&#xff0c; 执行完之后看一下&#xff0c;安全性-登录名下面有了rabbit&…

【ARM Coresight Debug 系列 -- Linux 断点 BRK 中断使用详细介绍】

文章目录 1.1 ARM BRK 指令1.2 BRK 立即数宏定义介绍1.3 断点异常处理流程1.3.1 el1_sync_handler1.3.2 el1_dbg 跟踪 1.4 debug 异常处理函数注册1.4.1 brk 处理函数的注册 1.1 ARM BRK 指令 ARMv8 架构的 BRK 指令是用于生成一个软件断点的。当处理器执行到 BRK 指令时&…

电脑办公助手之桌面便签,助力高效率办公

在现代办公的快节奏中&#xff0c;大家有应接不暇的工作&#xff0c;每天面对着复杂的工作任务&#xff0c;总感觉时间不够用&#xff0c;而且工作无厘头。对于这种状态&#xff0c;大家可以选择在电脑上安装一款好用的办公便签软件来辅助日常办公。 敬业签是一款专为办公人士…