springboot快速集成websocket

websocket无非就是能够让我们的浏览器与浏览器之间、浏览器与服务器做到及时交互,

目前来说,我使用到的场景就两个:1.用户与用户的聊天室,2.用户与AI的聊天室

websocket实际用起来比较简单,

从后端来说,以springboot为例,

导入依赖坐标

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

载入配置开启websocket服务

没有正确实现的话,控制台会报错No mapping for get /** 。原因就是spring没有正确识别websocket的路径映射,websocket的路径映射不仅需要@ServerEndpoint注解,还需要spring正确扫描并注册websocket端点。因此我们不仅需要导入websocket的config,还需要确保spring正确管理serverEndpoint

@Configuration
@Slf4j
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        log.info("开启websocket配置...");
        // 自定义的 WebSocketHandler,可以在这里注册
        // registry.addHandler(myWebSocketHandler, "/websocketClient/{userId}");
    }
}

类的基础形式

@ServerEndpoint(value = "/websocketClient/{userId}")
@Component
@Slf4j
public class WebSocketClient{}

常见成员变量

/**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
     */
    private static int onlineCount = 0;

    /**
     * concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象
     */
    private static final ConcurrentHashMap<String, WebSocketClient> webSocketMap = new ConcurrentHashMap<>();


    /**
     * websocket的会话对象,与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 用户id 唯一标识
     */
    private String userId;

常见方法:onOpen(建立成功会调用的方法)、onClose(连接关闭调用的方法)、onMessage(收到客户端消息后调用的方法)、onError(发生错误时调用的方法)、sendMessage(向客户端推送消息)

onOpen方法

   @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        //加入map
        webSocketMap.put(userId, this);
        log.info("WebSocket客户端{}连接成功,客户端标识:{},当前在线人数:{}", session.getId(), userId, getOnlineCount());
    }

onClose方法

    @OnClose
    public void onClose() {
        //从map中删除
        webSocketMap.remove(userId);
        log.info("WebSocket客户端{}连接断开,客户端标识:{},当前在线人数:{}", session.getId(), userId, getOnlineCount());
    }

onMessage方法

    @OnMessage
    public void onMessage(String message, Session session) throws Exception {
        // 心跳检测响应
        if (StringUtils.equalsIgnoreCase("ping", message)) {
            sendMessage("pong");
            log.info("WebSocket服务端已回复客户端{}的心跳检测:pong", session.getId());
            return;
        }
        //todo 存入mysql之类
    }

onError方法

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误{}", session.getId(), error);
        error.printStackTrace();
    }

sendMessage方法

    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("客户端{}发送消息{{}}失败", session.getId(), message);
        }
    }

在sendMessage方法的基础上实现了具体消息的发送:

如果是对具体的用户发送消息,则在websocketMap中取出对应的WebsocketClient再调用sendMessage方法

如果是群发消息,则直接遍历websocketMap并依次发送sendMessage即可

public static void sendMessageByUserId(String userId, String message) throws IOException {
        log.info("给用户{}发送{}信息", userId, message);
        if (StrUtil.isNotBlank(userId) && webSocketMap.containsKey(userId)) {
            webSocketMap.get(userId).sendMessage(message);
        }
    }
    public static void sendInfo(String message) {
        for (String item : webSocketMap.keySet()) {
            webSocketMap.get(item).sendMessage(message);
        }
    }

 

posted @ 2025-03-18 16:05  天启A  阅读(607)  评论(0)    收藏  举报