SpringBoot集成WebSocket

1、导包

在pom.xml中引入依赖

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

2、新增WebSocketConfig配置类

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


/**
 * @Description: WebSocket配置类。开启WebSocket的支持
 * @Author: mzq
 * @date: 2024-10-29
 */

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

注意:

即使导包后也可能会出现

import org.springframework.web.socket.server.standard.ServerEndpointExporter;找不到和

ServerEndpointExporter找不到的问题

解决方法:

进入阿里云maven仓库,找到对应maven手动下载并手动导入依赖

仓库服务

3、配置WebSocketServer服务

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocket服务器类,用于处理WebSocket连接、消息、错误等事件。
 *
 * @author mzq
 * @date 2024-10-29
 */
@Component
@Slf4j
@ServerEndpoint("/websocket/{sid}")
public class WebSocketServer {

    // 记录当前在线客户端的数量
    private static AtomicInteger onlineSessionClientCount = new AtomicInteger(0);

    // 存储在线会话的映射表
    private static Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>();

    // 当前连接的标识符
    private String sid;

    // 当前会话对象
    private Session session;

    /**
     * 当WebSocket连接打开时调用的方法。
     * @param sid 客户端的唯一标识符
     * @param session 当前WebSocket会话
     */
    @OnOpen
    public void onOpen(@PathParam("sid") String sid, Session session) {
        log.info("连接建立中 ==> session_id = {}, sid = {}", session.getId(), sid);

        // 将当前会话添加到在线会话映射表中
        onlineSessionClientMap.put(sid, session);

        // 在线数量+1
        onlineSessionClientCount.incrementAndGet();

        // 设置当前连接的标识符及会话
        this.sid = sid;
        this.session = session;

        // 发送消息给当前连接
        sendToOne(sid, "连接成功");

        // 输出当前在线数量以及连接信息
        log.info("连接建立成功,当前在线数为:{} ==> 开始监听新连接:session_id = {}, sid = {},。", onlineSessionClientCount, session.getId(), sid);
    }

    /**
     * 当WebSocket连接关闭时调用的方法。
     * @param sid 客户端的唯一标识符
     * @param session 当前WebSocket会话
     */
    @OnClose
    public void onClose(@PathParam("sid") String sid, Session session) {
        // 移除当前会话
        onlineSessionClientMap.remove(sid);

        // 在线数量-1
        onlineSessionClientCount.decrementAndGet();

        // 输出当前在线数量以及关闭的连接信息
        log.info("连接关闭成功,当前在线数为:{} ==> 关闭该连接信息:session_id = {}, sid = {},。", onlineSessionClientCount, session.getId(), sid);
    }

    /**
     * 当从客户端接收到消息时调用的方法。
     * @param message 接收到的消息
     * @param session 当前WebSocket会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("服务端收到客户端消息 ==> fromSid = {},  message = {}", sid, message);
    }

    /**
     * 当WebSocket发生错误时调用的方法。
     * @param session 当前WebSocket会话
     * @param error 错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket发生错误,错误信息为:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 向所有在线客户端发送消息。
     * @param message 要发送的消息
     */
    public void sendToAll(String message) {
        // 遍历在线会话映射表
        onlineSessionClientMap.forEach((onlineSid, toSession) -> {
            try {
                log.info("服务端给客户端群发消息 ==> toSid = {}, message = {}", onlineSid, message);
                toSession.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                log.error("发送消息给客户端时发生错误:{}", onlineSid, e);
                onlineSessionClientMap.remove(onlineSid);
            }
        });
    }

    /**
     * 向单个客户端发送消息。
     * @param toSid 目标客户端的唯一标识符
     * @param message 要发送的消息
     */
    public void sendToOne(String toSid, String message) {
        Session toSession = onlineSessionClientMap.get(toSid);
        if (toSession == null) {
            log.error("服务端给客户端发送消息 ==> toSid = {} 不存在, message = {}", toSid, message);
            return;
        }

        // 使用异步方式发送消息
        log.info("服务端给客户端发送消息 ==> toSid = {}, message = {}", toSid, message);
        toSession.getAsyncRemote().sendText(message);

        // 下面是同步发送消息的备用方法
        // try {
        //     toSession.getBasicRemote().sendText(message);
        // } catch (IOException e) {
        //     log.error("发送消息失败,WebSocket IO异常");
        //     e.printStackTrace();
        // }
    }
}

4、服务测试

此时可启动对应服务,使用WebSocket在线测试工具进行测试

5、前端配置

// 创建WebSocket URL并为每个会话附加随机字符串作为ids
const getRandomString = length => [...Array(length)].map(() => Math.floor(Math.random() * 36).toString(36)).join('');
let ws;
//在onMounted中挂载WebSocket
onMounted(() => {
  const ws_url = `ws://127.0.0.1:9406/websocket/${getRandomString(8)}`;
  console.log('----------WebSocket连接地址----------',ws_url);
  ws = new WebSocket(ws_url);
  ws.onopen = function () {
    console.log('----------WebSocket连接成功----------');
  };
  ws.onmessage = function (event) {
    console.log('收到服务器消息:', event.data);
    const webSocketData = JSON.parse(event.data);
    console.log('----------解析后的数据:----------' , webSocketData)
  }
});

//关闭页面时销毁WebSocket
onUnmounted(() => {
  ws.close();
  console.log('----------WebSocket连接已关闭----------');
});

6、部署服务器端

1、 在 nacos 中网关部分增加配置

#这里以pmccsoft-heatingbusinesspro服务为例

- id: pmccsoft-heatingbusinesspro-websocket
  uri: lb:ws://pmccsoft-heatingbusinesspro
  predicates:
    - Path=/websocket/**

注意配置中不要添加以下内容

filters:

        - StripPrefix=1

不需要鉴权配置则在网关最后添加白名单,例如:

security:
  ignore:
    whites:
      - /websocket/**

前端连接请求地址

const ws_url = `ws://127.18.177.208:8080/websocket/${getRandomString(8)}`
posted @ 2025-04-21 10:03  溪山晚风  阅读(593)  评论(0)    收藏  举报