直接选 spring websocket,因其能与 spring 生态(@autowired、事务、security)深度集成,避免原生 serverendpoint 的配置分散、注入失败和拦截困难等问题。

WebSocket服务器端用 ServerEndpoint 还是 Spring WebSocket?
直接选 Spring WebSocket,除非你明确要脱离 Spring 生态或部署在纯 Java EE 容器里。原生 ServerEndpoint 依赖容器(如 Tomcat 8+)的 JSR-356 实现,配置分散、拦截难、没法和 Spring 的 @Autowired、事务、安全机制联动。
常见错误现象:NullPointerException 在 @OnOpen 方法里注入 service;或者握手成功但后续消息收不到——大概率是没配好 WebSocketHandler 的注册路径或没启用 @EnableWebSocket。
- Spring Boot 项目只需加
spring-boot-starter-websocket依赖 - 配置类里用
@Bean注册WebSocketHandler,再通过WebSocketConfigurer的registerWebSocketHandlers绑定路径(比如/ws) - 别漏掉前端连接时的协议前缀:
ws://(开发)或wss://(生产),浏览器控制台报Failed to construct 'WebSocket'多半是协议或路径错了
客户端连不上 WebSocket?先检查这三件事
90% 的连接失败不是代码问题,而是环境链路断在中间。WebSocket 建立在 HTTP 升级(Upgrade: websocket)之上,任何中间层都可能阻断它。
使用场景:本地开发能通,上测试环境就 net::ERR_CONNECTION_REFUSED 或卡在 pending;Nginx 反向代理后出现 101 Switching Protocols 但立刻断开。
立即学习“Java免费学习笔记(深入)”;
- Nginx 必须显式透传升级头:
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade"; - 后端服务若启用了 Spring Security,需放行 WebSocket 握手路径(如
/ws/**),否则403静默拦截 - 浏览器开发者工具 Network 标签页里找
ws类型请求,点开看 Headers → Request Headers 是否含Sec-WebSocket-Key和Upgrade: websocket;没有说明根本没走 WebSocket 协议
TextMessage 和 BinaryMessage 怎么选?
绝大多数业务场景用 TextMessage 就够了。WebSocket 协议本身不关心内容格式,但 Java 端的 WebSocketSession.sendMessage() 对文本和二进制做了类型隔离,混用会抛 IllegalArgumentException。
性能影响:文本消息会被自动编码为 UTF-8 字节数组,如果传输大量非文本数据(如图片 base64、压缩 JSON),用 BinaryMessage 能省一次编码开销,但调试难度上升——Wireshark 抓包看到的是乱码,日志里也打不出可读内容。
- 前端
send()发字符串,默认走服务端handleTextMessage() - 发
ArrayBuffer或Blob,才触发handleBinaryMessage() - 不要在
TextMessage里塞超长 JSON 字符串(>1MB),Tomcat 默认限制 1MB,会直接关闭连接,改配置得设maxTextMessageBufferSize
连接断开后怎么重连?别在前端硬写 setTimeout
浏览器原生 WebSocket 对象没有内置重连逻辑,手动轮询容易触发连接风暴或忽略状态判断。更可靠的方式是用状态机 + 指数退避,且必须区分“主动关闭”和“异常断开”。
容易踩的坑:用户切后台再切回来,页面没销毁但 WebSocket 实例已失效,继续调 send() 报 InvalidStateError;或者重连时没清空旧的 onmessage 回调,导致消息被重复处理。
- 监听
onclose事件,检查event.code:1000 是正常关闭,其他(如 1006)算异常,才触发重连 - 用
setTimeout启动重连时,每次延时应递增(如 1s → 2s → 4s),避免雪崩 - 每次新建
WebSocket实例前,确保旧实例的onmessage/onerror已置为null,防止内存泄漏
复杂点在于服务端也要配合:心跳超时时间(setMaxIdleTimeout)得比前端重连间隔短,否则连接在中间被单方面释放,前端还在傻等。











