当前位置: 首页 > news >正文

SpringBoot实现即时通讯

SpringBoot实现即时通讯

功能简述

  • 好友管理
  • 群组管理
  • 聊天模式:私聊、群聊
  • 消息类型:系统消息、文本、语音、图片、视频
  • 会话列表、发送消息、接收消息

核心代码

package com.qiangesoft.im.core;import com.alibaba.fastjson2.JSONObject;
import com.qiangesoft.im.constant.ChatTypeEnum;
import com.qiangesoft.im.constant.ImMessageBodyTypeEnum;
import com.qiangesoft.im.service.IImGroupUserService;
import com.qiangesoft.im.util.SpringUtil;
import com.qiangesoft.im.pojo.dto.PingDTO;
import com.qiangesoft.im.pojo.vo.PongVO;
import com.qiangesoft.im.pojo.vo.ImMessageVO;
import com.qiangesoft.im.pojo.dto.ImMessageDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;/*** 聊天会话** @author qiangesoft* @date 2023-08-30*/
@Slf4j
@ServerEndpoint("/ws/im/{userId}")
@Component
public class ImWebSocketServer {/*** concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。*/private static final ConcurrentHashMap<Long, ImWebSocketServer> WEBSOCKET_MAP = new ConcurrentHashMap<>();/*** 与某个客户端的连接会话,需要通过它来给客户端发送数据*/private Session session;/*** 连接建立成功调用的方法:用map存客户端对应的WebSocket对象*/@OnOpenpublic void onOpen(Session session, @PathParam("userId") Long userId) {this.session = session;if (WEBSOCKET_MAP.containsKey(userId)) {WEBSOCKET_MAP.remove(userId);WEBSOCKET_MAP.put(userId, this);} else {WEBSOCKET_MAP.put(userId, this);}log.info("User [{}] connection opened=====>", userId);PongVO pongVO = new PongVO();pongVO.setType(ImMessageBodyTypeEnum.SUCCESS.getCode());pongVO.setContent("连接成功");pongVO.setTimestamp(System.currentTimeMillis());doSendMessage(JSONObject.toJSONString(pongVO));}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(Session session, @PathParam("userId") Long userId, String message) {log.info("User [{}] send a message, content is [{}]", userId, message);PingDTO pingDTO = null;try {pingDTO = JSONObject.parseObject(message, PingDTO.class);} catch (Exception e) {log.error("消息解析失败");e.printStackTrace();}if (pingDTO == null || !ImMessageBodyTypeEnum.PING.getCode().equals(pingDTO.getType())) {sendInValidMessage();return;}PongVO pongVO = new PongVO();pongVO.setType(ImMessageBodyTypeEnum.PONG.getCode());pongVO.setContent("已收到消息~");pongVO.setTimestamp(System.currentTimeMillis());doSendMessage(JSONObject.toJSONString(pongVO));}/*** 连接关闭调用的方法*/@OnClosepublic void onClose(Session session, @PathParam("userId") Long userId) {close(session, userId);log.info("User {} connection is closed<=====", userId);}/*** 报错** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {error.printStackTrace();}/*** 指定的userId服务端向客户端发送消息*/public static void sendMessage(ImMessageDTO message) {String chatType = message.getChatType();if (ChatTypeEnum.GROUP.getCode().equals(chatType)) {sendGroupMessage(message);}if (ChatTypeEnum.PERSON.getCode().equals(chatType)) {sendPersonMessage(message);}}/*** 指定的userId服务端向客户端发送消息*/public static void offline(Long userId) {ImWebSocketServer webSocketServer = WEBSOCKET_MAP.get(userId);if (webSocketServer != null) {PongVO pongVO = new PongVO();pongVO.setType(ImMessageBodyTypeEnum.OFFLINE.getCode());pongVO.setContent("设备被挤下线");pongVO.setTimestamp(System.currentTimeMillis());webSocketServer.doSendMessage(JSONObject.toJSONString(pongVO));close(webSocketServer.session, userId);}}/*** 自定义关闭** @param session* @param userId*/public static void close(Session session, Long userId) {if (WEBSOCKET_MAP.containsKey(userId)) {try {session.close();} catch (IOException e) {e.printStackTrace();}WEBSOCKET_MAP.remove(userId);}}/*** 获取在线用户信息** @return*/public static Map<Long, ImWebSocketServer> getOnlineUser() {return WEBSOCKET_MAP;}/*** 发送无效消息*/private void sendInValidMessage() {PongVO pongVO = new PongVO();pongVO.setType(ImMessageBodyTypeEnum.FAIL.getCode());pongVO.setContent("无效消息");pongVO.setTimestamp(System.currentTimeMillis());doSendMessage(JSONObject.toJSONString(pongVO));}/*** 发送群组消息** @param message*/private static void sendGroupMessage(ImMessageDTO message) {Long receiverId = message.getReceiverId();IImGroupUserService groupUserService = SpringUtil.getBean(IImGroupUserService.class);List<Long> userIdList = groupUserService.listUserIdByGroupId(receiverId);MessageHandlerService messageHandlerService = SpringUtil.getBean(MessageHandlerService.class);ImMessageVO messageVO = messageHandlerService.buildVo(message);PongVO pongVO = new PongVO();pongVO.setType(ImMessageBodyTypeEnum.MESSAGE.getCode());pongVO.setContent(messageVO);pongVO.setTimestamp(System.currentTimeMillis());String messageStr = JSONObject.toJSONString(pongVO);for (Long userId : userIdList) {ImWebSocketServer webSocketServer = WEBSOCKET_MAP.get(userId);if (webSocketServer != null) {if (!userId.equals(message.getSenderId())) {webSocketServer.doSendMessage(messageStr);}}}}/*** 发送私聊消息** @param message*/private static void sendPersonMessage(ImMessageDTO message) {Long receiverId = message.getReceiverId();ImWebSocketServer webSocketServer = WEBSOCKET_MAP.get(receiverId);if (webSocketServer != null) {MessageHandlerService messageHandlerService = SpringUtil.getBean(MessageHandlerService.class);ImMessageVO messageVO = messageHandlerService.buildVo(message);PongVO pongVO = new PongVO();pongVO.setType(ImMessageBodyTypeEnum.MESSAGE.getCode());pongVO.setContent(messageVO);pongVO.setTimestamp(System.currentTimeMillis());webSocketServer.doSendMessage(JSONObject.toJSONString(pongVO));}}/*** 实现服务器推送到对应的客户端*/private void doSendMessage(String message) {try {this.session.getBasicRemote().sendText(message);} catch (IOException e) {e.printStackTrace();}}
}

