文章目录

  • 前言
    • 关于需求实现的对比(轮询与 `WebSocket` )
    • 关于本篇文章
  • 参考目录
    • 代码实现参考
    • 原理分析参考
  • 集成流程
    • 1、Maven
    • 2、WebSocket 配置类 `WebSocketConfig`
    • 3、实体类 `WebSocketClient`
    • 4、WebSocket 服务 `WebSocketService`
    • 5、测试方法
    • 6、权限修改
    • 7、`ruoyi-ui` 前端修改
  • 效果演示
  • 底层调用流程
    • ##、流程简图
    • #1、发送消息请求 `WebSocketService#sendMessage`
    • #2、`BasicWebSocketSessionRemoteEndpoint#sendText`
    • #3、`WebSockets#sendTextBlocking`
    • #4、发送消息的方法 `WebSockets#sendBlockingInternal`
    • #5、`WebSocketChannel#send`
    • #6、`AbstractFramedStreamSinkChannel#send`
    • #7、`AbstractFramedStreamSinkChannel#flush`

前言

上一篇文章 集成了 JavaMail 到框架中,除了邮件发送以外,也学习了另外一种消息推送的方式 —— WebSocket

主要的需求是从服务端发送消息到客户端,前端控制台能够接收并打印出消息(前端也可以将控制台的消息以弹窗的形式展示,在本文中只是将消息打印到客户端控制台)。

关于需求实现的对比(轮询与 WebSocket

从服务端到客户端的消息也可以用 轮询 的方式实现,但是轮询相对而言比较耗费资源,客户端每隔一段时间就要请求接口查询有没有新的消息,有的话就获取并展示。

轮询之所以比较耗费资源,原因之一是因为间隔的时间不能太长,如果要求实时性比较高的情况下,轮询的时间间隔设置得小,请求的次数就会很密集。

相比之下,WebSocket 的优势就体现在当有新消息的时候直接由服务端推送到客户端,两者只需要保持连接即可。

但是 WebSocket 也不是万能的,某些场景下 WebSocket 的消息可能会丢失(客户端没有接收到),而轮询的消息一般是持久化的消息,丢失的可能性比 WebSocket 小。

关于本篇文章

本文的主要目的还是学习为主,因为技术所限不会太过深入,有很多不错参考的目录也会一一列举出来。

WebSocket 的使用场景还有很多,在找资料的时候也有很多使用场景是 实时聊天,有兴趣的朋友可以自行去查找一下,在这里就不详细说明了。

我个人认为,技术没有优劣,只有合适与否。

参考目录

代码实现参考

  • SpringBoot+Vue整合WebSocket实现前后端消息推送
    这篇文章的大部分代码是参照这篇博客去实现的,有些地方根据实际情况作出了改动,所以下文只贴出改动的代码,其他代码可以去这篇博客查看。
  • WebSockets

原理分析参考

  • 阮一峰老师 - WebSocket 教程
  • 廖雪峰老师 - 使用WebSocket
  • websocket的源码分析,使用,还有一些常见疑问

集成流程

代码集成的环境依然是【RuoYi-Vue-Plus】V3.5.0 版本,但集成的功能与版本关联性不大。

1、Maven

在 common 模块引入:

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

版本是 Spring Boot 的默认版本:

点击进去也可以看到是 2.5.8 版本:

2、WebSocket 配置类 WebSocketConfig

在 framework 模块 config 下添加:

@EnableWebSocket
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter(){return  new ServerEndpointExporter();}}

3、实体类 WebSocketClient

可以放在 common 模块下,方便调用。

4、WebSocket 服务 WebSocketService

同样放在 common 模块。

需要标注注解 @ServerEndpoint,该注解声明并创建了 WebSocket 端点, 并且指明了请求路径:

userName 为客户端请求时携带的参数,用于服务端区分客户端使用。

该类还有几个方法:

  • @OnOpen 注解标注的方法:连接建立成功调用的方法。
  • @OnClose 注解标注的方法:连接关闭调用的方法。
  • @OnMessage 注解标注的方法:收到客户端消息后调用的方法。
  • @OnError 注解标注的方法:连接发生异常时候触发的方法。
  • 向指定客户端发送消息的方法:
  • 批量向客户端发送消息

5、测试方法

测试方法在 Demo 模块新加入一个测试的方法直接调用步骤 4 中的方法即可。

