WebSocket实现群聊功能、房间隔离
- 引用WebSocket相关依赖
<dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-websocket</artifactId><version>4.3.30.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-messaging</artifactId><version>4.3.30.RELEASE</version></dependency>
- 后端代码实现,很简单分为消息封装和消息处理
package com.risen.brain.websocket;import com.alibaba.fastjson.JSONObject;
import com.risen.brain.websocket.entity.Message;
import com.risen.brain.websocket.entity.MessageData;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;/*** @author Administrator*/
@ServerEndpoint("/public/xzdnTaskSocket/{platform}/{groupId}/{selfId}")
@Component
public class MessageWebSocket {private static Map<String, MessageWebSocket> userMap = new ConcurrentHashMap<>();private static Map<String, Set<MessageWebSocket>> roomMap = new ConcurrentHashMap<>();private Session session;private String selfId;//建立连接成功调用@OnOpenpublic void onOpen(Session session, @PathParam(value = "platform") String platform, @PathParam(value = "groupId") String groupId, @PathParam(value = "selfId") String selfId) {this.session = session;this.selfId = selfId;userMap.put(selfId, this);if (!roomMap.containsKey(groupId)) {Set<MessageWebSocket> set = new HashSet<>();set.add(userMap.get(selfId));roomMap.put(groupId, set);} else {roomMap.get(groupId).add(this);}MessageData messageData = new MessageData();messageData.setSelfId(selfId);messageData.setGroupId(groupId);messageData.setPlatform(platform);messageData.setType("meta");messageData.setDetailType("online");System.out.println(selfId + "加入了群聊!");Message dataMessage = new Message(selfId + "加入了群聊!");messageData.setMessages(Arrays.asList(dataMessage));sendMessageTo(messageData, groupId, selfId);}//关闭连接时调用@OnClosepublic void onClose(@PathParam(value = "selfId") String selfId, @PathParam("groupId") String groupId) {if (roomMap.containsKey(groupId)) {Set<MessageWebSocket> set = roomMap.get(groupId);for (MessageWebSocket item : set) {if (item.selfId.equals(selfId)) {set.remove(item);}}}}//收到客户端信息@OnMessagepublic void onMessage(String message, @PathParam(value = "platform") String platform, @PathParam(value = "selfId") String selfId, @PathParam("groupId") String groupId) {MessageData messageData = new MessageData();List<Message> objects = new ArrayList<>();Message mg = new Message();mg.setData(message);mg.setType("text");objects.add(mg);messageData.setMessages(objects);messageData.setPlatform(platform);sendMessageTo(messageData, groupId, selfId);//根据bean名称获取对象,可以对消息进行记录//IXzdnInvestigateCityService xzdnInvestigateCityService1 = SpringUtil.getBean("xzdnInvestigateCityService");//XzdnInvestigateCity city = new XzdnInvestigateCity();//List<Map> groupCityCount = xzdnInvestigateCityService1.findGroupCityCount(city);System.out.println("2222");/*TableDynaModel tableDynaModel = tableDynaDao.newDynaModel("xzdn_task_message_info");tableDynaModel.set("xzdnMessageId", messageData.getId());tableDynaModel.set("xzdnMessagePlatform", messageData.getPlatform());tableDynaModel.set("xzdnMessageSelfid", messageData.getSelfId());tableDynaModel.set("xzdnMessageSelfname", messageData.getSelfName());tableDynaModel.set("xzdnMessageSelfdeptid", messageData.getSelfDeptId());tableDynaModel.set("xzdnMessageSelfdeptname", messageData.getSelfDeptName());tableDynaModel.set("xzdnMessageTime", messageData.getTime());tableDynaModel.set("xzdnMessageType", messageData.getType());tableDynaModel.set("xzdnMessageDetailtype", messageData.getDetailType());tableDynaModel.set("xzdnMessageMessagetype", messageData.getMessageType());tableDynaModel.set("xzdnMessageGroupid", messageData.getGroupId());tableDynaModel.set("xzdnMessageMessages", JSONObject.toJSONString(messageData.getMessages()));tableDynaDao.save(tableDynaModel);*/}//错误时调用@OnErrorpublic void onError(Session session, Throwable throwable) {System.out.println("发生错误");throwable.printStackTrace();}/*** 群聊** @param message 消息* @param groupId 房间号* @param selfId 发送人*/public static void sendMessageTo(MessageData message, String groupId, String selfId) {message.setGroupId(groupId);message.setSelfId(selfId);if (roomMap.containsKey(groupId)) {for (MessageWebSocket item : roomMap.get(groupId)) {//if (!item.selfId.equals(selfId)) {item.session.getAsyncRemote().sendText(JSONObject.toJSONString(message));// }}}}/*** 私聊** @param message 消息* @param toSelfId 接收人*/public void sendUserTo(String message, String toSelfId) {if (userMap.containsKey(toSelfId)) {userMap.get(toSelfId).session.getAsyncRemote().sendText(message);}}
}
- 消息封装,里面包含房间信息,用户信息、消息
package com.risen.brain.websocket.entity;import lombok.Data;@Data
public class Message {/*** 消息类型 text,json,image,audio,video,file,markdown,btn*/private String type;/*** 内容 媒体类容均为文件id/url/详情json*/private String data;public Message() {}public Message(String data) {this.data = data;this.type = "text";}
}package com.risen.brain.websocket.entity;import com.risen.brain.utils.SequenceUtils;
import lombok.Data;import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;/*** Created with IntelliJ IDEA.* * @Author: yxz* @Date: 2021/10/18/10:21* @Description:*/
@Data
public class MessageData {/*** 事件唯一标识符*/private BigInteger id;/*** 实现平台名称,协议名称 web,dd*/private String platform;/*** 消息发送人 id*/private String selfId;/*** 消息发送人*/private String selfName;/*** 消息发送人部门id*/private String selfDeptId;/*** 消息发送人部门*/private String selfDeptName;/*** 事件发生时间(Unix 时间戳),单位:秒*/private Long time;/*** 事件类型,必须是 meta、message、notice、request 中的一个,分别表示元事件、消息事件、通知事件和请求事件*/private String type;/*** 事件详细类型* meta: online,heartbeat 分别表示 首次连接,心跳包* message:* notice: remove 删除通知* request:*/private String detailType;/*** 消息类型 1:群消息*/private String messageType;/*** 群消息时的群id*/private String groupId;/*** 消息段*/private List<Message> messages;public MessageData() {this.id = SequenceUtils.nextId();this.time = LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();}
}
- 前端代码实现,本人不是专业前端,网上随便找个了页面做交互
<!DOCTYPE HTML>
<html><head><meta charset="UTF-8"><title>My WebSocket</title><style>#message {margin-top: 40px;border: 1px solid gray;padding: 20px;}</style>
</head><body>
<button onclick="conectWebSocket()">连接WebSocket</button>
<button onclick="closeWebSocket()">断开连接</button>
<hr />
<br />
消息:<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<div id="message"></div>
</body>
<script type="text/javascript">//获取地址栏参数,key:参数名称const urlParams = new URLSearchParams(window.location.search);//发信息用户const user = urlParams.get('user');//房间名称const type = urlParams.get('group');var websocket = null;function conectWebSocket() {//判断当前浏览器是否支持WebSocketif ('WebSocket' in window) {websocket = new WebSocket("ws://localhost:8081/xzdn/public/xzdnTaskSocket/web/"+type+"/"+user);} else {alert('Not support websocket')}//连接发生错误的回调方法websocket.onerror = function () {setMessageInnerHTML("error");};//连接成功建立的回调方法websocket.onopen = function (event) {setMessageInnerHTML("tips: 连接成功!");}//接收到消息的回调方法websocket.onmessage = function (event) {setMessageInnerHTML(event.data);}//连接关闭的回调方法websocket.onclose = function () {setMessageInnerHTML("tips: 关闭连接");}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function () {websocket.close();}}//将消息显示在网页上function setMessageInnerHTML(innerHTML) {document.getElementById('message').innerHTML += innerHTML + '<br/>';}//关闭连接function closeWebSocket() {websocket.close();}//发送消息function send() {var message = document.getElementById('text').value;websocket.send(message,"web",user,type);}</script></html>
- 最终效果
超人发送信息:房间号:加班放假
王亮在房间“加班放假”中,收到了超人信息,并进行回答
四环单独在“吃喝玩乐”房间并没有收到房间“加班放假”中的消息
相关文章:

