ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WebSocket技术详解:从协议原理到SpringBoot实战

WebSocket技术详解:从协议原理到SpringBoot实战 1. WebSocket 技术全景解析从协议原理到实战落地WebSocket 不是简单的升级版HTTP而是一种全新的全双工通信协议。2011年成为IETF标准RFC 6455后它彻底改变了客户端与服务器的交互模式。想象一下打电话和发短信的区别——HTTP就像发短信每次都要重新建立连接而WebSocket则是持续通话双方可以随时自由交流。在实时性要求高的场景下如在线游戏、金融交易、协同编辑传统轮询方式会导致高达70%的带宽浪费在无用的HTTP头信息上平均300ms以上的消息延迟服务器承受不必要的连接建立/销毁开销WebSocket通过一次HTTP握手升级连接后续所有通信都基于二进制帧传输。实测数据显示消息延迟可控制在50ms以内带宽利用率提升3-5倍单服务器可维持10万并发连接1.1 协议握手过程深度拆解典型握手请求头示例GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ Sec-WebSocket-Version: 13服务器响应必须包含HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbKxOo关键验证步骤客户端生成16字节随机Base64编码密钥Sec-WebSocket-Key服务器拼接固定GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11对组合字符串做SHA-1哈希后再Base64编码比较计算结果与Sec-WebSocket-Accept安全提示务必验证Origin头防止CSRF攻击生产环境必须使用wss://TLS加密1.2 数据帧格式精要WebSocket帧最小仅2字节结构如下0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 -------------------------------------------------------- |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len126/127) | | |1|2|3| |K| | | ------------------------- - - - - - - - - - - - - - - - | Extended payload length continued, if payload len 127 | - - - - - - - - - - - - - - - ------------------------------- | |Masking-key, if MASK set to 1 | -------------------------------------------------------------- | Masking-key (continued) | Payload Data | -------------------------------- - - - - - - - - - - - - - - - : Payload Data continued ... : - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Payload Data continued ... | ---------------------------------------------------------------关键字段说明FIN标记是否为消息最后一帧Opcode0x1文本帧/0x2二进制帧/0x8关闭帧/0x9心跳Ping/0xA心跳PongMask客户端到服务端必须掩码安全规范Payload长度7位表示≤125字节126表示后续2字节扩展长度127表示8字节扩展2. SpringBoot实战构建高可用WebSocket服务2.1 服务端完整实现pom.xml必备依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-websocket/artifactId /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency配置类示例Configuration EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), /ws) .setAllowedOrigins(*) .addInterceptors(new HttpSessionHandshakeInterceptor(){ Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, MapString, Object attributes) { // 提取token进行鉴权 String token ((ServletServerHttpRequest) request) .getServletRequest().getParameter(token); if(!validateToken(token)) { return false; } attributes.put(userId, extractUserId(token)); return true; } }); } Bean public WebSocketHandler myHandler() { return new MyWebSocketHandler(); } }消息处理器核心逻辑public class MyWebSocketHandler extends TextWebSocketHandler { private static final ConcurrentHashMapString, WebSocketSession sessions new ConcurrentHashMap(); Override public void afterConnectionEstablished(WebSocketSession session) { String userId (String) session.getAttributes().get(userId); sessions.put(userId, session); log.info(用户 {} 连接成功当前在线 {}, userId, sessions.size()); } Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { // 处理JSON消息示例 JSONObject msg JSON.parseObject(message.getPayload()); switch(msg.getString(type)) { case chat: forwardMessage(msg.getString(to), new TextMessage(来自msg.getString(from): msg.getString(content))); break; case heartbeat: session.sendMessage(new TextMessage({\type\:\pong\})); break; } } private void forwardMessage(String userId, TextMessage message) { WebSocketSession target sessions.get(userId); if(target ! null target.isOpen()) { try { target.sendMessage(message); } catch (IOException e) { log.error(消息转发失败, e); } } } }2.2 客户端实现方案对比浏览器原生APIconst socket new WebSocket(wss://example.com/ws?tokenxxx); socket.onopen () { console.log(连接已建立); socket.send(JSON.stringify({type: chat, to: user2, content: 你好})); }; socket.onmessage (event) { const data JSON.parse(event.data); if(data.type chat) { appendMessage(data.from, data.content); } }; // 心跳检测 setInterval(() { if(socket.readyState WebSocket.OPEN) { socket.send(JSON.stringify({type: heartbeat})); } }, 30000);SpringBoot客户端Configuration public class ClientWebSocketConfig { Bean public WebSocketClient webSocketClient() { return new StandardWebSocketClient(); } Bean public WebSocketConnectionManager connectionManager( WebSocketClient webSocketClient, ClientWebSocketHandler handler) { WebSocketConnectionManager manager new WebSocketConnectionManager( webSocketClient, handler, ws://localhost:8080/ws?tokenxxx ); manager.setAutoStartup(true); return manager; } } Component public class ClientWebSocketHandler extends TextWebSocketHandler { Override public void afterConnectionEstablished(WebSocketSession session) { session.sendMessage(new TextMessage({\type\:\register\})); } Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { System.out.println(收到消息: message.getPayload()); } }3. 生产环境进阶方案3.1 集群会话管理单机方案问题用户连接分散在不同实例广播消息无法全覆盖会话状态不同步Redis分布式方案Configuration public class RedisWebSocketConfig { Bean public RedisMessageListenerContainer redisContainer( RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { RedisMessageListenerContainer container new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.addMessageListener(listenerAdapter, new PatternTopic(/topic/msg)); return container; } Bean public MessageListenerAdapter listenerAdapter(RedisMessageReceiver receiver) { return new MessageListenerAdapter(receiver, receiveMessage); } } Component public class RedisMessageReceiver { Autowired private SimpMessagingTemplate messagingTemplate; public void receiveMessage(String message) { JSONObject msg JSON.parseObject(message); messagingTemplate.convertAndSendToUser( msg.getString(to), /queue/msg, msg.getString(content)); } }3.2 性能优化参数关键配置项application.ymlserver: tomcat: max-threads: 200 max-connections: 10000 websocket: max-binary-message-buffer-size: 8192 max-text-message-buffer-size: 8192 max-session-idle-timeout: 1800000 spring: redis: lettuce: pool: max-active: 50 max-idle: 10 min-idle: 53.3 监控与运维Prometheus监控指标示例Bean public MeterRegistryCustomizerMeterRegistry metricsCommonTags() { return registry - registry.config().commonTags( application, websocket-service, region, System.getenv(REGION) ); } Scheduled(fixedRate 60000) public void reportMetrics() { Metrics.gauge(websocket.sessions.active, MyWebSocketHandler.getSessionCount()); }健康检查端点Component public class WebSocketHealthIndicator implements HealthIndicator { Override public Health health() { if(MyWebSocketHandler.getSessionCount() 0) { return Health.up() .withDetail(sessions, MyWebSocketHandler.getSessionCount()) .build(); } return Health.down().build(); } }4. 典型问题排查手册4.1 连接建立失败常见错误Error during WebSocket handshake: Unexpected response code: 403解决方案检查CORS配置验证CSRF防护白名单确认握手拦截器逻辑4.2 消息丢失处理重发机制实现Slf4j public class GuaranteedMessageSender { private final WebSocketSession session; private final ConcurrentHashMapString, MessageRecord pending new ConcurrentHashMap(); public void sendWithRetry(String messageId, String payload) { CompletableFuture.runAsync(() - { int retry 0; while(retry 3) { try { session.sendMessage(new TextMessage(payload)); pending.put(messageId, new MessageRecord( System.currentTimeMillis(), payload )); break; } catch (IOException e) { log.warn(消息发送失败重试 {}, retry, e); Thread.sleep(1000 * (retry 1)); retry; } } }); } Data AllArgsConstructor private static class MessageRecord { private long timestamp; private String payload; } }4.3 内存泄漏预防关键检查点及时移除断开连接的session引用设置合理的消息缓冲区大小监控WebSocketSession对象数量避免在handler中保存大对象Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { String userId (String) session.getAttributes().get(userId); sessions.remove(userId); log.info(用户 {} 断开连接原因{}, userId, status.getReason()); }5. 行业应用场景深度剖析5.1 金融实时行情系统架构特点每秒推送5000条行情数据压缩率要求高采用permessage-deflate扩展分级订阅模式性能优化点二进制协议设计public class MarketDataEncoder extends BinaryMessageCodec { private static final byte HEADER (byte) 0xA5; Override protected byte[] encodePayload(Message? message) { MarketData data (MarketData) message.getPayload(); ByteBuffer buf ByteBuffer.allocate(32); buf.put(HEADER); buf.putLong(data.getInstrumentId()); buf.putDouble(data.getPrice()); buf.putInt(data.getVolume()); return buf.array(); } }增量更新策略// 客户端处理增量更新 socket.onmessage (event) { const view new DataView(event.data); if(view.getUint8(0) 0xA5) { const instrumentId view.getBigUint64(1); const price view.getFloat64(9); const volume view.getInt32(17); updatePrice(instrumentId, price, volume); } };5.2 在线协作编辑器冲突解决算法public class OperationalTransform { public static String applyTransform(String document, ListOperation operations) { for(Operation op : operations) { switch(op.getType()) { case INSERT: document document.substring(0, op.getPosition()) op.getText() document.substring(op.getPosition()); break; case DELETE: document document.substring(0, op.getPosition()) document.substring(op.getPosition() op.getLength()); break; } } return document; } }实时同步流程客户端本地操作立即生效发送操作到服务端服务端广播给其他客户端收到远程操作后应用OT算法5.3 物联网设备监控设备连接管理public class DeviceSessionManager { private final ConcurrentHashMapString, DeviceSession sessions; public void onDeviceConnected(String deviceId, WebSocketSession session) { DeviceSession deviceSession new DeviceSession(deviceId, session); sessions.put(deviceId, deviceSession); startHealthCheck(deviceId); } private void startHealthCheck(String deviceId) { ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); scheduler.scheduleAtFixedRate(() - { DeviceSession session sessions.get(deviceId); if(session ! null session.isActive()) { try { session.sendPing(); } catch (IOException e) { log.warn(设备 {} 心跳检测失败, deviceId); sessions.remove(deviceId); scheduler.shutdown(); } } }, 0, 30, TimeUnit.SECONDS); } }6. 安全防护体系构建6.1 认证授权方案JWT鉴权实现public class JwtHandshakeInterceptor extends HttpSessionHandshakeInterceptor { Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, MapString, Object attributes) { String token ((ServletServerHttpRequest) request) .getServletRequest().getParameter(token); try { Claims claims Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); attributes.put(userId, claims.getSubject()); attributes.put(roles, claims.get(roles, List.class)); return true; } catch (JwtException e) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return false; } } }6.2 消息加密方案AES消息加密器public class AesMessageConverter implements MessageConverter { private final SecretKeySpec secretKey; public AesMessageConverter(String key) { secretKey new SecretKeySpec( Base64.getDecoder().decode(key), AES); } Override public Object fromMessage(Message? message, Class? targetClass) { byte[] encrypted (byte[]) message.getPayload(); try { Cipher cipher Cipher.getInstance(AES/CBC/PKCS5Padding); cipher.init(Cipher.DECRYPT_MODE, secretKey); return new String(cipher.doFinal(encrypted)); } catch (Exception e) { throw new MessageConversionException(解密失败, e); } } }6.3 DDOS防护策略限流过滤器配置Bean public FilterRegistrationBeanRateLimitFilter rateLimitFilter() { FilterRegistrationBeanRateLimitFilter registration new FilterRegistrationBean(); registration.setFilter(new RateLimitFilter( 100, // 每秒100次连接 10 // 每个IP最多10个并发连接 )); registration.addUrlPatterns(/ws/*); return registration; }7. 性能压测与调优7.1 基准测试方案JMeter测试配置Thread Group: - Number of Threads: 1000 - Ramp-Up Period: 60 - Loop Count: Forever WebSocket Request: - Server: ws://localhost:8080/ws - Connection Timeout: 5000 - Response Timeout: 20000 - Message Backlog: 1007.2 关键性能指标测试环境4核8G结果指标单机性能集群(3节点)最大连接数12,00035,000消息延迟(P99)85ms120ms吞吐量(1KB消息)8,000/s22,000/s内存占用(10K连接)1.2GB4GB7.3 调优经验总结Linux内核参数优化# 增加文件描述符限制 ulimit -n 1000000 echo fs.file-max 1000000 /etc/sysctl.conf # TCP参数优化 echo net.ipv4.tcp_max_syn_backlog 8192 /etc/sysctl.conf echo net.core.somaxconn 8192 /etc/sysctl.conf echo net.ipv4.tcp_tw_reuse 1 /etc/sysctl.confJVM参数建议-server -Xms4g -Xmx4g -XX:UseG1GC -XX:MaxGCPauseMillis200 -XX:ParallelGCThreads4 -XX:ConcGCThreads2 -XX:HeapDumpOnOutOfMemoryError避免的陷阱不要在每个消息处理中创建新对象谨慎使用Async注解避免阻塞IO操作控制日志输出频率8. 协议扩展与未来演进8.1 扩展协议支持permessage-deflate压缩Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(8192); container.setMaxBinaryMessageBufferSize(8192); container.setAsyncSendTimeout(5000L); container.setMaxSessionIdleTimeout(1800000L); // 启用压缩 MapString, String parameters new HashMap(); parameters.put(permessage-deflate, true); container.setUserProperties(parameters); return container; }8.2 WebSocket与HTTP/3QUIC协议优势连接迁移切换网络不断连多路复用无队头阻塞0-RTT快速重连实验性支持// 需要支持HTTP/3的客户端库 Bean public WebSocketClient http3Client() { return new JettyQuicWebSocketClient( new ClientQuicConfiguration( QuicConfig.newBuilder() .withMaxRecvUdpPayloadSize(1452) .build() ) ); }8.3 替代方案对比技术延迟吞吐量开发复杂度适用场景WebSocket50ms高中全双工实时通信SSE100ms中低服务器单向推送MQTT80ms高高IoT设备通信gRPC流60ms很高高微服务间通信Long Polling300ms低低兼容性要求高的简单场景9. 开发调试技巧合集9.1 Chrome开发者工具网络帧分析打开Chrome DevTools → Network筛选WebSocket连接查看Frames标签页绿色箭头发送的消息红色箭头接收的消息可查看每条消息的时间戳和内容9.2 Wireshark抓包分析过滤规则示例tcp.port 8080 (http || websocket)关键字段解析HTTP/1.1 101 Switching Protocols握手成功WebSocket Opcode8表示关闭帧Masking-key客户端消息必须掩码9.3 服务端调试端点Spring Actuator配置management: endpoints: web: exposure: include: websocketstats endpoint: websocketstats: enabled: true获取统计信息curl http://localhost:8080/actuator/websocketstats输出示例{ sessions: 142, sendQueueSize: 0, sendBufferSize: 8192, textMessageSizeStats: { count: 1250, max: 1024, mean: 342.5 } }10. 客户端兼容性解决方案10.1 降级策略设计检测与回退流程function connectWebSocket() { if(WebSocket in window) { return new WebSocket(url); } else if(MozWebSocket in window) { return new MozWebSocket(url); } else { startLongPolling(); } } function startLongPolling() { function poll() { fetch(/poll).then(res { handleMessages(res.json()); poll(); }); } poll(); }10.2 移动端优化实践Android保活策略public class WebSocketService extends Service { private WebSocketClient client; Override public int onStartCommand(Intent intent, int flags, int startId) { startForeground(NOTIFICATION_ID, createNotification()); client new WebSocketClient(URI.create(wss://example.com/ws)) { Override public void onReconnect() { // 网络恢复后自动重连 } }; client.connect(); return START_STICKY; } private Notification createNotification() { // 创建前台服务通知 } }iOS后台维持技巧func applicationDidEnterBackground(_ application: UIApplication) { var bgTask UIBackgroundTaskIdentifier.invalid bgTask application.beginBackgroundTask { application.endBackgroundTask(bgTask) } DispatchQueue.global().async { while true { if !self.webSocket.isConnected { self.webSocket.connect() } Thread.sleep(forTimeInterval: 30) } } }11. 架构设计模式演进11.1 网关集成方案Spring Cloud Gateway配置spring: cloud: gateway: routes: - id: websocket_route uri: lb://ws-service predicates: - Path/ws/** filters: - StripPrefix1 metadata: websocket: trueNginx反向代理配置map $http_upgrade $connection_upgrade { default upgrade; close; } server { location /ws/ { proxy_pass http://backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_set_header Host $host; # 重要超时参数 proxy_read_timeout 86400s; proxy_send_timeout 86400s; } }11.2 微服务消息桥接跨服务消息转发Configuration public class MessagingBridgeConfig { Bean public IntegrationFlow websocketToKafkaFlow( WebSocketMessageHandler webSocketHandler, KafkaTemplateString, String kafkaTemplate) { return IntegrationFlows .from(webSocketHandler) .filter(Message.class, m - ((Message?) m).getHeaders().get(type).equals(order)) .transform(Message.class, m - ((TextMessage) m.getPayload()).getPayload()) .handle(kafkaTemplate) .get(); } }11.3 状态同步架构CRDT数据结构示例public class WSyncDocument { private final MapString, CRDTNode nodes new ConcurrentHashMap(); public void applyOperation(Operation op) { nodes.compute(op.getNodeId(), (id, node) - { if(node null) { node new CRDTNode(op.getNodeId()); } node.apply(op); return node; }); } public String getContent() { return nodes.values().stream() .sorted(Comparator.comparing(CRDTNode::getTimestamp)) .map(CRDTNode::getValue) .collect(Collectors.joining()); } }12. 前沿技术融合探索12.1 WebAssembly加速消息处理优化// message_processor.cpp extern C { EMSCRIPTEN_KEEPALIVE void processBinaryMessage(uint8_t* data, int length) { // 高性能二进制处理 } }JavaScript调用WebAssembly.instantiateStreaming(fetch(processor.wasm)) .then(obj { const process obj.instance.exports.processBinaryMessage; socket.onmessage (event) { const data new Uint8Array(event.data); process(data, data.length); }; });12.2 WebRTC结合方案P2P文件传输示例// 通过WebSocket交换信令 socket.on(offer, async (offer) { const pc new RTCPeerConnection(); await pc.setRemoteDescription(offer); const answer await pc.createAnswer(); socket.emit(answer, answer); pc.ondatachannel (event) { event.channel.onmessage (e) { // 处理接收到的文件分片 }; }; });12.3 区块链消息验证消息签名验证// 智能合约验证逻辑 function verifyMessage( address sender, string memory message, bytes memory signature ) public pure returns (bool) { bytes32 hash keccak256(abi.encodePacked(message)); return sender hash.recover(signature); }Java签名生成public String signMessage(String privateKey, String message) { Sign.SignatureData signature Sign.signPrefixedMessage( Hash.sha3(message.getBytes()), Numeric.toBigInt(privateKey)); return Numeric.toHexString( ByteUtils.concat( signature.getR(), signature.getS(), signature.getV())); }13. 质量保障体系13.1 自动化测试策略集成测试示例SpringBootTest(webEnvironment RANDOM_PORT) public class WebSocketIntegrationTest { LocalServerPort private int port; Test public void testMessageEcho() throws Exception { WebSocketClient client new StandardWebSocketClient(); WebSocketSession session client.doHandshake( new TextWebSocketHandler() { Override protected void handleTextMessage(WebSocketSession session, TextMessage message) { assertEquals(Hello, message.getPayload()); } }, ws://localhost: port /ws ).get(); session.sendMessage(new TextMessage(Hello)); Thread.sleep(1000); session.close(); } }13.2 混沌工程实践网络故障注入Bean public ChaosInterceptor chaosInterceptor() { return new ChaosInterceptor( 0.01, // 1%概率丢包 100, // 最大延迟100ms 0.005 // 0.5%概率错误响应 ); } Configuration public class ChaosWebSocketConfig extends WebSocketConfigurer { Autowired private ChaosInterceptor interceptor; Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(echoHandler(), /ws) .addInterceptors(interceptor); } }13.3 全链路压测Locust测试脚本from locust import HttpUser, task, between from websocket import create_connection class WebSocketUser(HttpUser): wait_time between(1, 3) task def chat(self): ws create_connection( wss://localhost/ws, header[Authorization: Bearer token] ) ws.send(Hello) ws.recv() ws.close()14. 成本优化指南14.1 连接密度提升连接复用方案Bean public WebSocketHandler multiplexHandler() { return new WebSocketHandlerDecoratorFactory() { Override public WebSocketHandler decorate(WebSocketHandler handler) { return new WebSocketSessionMultiplexer(handler, 10); } }; } public class WebSocketSessionMultiplexer extends TextWebSocketHandler { private final MapString, WebSocketSession channels new ConcurrentHashMap(); public void handleTextMessage(WebSocketSession session, TextMessage message) { String channelId extractChannelId(message); WebSocketSession target channels.get(channelId); if(target ! null) { target.sendMessage(message); } } }14.2 带宽压缩方案消息差分算法public class DiffMessageCodec extends AbstractMessageCodec { private final MapString, String lastMessages new ConcurrentHashMap(); Override protected byte[] encodePayload(Message? message) { String current (String) message.getPayload(); String last lastMessages.get(message.getHeaders().getId()); if(last ! null) { String diff StringDiff.diff(last, current); if(diff.length() current.length() * 0.7) { return (DIFF: diff).getBytes(); } } lastMessages.put(message.getHeaders().getId(), current); return (FULL: current).getBytes(); } }14.3 服务器选型建议机型配置参考表连接规模推荐配置预估成本月1K2核4G$201K-5K4核8G 负载均衡$1505K-20K8核16G集群$60020K-100K16核32G Redis$2500100K专用网络设备定制报价15. 经典案例复盘15.1 在线教育平台挑战500并发课堂需同步白板、视频、聊天跨国网络延迟解决方案区域网关分发分层消息优先级增量白板同步技术指标端到端延迟 200ms跨国消息丢失率 0.001%支持10万用户同时在线15.2 智能家居中控设备协议适配public class DeviceProtocolAdapter { public WebSocketMessage toWebSocket(DeviceMessage deviceMsg) { switch(deviceMsg.getProtocol()) { case MODBUS: return convertModbus(deviceMsg); case MQTT: return convertMqtt(deviceMsg); case ZIGBEE: return convertZigbee(deviceMsg); default: throw new UnsupportedOperationException(); } } private WebSocketMessage convertModbus(DeviceMessage msg) { // 解析Modbus RTU报文 byte[] data msg.getRawData(); int address data[0] 0xFF; int funcCode data[1] 0xFF; // 转换为JSON格式 JSONObject json new JSONObject(); json.put(type, modbus); json.put(address, address); json.put(function, funcCode); return new TextMessage(json.toString()); } }15.3 大型MMO游戏帧同步优化// Unity客户端实现 public class NetworkManager : MonoBehaviour { private WebSocket ws; private Queuebyte[] messageQueue new Queuebyte[](); void Start() { ws new WebSocket(wss://game.example.com/ws); ws.OnMessage (sender, e) { lock(messageQueue) { messageQueue.Enqueue(e.RawData); } }; ws.Connect(); } void Fixed
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进