MeteorCat / WebSocket服务

Created Mon, 01 Jan 2024 22:53:21 +0800 Modified Wed, 29 Oct 2025 23:25:00 +0800
738 Words

WebSocket挂载

后续 SpringBoot 引入了 WebScoket 组件, 可以直接引入提供了 H5 游戏当做简单服务端, 主要选型基于以下考虑:

  • WebSocket直接集成 SpringBoot 可以直接复用Java经验开发
  • H5/微信小游戏端基本上弱交互性, 大部分不需要复杂不停机热更, 可以直接重启服务
  • Netty 需要从底层开发许多附加模块且需要 Socket 方面知识
  • 有的 H5 只需要简单 JSON 格式处理, 后续集成 Protobuf 处理

这里引入基础 Websocket 库:

<dependencies>
    <!-- WebSocket库 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
</dependencies>

这里先编写个 echo 挂载服务测试:

package com.app.ford;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;


/**
 * 全局 WebSocket 挂载服务
 * BinaryWebSocketHandler 代表了二进制传输数据
 * TextWebSocketHandler 代表了文本流传输数据
 */
@Component
public class FordWebSocketHandler extends TextWebSocketHandler {

    /**
     * 日志库
     */
    Logger logger = LoggerFactory.getLogger(FordWebSocketHandler.class);


    /**
     * Established - 会话连接回调
     *
     * @param session 会话句柄
     * @throws Exception 异常错误
     */
    @Override
    public void afterConnectionEstablished(@NonNull WebSocketSession session) throws Exception {
        logger.debug("Established = {}", session);
    }


    /**
     * Closed - 会话连接关闭回调
     *
     * @param session 会话句柄
     * @param status  关闭状态
     * @throws Exception 异常错误
     */
    @Override
    public void afterConnectionClosed(@NonNull WebSocketSession session, @NonNull CloseStatus status) throws Exception {
        logger.debug("Close = {},{}", session, status);
    }

    /**
     * Error - 会话错误异常回调
     *
     * @param session   会话句柄
     * @param exception 回调错误
     * @throws Exception 异常错误
     */
    @Override
    public void handleTransportError(@NonNull WebSocketSession session, @NonNull Throwable exception) throws Exception {
        logger.debug("Error = {},{}", session, exception.toString());
    }

    /**
     * Text - 接收到文本数据回调
     * 如果二进制数据则是 handleBinaryMessage 回调
     *
     * @param session Websocket
     * @param message data
     * @throws Exception Error
     */
    @Override
    protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) throws Exception {
        logger.debug("Message = {},{}", session, message.getPayload());

        // echo 返回数据
        session.sendMessage(message);
    }
}

之后就是编写服务配置将服务挂载和写入:

package com.app.ford.config;

import com.app.ford.FordWebSocketHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

/**
 * 全局的 WebSocket 挂载配置器
 */
@Configuration
@EnableWebSocket
public class FordWebSocketConfig implements WebSocketConfigurer {

    /**
     * 访问路径
     */
    @Value("${websocket.server.path:/}")
    private String serverPath;

    /**
     * 传输数据缓存大小
     */
    @Value("${websocket.buffer.max.size:8192}")
    private Integer bufferMaxSize;


    /**
     * 待机主动中断时间
     */
    @Value("${websocket.idle.timeout:600000}")
    private Long idleTimeout;


    /**
     * 允许跨域地址
     */
    @Value("${websocket.allowed.origins:*}")
    private String allowOrigins;


    /**
     * 默认加载的服务
     */
    private final FordWebSocketHandler handler;

    /**
     * 获取全局的 Websocket 运行时
     *
     * @param handler 运行时
     */
    public FordWebSocketConfig(FordWebSocketHandler handler) {
        this.handler = handler;
    }


    /**
     * 注册运行时句柄
     *
     * @param registry 注册器
     */
    @Override
    public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
        if (handler == null) {
            throw new RuntimeException("failed by WebSocketHandler: FordWebSocketHandler");
        }
        registry.addHandler(handler, serverPath).setAllowedOrigins(allowOrigins);
    }


    /**
     * 全局 Servlet 的配置容器
     *
     * @return ServletServerContainerFactoryBean
     */
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxTextMessageBufferSize(bufferMaxSize);
        container.setMaxBinaryMessageBufferSize(bufferMaxSize);
        container.setMaxSessionIdleTimeout(idleTimeout);
        return container;
    }
    
}

目前启动下就能挂载服务, 之后访问地址服务就能 echo 回显出数据.