侧边栏壁纸
博主头像
小城雨巷 博主等级

行动起来,活在当下

  • 累计撰写 20 篇文章
  • 累计创建 6 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

SpringBoot 集成 Websocket

Administrator
2023-10-18 / 0 评论 / 0 点赞 / 10 阅读 / 0 字

Gitee 项目地址

项目地址

引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

创建配置文件

然后创建一个配置文件,注入ServerEndpointExporter,简单来说就是让SpringBoot识别Websocket的注解

package com.oy.websocket.config;

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

/**
 * WebSocket服务配置
 * @Author ouyangtengfei
 * @Date 2022/3/16 09:08
 */

@Configuration
public class WebsocketConfig {

    /**
     * 注入一个ServerEndpointExporter
     * 该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

构建消息模板类

package com.oy.websocket.dto;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * @Author ouyangtengfei
 * @Date 2022/3/16 09:09
 */

@Data
@ToString
@NoArgsConstructor
public class WebsocketMsgDTO {

    /**
     * 发送消息用户
     */
    private String uid;
    /**
     * 接收消息用户
     */
    private String toUId;
    /**
     * 消息内容
     */
    private String content;
    /**
     * 消息时间
     */
    private String dateTime;
    /**
     * 用户列表
     */
    private List onlineUser;

    /**
     * 统一消息模版
     * @param uid 发送消息用户
     * @param content 消息内容
     * @param onlineUser 在线用户列表
     */
    public WebsocketMsgDTO(String uid, String content, List onlineUser) {
        this.uid = uid;
        this.content = content;
        this.onlineUser = onlineUser;
        this.dateTime = localDateTimeToString();
    }


    /**
     * 获取当前时间
     * @return String 12:00:00
     */
    private String localDateTimeToString() {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        return dateTimeFormatter.format( LocalDateTime.now());
    }
}

最后上服务端逻辑代码

@ServerEndpoint(value="") 这个是Websocket服务url前缀,{uid}类似于ResutFul风格的参数

package com.oy.websocket.config;

import com.alibaba.fastjson.JSONObject;
import com.oy.websocket.dto.WebsocketMsgDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author ouyangtengfei
 * @Date 2022/3/16 09:11
 */

@ServerEndpoint(value = "/webSocket/{uid}")
@Component
@Slf4j
public class WebSocketServer {

    /**
     * 机器人发言名称
     */
    private static final String SPOKESMAN_ADMIN = "机器人";

    /**
     * concurrent包的线程安全Set
     * 用来存放每个客户端对应的Session对象
     */
    private static final ConcurrentHashMap SESSION_POOLS = new ConcurrentHashMap<>();

    /**
     * 静态变量,用来记录当前在线连接数。
     * 应该把它设计成线程安全的。
     */
    private static final AtomicInteger ONLINE_NUM = new AtomicInteger();

    /**
     * 获取在线用户列表
     * @return List
     */
    private List getOnlineUsers() {
        return new ArrayList<>(SESSION_POOLS.keySet());
    }

    /**
     * 用户建立连接成功调用
     * @param session 用户集合
     * @param uid     用户标志
     */
    @OnOpen
    public void onOpen(Session session, @PathParam(value = "uid") String uid) {
        // 将加入连接的用户加入SESSION_POOLS集合
        SESSION_POOLS.put(uid, session);
        // 在线用户+1
        ONLINE_NUM.incrementAndGet();
        sendToAll(new WebsocketMsgDTO(SPOKESMAN_ADMIN, uid + " 加入连接!", getOnlineUsers()));
    }

    /**
     * 用户关闭连接时调用
     * @param uid 用户标志
     */
    @OnClose
    public void onClose(@PathParam(value = "uid") String uid) {
        // 将加入连接的用户移除SESSION_POOLS集合
        SESSION_POOLS.remove(uid);
        // 在线用户-1
        ONLINE_NUM.decrementAndGet();
        sendToAll(new WebsocketMsgDTO(SPOKESMAN_ADMIN, uid + " 断开连接!", getOnlineUsers()));
    }

    /**
     * 服务端收到客户端信息
     * @param message 客户端发来的string
     * @param uid     uid 用户标志
     */
    @OnMessage
    public void onMessage(String message, @PathParam(value = "uid") String uid) {
        log.info("Client:[{}], Message: [{}]", uid, message);
        // 接收并解析前端消息并加上时间,最后根据是否有接收用户,区别发送所有用户还是单个用户
        //  WebsocketMsgDTO msgDTO = JSONObject.parseObject(message, WebsocketMsgDTO.class);
        //  msgDTO.setDateTime(localDateTimeToString());
        WebsocketMsgDTO msgDTO = new WebsocketMsgDTO();
        msgDTO.setDateTime(localDateTimeToString());
        msgDTO.setContent(message);
        msgDTO.setToUId(uid);
        // 如果有接收用户就发送单个用户
        if (Strings.isNotBlank(msgDTO.getToUId())) {
            sendMsgByUid(msgDTO);
            return;
        }
        // 否则发送所有人
        sendToAll(msgDTO);
    }

    /**
     * 给所有人发送消息
     * @param msgDTO msgDTO
     */
    private void sendToAll(WebsocketMsgDTO msgDTO) {
        //构建json消息体
        String content = JSONObject.toJSONString(msgDTO);
        // 遍历发送所有在线用户
        SESSION_POOLS.forEach((k, session) ->  sendMessage((Session) session, content));
    }

    /**
     * 给指定用户发送信息
     */
    private void sendMsgByUid(WebsocketMsgDTO msgDTO) {
        sendMessage((Session) SESSION_POOLS.get(msgDTO.getToUId()), JSONObject.toJSONString(msgDTO));
    }

    /**
     * 发送消息方法
     * @param session 用户
     * @param content 消息
     */
    private void sendMessage(Session session, String content){
        try {
            if (Objects.nonNull(session)) {
                // 使用Synchronized锁防止多次发送消息
                synchronized (session) {
                    // 发送消息
                    session.getBasicRemote().sendText(content);
                }
            }
        } catch (IOException ioException) {
            log.info("发送消息失败:{}", ioException.getMessage());
            ioException.printStackTrace();
        }
    }

    /**
     * 获取当前时间
     * @return String 12:00:00
     */
    private String localDateTimeToString() {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        return dateTimeFormatter.format( LocalDateTime.now());
    }

    @OnError
    public void onerror(Session session, Throwable throwable){
        System.out.println();

        log.error(throwable.getMessage(), throwable);
    }
}


0

评论区