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

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

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

这一次的发出来的文章可能有一点点乱,希望不会被嫌弃。

	代码还在一步一步更新,如果有幸被大佬看到,恳求指点一二。

一、问题背景

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

一对一聊天:

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

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

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

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

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

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

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

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

二、解决办法

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

1、加一个长轮询

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

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


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


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 注入
    @Autowired
    public void setUserService(UsersServiceImpl userService) {
        WebSocketOneToOne.userService = userService;
    }
    private static MessageServiceImpl messageService;
    @Autowired
    public 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为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public 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);
        }
    }



    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public 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
     *            可选的参数
     */
    @OnMessage
    public 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
     */
    @OnError
    public 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 询问

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值