WebSocket 消息太大连接为什么断了?多线程向 session 发消息为什么报错?STOMP 的 /app 和 /topic 有什么区别?客户端能不能直接广播?简单代理能不能集群?这篇用手写的最小 WebSocket 客户端在 Spring Boot 4.0.8 上逐个验证。
握手响应:HTTP/1.1 101 ;Upgrade=websocket;Connection=upgrade;Sec-WebSocket-Accept 校验通过=True 发送 hello → 收到 echo:hello 发送 4000 字节 → 收到 echo:<4000 字节> 发送 8192 字节 → 收到 echo:<8192 字节> 发送 8193 字节 → 收到 CLOSE code=1009 (服务端)连接建立:session=77ab 线程=http-nio-9601-exec-2 协议= (服务端)连接关闭:CloseStatus[code=1009, reason=The decoded text message was too big for the output buffer and the endpoint does not support partial messages]
默认文本消息上限 8192 字节,超限整条连接断开。
bigbuf:发送 20000 字节 → echo:<20000 字节> 发送 65536 字节 → echo:<65536 字节> 发送 65537 字节 → CLOSE code=1009
ServletServerContainerFactoryBean 显式设置。
直接用 session.sendMessage,两个线程各发 300 条:客户端收到 115 条普通消息;SUMMARY ok=115 errors=485 first=IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method 用 ConcurrentWebSocketSessionDecorator 包起来,两个线程各发 300 条:客户端收到 600 条普通消息;SUMMARY ok=600 errors=0 first=
WebSocketSession 不是线程安全的,并发发送要包装。
客户端 1 CONNECT → CONNECTED | version:1.2 | heart-beat:0,0
客户端 2 CONNECT → CONNECTED | version:1.2 | heart-beat:0,0
客户端 3 CONNECT → CONNECTED | version:1.2 | heart-beat:0,0
客户端 3(没订阅)发 SEND /app/hello "Tom" 之后:
客户端 1 收到: MESSAGE | destination:/topic/greetings | content-type:text/plain;charset=UTF-8 | subscription:sub-1 | message-id:5ae9d4ab-6d44-4997-96fc-0b51eefec4df-1 | content-length:10 | | Hello, Tom
客户端 2 收到: MESSAGE | destination:/topic/greetings | content-type:text/plain;charset=UTF-8 | subscription:sub-2 | message-id:49eae2fe-7051-4674-ae60-211af69edf52-0 | content-length:10 | | Hello, Tom
客户端 3 收到: (超时,没有收到)
客户端 3 直接 SEND 到 /topic/greetings(不经过 @MessageMapping):
客户端 1 收到: MESSAGE | destination:/topic/greetings | content-type:text/plain | subscription:sub-1 | message-id:5ae9d4ab-6d44-4997-96fc-0b51eefec4df-3 | content-length:42 | | 绕过 @MessageMapping,直接发到 /topic
客户端 2 收到: MESSAGE | destination:/topic/greetings | content-type:text/plain | subscription:sub-2 | message-id:49eae2fe-7051-4674-ae60-211af69edf52-2 | content-length:42 | | 绕过 @MessageMapping,直接发到 /topic
SEND 到没有 @MessageMapping 的 /app/unknown → (超时,没有收到)
(服务端)@MessageMapping 收到 'Tom' 线程=clientInboundChannel-16/app 走应用方法,/topic 是代理;客户端能直发 /topic。
客户端声明 heart-beat:10000,10000 → CONNECTED | version:1.2 | heart-beat:0,0 -- 五补:enableSimpleBroker 设置 heartbeat 5000 并提供 TaskScheduler 客户端声明 heart-beat:10000,10000 → CONNECTED | version:1.2 | heart-beat:5000,5000
简单代理默认关闭心跳,需要显式配置。
订阅者连在实例 A,发送者连在实例 B 发 SEND /app/hello → 实例 A 上的订阅者收到: (超时,没有收到) 同一个发送者(实例 B)自己订阅后再发 Bob:实例 B 上的订阅者收到: MESSAGE | destination:/topic/greetings | content-type:text/plain;charset=UTF-8 | subscription:s | message-id:e78c1607-b74e-4243-b8bf-278b5b92cbae-0 | content-length:10 | | Hello, Bob ;实例 A 上的订阅者收到: (超时,没有收到)
简单代理在内存里,跨实例不广播。