相关文章:

SpringBoot实现即时通讯

SpringBoot实现即时通讯 功能简述 好友管理群组管理聊天模式&#xff1a;私聊、群聊消息类型&#xff1a;系统消息、文本、语音、图片、视频会话列表、发送消息、接收消息 核心代码 package com.qiangesoft.im.core;import com.alibaba.fastjson2.JSONObject; import com.q…...

【每日一题】LeetCode——反转链表

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 | 《数据结构与算法》 | 《C生万物》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有…...

精通Python爬虫:掌握日志配置

源码分享 https://docs.qq.com/sheet/DUHNQdlRUVUp5Vll2?tabBB08J2 在开发Python爬虫时&#xff0c;日志记录是一个不可或缺的特性&#xff0c;它帮助我们捕捉运行时信息、调试代码和监控爬虫的健康状况。合理地配置日志系统是提高爬虫可维护性的关键。本篇技术博客将详细介绍…...

Python_百度贴吧评论情感分析

一、评论爬取 以百度贴吧中“美团骑手吧”为例&#xff0c;对页面中的帖子评论进行爬取&#xff0c;并将结果以json的格式保存到本地中。 from lxml import etree import requests import json# 根据网页url获取评论 def GetComments(url):# 使用requests库发送GET请求&#…...

如何运行心理学知识(心流)来指导工作和生活

如何运用心流来指导工作和生活 如何联系我 作者&#xff1a;鲁伟林 邮箱&#xff1a;thinking_fioa163.com或vlinyes163.com GitHub&#xff1a;https://github.com/thinkingfioa/ReadingSummary 版权声明&#xff1a;文章和记录为个人所有&#xff0c;如果转载或个人学习…...

精简还是全能?如何在 Full 和 Lite 之间做出最佳选择!关于Configuration注解的Full模式与Lite模式(SpringBoot2)

&#x1f3c3;‍♂️ 微信公众号: 朕在debugger© 版权: 本文由【朕在debugger】原创、需要转载请联系博主&#x1f4d5; 如果文章对您有所帮助&#xff0c;欢迎关注、点赞、转发和订阅专栏&#xff01; 前言 关于 Configuration 注解&#xff0c;相信在座的各位 Javaer 都…...

springboot微信小程序uniapp学习计划与日程管理系统

基于springboot学习计划与日程管理系统&#xff0c;确定学习计划小程序的目标&#xff0c;明确用户需求&#xff0c;学习计划小程序的主要功能是帮助用户制定学习计划&#xff0c;并跟踪学习进度。页面设计主要包括主页、计划学习页、个人中心页等&#xff0c;然后用户可以利用…...

236.二叉树的最近公共祖先

​​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;236. 二叉树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 分别获得从根节点到两个目标节点的链路&#xff0c;寻找到最后一个相同节点即可。 解题代码&#xff1a; /***…...

