使用WebSocket实现聊天功能
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、数据库设计
- 二、实现代码
- 1.SessionWrap
- 2.websocket
- 3.insertMessage
- 4.清除未读
前言
使用WebSocket实现一对一的聊天功能与未读消息功能
一、数据库设计
会话表
| 字段名 | 字段类型 | 长度 | 注释 |
|---|---|---|---|
| conversation_id | int | 11 | 会话ID |
| create_time | datetime | 创建时间 | |
| conversation_type | int | 1 | 会话类型 |
消息表
| 字段名 | 字段类型 | 长度 | 注释 |
|---|---|---|---|
| message_id | int | 11 | 消息ID |
| conversation_id | int | 11 | 会话ID |
| sender_id | int | 11 | 发送者ID |
| receiver_id | in t | 11 | 接收者ID |
| content | text | 消息内容 | |
| type | int | 2 | 消息类型 |
| information | varchar | 255 | 信息 |
| sender_img | int | 11 | 发送者头像ID |
| receiver_img | int | 11 | 接收者头像ID |
| message_status | int | 1 | 消息状态(1已读,0未读) |
| create_time | datetime | 创建时间 |
二、实现代码
1.SessionWrap
@Data
public class SessionWrap {private String from; // 连接人idprivate String type; // 连接类型private Session session;private Date lastTime;
}
2.websocket
@Component
@ServerEndpoint(value = "/api/websocket/{from}/{type}")
public class WebSocketServer {@Autowiredprivate RqriMessageService rqriMessageService;public static WebSocketServer webSocketServer;// 所有的连接会话private static CopyOnWriteArraySet<SessionWrap> sessionList = new CopyOnWriteArraySet<>();private String from;private String type;@PostConstructpublic void init() {webSocketServer = this;webSocketServer.rqriMessageService = this.rqriMessageService;}@OnOpenpublic void onOpen(Session session, @PathParam(value = "from") String from, @PathParam(value = "type") String type) {this.from = from;this.type = type;try {// 遍历list,如果有会话,更新,如果没有,创建一个新的for (SessionWrap item : sessionList) {if (item.getFrom().equals(from) && item.getType().equals(type)) {item.setSession(session);item.setLastTime(new Date());log.info("【websocket消息】更新连接,总数为:" + sessionList.size());return;}}SessionWrap sessionWrap = new SessionWrap();sessionWrap.setFrom(from);sessionWrap.setType(type);sessionWrap.setSession(session);sessionWrap.setLastTime(new Date());sessionList.add(sessionWrap);log.info("【websocket消息】有新的连接,总数为:" + sessionList.size());} catch (Exception e) {log.info("【websocket消息】连接失败!错误信息:" + e.getMessage());}}@OnClosepublic void onClose() {try {sessionList.removeIf(item -> item.getFrom().equals(from) && item.getType().equals(type));log.info("【websocket消息】连接断开,总数为:" + sessionList.size());} catch (Exception e) {log.info("【websocket消息】连接断开失败!错误信息:" + e.getMessage());}}@OnMessagepublic void onMessage(String message, Session session) {try {if ("ping".equals(message)) {session.getBasicRemote().sendText("ping"); // 心跳检测} else {// 将消息插入到数据库JSONObject r = webSocketServer.rqriMessageService.insertMessage(message);// 成功if (r.getInteger("code") == 200) {JSONObject data = r.getJSONObject("data");String senderId = data.getString("senderId"); // 发送者String receiverId = data.getString("receiverId"); // 接收者for (SessionWrap item : sessionList) {if (senderId.equals(item.getFrom()) || receiverId.equals(item.getFrom()) ) {item.getSession().getBasicRemote().sendText(r.toJSONString());} }log.info("【websocket消息】发送消息:" + r.toJSONString());}}} catch (Exception e) {log.info("【websocket消息】发送消息失败!错误信息:" + e.getMessage());}}@OnErrorpublic void onError(Session session, Throwable error) {log.error("用户错误,原因:"+error.getMessage());error.printStackTrace();}}
3.insertMessage
private final String rqriMessageStr = "rqri_message_unread_";public JSONObject insertMessage(String message) {JSONObject jsonObject = new JSONObject();RqriMessage rqriMessage = JSONObject.parseObject(message, RqriMessage.class);// 把消息添加到数据库int i = rqriMessageMapper.insertSelective(rqriMessage);// 将未读信息添加到redis 添加接收者的未读String conversationId = String.valueOf(rqriMessage.getConversationId());String receiverId = String.valueOf(rqriMessage.getReceiverId());String key = rqriMessageStr + conversationId + "_" + receiverId;if (redisUtils.get(key) == null) {redisUtils.set(key, 1, 0); // 设置永不过期} else {redisUtils.incr(key, 1); // 未读数量添加1}jsonObject.put("code", 200);jsonObject.put("data", rqriMessage);// 发送者的id和未读数量,返回给前端渲染到页面HashMap<String, Integer> map = new HashMap<>();map.put("num", Integer.valueOf(redisUtils.get(key).toString()));map.put("id", rqriMessage.getSenderId());jsonObject.put("isread", map);return jsonObject;
}
4.清除未读
最后在进入聊天页面和退出聊天页面时把未读数量清零。
相关文章:
使用WebSocket实现聊天功能
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、数据库设计二、实现代码1.SessionWrap2.websocket3.insertMessage4.清除未读 前言 使用WebSocket实现一对一的聊天功能与未读消息功能 一、数据库设计 会话…...
Ubuntu升级Cmake、gcc、g++
背景 最近要安装llvm,我选择的是从源码安装,所以要使用Cmake进行构建项目。但是服务器上的Cmake、gcc、g的版本都太低了,不符合要求,所以要对此进行升级。在本博客中采用的升级方法不一定是最好的方法(因为我也是参考…...
8月28日上课内容 第四章 MySQL备份与恢复
本章结构 前言:日志⭐⭐ MySQL 的日志默认保存位置为 /usr/local/mysql/data ##配置文件 vim /etc/my.cnf [mysqld] ##错误日志,用来记录当MySQL启动、停止或运行时发生的错误信息,默认已开启 log-error/usr/local/mysql/data/mysql_error.l…...
es字段查询加keyword和不加keyword的区别
在ES(Elasticsearch)中,查询字段名后面加上"keyword"和不加"keyword"有着不同的含义和用途。 当字段名后面加上"keyword"时,表示该字段是一个keyword类型的字段。Keyword类型的字段会将文本作为一…...
前端JavaScript将数据转换成JSON字符串以及将JSON字符串转换成对象的两个API
在前端 JavaScript 中,你可以使用 JSON.stringify() 方法将 JavaScript 数据转换成 JSON 字符串,以及使用 JSON.parse() 方法将 JSON 字符串转换成 JavaScript 对象。下面是这两个 API 的详细说明和示例: JSON.stringify(): 用于…...
Spring——Spring Boot基础
文章目录 第一个helloword项目新建 Spring Boot 项目Spring Boot 项目结构分析SpringBootApplication 注解分析新建一个 Controller大功告成,运行项目 简而言之,从本质上来说,Spring Boot 就是 Spring,它做了那些没有它你自己也会去做的 Spri…...
Python基础之基础语法(二)
Python基础之基础语法(二) 语言类型 静态语言 如:C C Java ina a 100 a 100 a abc # 不可以静态语言需要指定声明标识符的类型,之后不可以改变类型赋值。静态语言变异的时候要检查类型,编写源代码,编译时检查错误。 动态语…...
docker常见面试问题详解
在面试的时候,面试官常常会问一些问题: docker是什么,能做什么?docker和虚拟机的区别是什么呢?docker是用什么做隔离的?docke的网络类型?docker数据之间是如何通信的?docker的数据保…...
Auto-GPT 学习笔记
Auto-GPT 学习笔记 Auto-GPT 简介 Auto-GPT 是一个基于 GPT-4 的自主智能体实验项目。它展示了大规模语言模型的规划、记忆和工具使用能力。Auto-GPT 的目标是实现一个完全自主的 AI 代理。GitHub 仓库 Auto-GPT 核心模块 规划(Planning) 使用强化学习策略进行多跳思考。通…...
代码随想录 - Day30 - 修剪二叉树,转换二叉树 + 二叉树总结
代码随想录 - Day30 - 修剪二叉树,转换二叉树 二叉树总结 669. 修剪二叉搜索树 有点像是删除二叉搜索树的变形,改变了删除条件而已。 递归法: class Solution:def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> O…...
[音视频] sdl 渲染到外部创建的窗口上
API SDL_CreateWindowFrom # 在外部窗口上创建窗口 其他 api 调用,按照之前的 代码 ui.setupUi(this); sdl_width ui.label->width(); sdl_height ui.label->height(); SDL_Init(SDL_INIT_VIDEO); sdl_win SDL_CreateWindowFrom((void*)ui.label->wi…...
MongoDB之索引
大数据量使用全集合查询,这是非常影响性能的,而索引可以加快查询效率,提高性能,所以这方面的知识也是必不可少的。 查询分析 explain()可以帮助我们分析查询语句性能。 语法 db.collection.find(...).explain()案例及结果 案…...
Redis的介绍
Redis的架构介绍如下: 1. 概述 Redis是一个基于内存的高性能NoSQL键值数据库,支持网络访问和持久化特性。 2. 功能架构 Redis提供字符串、哈希、列表、集合、有序集合、位数组等多种数据结构,支持事务、Lua脚本、发布订阅、流水线等功能。 3. 技术架构 Redis使用单线程的…...
一文了解Docker的用法
一、什么是Docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 容器是…...
netcat的使用
目录 netcat简介 nc的使用场景 nc实现通信 创建一个服务端 创建一个客户端 具体案例 环境 win10在具体路径下执行命令 win7在具体路径下执行命令 netcat文件传输 nc文件传输的利用 服务器等待接收文件 客户端向服务器发送文件 服务器向连接的客户端发送文件 客户…...
深度学习推荐系统(二)Deep Crossing及其在Criteo数据集上的应用
深度学习推荐系统(二)Deep Crossing及其在Criteo数据集上的应用 在2016年, 随着微软的Deep Crossing, 谷歌的Wide&Deep以及FNN、PNN等一大批优秀的深度学习模型被提出, 推荐系统全面进入了深度学习时代, 时至今日,…...
前端常用 Vue3 项目组件大全
Vue.js 是一种流行的 JavaScript 前端框架,它简化了构建交互式的用户界面的过程。Vue3 是 Vue.js 的最新版本,引入了许多新的特性和改进。在 Vue3 中,组件是构建应用程序的核心部分,它们可以重用、组合和嵌套。下面是一些前端开发…...
javaee spring 静态代理
静态代理 package com.test.staticProxy;public interface IUsersService {public void insert(); }package com.test.staticProxy;//目标类 public class UsersService implements IUsersService {Overridepublic void insert() {System.out.println("添加用户");…...
Java 包装类和Arrays类(详细解释)
目录 包装类 作用介绍 包装类的特有功能 Arrays类 Arrays.fill() Arrays.toString() Arrays.sort() 升序排序 降序排序 Arrays.equals() Arrays.copyOf() Arrays.binarySearch() 包装类 作用介绍 包装类其实就是8种基本数据类型对应的引用类型。 基本数据类型引用…...
elementUi中的el-table表格的内容根据后端返回的数据用不同的颜色展示
效果图如下: 首先 首先:需要在表格行加入 <template slot-scope"{ row }"> </template>标签 <el-table-column prop"usable" align"center" label"状态" width"180" ><templ…...
Selenium动作链原理与Go实战:模拟人类交互的底层机制
1. 为什么“动作链”不是锦上添花,而是Selenium自动化绕不开的生死线你写过driver.FindElement(By.Id("submit")).Click(),也用过SendKeys("hello"),甚至加了Thread.Sleep(2000)等页面加载——但当你要拖拽一个滑块完成验…...
工业 CAN 通信利器!六通道隔离集线器,中继滤波稳组网
工业 CAN 总线距离受限、速率不匹配、数据拥堵、故障难排查?三格电子SG-CanHub-600 六通道 CAN 集线器,工业级隔离中继,信号再生 智能滤波,轻松解决 CAN 网络通信难题!⚙️ 硬核实力,工业通信强支撑✅ 六通…...
Mardi 品牌创始人是谁?一文读懂法国 Mardi Ladin
法国 Mardi Ladin 品牌创始人是La Bergon(Baudino Cd L),一位出身法国时尚世家的设计师,品牌的灵感直接来自于 1975 年法国经典电影《表兄妹》中入围奥斯卡最佳女主角的角色 "玛尔蒂 MARDI"。创始人 La Bergon 解析La B…...
(二) 1. Q-learning的遗憾界分析-高效的Q-learning算法
高效的Q-learning算法 1.1. 无模型算法 1.2. UCB算法 1.3. 文献回顾 无模型(Model-free)强化学习算法(如 Q-learning)无需显式地对环境进行建模,而是直接对价值函数或策略进行参数化和更新。与基于模型(Model-based)的方法相比,这类算法通常更简单、更灵活,因此在现代…...
RAG三大冲突与三大死穴及解决方案
RAG :向量召回 稀疏匹配 重排序融合 动态裁剪 —— 冲突根源与工程解法 面向开发者的深度技术解析:揭开 RAG 检索 pipeline 中三个环节的底层冲突,以及幻觉漂移、上下文溢出、检索冗余三大企业级死穴的根治方案。 GitHub 项目地址…...
yt-fts高级配置技巧:数据库路径、Chroma设置与性能优化
yt-fts高级配置技巧:数据库路径、Chroma设置与性能优化 【免费下载链接】yt-fts YouTube Full Text Search - Search all of YouTube from the command line 项目地址: https://gitcode.com/gh_mirrors/yt/yt-fts yt-fts是一款强大的YouTube全文搜索工具&…...
围棋AI训练新境界:5步掌握KaTrain智能陪练核心技巧
围棋AI训练新境界:5步掌握KaTrain智能陪练核心技巧 【免费下载链接】katrain Improve your Baduk skills by training with KataGo! 项目地址: https://gitcode.com/gh_mirrors/ka/katrain 想要在围棋对弈中快速提升水平?KaTrain作为一款基于Kata…...
终极指南:为VSCode换上节日主题图标,圣诞节、万圣节等季节性装饰一键搞定
终极指南:为VSCode换上节日主题图标,圣诞节、万圣节等季节性装饰一键搞定 【免费下载链接】vscode-icons Custom Visual Studio Code Icons 项目地址: https://gitcode.com/gh_mirrors/vsc/vscode-icons vscode-icons是一款为Visual Studio Code提…...
从文字问诊到具身交付:健康 Agent 产品落地实战
当健康 Agent 跳出冰冷文字框,升级为能开口、有表情、会互动的 3D 数字医生,产品体验会发生质变。本文完整记录:我如何用魔珐星云 SDK,把文字健康 Agent 落地为可交互数字员工的实战全过程。 一、产品痛点:健康 Agent&…...
LangChain 是什么?从零开始学会 LangChain 的工程实践指南
LangChain 是什么?从零开始学会 LangChain 的工程实践指南 1. 文章背景:为什么这个主题重要 在大模型应用开发中,很多人第一次接触 LangChain,是因为想快速做一个“基于大模型的应用”:例如知识库问答、RAG 检索增强生…...