WebSocket实现群聊功能、房间隔离
引用WebSocket相关依赖 <dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version></dependency><dependency><groupId>org.springframework</grou…...

顶顶通呼叫中心中间件实现随时启动和停止质检(mod_cti基于FreeSWITCH)
文章目录 前言联系我们拨号方案启动停止ASR执行FreeSWITCH 命令接口启动ASR接口停止ASR接口 通知配置cti.json配置质检结果写入数据库 前言 顶顶通呼叫中心中间件的实时质检功能是由两个模块组成:mod_asr 和 mod_qc。 mod_asr:负责调用ASR将用户们在通…...

基于conda包的环境创建、激活、管理与删除
Anaconda是一个免费、易于安装的包管理器、环境管理器和 Python 发行版,支持平台包括Windows、macOS 和 Linux。下载安装地址:Download Anaconda Distribution | Anaconda 很多不同的项目可能需要使用不同的环境。例如某个项目需要使用pytorch1.6&#x…...
处理线程安全的列表CopyOnWriteArrayList 和Collections.synchronizedList
ConcurrentModificationException 是 Java 中的一种异常,用于指示在迭代集合时,该集合的结构发生了并发修改。 在 Java 中,许多集合类(如 ArrayList, HashMap 等)都不是线程安全的。如果一个线程在迭代集合的同时&…...

技术成神之路:设计模式(六)策略模式
1.介绍 策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,封装每一个算法,并使它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户端。 2.主要作用 策略模式的主要作用是将算法或行为…...
华为OD机考题(HJ90 合法IP)
前言 经过前期的数据结构和算法学习,开始以OD机考题作为练习题,继续加强下熟练程度。 描述 IPV4地址可以用一个32位无符号整数来表示,一般用点分方式来显示,点将IP地址分成4个部分,每个部分为8位,表示成…...

