springboot+vue实现websocket通信实例,进入页面建立连接
springboot+vue实现websocket通信实例

进入页面建立连接

前端代码:
<template><div class="app-container"><el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="姓名" prop="name"><el-inputv-model="queryParams.name"placeholder="请输入姓名"clearable@keyup.enter.native="handleQuery"/></el-form-item><el-form-item label="密码" prop="pwd"><el-inputv-model="queryParams.pwd"placeholder="请输入密码"clearable@keyup.enter.native="handleQuery"/></el-form-item><el-form-item><el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></el-form-item></el-form><el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-buttontype="primary"plainicon="el-icon-plus"size="mini"@click="handleAdd"v-hasPermi="['testuser:testuser:add']">新增</el-button></el-col><el-col :span="1.5"><el-buttontype="success"plainicon="el-icon-edit"size="mini":disabled="single"@click="handleUpdate"v-hasPermi="['testuser:testuser:edit']">修改</el-button><el-buttontype="success"plainicon="el-icon-edit"size="mini":disabled="single"@click="handleTestNormal">自定义</el-button></el-col><el-col :span="1.5"><el-buttontype="danger"plainicon="el-icon-delete"size="mini":disabled="multiple"@click="handleDelete"v-hasPermi="['testuser:testuser:remove']">删除</el-button></el-col><el-col :span="1.5"><el-buttontype="warning"plainicon="el-icon-download"size="mini"@click="handleExport"v-hasPermi="['testuser:testuser:export']">导出</el-button></el-col><right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar></el-row><el-table v-loading="loading" :data="testuserList" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="主键" align="center" prop="id" /><el-table-column label="姓名" align="center" prop="name" /><el-table-column label="密码" align="center" prop="pwd" /><el-table-column label="操作" align="center" class-name="small-padding fixed-width"><template slot-scope="scope"><el-buttonsize="mini"type="text"icon="el-icon-edit"@click="handleUpdate(scope.row)"v-hasPermi="['testuser:testuser:edit']">修改</el-button><el-buttonsize="mini"type="text"icon="el-icon-edit"@click="handleTestNormal(scope.row)"v-hasPermi="['testuser:testuser:edit']">自定义</el-button><el-buttonsize="mini"type="text"icon="el-icon-delete"@click="handleDelete(scope.row)"v-hasPermi="['testuser:testuser:remove']">删除</el-button></template></el-table-column></el-table><paginationv-show="total>0":total="total":page.sync="queryParams.pageNum":limit.sync="queryParams.pageSize"@pagination="getList"/><!-- 添加或修改testuser对话框 --><el-dialog :title="title" :visible.sync="open" width="500px" append-to-body><el-form ref="form" :model="form" :rules="rules" label-width="80px"><el-form-item label="姓名" prop="name"><el-input v-model="form.name" placeholder="请输入姓名" /></el-form-item><el-form-item label="密码" prop="pwd"><el-input v-model="form.pwd" placeholder="请输入密码" /></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button type="primary" @click="submitForm">确 定</el-button><el-button @click="cancel">取 消</el-button></div></el-dialog></div>
</template><script>
import { listTestuser, getTestuser, delTestuser, addTestuser, updateTestuser, handleTest} from "@/api/testuser/testuser";export default {name: "Testuser",data() {return {// 遮罩层loading: true,// 选中数组ids: [],// 非单个禁用single: true,// 非多个禁用multiple: true,// 显示搜索条件showSearch: true,// 总条数total: 0,// testuser表格数据testuserList: [],// 弹出层标题title: "",// 是否显示弹出层open: false,// 查询参数queryParams: {pageNum: 1,pageSize: 10,name: null,pwd: null,},// 表单参数form: {},// 表单校验rules: {},//自定义表单校验check: {},reconnectCount : 0};},created() {this.getList();this.connectWebSocket();},methods: {connectWebSocket: function() {// 创建 WebSocket 连接if('WebSocket' in window){this.socket = new WebSocket("ws://127.0.0.1:8080/websocket/yuanrenjie");} else{alert('Not support websocket')};//最大尝试链接次数const maxReconnectAttempts = 10;const reconnectInterval = 1000; // 1秒const tryReconnect = () => {if (this.reconnectCount < maxReconnectAttempts) {console.log('Reconnecting...');this.connectWebSocket();this.reconnectCount++;} else {console.log('Exceeded max reconnect attempts, stopping reconnecting.');}};this.socket.addEventListener('open', (event) => {console.log('WebSocket 连接已建立')let msg = {// clientName: '发送erp仓库连接消息',clientName: '',message: null,printPiece: 1,labelInfo: null}let json = JSON.stringify(msg)this.socket.send(json)})this.socket.addEventListener('message', (event) => {//接收到的数据const receivedData = event.data;// alert('接收到的信息receivedData: ' + receivedData);console.log('Received data:', receivedData)})// socket.addEventListener('close', (event) => {this.socket.addEventListener('close', (event) => {console.log('WebSocket connection closed')// 在连接关闭时触发重连逻辑// setTimeout(() => {// console.log('Reconnecting...')// this.connectWebSocket()// }, 1000) // 1秒后重连// 清除重连计数,防止无限递增// 在连接关闭后,间隔一段时间进行重连setTimeout(tryReconnect, reconnectInterval);})// socket.addEventListener('error', (event) => {this.socket.addEventListener('error', (event) => {console.error('WebSocket error:', event)// 在错误发生时触发重连逻辑// setTimeout(() => {// console.log('Reconnecting...')// this.connectWebSocket()// }, 1000) // 1秒后重连// 在连接错误后,间隔一段时间进行重连setTimeout(tryReconnect, reconnectInterval);})window.addEventListener('beforeunload', (event) => {// 当 tab 关闭时,关闭 WebSocket 连接this.socket.close();});},/** 查询testuser列表 */getList() {this.loading = true;listTestuser(this.queryParams).then(response => {this.testuserList = response.rows;this.total = response.total;this.loading = false;});},// 取消按钮cancel() {this.open = false;this.reset();},// 表单重置reset() {this.form = {id: null,name: null,pwd: null,createTime: null,updateTime: null};this.resetForm("form");},/** 搜索按钮操作 */handleQuery() {this.queryParams.pageNum = 1;this.getList();},/** 重置按钮操作 */resetQuery() {this.resetForm("queryForm");this.handleQuery();},// 多选框选中数据handleSelectionChange(selection) {this.ids = selection.map(item => item.id)this.single = selection.length!==1this.multiple = !selection.length},/** 新增按钮操作 */handleAdd() {this.reset();this.open = true;this.title = "添加testuser";},/** 修改按钮操作 */handleUpdate(row) {this.reset();const id = row.id || this.idsgetTestuser(id).then(response => {this.form = response.data;this.open = true;this.title = "修改testuser";});},/** 自定义按钮操作 */handleTestNormal(row) {if(row.id){handleTest(this.check).then(response => {});}else {const id = row.id || this.idsgetTestuser(id).then(response => {this.check = response.data;handleTest(this.check).then(response => {});});}},/** 提交按钮 */submitForm() {this.$refs["form"].validate(valid => {if (valid) {if (this.form.id != null) {updateTestuser(this.form).then(response => {this.$modal.msgSuccess("修改成功");this.open = false;this.getList();});} else {addTestuser(this.form).then(response => {this.$modal.msgSuccess("新增成功");this.open = false;this.getList();});}}});},/** 删除按钮操作 */handleDelete(row) {const ids = row.id || this.ids;this.$modal.confirm('是否确认删除testuser编号为"' + ids + '"的数据项?').then(function() {return delTestuser(ids);}).then(() => {this.getList();this.$modal.msgSuccess("删除成功");}).catch(() => {});},/** 导出按钮操作 */handleExport() {this.download('testuser/testuser/export', {...this.queryParams}, `testuser_${new Date().getTime()}.xlsx`)}/** 导出发货计划 */// handleExportExcelNeed(row) {// const ids = row.id || this.ids;// toExcel(ids).then(response => {// this.$alert(response.msg, "导出成功", response.msg);// });//// }}
};
</script>
后端java:
开启配置
package com.ruoyi.web.core.config;import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Component
public class WebSocketConfig {/*** ServerEndpointExporter 作用** 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint** @return*/@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
具体接口:
package com.ruoyi.web.core.config;import com.ruoyi.common.annotation.Anonymous;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;@Anonymous
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}") // 接口路径 ws://localhost:8087/webSocket/userId;
public class WebSocketServer {//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 用户ID*/private String userId;//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。//虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。// 注:底下WebSocket是当前类名private static CopyOnWriteArraySet<WebSocketServer> webSockets =new CopyOnWriteArraySet<>();// 用来存在线连接用户信息private static ConcurrentHashMap<String,Session> sessionPool = new ConcurrentHashMap<String,Session>();/*** 链接成功调用的方法*/@OnOpenpublic 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) {}}/*** 链接关闭调用的方法*/@OnClosepublic void onClose() {try {webSockets.remove(this);sessionPool.remove(this.userId);log.info("【websocket消息】连接断开,总数为:"+webSockets.size());} catch (Exception e) {}}/*** 收到客户端消息后调用的方法** @param message* @param*/@OnMessagepublic void onMessage(String message) {log.info("【websocket消息】收到客户端消息:"+message);}/** 发送错误时的处理* @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误,原因:"+error.getMessage());error.printStackTrace();}// 此为广播消息public void sendAllMessage(String message) {log.info("【websocket消息】广播消息:"+message);for(WebSocketServer webSocket : webSockets) {try {if(webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {e.printStackTrace();}}}// 此为单点消息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) {e.printStackTrace();}}}// 此为单点消息(多人)public void sendMoreMessage(String[] userIds, String message) {for(String userId:userIds) {Session session = sessionPool.get(userId);if (session != null&&session.isOpen()) {try {log.info("【websocket消息】 单点消息:"+message);session.getAsyncRemote().sendText(message);} catch (Exception e) {e.printStackTrace();}}}}}相关文章:
springboot+vue实现websocket通信实例,进入页面建立连接
springbootvue实现websocket通信实例 进入页面建立连接 前端代码: <template><div class"app-container"><el-form :model"queryParams" ref"queryForm" size"small" :inline"true" v-show&qu…...
【个人记录】同步Linux服务器时间和时区
修改时区 timedatectl set-timezone Asia/Shanghai时间同步 使用ntp进行同步,时间服务器使用阿里云NTP服务器 安装NTP服务 yum install -y ntp写入NTP配置文件 cat > /etc/ntp.conf <<EOF driftfile /var/lib/ntp/drift restrict default nomodify no…...
面试常问-如何判断链表有环、?
如何判断链表有环 题目:解决方案一:解决方案二:解决方案三: 题目: 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,…...
基于springboot实现农机电招平台系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现农机电招平台系统演示 摘要 随着农机电招行业的不断发展,农机电招在现实生活中的使用和普及,农机电招行业成为近年内出现的一个新行业,并且能够成为大群众广为认可和接受的行为和选择。设计农机电招平台的目的就是借助计算…...
森林无人机高效解决巡查难题,林区防火掀新篇
山东省某市为了强化森林火灾防范,采用了一项新兴手段——复亚智能无人机森林火情监测系统。这套系统在AI飞行大脑的指挥下,让无人机在空中巡逻,实现了无人机森林防火系统的实施落地。 一、AI大脑如何引领森林无人机高空巡逻? 在山…...
python 爬虫之 爬取网站信息并保存到文件
文章目录 前期准备探索该网页的HTML码的特点开始编写代码存入文件总的程序文件存储效果 前期准备 随便找个网站进行爬取,这里我选择的是(一个卖书的网站) https://www.bookschina.com/24hour/62700000/ 我的目的是爬取这个网站的这个页面的书籍的名称以…...
kubelet漏洞CVE-2020-8559复现与分析
首先下载源码 git clone --branch v1.17.1 --single-branch https://github.com/kubernetes/kubernetes.git 参考 移花接木:看CVE-2020-8559如何逆袭获取集群权限-腾讯云开发者社区-腾讯云...
基于C#实现奇偶排序
这篇就从简单一点的一个“奇偶排序”说起吧,不过这个排序还是蛮有意思的,严格来说复杂度是 O(N2),不过在多核的情况下,可以做到 N2 /(m/2)的效率,这里的 m 就是待排序的个数,当 m100,复杂度为 N…...
Kibana部署
服务器 安装软件主机名IP地址系统版本配置KibanaElk10.3.145.14centos7.5.18042核4G软件版本:nginx-1.14.2、kibana-7.13.2-linux-x86_64.tar.gz 1. 安装配置Kibana (1)安装 [rootelk ~]# tar zxf kibana-7.13.2-linux-x86_64.tar.gz -C…...
【Linux】了解进程的基础知识
进程 1. 进程的概念1.1 进程的理解1.2 Linux下的进程1.3 查看进程属性1.4 getpid和getppid 2. 创建进程3. 进程状态4. 进程优先级5. 进程切换6. 环境变量7. 本地变量与内建命令 1. 进程的概念 一个已经加载到内存中的程序,叫做进程(也叫任务)…...
ES6 — ES14 新特性
一、ES6 新特性(2015) 1. let和const 在ES6中,新增了let和const关键字,其中 let 主要用来声明变量,而 const 通常用来声明常量。let、const相对于var关键字有以下特点: 特性varletconst变量提升✔️全局…...
附录12-time.h的常用方法
目录 1 数据类型 1.1 time_t 1.2 tm 1.3 clock_t 2 相关知识 3 获取从1970年1月1日以来的UTC秒数 time() 4 获取本时区时间字符串 ctime() 5 获取GMT时间的tm gmttime() 6 获取本地时间的tm localtime() 7 记录当前毫秒数 clock() 8 将表示本地时间的tm转…...
C语言公交车之谜(ZZULIOJ1232:公交车之谜)
题目描述 听说郑州紫荆山公园有英语口语角,还有很多外国人呢。为了和老外对上几句,这周六早晨birdfly拉上同伴早早的就坐上了72路公交从学校向紫荆山进发。一路上没事干,birdfly开始思考一个问题。 从学校到紫荆山公园共有n(1<n<20)站路…...
Liunx Ubuntu Server 安装配置 Docker
1. 安装Docker 1.1 更新软件包列表 sudo apt update1.2 添加Docker存储库 sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-a…...
Oracle ORA12514 监听程序当前无法识别连接描述符中请求的服务
最简单的有可能是你的服务还没有开启,需要启动服务!!!! 在连接数据库的时候,有时会遇到一个“ORA12514:监听程序当前无法识别连接描述符中请求的服务”的错误,这个错误其实就是数据…...
druid keepAlive 导致数据库连接数飙升
一.背景 应用在执行完某个复杂业务,主要包含20几个查询SQL的操作后,会导致数据库连接池一直升高 druid版本:1.2.11 druid配置文件: spring.datasource.druid.maxActive100 spring.datasource.druid.initialSize20 spring.datas…...
四川竹哲电子商务有限公司深耕抖音电商服务领域
随着数字经济的飞速发展,抖音电商服务成为了越来越多企业的首选。在这个充满机遇与挑战的时代,四川竹哲电子商务有限公司以其卓越的实力和专业的服务,成为了抖音电商服务领域的佼佼者。 一、深耕抖音电商服务领域 作为一家专注于抖音电商服务…...
爬虫中XPath语法四个重要概念及示例
一、根节点与非根节点 1、/div :选择div节点,只有当它是文档的根节点时。 2、//div:选择文档中所有的div节点(包括非根节点)。 二、通过属性选择节点 1、//href:选择带href属性的所有节点。 2、//a[hrefhttp://ba…...
MySQL-03-索引
索引是提高MySQL查询性能的一个重要途径,但过多的索引可能会导致过高的磁盘使用率以及过高的内存占用,从而影响应用程序的整体性能。应当尽量避免事后才想起添加索引,因为事后可能需要监控大量的SQL才能定位到问题所在,而且添加索…...
CSS-长度单位篇
px:像素em:相对元素font-size的倍数rem:相对根字体大小,html标签就是根%:相对父元素计算 注意:CSS中设置长度,必须加单位,否则样式无效!...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
