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 回显出数据.