websocket握手必须用http发起,因浏览器安全沙箱禁止前端js直接建原始tcp连接,只能通过websocket构造函数发起带upgrade: websocket头的http请求,服务器返回101状态码并校验sec-websocket-accept后才升级协议。

WebSocket握手为什么必须用HTTP发起?
因为浏览器安全策略限制,不允许前端代码直接新建原始TCP连接;所有网络请求必须走浏览器已支持的协议栈。WebSocket握手本质是一次“伪装成HTTP的协商”,客户端发一个带Upgrade: websocket和Sec-WebSocket-Key头的GET请求,服务端若支持,就回HTTP/1.1 101 Switching Protocols并带上Sec-WebSocket-Accept——这个值不是随便生成的,是把客户端key拼上固定字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11后做SHA-1 base64编码得出的。只要任一端校验失败,连接就断在握手阶段,不会进入WebSocket数据帧通信。
常见错误现象:
- 控制台报
Failed to construct 'WebSocket': The URL's scheme must be either 'ws' or 'wss':URL用了http://或漏写协议前缀 - Network面板看到状态码
400或426:服务端没识别Upgrade头,或拒绝协议升级(比如Nginx没配proxy_set_header Upgrade $http_upgrade;) - 握手成功但后续收不到消息:服务端实际没切换协议,还在用HTTP方式读取body,导致WebSocket帧被当垃圾丢弃
onclose事件里的event.code有哪些关键值?
event.code是判断断连原因最直接的依据,不能只看event.wasClean布尔值。真实项目里,你得靠它区分是用户主动关页面、网络闪断,还是服务端异常下线。
重点关注这几个值:
-
1000:正常关闭(调用socket.close()或服务端发close帧) -
1001:终端离开(比如页面刷新、tab关闭,Chrome会触发这个) -
1006:非正常关闭(底层TCP断开、心跳超时、无响应),**这是重连逻辑必须覆盖的主力场景** -
1009:消息过大被服务端拒绝(比如单条超过8MB,默认限制) -
4000–4999:应用自定义码(建议和服务端约定好含义,如4001=token过期,4002=权限不足)
注意:1006在不同浏览器行为不一致——Firefox可能不抛出onerror,只走onclose;Safari有时会把网络抖动也报成1006,所以重连前最好加个简单心跳探测。
为什么bufferedAmount突然暴涨却不触发onerror?
bufferedAmount是WebSocket对象上唯一能反映发送队列积压程度的属性,单位是字节。它涨不代表出错,只说明send()调用太快,而底层TCP还没把数据真正推到对端。这在弱网或服务端处理慢时很常见。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
容易踩的坑:
- 误以为
bufferedAmount > 0就该重连——其实只要连接还活着,它就可能持续堆积,直到TCP窗口满或对方ACK延迟 - 用
setInterval轮询检查bufferedAmount,结果发现数值忽高忽低,误判为不稳定——这是TCP拥塞控制和ACK节奏导致的正常波动 - 没设上限直接狂发,最终触发浏览器强制断连(Chrome约
128MB缓冲上限),此时onclose.code可能是1009或1011
实操建议:监控阈值建议设在512KB左右,超了就暂停send(),等onopen或onmessage回调里再恢复;别依赖它做连接健康判断,它和readyState一样,只是瞬时快照。
子协议(protocol参数)到底有什么用?
子协议不是可有可无的装饰字段,它是客户端和服务端在握手阶段就协商好的应用层语义标识。比如传['chat', 'json'],服务端就能据此启用聊天专用的编解码器和鉴权流程;传['binary-v1'],就跳过UTF-8校验直接收二进制帧。
关键点:
- 服务端必须显式声明支持该子协议,否则握手返回
400(如Spring WebSocket里要配webSocketHandler.setAllowedOrigins(...)并注册对应SubProtocolHandler) - 浏览器端传的是字符串数组,但服务端收到的是逗号分隔的单字符串(如
'chat,json'),解析时注意格式 - 子协议名不能含空格或特殊字符,且长度受限(RFC规定最多
255字节) - 它不影响传输性能,但决定了服务端如何解析后续帧——比如没传
json,服务端可能把{"msg":"hi"}当纯文本处理,导致JSON解析失败
真实项目中,子协议常被用来做灰度发布:新旧两套消息格式共存时,客户端连的时候指定'v2',服务端就走新逻辑,避免全量切流风险。










