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>
  • 最终效果

超人发送信息:房间号:加班放假

chao在这里插入图片描述

王亮在房间“加班放假”中,收到了超人信息,并进行回答

在这里插入图片描述

四环单独在“吃喝玩乐”房间并没有收到房间“加班放假”中的消息
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安全防御:智能选路

目录 一、智能选路 1.1 就近选路 1.2 策略路由 1.3 虚拟系统---VRF 二、全局选路策略 1&#xff0c;基于链路带宽进行负载分担 2&#xff0c;基于链路质量进行负载分担 3&#xff0c;基于链路权重的负载分担 4&#xff0c;根据链路优先级的主备备份 DNS透明代理 一、…

Java学习高级四

JDK8开始&#xff0c;接口新增了三种形式的方法 接口的多继承 内部类 成员内部类 静态内部类 局部内部类 匿名内部类 import javax.swing.*; import java.awt.event.ActionEvent;public class Test {public static void main(String[] args) {// 扩展 内部类在开发中的真实使用…

STM32的TIM1之PWM互补输出_死区时间和刹车配置

STM32的TIM1之PWM互补输出_死区时间和刹车配置 1、定时器1的PWM输出通道 STM32高级定时器TIM1在用作PWM互补输出时&#xff0c;共有4个输出通道&#xff0c;其中有3个是互补输出通道&#xff0c;如下&#xff1a; 通道1&#xff1a;TIM1_CH1对应PA8引脚,TIM1_CH1N对应PB13引…

‍我想我大抵是疯了,我喜欢上了写单元测试

前言 大家好我是聪。相信有不少的小伙伴喜欢写代码&#xff0c;但是对于单元测试这些反而觉得多此一举&#xff0c;想着我都在接口文档测过了&#xff01;还要写什么单元测试&#xff01;写不了一点&#xff01;&#xff01; 由于本人也是一个小小程序猿&#x1f649;&#xf…

nginx代理缓存

在服务器架构中&#xff0c;反向代理服务器除了能够起到反向代理的作用之外&#xff0c;还可以缓存一些资源&#xff0c;加速客户端访问&#xff0c;nginx的ngx_http_proxy_module模块不仅包含了反向代理的功能还包含了缓存功能。 1、定义代理缓存规则 参数详解&#xff1a; p…

【前端】ikun-qrcode:极简的二维码生成组件,使用view而非canvas避免层级问题

文章目录 背景ikun-qrcode界面效果如何发布一款自己的插件到uniapp市场。&#xff08;5分钟搞定&#xff09; 背景 之前在uniapp上100行搞定二维码生成&#xff0c; 现在封装为vue组件分享出来&#xff1a; 下载地址&#xff1a; https://ext.dcloud.net.cn/plugin?id19351 …

【MySQL篇】Percona XtraBackup工具备份指南:常用备份命令详解与实践(第二篇,总共五篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

jenkins系列-01.docker安装jenkins

进入官网&#xff1a;https://www.jenkins.io/ 使用LONG term support版本&#xff1a;2.387.1 docker pull jenkins/jenkins:2.387.1-lts 拉取镜像&#xff1a; 编写docker-compose文件&#xff1a; 启动jenkins: 查看启动日志&#xff1a; 默认生成的密码&#xff1a;…

防火墙NAT地址转换和智能选举综合实验

一、实验拓扑 目录 一、实验拓扑 二、实验要求&#xff08;接上一个实验要求后&#xff09; 三、实验步骤 3.1办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换) 3.2分公司设备可以通过总公司的移动链路和电信链路访…

【Android】活动之间的穿梭

引入 在活动的初学建立了一个简单的活动&#xff0c;但只有一个活动不是过于简单&#xff0c;在你使用手机的时候按下一个按钮可能会跳转到下一个界面&#xff0c;此时就是活动之间的穿梭&#xff1a;使用Intent在活动之间穿梭 Intent&#xff1a;是android程序中各组件之间进…

Android10.0 锁屏分析-KeyguardPatternView图案锁分析

首先一起看看下面这张图&#xff1a; 通过前面锁屏加载流程可以知道在KeyguardSecurityContainer中使用getSecurityView()根据不同的securityMode inflate出来&#xff0c;并添加到界面上的。 我们知道&#xff0c;Pattern锁所使用的layout是 R.layout.keyguard_pattern_view&a…

HarmonyOS NEXT学习——@BuilderParam装饰器

初步理解&#xff0c;相当于VUE的插槽slot Builder function overBuilder() {}Component struct Child {label: string ChildBuilder customBuilder() {}Builder customChangeThisBuilder() {}BuilderParam customBuilderParam: () > void this.customBuilder; // 使用自定…

RK3568笔记三十六:LED驱动开发(设备树)

若该文为原创文章&#xff0c;转载请注明原文出处。 记录使用设备树编写一个简单的 LED 灯驱动程序 一、编程思路 程序编写的主要内容为添加 LED 灯的设备树节点、在驱动程序中使用 of 函数获取设备节点中的 属性&#xff0c;编写测试应用程序。 • 首先向设备树添加 LED 设备…

使用Python Turtle绘制圣诞树和装饰

简介(❤ ω ❤) 在这篇文章中&#xff0c;我们将探索如何使用Python的Turtle模块来绘制一个充满节日气氛的圣诞树&#xff0c;以及一些可爱的装饰品。Turtle是一个受Logo语言启发的图形库&#xff0c;非常适合初学者学习编程和创建图形。 码农不是吗喽&#xff08;大学生版&…

STM32智能工业自动化监控系统教程

目录 引言环境准备智能工业自动化监控系统基础代码实现&#xff1a;实现智能工业自动化监控系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;工业自动化与管理问题解决方案与优化收尾与总结 1. 引言 智能…

初学者如何通过建立个人博客盈利

建立个人博客不仅能让你在网上表达自己&#xff0c;还能与他人建立联系。通过博客&#xff0c;可以创建自己的空间&#xff0c;分享想法和故事&#xff0c;并与有相似兴趣和经历的人交流。 本文将向你展示如何通过建立个人博客来实现盈利。你将学习如何选择博客主题、挑选合适…

React学习笔记02-----React基本使用

一、React简介 想实现页面的局部刷新&#xff0c;而不是整个网页的刷新。AJAXDOM可以实现局部刷新 1.特点 &#xff08;1&#xff09;虚拟DOM 开发者通过React来操作原生DOM&#xff0c;从而构建页面。 React通过虚拟DOM来实现&#xff0c;可以解决DOM的兼容性问题&#x…

如何在gitee上创建远程仓库?

登录gitee网站后 填写自己的仓库信息后点击创建 然后来到一个新的界面可以看到自己的仓库地址 这样一个空白的仓库就建立好了 也可以按需选择初始化仓库

Python爬虫入门篇学习记录

免责声明 本文的爬虫知识仅用于合法和合理的数据收集&#xff0c;使用者需遵守相关法律法规及目标网站的爬取规则&#xff0c;尊重数据隐私&#xff0c;合理设置访问频率&#xff0c;不得用于非法目的或侵犯他人权益。因使用网络爬虫产生的任何法律纠纷或损失&#xff0c;由使用…

快手开源LivePortrait,实现表情姿态极速迁移,GitHub 6.5K Star

近日&#xff0c;快手可灵大模型团队开源了名为LivePortrait的可控人像视频生成框架&#xff0c;能够准确、实时地将驱动视频的表情、姿态迁移到静态或动态人像视频上&#xff0c;生成极具表现力的视频结果。如下动图所示&#xff1a; 来自网友测试LivePortrait 来自网友测试Li…