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.mzph.cn/pingmian/46737.shtml

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

相关文章

mac电脑pdf合并,macpdf合并成一个pdf

在数字化办公和学习的今天&#xff0c;pdf文件因其跨平台兼容性强、格式稳定而成为了最受欢迎的文档格式之一。但随之而来的问题也接踵而至&#xff0c;如何将多个pdf文件合并为一个&#xff1f;这不仅关系到文档的整洁性&#xff0c;更是时间管理的重要环节。今天&#xff0c;…

股指期货如何交易?有哪些交易策略和操作技巧?

股指期货交易提供了多种策略和技巧&#xff0c;以适应不同的市场情况和投资者需求。以下是对股指期货交易的一些基本策略和操作技巧的介绍。 1.卖出股指期货套期保值 对于那些已经持有或预计将持有股票的投资者&#xff0c;如果对未来市场走势不确定或预期市场将下跌&#xf…

PostgreSQL安装/卸载(CentOS、Windows)

说明&#xff1a;PostgreSQL与MySQL一样&#xff0c;是一款开源免费的数据库技术&#xff0c;官方口号&#xff1a;The World’s Most Advanced Open Source Relational Database.&#xff08;世界上最先进的开源关系数据库&#xff09;&#xff0c;本文介绍如何在Windows、Cen…

windows下pip修改镜像地址

不修改镜像地址安装时候就会很慢 永久修改 pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/ 清华大学源&#xff1a;https://pypi.tuna.tsinghua.edu.cn/simple/ 豆瓣源 &#xff1a;http://pypi.douban.com/simple/ 腾讯源&#xff1a;http://mir…

【C++ Primer Plus】学习笔记 2

文章目录 前言一、简单变量1. 变量名2. 整型2.1 整型类型2.2 无符号类型2.3 char 类型2.4 signed char 和unsigned char2.5 bool类型 3.浮点数4.类型转换 总结 前言 该系列文章请点击专栏查看 一、简单变量 1. 变量名 C提倡使用有一定含义的变量名&#xff0c;必须遵循几种简…

ai绘画软件哪个好用?这5个工具值得尝试

这段时间云南上亿只蝴蝶翩翩起舞&#xff0c;仿佛大自然最绚烂的画布&#xff0c;不禁让人思考&#xff0c;若能将这份灵动与色彩完美捕捉于画布之上&#xff0c;该是多么奇妙的事。 幸运的是&#xff0c;现在无需远行&#xff0c;无需等待&#xff0c;只需一些创意&#xff0…

【笔记-MyBatis】StatementHandler

Author&#xff1a;赵志乾 Date&#xff1a;2024-07-15 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 简介 StatementHandler封装了对JDBC各类Statement的操作&#xff0c;如设置fetchSize属性、设置查询超时时间、与数据库进行交互等&…

【JS红宝书学习笔记】第25章 客户端存储

第25章 客户端存储 Cookie HTTP是无状态的&#xff0c;也就是说&#xff0c;你这次访问服务器&#xff0c;关闭后再次访问服务器&#xff0c;服务器是意识不到又是你来访问的。 登录时&#xff0c;浏览器需要帮我们在每一次请求里加入用户名和密码&#xff0c;这样才能做到保…

Supervised Contrastive Learning

paperhttps://arxiv.org/abs/2004.11362githubhttps://github.com/HobbitLong/SupContrast个人博客位置http://myhz0606.com/article/SupCon 1 Motivation 经典的自监督对比学习方法以instance discrimination作为pretext task。在这种方法中&#xff0c;会对batch的图片进行…

【书生大模型实战】L0-Git 基础知识

一、关卡任务 任务1: 破冰活动&#xff1a;自我介绍任务2: 实践项目&#xff1a;构建个人项目 二、实验过程 2.1 破冰行动 每位参与者提交一份自我介绍。 提交地址&#xff1a;GitHub - InternLM/Tutorial: LLM Tutorial 的 camp3 分支&#xff5e; 要求&#xff1a; 命名…

