【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();

    }
}

 

每天進步一點點

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