基于SpringBoot + Vue的项目整合WebSocket的入门教程

news2024/9/19 19:15:07

1、WebSocket简介

  WebSocket是一种网络通信协议,可以在单个TCP连接上进行全双工通信。它于2011年被IETF定为标准RFC 6455,并由RFC7936进行补充规范。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。这使得客户端和服务器之间的数据交换变得更加简单,并允许服务端主动向客户端推送数据。

2、服务端环境搭建

  服务端基于SpringBoot实现,首先引入对应的jar包,然后进行ServerEndpointExporter配置,然后再定义了一个WebSocket操作类,最后编写了一个测试的类WebSocketController(一个普通的Controller类)。

2.1、maven依赖
<!-- websocket 依赖 -->
<dependency>
   <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2.2、配置类WebSocketConfig

  ServerEndpointExporter是Spring框架中的一个类,主要用于在Spring Boot应用中启动和管理WebSocket服务器端点。

  在Spring Boot内置容器(嵌入式容器)中运行时,必须由ServerEndpointExporter提供ServerEndpointExporter bean,它会在启动时自动扫描和注册应用中的WebSocket端点。但在Tomcat等其他容器中运行时,容器的扫描工作会由容器自己处理,不需要手动注入ServerEndpointExporter bean。

@Configuration
public class WebSocketConfig {
    /**
     * 	注入ServerEndpointExporter,
     * 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
2.3、WebSocket操作类

  WebSocket操作类定义了WebSocket中的onOpen()、onMessage()、onClose()、onError()等方法,同时提供了一个发送广播(全部订阅用户)和点对点信息的方法。

1、这里@ServerEndpoint(“/api/websocket/{userId}”)中的定义可以根据自己的需要进行修改,因为我的项目里使用了SpringSecurity了,为了避免登录鉴权,这里使用“/api/**”配置了免登陆Api。后续会继续完善需要登录鉴权的使用方式。
2、这里的WebSocket操作类,每次建立 WebSocket 连接时,就会初始化一个新的 bean。这是由于 WebSocket 是一种双向的、长时间的通信机制,它需要维护每个连接的状态,处理每个从客户端发来的消息,并根据这些消息生成响应消息发送回客户端。因此,为每个 WebSocket 连接创建一个单独的 bean 是必要的,这样可以让 Spring Boot 为每个连接提供必要的管理和生命周期控制。这种方式也可以使用单例模式实现,后续再更新相关用法。

@Component
@Slf4j
@ServerEndpoint("/api/websocket/{userId}")
public class WebSocket {
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    //用户ID
    private String userId;

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    //虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
    private static CopyOnWriteArraySet<WebSocket> webSockets =new CopyOnWriteArraySet<>();
    // 用来存在线连接用户信息
    private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();

    /**
     * 链接成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value="userId")String userId) {
        try {
            this.session = session;
            this.userId = userId;
            webSockets.add(this);
            sessionPool.put(userId, session);
            log.info("WebSocket消息有新的连接,总数为:"+webSockets.size());
        } catch (Exception e) {
            log.error("WebSocket异常-链接失败(onOpen),原因:" + e.getMessage());
        }
    }

    /**
     * 链接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        try {
            webSockets.remove(this);
            sessionPool.remove(this.userId);
            log.info("WebSocket消息连接断开,总数为:"+webSockets.size());
        } catch (Exception e) {
            log.error("WebSocket异常-链接关闭失败(onClose),原因:" + e.getMessage());
        }
    }
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("WebSocket消息收到客户端消息:"+message);
    }

    /** 发送错误时的处理
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误,原因:"+error.getMessage());
        log.error("WebSocket异常-错误信息(onError),原因:" + error.getMessage());
    }


    // 此为广播消息
    public void sendAllMessage(String message) {
        log.info("WebSocket消息-广播消息:"+message);
        for(WebSocket webSocket : webSockets) {
            try {
                if(webSocket.session.isOpen()) {
                    webSocket.session.getAsyncRemote().sendText(message);
                }
            } catch (Exception e) {
                log.error("WebSocket异常-广播消息异常(sendAllMessage),原因:" + e.getMessage());
            }
        }
    }

    // 此为单点消息
    public void sendOneMessage(String userId, String message) {
        Session session = sessionPool.get(userId);
        if (session != null && session.isOpen()) {
            try {
                log.info("WebSocket消息-点对点消息:"+message);
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                log.error("WebSocket异常-点对点消息异常(sendOneMessage),原因:" + e.getMessage());
            }
        }
    }

}
2.4、测试类WebSocketController

  这个测试类,主要是为了测试发送信息后,页面可以自动更新。至此,服务端的配置就完成了。

@Controller
@RequestMapping("/api/msg")
public class WebSocketController {

    @Resource
    private WebSocket webSocket;

    @RequestMapping("/all/{msg}") // 将消息发送到/topic/greetings路径下
    public void all(@PathVariable String msg) {
        //创建业务消息信息
        JSONObject obj = new JSONObject();
        obj.put("msg", msg);//消息内容
        //全体发送
        webSocket.sendAllMessage(obj.toJSONString());
    }
    @RequestMapping("/{userId}/{msg}") // 将消息发送到/topic/greetings路径下
    public void sendUser(@PathVariable String userId,@PathVariable String msg) {
        //创建业务消息信息
        JSONObject obj = new JSONObject();
        obj.put("msg", msg);//消息内容
        webSocket.sendOneMessage(userId, obj.toJSONString());
    }

}

3、前端环境搭建

  前端是基于Vue实现,具体代码如下:

<template>
  <div class="order-list">
    <span>TestMsg:{{ message }}</span>
  </div>
</template>
<script>
export default {
    name: 'HomeView',
    data() {
      return {
          message:''
      }
    },
    components: {},
    created() {},
    mounted() {
      //初始化websocket
      this.initWebSocket()
    },
    destroyed: function () { // 离开页面生命周期函数
    	this.websocketclose();
    },
    computed: {},
    methods: {
      initWebSocket: function () { // 建立连接
        var userId = "test"//this.COMMON.getStorage("user");
        //对应@ServerEndpoint("/api/websocket/{userId}")中的地址
        var url = "ws://ip:port/项目名/api/websocket/" + userId;
        this.websock = new WebSocket(url);
        this.websock.onopen = this.websocketonopen;
        this.websock.send = this.websocketsend;
        this.websock.onerror = this.websocketonerror;
        this.websock.onmessage = this.websocketonmessage;
        this.websock.onclose = this.websocketclose;
      },
      // 连接成功后调用
      websocketonopen: function () {
        console.log("WebSocket连接成功");
      },
      // 发生错误时调用
      websocketonerror: function (e) {
        console.log("WebSocket连接发生错误" + JSON.stringify(e));
      },
      // 给后端发消息时调用
      websocketsend: function (e) {
        console.log("WebSocket连接发生错误" + JSON.stringify(e));
      },
// 接收后端消息
      // vue 客户端根据返回的cmd类型处理不同的业务响应
      websocketonmessage: function (e) {
        this.message = data;
      },
      // 关闭连接时调用
      websocketclose: function (e) {
        console.log("connection closed (" + e.code + ")");
      }
    },
  }
</script>
<style lang="scss"></style>

4、测试

  至此,我们就完成了WebSocket的服务端和前端的环境搭建,首先启动后台服务,然后启动前端服务,进入上述页面,这个时候就会建立起来链接,然后访问“http://localhost:8803/qriver-lab/api/msg/test/6666”,其中test对应的是userId,6666是msg信息,这个时候就会发现页面会自动显示6666,不需要进行刷新。
在这里插入图片描述

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

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

相关文章

快手根据ID取商品详情 API 返回值说明

item_get-根据ID取商品详情 ks.item_get 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;API接口secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_s…

来看看Python都有哪些特点(优点和缺点)

Python 是一种开源的解释型脚本编程语言&#xff0c;它之所以非常流行&#xff0c;主要有三点原因&#xff1a; Python 简单易用&#xff0c;学习成本低&#xff0c;看起来非常优雅干净&#xff1b;Python 标准库和第三库众多&#xff0c;功能强大&#xff0c;既可以开发小工具…

Allegro引流方式有哪些?Allegro平台注意事项

正确的引流&#xff0c;你的平台才会让更多人发现&#xff0c;才能提高转化率&#xff0c;那么Allegro引流方式有哪些&#xff0c;Allegro平台注意事项是什么呢&#xff1f; Allegro引流方式有哪些 商品优化&#xff1a;在Allegro上&#xff0c;关键是确保您的商品能够在搜索…

智慧公厕实现公共厕所全方位“上云用数赋智”根本之道

智慧公厕是一种全新的公共厕所管理形式&#xff0c;主要的特点是集合了物联网、互联网、云计算、大数据、区块链等技术&#xff0c;通过云服务、大数据融合应用以及智能化的管理手段&#xff0c;对公共厕所进行全面升级与改造&#xff0c;提升其服务质量和管理效率。在智慧公厕…

用Canape录制数据的操作方法

介绍 本文档可帮助读者实现用canape上车录制所需数据的方法。 一、打开ASAP2 Studio 软件,先对elf中的变量进行A2L转换 1、首先在电脑上插入canape盒子,打开你的ASAP2 Studio 软件,对elf中的变量进行A2L转换。 2、点击新建 New Database。 下面就是新建后的界面。 3、按…

工单管理系统如何操作?在线工单系统有什么作用?

在公司高速发展的过程中&#xff0c;管理者越来越认识到工单管理系统的重要性。工单系统能够利用后台系统整合多渠道客户数据&#xff0c;实现全面、系统化的客户数据管理&#xff0c;进而帮助企业优化工作流程管理&#xff0c;显著降低成本。在线工单系统能够自动分配工单&…

服务器巡检表-监控指标

1、巡检指标 系统资源K8S集群NginxJAVA应用RabbitMQRedisPostgreSQLElasticsearchELK日志系统 2、巡检项 检查项目 检查指标 检查标准 系统资源 CPU 使用率 正常&#xff1a;&#xff1c;70% 低风险&#xff1a;≥ 70% 中风险&#xff1a;≥ 85% 高风险&#xff1a;≥ 9…

Pod和容器设计模式

为什么需要Pod 一些应用的实现是需要多个进程配合完成的&#xff0c;由于容器实际上是一个“单进程”模型&#xff0c;如果在容器里启动多个进程会存在进程管理的难题。在Kubernetes里面&#xff0c;实际上会被定义为一个拥有四个容器的Pod。 Pod相当于进程组 Kubernetes 是…

第一课 实现用WASD控制一个物体前后左右移动

using System.Collections; using System.Collections.Generic; using UnityEngine;//实现让被挂在的物体往前移动 //按下W键往前移动&#xff0c;按下S键往后移动 public class RoleMove : MonoBehaviour { public float myspeed 0.1f;void Update(){if (Input.GetKey(KeyCo…

英飞凌TC3xx--深度手撕HSM安全启动(三)--TC3xx HSM系统架构

今天聊TC3xx HSM系统,包括所用内核、UCB相关信息、Host和HSM交互方式。 1、HSM系统架构 下图来源于英飞凌官网培训材料。 TC3xx的HSM内核是一颗32位的ARM Cortex M3,主频可达100MHz,支持对称算法AES128、非对称算法PKC(Public Key Crypto) ECC256、Hash SHA2,以及T…

(2023,DiffWA 水印 攻击 )DiffWA:用于水印攻击的扩散模型

DiffWA: Diffusion Models for Watermark Attack 公众号&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 进交流群获取资料&#xff09; 目录 0. 摘要 1. 简介 2. 背景 2.1 HiDDeN 2.2 去噪扩散模型 3. 提出的方法 3.1 准备工作 3.2 DiffWA 框架 3.3…

Xshell只能打开一个会话、左边栏消失不见、高级设置在哪儿、快捷键设置解决

Xshell只能打开一个会话、左边会话栏消失不见、高级设置在哪儿解决 1.问题&#xff1a; xshell会话&#xff08;窗口&#xff09;上方切换栏不见了的处理办法 解决方法&#xff1a;ctrl shift t 2.问题&#xff1a; 左边会话管理器不见了 解决方法&#xff1a; 3.问题…

国内访问香港服务器选择什么路线?

​  国内访问香港服务器可以选择多种路线。首先&#xff0c;我们了解下各个线路的速度延迟。 一、CN2直连&#xff1a;解决了不同互联网服务提供商之间访问的难题&#xff0c;不需要绕到国际网络再从中国的三个网络入口进入。 二、优化直连&#xff1a;全国平均延迟60ms&…

【C++】递归,搜索与回溯算法入门介绍和专题一讲解

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 我的gitee&#xff1a;C仓库 个人专栏&#xff1a;C专栏 前言 从本文开始进入递归&#xff0c;搜索与回溯算法专题讲解。 文章目录 前言一、名词解释1、什么是递归&#xff1f;2、为什么会用到递归&#xff1f;3、如何理解…

ARKit功能初学

文章目录 一、ARKit简介二、ARKit API 中的几个主要的类1. ARSCNView2. ARSession3. ARFrame4.ARAnchor5. ARWorldTrackingSessionConfiguration6. ARSCNViewDelegate7. ARSessionDelegate 三、ARKit示例1. 导入框架2. 设置SceneKit View3. 配置ARSCNView Session4. Camera 授权…

MyBatis数据库操作

文章目录 前言一、MyBatis的各种查询功能1.查询一个实体类对象2.查询一个List集合3.查询单个数据4.查询一条数据为map集合5.查询多条数据为map集合方法一方法二 6.测试类 二、特殊SQL的执行1.模糊查询2.批量删除3.动态设置表名5.添加功能获取自增的主键6.测试类 三、自定义映射…

CentOS 7 openssl 3.0.10 rpm包制作 —— 筑梦之路

源码下载地址&#xff1a; https://www.openssl.org/source/openssl-3.0.10.tar.gz 编写spec文件&#xff1a; cat << EOF > openssl.specSummary: OpenSSL 3.0.10 for CentosName: opensslVersion: %{?version}%{!?version:3.0.10}Release: 1%{?dist}Obsoletes…

Azure + React + ASP.NET Core 项目笔记一:项目环境搭建(一)

不重要的目录标题 前提条件第一步&#xff1a;新建文件夹第二步&#xff1a;使用VS/ VS code/cmd 打开该文件夹第三步&#xff1a;安装依赖第四步&#xff1a;试运行react第五步&#xff1a;整理项目结构 前提条件 安装dotnet core sdk 安装Node.js npm 第一步&#xff1a;新…

【最新!七麦下载量analysis参数】逆向分析与Python实现加密算法

文章目录 1. 写在前面2. 请求分析3. 加密分析4. 算法实现 1. 写在前面 之前出过一个关于榜单analysis的分析&#xff0c;有兴趣的可以查看这篇文章&#xff1a;七麦榜单analysis加密分析 最近运营团队那边有同事找到我们&#xff0c;说工作中偶尔需要统计分析一下某APP在一些主…

DOM渲染与优化 - CSS、JS、DOM解析和渲染阻塞问题

文章目录 DOM渲染面试题DOM的渲染过程DOM渲染的时机与渲染进程的概述浏览器的渲染流程1. 解析HTML生成DOM树&#xff1a;遇到<img>标签加载图片2. 解析CSS生成CSSOM(CSS Object Model): 遇见背景图片链接不加载3. 将DOM树和CSSOM树合并生成渲染树&#xff1a;加载可视节点…