SpringBoot+Mybatis-Plus使用webSocket实现一对一聊天(第三次修改)

上一版代码链接:https://blog.csdn.net/w75545521/article/details/108600051

这一次的发出来的文章可能有一点点乱,希望不会被嫌弃。代码还在一步一步更新,如果有幸被大佬看到,恳求指点一二。

一、问题背景

之前代码完成以后去写群聊天功能,发现新的问题

一对一聊天:

草丛伦 对 菊花信 说:“咱们要藏在哪个草里。”

这时 五秒真男人 对 草丛伦 说:“五秒太快啦,救命啊!~~~~”

对于 五秒真男人 来说 草丛伦 现在的状态是对方已经断开链接

草丛伦 不能实时的接收到 五秒真男人 的求救

只有 草丛伦 打开跟 五秒真男人 的聊天界面以后

才会把 五秒真男人 之前发来的消息推送给 草丛伦

但此时 五秒真男人 早已在温泉中默默的画圈圈

好吧确实有点乱,我也没办法,反正就是这么个意思,嘿嘿。

二、解决办法

经过反复思考,目前想到了两种解决办法:

1、加一个长轮询

 长轮询(comet):当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 。 客户端JavaScript响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。

结合之前想用Redis存储聊天记录(无奈只能存储最后一条记录而放弃),用一个方法去Redis中查询数据就否发生改变,如果有的话获取最后一条记录回显在聊列表,提示有新消息,打开该聊天记录之后建立新的WebSocekt连接调用离线消息方法推送新消息 。


第一种方法我没有用代码实现,感觉有点浪费资源,重点是第二种方法,代码贴出来,以便各位指正。


2、去除RoomId改用SessionId

1) 一对一聊天

/*** @description:一对一聊天* 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端** @author 火烛* @since 2020-9-17*/@RestController
@ServerEndpoint(value = "/webSocketOneToOne/{userId}")
public class WebSocketOneToOne {// 这里使用静态,让 service 属于类private static UsersServiceImpl userService;// 注入的时候,给类的 service 注入@Autowiredpublic void setUserService(UsersServiceImpl userService) {WebSocketOneToOne.userService = userService;}private static MessageServiceImpl messageService;@Autowiredpublic void setChatMsgService(MessageServiceImpl messageService) {WebSocketOneToOne.messageService = messageService;}// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。private static int onlineCount;//实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key为用户标识private static final Map<String,Session> connections = new ConcurrentHashMap<>();// 与某个客户端的连接会话,需要通过它来给客户端发送数据private String sessionId;private String sendId;/*** 连接建立成功调用的方法** @param session* 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据*/@OnOpenpublic void onOpen(@PathParam("userId") String userId, Session session) {this.sessionId = session.getId();this.sendId = userId;connections.put(sendId,session);     //添加到map中addOnlineCount();               //在线数加System.out.println(userId);System.out.println("--------------连接-------------" + this.sessionId);System.out.println("有新连接加入!新用户:"+sendId+",当前在线人数为" + getOnlineCount());List<Message> messageList = messageService.queryByType(sendId);for (Message message : messageList){downSend(message.getContent(), message.getSendId(), message.getReceiveId(), message.getType(), sessionId);}}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {Users users = new Users();users.setId(sendId);users.setDownTime(String.valueOf(LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8))));userService.updateById(users);connections.remove(sendId);  // 从map中移除subOnlineCount();          // 在线数减System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());}/*** 收到客户端消息后调用的方法** @param message*            客户端发送过来的消息* @param session*            可选的参数*/@OnMessagepublic void onMessage(String message, Session session) {System.out.println("--------------接收消息-------------" + session.getId());System.out.println("来自客户端的消息:" + message);JSONObject json= JSON.parseObject(message);String msg = (String) json.get("message");  //需要发送的信息String receiveId = (String) json.get("receiveId");      //发送对象的用户标识(接收者)String type = (String) json.get("type");      //发送对象的用户标识(接收者)send(msg,sendId,receiveId,type, session.getId());}/*** 发生错误时调用** @param session* @param error*/@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}//发送给指定角色public void send(String msg,String sendId,String receiveId,String type, String sessionId){System.out.println("--------------推送消息-------------" + sessionId);Message message = new Message();message.setId(MyUtils.getRandomString(10));message.setContent(msg);message.setCreateTime(LocalDateTime.now());//时间格式化DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");message.setCreateTimeM(String.valueOf(dateTimeFormatter.format(LocalDateTime.now())));message.setReceiveId(receiveId);message.setSendId(sendId);message.setType(type);try {Users u = userService.getById(sendId);//from具体用户Session confrom = connections.get(sendId);if(confrom!=null){if(sessionId.equals(confrom.getId())){System.out.println("confrom.getId()-------" +   confrom.getId());Map map = MapUnite.getMap(message);messageService.save(message);map.put("avatar",u.getIcon());confrom.getBasicRemote().sendText(JSON.toJSONString(map));}}//to指定用户Session con = connections.get(receiveId);if(con!=null){System.out.println("con.getId()--------" + con.getId());if (sessionId.equals(this.sessionId)) {Map map = MapUnite.getMap(message);map.put("avatar", u.getIcon());HashMap<String, Map<String, String>> stringMapHashMap = new HashMap<>();stringMapHashMap.put(sendId, map);con.getBasicRemote().sendText(JSON.toJSONString(stringMapHashMap));}}} catch (IOException e) {e.printStackTrace();}}//发送离线消息给指定角色public void downSend(String msg,String sendId,String receiveId,String type, String sessionId){Message message = new Message();message.setId(MyUtils.getRandomString(10));message.setContent(msg);message.setCreateTime(LocalDateTime.now());//时间格式化DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");message.setCreateTimeM(String.valueOf(dateTimeFormatter.format(LocalDateTime.now())));message.setReceiveId(receiveId);message.setSendId(sendId);message.setType(type);try {Users u = userService.getById(sendId);System.out.println(u);//to指定用户Session con = connections.get(receiveId);if(con!=null){System.out.println("con.getId()--------" + con.getId());if (sessionId.equals(this.sessionId)) {Map map = MapUnite.getMap(message);map.put("avatar", u.getIcon());HashMap<String, Map<String, String>> stringMapHashMap = new HashMap<>();stringMapHashMap.put(sendId, map);con.getBasicRemote().sendText(JSON.toJSONString(stringMapHashMap));}}} catch (IOException e) {e.printStackTrace();}}public static synchronized int getOnlineCount() {return onlineCount;}public static synchronized void addOnlineCount() {WebSocketOneToOne.onlineCount++;}public static synchronized void subOnlineCount() {WebSocketOneToOne.onlineCount--;}
}

