【springboot】 集成 websocket

聊天室的前期预研

使用springboot+websocket 搭建框架

1、项目创建,省略

2、jar包

        <!--webSocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.4</version>
        </dependency>
        <!-- thymeleaf模版引擎 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>     

3、websocket创建

package com.hake.pms.chatroom;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hake.pms.chatroom.entity.Chat;
import com.hake.pms.chatroom.service.IChatService;
import com.hake.utils.HkStringUtil;
import com.hake.utils.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;

/**
 * websocket的处理类。
 *  * 作用相当于HTTP请求
 *  * 中的controller
 * @author liuyue
 * @date 2024/2/29 9:54
 */
@Component
@Slf4j
@ServerEndpoint("/ws/sendMessage/{userId}")
public class WebSocketServer {

    private static WebSocketServer webSocketServer;

    @Lazy
    @Autowired
    IChatService iChatService;

    /**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
    private static int onlineCount = 0;
    /**concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。*/
    private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
    /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
    private Session session;
    /**接收userId*/
    private String userId = "";

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

    /**
     * 连接建立成
     * 功调用的方法
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("userId") String userId) {
        userId = chkUserId(userId);
        this.session = session;
        this.userId=userId;
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            //加入set中
            webSocketMap.put(userId,this);
        }else{
            //加入set中
            webSocketMap.put(userId,this);
            //在线数加1
            addOnlineCount();
        }
        //记录进入聊天信息
        Chat chat = null;

        chat = writeInMsg(userId, "欢迎进入聊天", null);
        log.info("用户:"+userId+",进入聊天 !!!!!!");
        log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
        //initMessage(chat,null);
        //群聊
        sendCrowdMessage(chat);
    }
    /**判断用户是否在线*/
    private String chkUserId(String userId) {
        if (HkStringUtil.isNullOrEmpty(userId)) {
            try {
                //判断用户是否登录
                userId = SessionUtil.getCurrentUser().getLoginNo();
            }catch (Exception e){
                log.error("用户ID为空");
                throw new RuntimeException("用户ID为空");
            }
        }
        return userId;
    }

    /**写进入聊天室记录*/
    private Chat writeInMsg(String userId, String msg, String toUser) {
        Chat chat = new Chat();
        chat.setFromUser(userId);
        chat.setToUser(toUser);
        chat.setMsgContent(msg);
        chat.setInRoom(0);
        chat.setReadFlag(0);
        chat.setCreateTime(LocalDateTime.now());
        webSocketServer.iChatService.save(chat);
        return chat;
    }

    /**
     * 连接关闭
     * 调用的方法
     */
    @OnClose
    public void onClose() {
        if(webSocketMap.containsKey(userId)){
            webSocketMap.remove(userId);
            //从set中删除
            subOnlineCount();
        }
        log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
    }

