根本原因是服务端未完成HTTP升级响应(未返回101 Switching Protocols),常见于路由未注册、路径不匹配或中间件拦截Upgrade请求;需确认服务端正确暴露端点、配置WebSocket支持并透传Origin头。

WebSocket 不是“学完视频就能直接跑通”的技术,**没配对的前后端握手、没处理好连接状态、没加心跳保活,90% 的新手在第 1 个 demo 就卡在 WebSocket connection failed 或 ERR_CONNECTION_REFUSED**。
你不需要先看一整套视频合集——真正卡住你的,就那几个关键动作。下面直奔实操环节。
为什么 new WebSocket('ws://localhost:8080/ws') 一直 pending 或报错?
根本原因不是代码写错了,而是服务端压根没完成 HTTP 升级响应(即没返回 101 Switching Protocols)。
常见错误现象:
- 浏览器控制台显示
Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss'→ 检查 URL 是否用了http://而非ws:// - Network 面板里看到请求状态是
(pending)或cancelled→ 服务端路由未注册 / 路径不匹配 / 中间件拦截了 upgrade 请求 - 返回
404或405→ 端点路径(如/ws)没被服务端正确暴露
实操建议:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 用
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" http://localhost:8080/ws手动模拟握手,确认是否返回101 - Node.js +
ws库时,确保new WebSocket.Server({ port: 8080 })启动成功,且没被其他进程占端口 - Spring Boot 中,
@Configuration类必须有@EnableWebSocket,且registry.addHandler(..., "/ws")的路径要和前端 URL 完全一致(注意结尾斜杠)
如何让客户端稳定收到服务端推送,而不是连上 30 秒就自动断开?
WebSocket 连接被静默断开,95% 是因为中间网络设备(如 Nginx、负载均衡、运营商网关)主动 kill 了空闲长连接。
这不是代码 bug,是部署环境的默认行为。
实操建议:
- 服务端必须实现心跳:定时发
ping帧(或自定义{"type":"ping"}消息),客户端收到后回pong(或{"type":"pong"}) - Nginx 配置里必须显式开启 WebSocket 支持:
proxy_http_version 1.1;、proxy_set_header Upgrade $http_upgrade;、proxy_set_header Connection "upgrade"; - 浏览器端不要只监听
onmessage,务必加上onclose和onerror处理逻辑,并在onclose里做自动重连(带退避策略,比如 1s→2s→4s)
用 JSON 传消息,为什么服务端收不到 data 或解析失败?
WebSocket 原生只认二进制帧或 UTF-8 文本帧,它不管你是 JSON 还是 XML。所谓 “JSON 消息”,其实是开发者自己约定的 payload 格式。
典型翻车点:
- 前端用
ws.send(JSON.stringify(obj)),但服务端没调JSON.parse()—— 直接当字符串处理,字段取不到 - Spring Boot 的
TextWebSocketHandler只能处理文本帧;如果前端误发了Blob或ArrayBuffer,会直接触发handleTransportError - Go 的
websocket92/websocket默认启用RegisterJsonRouter,但它要求消息体必须是标准 JSON 对象(不能是数组或纯字符串),否则 panic
实操建议:
- 统一约定:所有消息外层为 JSON 对象,含
type字段(如{"type":"chat","content":"hi"}),避免裸字符串 - 服务端接收后先做
try/catch JSON.parse,失败则丢弃并记录日志,别让单条坏消息 crash 整个连接 - 调试时用
ws.onmessage = e => console.log('raw:', e.data)看原始数据,再决定要不要 parse
本地能跑通,部署到服务器就跨域或连不上?
跨域不是前端能绕过的限制,而是浏览器对 ws:// 连接的 Origin 校验机制。它和 HTTP 的 CORS 不是一回事,但表现类似。
关键点:
-
ws://协议本身无同源限制,但浏览器在发起握手请求时,会带上Originheader,服务端可据此拒绝非法来源 - Spring Boot 默认允许所有 Origin(
setAllowedOrigins("*")),但生产环境必须改成具体域名,如["https://myapp.com"] - Node.js +
ws库需手动校验:const origin = req.headers.origin; if (!allowedOrigins.includes(origin)) return res.end(); - 如果用 Nginx 反向代理,确保
proxy_set_header Origin $scheme://$host;透传正确 Origin,否则后端拿到的是http://localhost导致校验失败
真正难的从来不是“怎么写第一行 new WebSocket”,而是握手能否过、心跳是否扛得住中间件、错误是否可感知可恢复。这些细节藏在配置和边界处理里,视频教程往往一笔带过。