ETL是什么,有哪些ETL工具?就业前景如何?

ETL是什么 ETL&#xff08;Extract-Transform-Load&#xff09;&#xff0c;用来描述将数据从来源端经过抽取(extract)、转换(transform)、加载(load)至目标端的过程。ETL一词较常用在数据仓库&#xff0c;但其对象并不限于数据仓库。它可以自动化数据处理过程&#xff0c;减少…...

无人机系统组装与调试,多旋翼无人机组装与调试技术详解,无人机飞控系统原理

多旋翼无人机飞控系统的组装 在开始组装前&#xff0c;确保您已准备好所有必要的工具和材料。这包括螺丝刀、电烙铁、焊台、杜邦线、飞控板、GPS模块、电机、桨叶等。 飞控安装 安全开关安装&#xff0c;将安全开关固定在机架上。将安全开关的线插到飞控SWITCH插口上。 电调…...

Log360,引入全新安全与风险管理功能,助力企业积极抵御网络威胁

ManageEngine在其SIEM解决方案中推出了安全与风险管理新功能&#xff0c;企业现在能够更主动地减轻内部攻击和防范入侵。 SIEM 这项新功能为Log360引入了安全与风险管理仪表板&#xff0c;Log360是ManageEngine的统一安全信息与事件管理&#xff08;SIEM&#xff09;解决方案…...

【开源】JAVA+Vue.js实现高校实验室管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实验管理模块2.4 实验设备模块2.5 实验订单模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示五、样例代码5.1 查询实验室设备5.2 实验放号5.3 实验预定 六、免责说明 一、摘…...

Flink CDC 与 Kafka 集成:Snapshot 还是 Changelog?Upsert Kafka 还是 Kafka?

博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维…...

极智一周 | 国产CPU系列汇总、鲲鹏、飞腾、平头哥 And so on

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多技术分享 大家好&#xff0c;我是极智视界&#xff0c;带来本周的 [极智一周]&#xff0c;关键词&#xff1a;国产CPU系列汇总、鲲鹏、飞腾、平头哥 And so on。 邀您加入我的知识星球「极智视界」&#xff0c;星球目前…...

PgSQL技术内幕 - case when表达式实现机制

PgSQL技术内幕 - case when表达式实现机制 CASE表达式如同 C语言中的if/else语句一样&#xff0c;为SQL添加了条件逻辑处理能力&#xff0c;可以根据不同条件返回不同结果。PgSQL支持两种语法&#xff1a;简单表达式和搜索表达式。 1、搜索表达式 语法如下&#xff1a; CASE WH…...

Android9~Android13 某些容量SD卡被格式化为内部存储时容量显示错误问题的研究与解决方案

声明:原创文章,禁止转载! Android9~Android13 某些容量SD卡被格式化为内部存储时容量显示错误问题的研究与解决方案 分析Android11 系统对于EMMC/UFS作为内部存储、SD卡被格式化为内部存储、SD卡/U盘被格式化为便携式存储的不同处理 一.现象描述 实测Android9 Android10 A…...

音视频色彩:RGB/YUV

目录 1.RGB 1.1介绍 1.2分类 1.2.1RGB16 1)RGB565 2)RGB555 1.2.2RGB24 1.2.3RGB222 2.YUV 2.1介绍 2.2分类 2.2.1 YUV444 2.2.2 YUV 422 2.2.3 YUV 420 2.3存储格式 2.3.1 YUYV 2.3.2 UYVY 2.3.3 YUV 422P 2.3.4 YUV420P/YUV420SP 2.3.5 YU12 和…...

MySQL之密码策略和用户授权

华子目录 密码策略查看数据库当前的密码策略密码策略详解caching_sha2_password_auto_generate_rsa_keyscaching_sha2_password_digest_roundscaching_sha2_password_private_key_pathcaching_sha2_password_public_key_pathdefault_password_lifetimedisconnect_on_expired_pa…...

电脑通电自启动设置

首先要进入BIOS&#xff0c;以华硕为例&#xff0c;按下电源键&#xff0c;在开机之前按下delete键&#xff0c;其他电脑可能是esc或者某个f键&#xff0c;请自行查找。 进入BIOS后要找到电源管理&#xff0c;可以在高级选项中找一找&#xff0c;如上图右下角选择高级模式。 …...

hive表加字段

目录 1.给表添加字段2.为什么使用cascade3.使用场景 1.给表添加字段 alter table database.tablename add columns(字段名 字段类型 comment 字段中文含义) cascade;2.为什么使用cascade 在Hive中&#xff0c;当你想要修改表结构&#xff0c;例如添加字段时&#xff0c;可能会…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...