    /**
     * 收到客户端消
     * 息后调用的方法
     * @param message
     * 客户端发送过来的消息
     **/
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("用户消息:"+userId+",报文:"+message);
        //可以群发消息
        //消息保存到数据库、redis
        if(StringUtils.isNotBlank(message)){
            try {
                //解析发送的报文
                //JSONObject jsonObject = JSON.parseObject(message);
                //追加发送人(防止串改)
                //jsonObject.put("fromUserId",this.userId);
                //String toUserId=jsonObject.getString("toUserId");
                JSONObject jsonObject = JSON.parseObject(message);
                //追加发送人(防止串改)
                jsonObject.put("fromUser",this.userId);
                String toUserId=jsonObject.getString("toUser");
                //传送给对应toUserId用户的websocket
                if(StringUtils.isNotBlank(message)&&webSocketMap.containsKey(message)){
                    Chat chat = JSON.parseObject(message, Chat.class);
                    webSocketMap.get(toUserId).sendMessage(chat,this.userId);
                }else{
                    //否则不在这个服务器上,发送到mysql或者redis
                    log.error("请求的userId:"+this.userId+"不在该服务器上");
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }


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

    public void initMessage(Chat message,String userId) {
        try {
            WebSocketServer webSocketServer = webSocketMap.get(message.getFromUser());
            this.session.getBasicRemote().sendText(JSON.toJSONString(message));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 实现服务
     * 器主动推送
     */
    public void sendMessage(Chat message,String userId) {
        try {
            if (webSocketMap.keySet().contains(userId)){
                WebSocketServer webSocketServer = webSocketMap.get(userId);
                this.session.getBasicRemote().sendText(message+":"+userId);
                String loginNo = SessionUtil.getCurrentUser().getLoginNo();
                webSocketServer.session.getBasicRemote().sendText(JSON.toJSONString(message));
            }else {
                this.session.getBasicRemote().sendText("对方不在线,请留言");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 发送消息服务
     * @author liuyue
     * @date 2024/3/1 10:55
     * @param chat
     */
    public static void sendUserMessage(Chat chat) {
        try {
            String toUser = chat.getToUser();
            if(webSocketMap.keySet().contains(toUser)){
                //在线
                chat.setCreateTime(LocalDateTime.now());
                chat.setInRoom(1);
                chat.setReadFlag(1);//已读
                //保存发送记录
                webSocketServer.iChatService.save(chat);
                //发送给对方界面
                WebSocketServer webSocketServer = webSocketMap.get(toUser);
                webSocketServer.session.getBasicRemote().sendText(JSON.toJSONString(chat));
                //发送给自己界面
                WebSocketServer webSocketServer2 = webSocketMap.get(chat.getFromUser());
                webSocketServer2.session.getBasicRemote().sendText(JSON.toJSONString(chat));
            }
        } catch (IOException e) {
            //不在线
            chat.setCreateTime(LocalDateTime.now());
            chat.setReadFlag(0); //未读
            //保存发送记录
            webSocketServer.iChatService.save(chat);
            log.error("发送消息失败:{}",e.getMessage());
        }
    }
    /**
     * 群发信息
     * @author liuyue
     * @date 2024/3/5 10:06
     * @param chat
     */

    public static void sendCrowdMessage(Chat chat) {
        try {
            chat.setCreateTime(LocalDateTime.now());
            //保存发送记录
            chat.setInRoom(1);
            webSocketServer.iChatService.save(chat);
            for (WebSocketServer webSocket : webSocketMap.values()) {
                chat.setToUser(webSocket.userId);
                webSocket.session.getBasicRemote().sendText(JSON.toJSONString(chat));
            }
        } catch (IOException e) {
            log.error("发送消息失败:{}",e.getMessage());
        }
    }
    /**
     *发送自定
     *义消息
     **/
    public static void sendInfo(Chat message, String userId) {
        log.info("发送消息到:"+userId+",报文:"+message);
        if(StringUtils.isNotBlank(userId) && webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message,userId);
        }else{
            log.error("用户"+userId+",不在线!");
        }
    }
    /**
     * 获得此时的
     * 在线人数
     * @return
     */
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    /**
     * 在线人
     * 数加1
     */
    public static synchronized void addOnlineCount() {
        WebSocketServer.onlineCount++;
    }

    /**
     * 在线人
     * 数减1
     */
    public static synchronized void subOnlineCount() {
        WebSocketServer.onlineCount--;
    }

    public static ConcurrentHashMap<String,WebSocketServer> getWebSocketMap() {
        return WebSocketServer.webSocketMap;
    }

}

4、聊天记录保存类

package com.hake.pms.chatroom.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;

import java.time.LocalDateTime;
import java.io.Serializable;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p>
 *
 * </p>
 *
 * @author Liuyue
 * @since 2024-03-01
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class Chat implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 发送人
     */
    private String fromUser;

    /**
     * 发送内容
     */
    private String msgContent;

    /**
     * 接收人
     */
    private String toUser;

    /** 
     * 0 、进入房间,1、聊天内容
     * @author liuyue
     * @date 2024/3/5 14:37
     */
    
    private int inRoom;
    /**
     *0未读,1已读
     * @author liuyue
     * @date 2024/3/4 16:05
     * @param null
     */

    private int readFlag;

    /**
     * 记录时间
     */
    private LocalDateTime createTime;


}

 

5、配置 WebSocketConfig 

package com.hake.configes;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * websocket配置
 * @author liuyue
 * @date 2024/2/29 9:41
 */

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {

        return new ServerEndpointExporter();

    }
}

 

每天进步一点点

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章