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,一经查实,立即删除!

相关文章

kafka入坑

快速入门 使用 Strimzi 项目时&#xff0c;在 Kubernetes 上启动和运行 Apache Kafka 集群非常简单&#xff01; Kubernetes Kind 是一个 Kubernetes 集群&#xff0c;以单个 Docker 镜像的形式实现&#xff0c;并以容器的形式运行。它主要用于测试 Kubernetes 本身&#xff…

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…

工程实践1 项目:小超市商品管理系统

#include<stdio.h> #include<stdlib.h>typedef struct item {int num;//编号char name[10];//名字int cost;//进价&#xff08;成本&#xff09;int price;//售价int initial_quantity;//进货量&#xff08;初始量&#xff09;int sale_quantity;//销售量int waste…

【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;必须遵循几种简…

PyCharm: Python 开发者的首选 IDE

PyCharm 是由 JetBrains 公司开发的一款专为 Python 语言设计的集成开发环境&#xff08;IDE&#xff09;。它以其强大的功能、高效的代码编辑、智能的代码分析和便捷的项目导航而受到全球开发者的青睐。本文将介绍 PyCharm 的核心特性、使用技巧以及如何充分利用 PyCharm 提升…

moment()获取时间

moment 是一个 JavaScript 日期处理类库。 使用&#xff1a; //安装 moment npm install moment -- save 引用 //在main.js中全局引入 import moment from "moment" 设定moment区域为中国 //import 方式 import moment/locale/zh-cn moment.locale(zh-cn); …

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

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

python 基础语法整理

注释声明命名规范数据类型简单数据类型复合数据类型 打印输出类型转换随机数获取布尔类型流程控制语句循环语句字符串操作拼接替换分割与连接大小写转换空白字符删除 切片列表操作访问/赋值判断是否存在元素添加/删除复制排序 元组集合字典空集合与空字典 函数声明多返回值函数…

【笔记-MyBatis】StatementHandler

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

【TFTP工具(Trivial File Transfer Protocol)】

TFTP工具(Trivial File Transfer Protocol)使用&#xff1a; 一、概述 分为服务端和客户端&#xff0c;想要通信&#xff0c;一个配成服务端另一个配成客户端。TFTP 用于 UDP 端口号 69 其文件传输活动 要想使用TFTP需要的步骤&#xff1a; ① 本地安装客户端工具 ② 服务器…

【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; 命名…

鸿蒙开发工程师面试-架构篇

1. 假如让你负责鸿蒙App架构设计&#xff0c;你会关注哪些方面&#xff1f; 分层架构设计&#xff1a; 将应用划分为产品定制层、基础特性层和公共能力层&#xff0c;以降低层间依赖性&#xff0c;提升代码可维护性。通过分层架构设计&#xff0c;进一步明确每层的职责和层间交…

R-CNN、Fast R-CNN和Faster R-CNN:目标检测的进化之路

在计算机视觉的世界里,目标检测是一个重要的任务,它的目标是找到图像中的特定物体,并标注出它们的位置。这项技术广泛应用于自动驾驶、安防监控等领域。为了让计算机能够准确高效地完成这一任务,科学家们提出了许多优秀的算法,其中最具代表性的就是R-CNN、Fast R-CNN和Fas…

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

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

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

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