MessageController

    /*** 聊天记录标记为已读** @param sendId* @param receiveId* @return*/@RequestMapping(value = "updateType", method = RequestMethod.PUT)@ApiOperation(value = "聊天记录标记为已读")public responseVo updateType(String sendId, String receiveId){responseVo result = messageService.updateType(sendId, receiveId);return result;}
}

SQL也稍微修改了一下(type = 0为未读,1为已读):
UPDATE message SET type = '1' WHERE send_id = #{sendId} AND receive_id = #{receiveId}


如有疑问请看上一版代码
链接:https://blog.csdn.net/w75545521/article/details/108600051
如再有疑问请留言或者加VX:qy773787875 询问

(修改完善)SpringBoot+Mybatis-Plus使用webSocket实现一对一聊天相关推荐

  1. 利用websocket实现一对一聊天

    一对一聊天websocket 1. 效果展示 2. 业务分析(逻辑展示...) 3. 技术点 功能 即时发送消息||随时发送消息 历史消息显示 已读未读状态 1. 效果展示 由于没做登录,就以jack ...

  2. SpringBoot+Mybatis多模块(module)项目搭建教程

    作者:枫本非凡 cnblogs.com/orzlin/p/9717399.html 编辑:Java知音 一.前言 最近公司项目准备开始重构,框架选定为SpringBoot+Mybatis,本篇主要记录 ...

  3. Jeecg-Boot 1.0 版本发布,基于SpringBoot+Mybatis+AntDesign快速开发平台

    基于SpringBoot+Mybatis+AntDesign企业级快速开发平台 引言:      Jeecg-Boot 一款基于代码生成器的J2EE快速开发框架!  采用前后端分离技术: Spring ...

  4. mybatis redis_基于人事年假管理的系统springboot+mybatis+redis+拦截器

    那么分享一个项目案例篇吧! 需求是:假设有一个部门表,一个员工表,一个年假表, 员工表里: 部门表: 休假表: 管理员表     项目原型: 效果图: 管理员登录: 登录成功回调: 进入后台首页,这里 ...

  5. springboot mybatis大学生校园宿舍管理系统源码含文档

    摘 要:宿舍是大学生学习与生活的主要场所之一,宿舍管理是高校学工管理事务中 尤为重要的一项.随着我国高校招生规模的进一步扩大,学生总体人数的不断增加, 宿舍管理工作变得愈加沉重和琐碎,学生宿舍信息的采 ...

  6. 基于ruoyi+vue+elementUI实现列表,新增,附件上传,tab+springBoot+mybatis+oracle序列+批量新增

    基于ruoyi+vue+elementUI实现列表,新增,附件上传,tab+springBoot+mybatis+oracle序列+批量新增 页面效果 列表页面 新增页面 详情页面 代码实现 列表+新 ...

  7. 基于javaweb框架的springboot mybatis宠物商城源码含论文设计文档

    在互联网高速发展.信息技术步入人类生活的情况下,电子贸易也得到了空前发展.网购几乎成为了人人都会进行的活动.近几年来,养宠物更是成为人们生活中重要的娱乐内容之一, 人们越来越多的讲感情也寄托给了宠物, ...

  8. 基于vue2+element+springboot+mybatis+jpa+mysql的学籍管理系统

    目录 整套系统源码下载 一.开发背景 二.用到的技术 三.开发使用的IDE 四.搭建开发环境 五.启动项目 六.学籍管理系统使用说明 七.结语 八.源码下载 基于vue2+element+spring ...

  9. SpringBoot + Vue 实现基于 WebSocket 的聊天室(单聊)

    前言 在前一篇文章SpringBoot 集成 STOMP 实现一对一聊天的两种方法中简单介绍了如何利用 STOMP 实现单聊,本文则将以一个比较完整的示例展示实际应用,不过本文并未使用 STOMP,而 ...

