使用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.清除未读

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

相关推荐
架构文摘JGWZ32 分钟前
Java 23 的12 个新特性!!
java·开发语言·学习
拾光师1 小时前
spring获取当前request
java·后端·spring
aPurpleBerry1 小时前
neo4j安装启动教程+对应的jdk配置
java·neo4j
我是苏苏2 小时前
Web开发:ABP框架2——入门级别的增删改查Demo
java·开发语言
xujinwei_gingko2 小时前
Spring IOC容器Bean对象管理-Java Config方式
java·spring
2301_789985942 小时前
Java语言程序设计基础篇_编程练习题*18.29(某个目录下的文件数目)
java·开发语言·学习
IT学长编程2 小时前
计算机毕业设计 教师科研信息管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
java·毕业设计·springboot·毕业论文·计算机毕业设计选题·计算机毕业设计开题报告·教师科研管理系统
m0_571957582 小时前
Java | Leetcode Java题解之第406题根据身高重建队列
java·leetcode·题解
程序猿小D2 小时前
第二百三十五节 JPA教程 - JPA Lob列示例
java·数据库·windows·oracle·jdk·jpa
掘根2 小时前
【网络】高级IO——poll版本TCP服务器
网络·数据库·sql·网络协议·tcp/ip·mysql·网络安全