使用WebSocket实现聊天功能

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

使用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

java 复制代码
@Data
public class SessionWrap {

    private String from;	// 连接人id
    private String type;	// 连接类型
    private Session session;
    private Date lastTime;
}

2.websocket

java 复制代码
@Component
@ServerEndpoint(value = "/api/websocket/{from}/{type}")
public class WebSocketServer {

    @Autowired
    private RqriMessageService rqriMessageService;

    public static WebSocketServer webSocketServer;

    // 所有的连接会话
    private static CopyOnWriteArraySet<SessionWrap> sessionList = new CopyOnWriteArraySet<>();

    private String from;
    private String type;


    @PostConstruct
    public void init() {
        webSocketServer = this;
        webSocketServer.rqriMessageService = this.rqriMessageService;

    }

    @OnOpen
    public 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());
        }
    }

    @OnClose
    public 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());
        }
    }

    @OnMessage
    public 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());
        }
    }


    @OnError
    public void onError(Session session, Throwable error) {
        log.error("用户错误,原因:"+error.getMessage());
        error.printStackTrace();
    }

}

3.insertMessage

java 复制代码
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.清除未读

最后在进入聊天页面和退出聊天页面时把未读数量清零。

相关推荐
爱读源码的大都督11 分钟前
为什么有了HTTP,还需要gPRC?
java·后端·架构
Lucky_Turtle30 分钟前
【Java Xml】Apache Commons Digester3解析
xml·java·apache
聪明的笨猪猪1 小时前
Java Redis “缓存设计”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
FIavor.1 小时前
我发送给Apifox是http://localhost:9002/goods/getByUserName?name=张三 为什么会是500哪里错了?
java·服务器·网络协议·http
ID_180079054731 小时前
京东获取整站实时商品详情数据|商品标题|数据分析提取教程
java·开发语言
IT大灰狼1 小时前
拌合站软件开发(27)监测各项IP设备可访问性
网络·网络协议·tcp/ip
微露清风1 小时前
系统性学习C++-第五讲-内存管理
java·c++·学习
计算机毕业设计木哥2 小时前
计算机毕业设计选题推荐:基于SpringBoot和Vue的快递物流仓库管理系统【源码+文档+调试】
java·vue.js·spring boot·后端·课程设计
235162 小时前
【LeetCode】146. LRU 缓存
java·后端·算法·leetcode·链表·缓存·职场和发展
聪明的笨猪猪2 小时前
Java Redis “运维”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试