WebSocket实现群聊功能、房间隔离

news2024/12/24 9:58:30
  • 引用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;


    //建立连接成功调用
    @OnOpen
    public 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);

    }

    //关闭连接时调用
    @OnClose
    public 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);
                }
            }
        }
    }

    //收到客户端信息
    @OnMessage
    public 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);*/
    }

    //错误时调用
    @OnError
    public 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() {
        //判断当前浏览器是否支持WebSocket
        if ('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.coloradmin.cn/o/1929113.html

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

相关文章

Golang | Leetcode Golang题解之第235题二叉搜索树的最近公共祖先

题目&#xff1a; 题解&#xff1a; func lowestCommonAncestor(root, p, q *TreeNode) (ancestor *TreeNode) {ancestor rootfor {if p.Val < ancestor.Val && q.Val < ancestor.Val {ancestor ancestor.Left} else if p.Val > ancestor.Val && q…

【区块链 + 智慧政务】基于区块链的可信数据档案管理系统 | FISCO BCOS应用案例

目前&#xff0c;我国的档案管理整体上实行“电子化”和“纸质”同步并存的“双套制”管理体系&#xff0c;这套管理规范体系在一 定程度上满足了电子文件安全存储的要求。但是随着云计算、大数据、区块链等现代信息技术的快速发展&#xff0c;以及 全国“互联网 政务服务”工…

TG创建小程序交互APP登录以及机器人信息

1、搜索 BotFather &#xff0c;输入命令 /newbot 创建机器人。 2、修改机器人信息 /mybots 编辑名称 : 修改机器人名称 编辑关于: 修改关于 hayden yyds&#xff0c;修改以后打开机器人会出现在下图 编辑描述 : 机器人的描述 编辑描述图片 : 机器人的图片 编辑 Botpic…

让前端和后端要“动手”的7大行为,你中了几个。

前后端分离导致了二者在工作中交叉非常多&#xff0c;不像之前前端搞完静态页面&#xff0c;拽给后端就行了。 这种交叉必然导致巨大的沟通成本&#xff0c;贝格前端工场将项目踩过的坑&#xff0c;给大家分享一下。 前端工程师最讨厌的后端行为包括&#xff1a; 1. 不提供清…

高端的食材,往往只需要最简单的烹饪!ORB,仅此一招,Alpha达到年化36%

常常看到有人提问&#xff0c;如何挖掘因子和策略&#xff1f;ORB 策略的改进历史能给我们许多启发。一是一个策略值得研究数十年&#xff1b;二是温故而知新是永远的法宝。沉下心来&#xff0c;真正吃透 IT 系统、吃透数据和已有策略&#xff0c;比追风要好得多。 炒股要炒大…

备考美国数学竞赛AMC10:吃透1200道历年真题和知识点(持续)

距离2024年AMC10美国数学竞赛开赛预计还有3个多月的时间&#xff0c;实践证明&#xff0c;做真题&#xff0c;吃透真题和背后的知识点是备考AMC8、AMC10有效的方法之一。 通过做真题&#xff0c;可以帮助孩子找到真实竞赛的感觉&#xff0c;而且更加贴近比赛的内容&#xff0c…

[AHK] WinHttpRequest.5.1报错 0x80092004 找不到对象或属性

目录 背景描述 用浏览器访问&#xff0c;正常返回 ​编辑 AHK v2官方示例源代码 AHK v2运行结果报错(0x80092004) 找不到对象或属性 用thqby大佬的WinHttpRequest.ahk库测试报错 0x80092004 找不到对象或属性 附&#xff1a; 用Apifox访问&#xff0c;也正常返回 AHK v1 …

怎样优化 PostgreSQL 中对复杂查询的并行执行计划?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 怎样优化 PostgreSQL 中对复杂查询的并行执行计划一、了解并行执行计划的基础知识二、优化并行执行计划…

MySQL索引特性(上)

目录 索引的重要 案例 认识磁盘 MySQL与存储 先来研究一下磁盘 扇区 定位扇区 结论 磁盘随机访问与连续访问 MySQL与磁盘交互基本单位 建立共识 索引的理解 建立测试表 插入多条记录 局部性原理 所有的MySQL的操作(增删查改)全部都是在MySQL当中的内存中进行的&am…

基于AT89C51单片机GSM模块的家庭防火防盗报警系统设计(含文档、源码与proteus仿真,以及系统详细介绍)

本篇文章论述的是基于AT89C51单片机GSM模块的家庭防火防盗报警系统设计的详情介绍&#xff0c;如果对您有帮助的话&#xff0c;还请关注一下哦&#xff0c;如果有资源方面的需要可以联系我。 目录 摘 要 原理图 仿真图 元器件清单 代码 系统论文 参考文献 资源下载…

内衣专用洗衣机怎么样?五样超卓臻品专业推荐!

在日常生活中&#xff0c;内衣洗衣机已成为现代家庭必备的重要家电之一。选择一款耐用、质量优秀的内衣洗衣机&#xff0c;不仅可以减少洗衣负担&#xff0c;还能提供高效的洗涤效果。然而&#xff0c;市场上众多内衣洗衣机品牌琳琅满目&#xff0c;让我们往往难以选择。那么&a…

【Python】Selenium怎么切换浏览器的页面

我们在爬网使用Selenium进行测试的时候&#xff0c;有时候想要点击浏览器里面的网址&#xff0c;跳到另一个页面上&#xff0c;获取第二个页面的内容。 可是有时候从官网进去&#xff0c;点击跳转到下一个页面以后&#xff0c;却没法定位到下一个页面的元素&#xff0c;这时候就…

【JVM基础01】——介绍-初识JVM运行流程

目录 1- 引言&#xff1a;初识JVM1-1 JVM是什么&#xff1f;(What)1-1-1 概念1-1-2 优点 1-2 为什么学习JVM?(Why) 2- 核心&#xff1a;JVM工作的原理&#xff08;How&#xff09;⭐2-1 JVM 的组成部分及工作流程2-2 学习侧重点 3- 小结(知识点大纲)&#xff1a;3-1 JVM 组成3…

Ubuntu 22.04.4 LTS (linux) 安装certbot 免费ssl证书申请 letsencrypt

1 安装certbot sudo apt update sudo apt-get install certbot 2 申请letsencrypt证书 sudo certbot certonly --webroot -w 网站目录 -d daloradius.域名.com 3 修改nginx 配置ssl 证书 # 配置服务器证书 ssl_certificate /etc/letsencrypt/live/daloradius.域名.com/f…

FPGA:基于复旦微FMQL10S400 /FMQL20S400 国产化核心板

复旦微电子是国内集成电路设计行业的领军企业之一&#xff0c;早在2000年就在香港创业板上市&#xff0c;成为行业内首家上市公司。公司的RFID芯片、智能卡芯片、EEPROM、智能电表MCU等多种产品在市场上的占有率位居行业前列。 今天介绍的是搭载复旦微 FMQL10S400/FMQL20S400的…

16001.WSL2 ubuntu20.04 编译安装 vsomeip

文章目录 1 vsomeip 编译安装1.1 vsomeip的安装1.2 编译提示错误1.3 编译hello_world示例1.4 运行服务器端 1 vsomeip 编译安装 1.1 vsomeip的安装 参考博文 https://blog.csdn.net/peterwanye/article/details/128386539 1.2 编译提示错误 ubuntu1-BJ-EE1000042:~/opt/vso…

【持续集成_05课_Linux部署SonarQube及结合开发项目部署】

一、Linux下安装SonarQube 1、安装sonarQube 前置条件&#xff1a;sonarQube不能使用root账号进行启动&#xff0c;所以需要创建普通用户及 其用户组 1&#xff09;创建组 2&#xff09;添加用户、组名、密码 3&#xff09;CMD上传qube文件-不能传到home路径下哦 4&#xff09…

【NLP大模型】词嵌入的空间表示与应用

文章目录 一、语义特征空间二、引入新维度&#xff1a;皇室三、语义特征向量的用途四、向量运算类比五、词嵌入的维度和应用词嵌入的应用 六、测量欧几里得距离向量计算向量和欧几里得距离 七、使用点积测量相似度八、创建词嵌入 一、语义特征空间 考虑“男人”、“女人”、“…

安防监控平台LntonAIServer视频监控管理平台裸土检测算法核心优势和应用场景

LntonAIServer裸土检测算法是一种基于人工智能技术的创新解决方案&#xff0c;旨在实现对裸土地表的自动识别。以下是对该算法的详细解析&#xff1a; 一、技术原理 LntonAIServer裸土检测算法利用深度学习和计算机视觉技术&#xff0c;通过捕捉视频或图像中的关键信息&#…

记录我使用poi库,中文却无法显示的问题

目录 前言&#xff1a; 正片&#xff1a; 修改word的编码格式 第一步&#xff1a; 第二步&#xff1a; 第三步&#xff1a; 第四步&#xff1a; 第五步&#xff1a; 修改idea编码格式 前言&#xff1a; &#x1f388;&#x1f388;&#x1f388;这是昨天晚上Blue遇到的…