最新文章

  1. python 特征选择卡方_特征选择
  2. 数据处理系列(四) 安装Elasticsearch用以存储和查询数据
  3. python国防_Python+Excel数据分析实战:军事体能考核成绩评定(二)基本框架和年龄计算...
  4. 【图论专题】单源最短路的扩展应用
  5. Django模拟新浪微博的@功能
  6. mysql pool not open_安装 MariaDb 时报错:Could not open mysql.plugin table
  7. 课时 28:理解容器运行时接口 CRI(知谨)
  8. openCV鼠标事件实例
  9. oracle 峰度 函数,Oracle Database 21c 十大新特性一览
  10. 我从参加#PerfMatters会议中学到的东西
  11. Fiddler如何查找登陆的可用cookie用于其他请求?方式一
  12. C语言八位彩灯循环左移,利用计数器和移位寄存器设计一个八个彩灯循环电路?...
  13. 九章算术卷第七 盈不足
  14. app登录界面背景 css_Google flutter这么火?撸一个APP登录界面(上)
  15. 十大排序算法——希尔排序(元素交换法和数组元素移动法C语言)
  16. 计算机编程—必备基础知识点
  17. 人生感悟:欲望适度则为利,欲望过度则为害
  18. 正版饥荒搜不到服务器,饥荒正版无法刷出服务器问题的解决方法
  19. python证书过期_简单python脚本监控SSL证书到期提醒
  20. python图书馆管理系统实验报告_基于python图书馆管理系统设计实例详解

热门文章

  1. 为什么中国程序员非要用英文编程,而不用汉字编程
  2. C++/java设计模式汇总
  3. 采购订单税码检查增强(badi)
  4. Linux登录mysql时 密码正确登录不进去,没有密码却能登进去
  5. win7台式计算机型号怎么查,win7怎么看电脑型号 win7电脑型号怎么查
  6. 690-文件管理和文件系统
  7. zip压缩包太大无法用unzip成功解压(保姆级)
  8. numpy 归一化 与 标准化
  9. Grafana 在 Kubernetes 中的使用
  10. 2012年信息系统项目管理师下半年上午考试习题与答案解析