websocket是在tcp上运行的独立协议,仅借http握手建立连接;浏览器因安全沙箱限制只能通过websocket构造函数发起连接,需服务端规范返回101状态码才能成功升级。

WebSocket 不是 HTTP 的替代品,也不是“升级版 HTTP”——它是在 TCP 上跑的独立协议,只是借了 HTTP 的握手阶段完成连接建立。理解这点,才能避开绝大多数面试和实操中的认知偏差。
WebSocket 握手为什么必须用 HTTP?
因为浏览器不允许前端 JS 直接发起原始 TCP 连接,安全沙箱只开放了 WebSocket 构造函数这一条合法路径。它内部会自动发一个带特定 header 的 HTTP 请求:
-
Upgrade: websocket和Connection: Upgrade告诉服务器“我想换协议” -
Sec-WebSocket-Key是客户端生成的一次性 base64 随机串(不是密钥),服务端要把它跟固定字符串拼起来再 SHA1 + base64 得到Sec-WebSocket-Accept - 如果服务端没按规范返回
101 Switching Protocols,连接立刻失败,onerror触发,readyState停在0(CONNECTING)
WebSocket 数据帧和 HTTP 头部完全无关
握手成功后,底层 TCP 连接复用,但后续所有通信都走 WebSocket 自定义的二进制帧格式,HTTP 头部彻底消失。这意味着:
- 你不能再用
fetch或XMLHttpRequest模拟 WebSocket 通信(它们连不到帧层) - Wireshark 抓包看到的是 WebSocket Frame,不是 HTTP Request/Response;Chrome DevTools 的 Network 面板里
ws://连接不显示请求头,只显示消息内容 - 代理、CDN、WAF 如果不识别
Upgrade流程或未开启 WebSocket 支持,会直接中断握手(常见于 Nginx 默认配置)
为什么 onmessage 收不到服务端推送?
最常被忽略的三个硬性前提:
- 服务端必须已调用对应连接的
send()方法(比如 Node.js 的ws.send()、Spring Boot 的SimpMessagingTemplate.convertAndSend()),不是“只要连上就自动推” - 客户端
readyState必须为1(OPEN),0或2(CLOSING)时调用send()会静默丢弃,也不会触发onerror - 若使用子协议(如
new WebSocket(url, ['json'])),服务端也必须在握手响应中声明匹配的Sec-WebSocket-Protocol,否则浏览器直接关闭连接
ws:// 和 wss:// 的实际区别不只是加密
开发环境用 ws://localhost:8080 很方便,但上线后几乎必须切 wss://,原因不止是 HTTPS 强制要求:
- 现代浏览器禁止混合内容:HTTPS 页面里不能新建
ws://连接(会直接报Mixed Content错误并阻断) - 多数企业防火墙、校园网、运营商网关会重置未加密的 WebSocket 连接(尤其检测到
Upgrade头时),wss://因 TLS 加密而“不可见”,更容易穿透 -
wss://默认走 443 端口,比ws://的 80 端口更少被中间设备干扰或限速
真正容易卡住人的地方,往往不在“怎么写”,而在“为什么连不上”——比如 Nginx 缺少 proxy_http_version 1.1 和 proxy_set_header Upgrade $http_upgrade,或者 Spring Boot 的 @EnableWebSocket 没配 WebSocketConfigurer 注册处理器。这些细节不验证,光看握手流程图毫无意义。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