实际使用可以是异步方式调用:

 /*** 通过 websocket 发送消息到客户端** @param userNames    用户名集合* @param title        消息标题* @param content      消息内容* @param sendUserName 发送人*/@Async@Overridepublic void sendMessageByWebSocket(List<String> userNames, String title, String content, String sendUserName) {log.info("sendMessageByWebSocket");// 筛选在线用户List<String> names = new ArrayList<>();// 获取所有登录用户Collection<String> keys = RedisUtils.keys(Constants.LOGIN_TOKEN_KEY + "*");keys.forEach(key -> {LoginUser loginUser = RedisUtils.getCacheObject(key);if (ObjectUtil.isNotNull(loginUser)) {// 匹配登录用户企业if (userNames.contains(loginUser.getUsername())) {names.add(loginUser.getUsername());}}});// 发送消息JSONObject jsonObject = new JSONObject();jsonObject.putOpt("title", title);jsonObject.putOpt("content", content);jsonObject.putOpt("from", sendUserName);WebSocketService.sendMessageBatch(names, jsonObject.toString());}

6、权限修改

使用 WebSocket 需要允许匿名访问,直接修改配置文件(application.yml)中的路径即可。

7、ruoyi-ui 前端修改

需要加入的文件 WebSocket.vue

原博客中客户端与服务端绑定参数的 userName 是直接写死在代码里面的,但是实际使用不会直接写,所以咨询了前端小姐姐之后改为了获取客户端当前用户的用户名 userName 进行绑定,用户名是唯一的,比较符合使用场景。

WebSocket.vue 文件中改动的地方如下:


完整代码:

<template><el-button @click="sendDataToServer">给后台发送消息</el-button>
</template><script>
import {mapState
} from 'vuex'
export default {name: "WebSocket", data() {return {// ws是否启动wsIsRun: false,// 定义ws对象webSocket: null,// ws请求链接(类似于ws后台地址)ws: '',// ws定时器wsTimer: null,}},async mounted() {this.wsIsRun = truethis.wsInit()},computed: {...mapState({name: state => state.user.name}),},methods: {sendDataToServer() {if (this.webSocket.readyState === 1) {this.webSocket.send('来自前端的数据')} else {throw Error('服务未连接')}},/*** 初始化ws*/wsInit() {console.log("userName: " + this.name)const wsuri = 'ws://127.0.0.1:8080/websocket/' + this.namethis.ws = wsuriif (!this.wsIsRun) return// 销毁wsthis.wsDestroy()// 初始化wsthis.webSocket = new WebSocket(this.ws)// ws连接建立时触发this.webSocket.addEventListener('open', this.wsOpenHanler)// ws服务端给客户端推送消息this.webSocket.addEventListener('message', this.wsMessageHanler)// ws通信发生错误时触发this.webSocket.addEventListener('error', this.wsErrorHanler)// ws关闭时触发this.webSocket.addEventListener('close', this.wsCloseHanler)// 检查ws连接状态,readyState值为0表示尚未连接,1表示建立连接,2正在关闭连接,3已经关闭或无法打开clearInterval(this.wsTimer)this.wsTimer = setInterval(() => {if (this.webSocket.readyState === 1) {clearInterval(this.wsTimer)} else {console.log('ws建立连接失败')this.wsInit()}}, 3000)},wsOpenHanler(event) {console.log('ws建立连接成功')},wsMessageHanler(e) {console.log('wsMessageHanler')console.log(e)//const redata = JSON.parse(e.data)//console.log(redata)},/*** ws通信发生错误*/wsErrorHanler(event) {console.log(event, '通信发生错误')this.wsInit()},/*** ws关闭*/wsCloseHanler(event) {console.log(event, 'ws关闭')this.wsInit()},/*** 销毁ws*/wsDestroy() {if (this.webSocket !== null) {this.webSocket.removeEventListener('open', this.wsOpenHanler)this.webSocket.removeEventListener('message', this.wsMessageHanler)this.webSocket.removeEventListener('error', this.wsErrorHanler)this.webSocket.removeEventListener('close', this.wsCloseHanler)this.webSocket.close()this.webSocket = nullclearInterval(this.wsTimer)}},}
}
</script><style scoped></style>

效果演示



服务端调用推送测试:

底层调用流程

##、流程简图

#1、发送消息请求 WebSocketService#sendMessage

#2、BasicWebSocketSessionRemoteEndpoint#sendText

sendText 接口:

sendText 实现方法:

【RuoYi-Vue-Plus】使用的是容器框架是 Undertow,所以实现类是 io.undertow.websockets 包下面的方法:

#3、WebSockets#sendTextBlocking

#4、发送消息的方法 WebSockets#sendBlockingInternal

#5、WebSocketChannel#send

#6、AbstractFramedStreamSinkChannel#send

#7、AbstractFramedStreamSinkChannel#flush


步骤 5、6、7 完成后消息发送完成,回到步骤 4:

【RuoYi-Vue-Plus】扩展笔记 02 - 集成 WebSocket 发送消息到客户端(源码)相关推荐

  1. socket接收的消息怎么更新到页面_spring boot 集成 websocket 实现消息主动

    前言 http协议是无状态协议,每次请求都不知道前面发生了什么,而且只可以由浏览器端请求服务器端,而不能由服务器去主动通知浏览器端,是单向的,在很多场景就不适合,比如实时的推送,消息通知或者股票等信息 ...

  2. spring boot 集成 websocket 实现消息主动推送

    前言 http协议是无状态协议,每次请求都不知道前面发生了什么,而且只可以由浏览器端请求服务器端,而不能由服务器去主动通知浏览器端,是单向的,在很多场景就不适合,比如实时的推送,消息通知或者股票等信息 ...

  3. linux命令 socket,如何从linux中的命令行向socket.io websocket发送消息?

    是否可以使用linux中的命令行向我的localhost服务器(节点)发送socket.io消息?我不确定这是否可行--从稀缺的谷歌搜索结果来看,我猜这不可能或不复杂-- 我的socket.io代码如 ...

  4. SpringMVC+Spring4+Mybatis3集成,开发简单Web项目+源码下载

    转载自   SpringMVC+Spring4+Mybatis3集成,开发简单Web项目+源码下载 基本准备工作 1.安装JDK1.6以上版本,安装与配置 2.下载mybatis-3.2.0版:htt ...

  5. springboot Vue孕期月子管理系统与早教交流系统java项目源码

    springboot Vue孕期月子管理系统与早教交流系统java项目源码 vue springboot的准妈妈孕期交流平台 本项目Springboot和vue.js源码,开发工具:idea,也支持e ...

  6. Slicer学习笔记(四十二)slicer c++源码编译

    Slicer学习笔记(四十二)slicer c++源码编译 1.cmake生成项目 2.编译失败的原因汇总 2.1.下载代码失败 之前在windows下编译slicer,没有做笔记. 后面再次编译还会 ...

  7. java计算机毕业设计ssm基于Vue的校园电脑租赁系统设计与开发19xy6(附源码、数据库)

    java计算机毕业设计ssm基于Vue的校园电脑租赁系统设计与开发19xy6(附源码.数据库) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(We ...

  8. 【vn.py学习笔记(六)】vn.py constant源码阅读、委托生命周期

    [vn.py学习笔记(六)]vn.py constant源码阅读.委托生命周期 写在前面 1 constant 1.1 Direction 1.2 Offset 1.3 Status 1.4 Prod ...

  9. 通过自定义组件学习Vue系列(二)【时间轴】(附源码)

    需求: 用于升级日志的显示 效果图: 实现原理: 主要区域分为两块,时间区和内容区,时间区是画一个圆点和显示一个时间,内容区左边一个竖线和文字显示 然后做一下循环,将每个日期的数据显示出来 布局采用f ...

最新文章

  1. 设计模式-UML图简单介绍
  2. matlab主程序和子函数不在一个文件夹下,怎么调用?
  3. Spring Boot 细节挖掘(全局异常处理)
  4. ios pan手势滑动消失动画_IOS UIPanGestureRecognizer手势使用及识别状态UIGestureRecognizerState...
  5. 最短路最小生成树水题
  6. [Bzoj2039][2009国家集训队]employ人员雇佣(最小割)
  7. Spug发布前端项目实战全过程
  8. 中国各省级行政区对应MODIS区块号
  9. su灯光插件_V-Ray for SketchUp渲染外部照明快速入门
  10. bin文件夹下的roslyn文件夹
  11. fm24c16c语言程序,单片机读写24C01~24C16程序
  12. OpenCV图像处理基础(C++版)
  13. php微秒,PHP 微秒
  14. 3个免费、无损压缩图片的工具,一键解决图片过大问题
  15. 新时代高校辅导员工作素养的新要求及提升路径(非原创)
  16. IDEA繁体问题解决
  17. Java初学者——小白篇(一)
  18. CodeBlocks调试简要教程
  19. 500拦截报错 axios_axios拦截设置和错误处理
  20. iOS连续订阅IAP自动续订服务端接入流程

热门文章

  1. Matlab代码:计及三方市场主体接入的园区综合能源系统能量管理
  2. 2022年Java学习笔记目录
  3. java jar 打印_三种Java打印PDF文档的实例代码
  4. 2022考研真题+汤家凤网课视频。祝2022考研朋友顺利上岸!
  5. oracle jdbc jar位置,Oracle JDBC ojdbc6 Jar作为Maven依赖项
  6. 自动驾驶常用定位方案
  7. Easy2D 轻量级游戏开发框架(1)
  8. Python自动化办公:读取pdf文档
  9. Ubuntu16.04+caffe+DIGITS的安装配置指南
  10. 光的反射规律: 反射光线、入射光线和法线