Linux驱动开发-04LED灯驱动实验(直接操作寄存器)

一、Linux 下LED 灯驱动原理 Linux 下的任何外设驱动&#xff0c;最终都是要配置相应的硬件寄存器。驱动访问底层的硬件除了使用内存映射将物理地址空间转化为虚拟地址空间&#xff0c;去进行读写修改&#xff0c;还可以通过各种子系统函数去进行操作 1.1 地址映射 MMU 全称…

视频语音转文字工具有哪些?提取视频文字就用这5个

作为一名大学毕业生&#xff0c;你是否也常靠几句简单的英语走遍天下&#xff0c;却在面对外语视频时感到手足无措&#xff1f; 别急&#xff0c;虽然在这个语言大家庭中&#xff0c;我们不可避免地要接触外语内容&#xff0c;但幸运的是&#xff0c;一些智能软件的出现&#…

leetcode日记(41)最大子数组和

以前大一的时候做过这题jpg&#xff0c;应该是个很经典的动态规划。 我首先的想法创建二维数组&#xff0c;横列代表起始位置纵列代表结束位置&#xff0c;依次补全数组&#xff0c;后来发现时间复杂度太高了&#xff1a; class Solution { public:int maxSubArray(vector<…

阿里云CDN- https(设计支付宝春节开奖业务)

HTTP相关概念 1. HTTP概述 http是最广泛的网络协议&#xff0c;是客户端与服务器之间的请求与应答的标准&#xff08;TCP&#xff09;&#xff0c;用于www服务器传输超文本到本地浏览器的传输协议&#xff0c;使浏览器更加高效&#xff0c;网络传输减少。 2.HTTPS概述 http…

Docker之在外执行docker内部命令(十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…

Go: IM系统技术架构梳理

概述 整个IM系统的一般架构如下 我们这张图展示了整个IM系统的一般架构可见分为四层那最上面这一层是前端&#xff0c;包括哪些东西呢&#xff1f; 它包括两部分&#xff0c;第一部分是跟用户直接交互的比如说各种IOS APP, 各种安卓 APP还有各种 web APP 在浏览器里面打开的以…

内网对抗-基石框架篇域树林域森林架构信任关系多域成员层级信息收集环境搭建

知识点&#xff1a; 1、基石框架篇-域树&域林架构-权限控制-用户和网络 2、基石框架篇-域树&域林架构-环境搭建-准备和加入 3、基石框架篇-域树&域林架构-信息收集-手工和工具1、工作组(局域网) 将不同的计算机按照功能分别列入不同的工作组。想要访问某个部门的…

MySQL篇:事务

1.四大特性 首先&#xff0c;事务的四大特性&#xff1a;ACID&#xff08;原子性&#xff0c;一致性&#xff0c;隔离性&#xff0c;持久性&#xff09; 在InnoDB引擎中&#xff0c;是怎么来保证这四个特性的呢&#xff1f; 持久性是通过 redo log &#xff08;重做日志&…

使用Nginx OpenResty与Redis实现高效IP黑白名单管理

1、引言 在当今数字化时代&#xff0c;网络安全已成为企业和个人用户关注的焦点。IP黑白名单作为一种有效的网络安全策略&#xff0c;允许我们精确控制对Web资源的访问权限。通过白名单&#xff0c;我们可以确保只有可信的IP地址能够访问敏感资源&#xff1b;而黑名单则可以阻…

嵌入式人工智能(2-树莓派4B开发板硬件环境搭建)

1.硬件开发环境&#xff08;T型板&#xff09; 树莓派4B开发板需要搭配面包板&#xff0c;T型板将40个GPIO口引出&#xff0c;再将T型板插到面包板上面。这个地方需要注意插接的方向&#xff0c;由于插树莓派引脚的排线没有防呆设计&#xff0c;因此&#xff0c;请注意方向&am…