值得关注的数据资产入表
不错的讲解视频,来自:第122期-杜海博士-《数据资源入表及数据资产化》-大数据百家讲坛-厦门大学数据库实验室主办第122期-杜海博士-《数据资源入表及数据资产化》-大数据百家讲坛-厦门大学数据库实验室主办-20240708_哔哩哔哩_bilibili...
Postman API性能测试:解锁高级技巧的宝库
🚀 Postman API性能测试:解锁高级技巧的宝库 在API开发和测试过程中,性能测试是确保API稳定性和可靠性的关键环节。Postman作为API测试的强大工具,提供了多种性能测试功能和高级技巧,帮助开发者深入分析API的性能表现…...

stm32中断详解
stm32中断详解 文章目录 stm32中断详解1.什么是中断?1.STM32中断系统特点2.中断处理流程3.中断配置与使用 2.AFIO寄存器3.NVIC寄存器3.中断分组、抢占优先级和响应优先级1. 中断分组2. 抢占优先级3. 响应优先级4.配置与应用 4.中断服务函数5.配置中断流程1.配置外设…...
【LeetCode】最小栈
目录 一、题目二、解法完整代码 一、题目 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。 void push(int val) 将元素val推入堆栈。 void pop() 删除堆栈顶部的元…...

链接追踪系列-09.spring cloud项目整合elk显示业务日志
准备工作: 参看本系列之前篇:服务器安装elastic search 本机docker启动的kibana-tencent 使用本机安装的logstash。。。 本微服务实现的logstash配置如下: 使用腾讯云redis 启动本机mysql 启动本机docker 启动nacos,微服务依赖它作为…...
老年生活照护实训室:让养老护理更个性化
本文探讨了老年生活照护实训室在实现养老护理个性化方面的重要作用。通过分析其提供的实践环境、专业培训、模拟案例和评估机制,阐述了如何培养护理人员的个性化服务能力,以满足老年人多样化的需求,提高养老护理的质量和满意度。 在老龄化社会…...
c++课后作业
把字符串转换为整数 int main() {char pn[21];cout << "请输入一个由数字组成的字符串: ";cin >> pn;int last 0;int res[10];int j strlen(pn);int idx 2;cout << "请选择(2-二进制,10-十进制…...

SpringBoot+Vue实现简单的文件上传(txt篇)
SpringBootVue实现简单的文件上传 1 环境 SpringBoot 3.2.1,Vue 2,ElementUI 2 页面 3 效果:只能上传txt文件且大小限制为2M,选择文件后自动上传。 4 前端代码 <template><div class"container"><el-…...
LLMs之RAG:GraphRAG(本质是名词Knowledge Graph/Microsoft微软发布)的简介、安装和使用方法、案例应用之详细攻略
LLMs之RAG:GraphRAG(本质是名词Knowledge Graph/Microsoft微软发布)的简介、安装和使用方法、案例应用之详细攻略 导读:2024年7月3日,微软正式开源发布GraphRAG。GraphRAG可以提高大型语言模型在私有数据集上的推理能力。 背景痛点࿱…...
Linux 之前的 Unix 桌面沉浮启示录
It takes more than open source, it takes open standards and consensus 仅仅开源还不足以实现开放,还需开放标准和建立共识 Steven J. Vaughan-Nichols Sat 27 Jan 2024 // 12:33 UTC 现在,由于有了安卓和 ChromeOS,Linux 已成为重要的终端…...
面试问题梳理:项目中防止配置中的密码泄露-Jasypt
背景 想起面试的时候,面试官问我现在大家用Spring框架,数据库、ES之类的密码都是配置在配置文件中的,有很大的安全隐患,你有考虑过怎么解决嘛? 当时我回答是可以在项目启动的过程中的命令行追加的方式,感觉…...
engine.addImportPath()用于向 QML 引擎添加新的模块搜索路径
engine.addImportPath() 是 QQmlApplicationEngine 类中的一个方法,用于向 QML 引擎添加新的模块搜索路径。这在需要加载自定义模块或从非标准位置加载 QML 文件时非常有用。通过使用 addImportPath() 方法,可以让 QML 引擎在额外的路径中查找 QML 模块。…...

ServiceNow UI Jelly模板注入漏洞复现(CVE-2024-4879)
0x01 产品简介 ServiceNow 是一个业务转型平台。通过平台上的各个模块,ServiceNow 可用于从人力资源和员工管理到自动化工作流程或作为知识库等各种用途。 0x02 漏洞概述 由于ServiceNow的Jelly模板输入验证不严格,导致未经身份验证的远程攻击者可通过构造恶意请求利用,在…...

项目部署笔记
1、安全组需开放(如果不开放配置nginx也访问不到) 2、域名解析配置IP(子域名也需配置IP,IP地址可以不同) 3、如果出现图片获其他的文件找不到的情况请仔细检查一下路径是否正确 4、服务器nginx配置SSL证书后启动报错: nginx: […...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...