websocket双向通信靠协议层原生全双工支持,握手阶段单向依赖http完成升级(客户端发upgrade请求,服务端回101响应),连接建立后使用独立帧协议通信,收发流完全解耦、低开销。

WebSocket 实现双向通信,靠的不是“模拟”或“轮询”,而是协议层原生支持的全双工数据通道。连接建立后,客户端和服务器各自拥有独立的发送/接收流,互不阻塞、无需协商谁先发。
WebSocket 握手阶段必须用 HTTP 协议
很多人误以为 WebSocket 是完全脱离 HTTP 的新协议,其实它依赖 HTTP 完成初始握手。客户端发起一个带 Upgrade: websocket 和 Connection: Upgrade 头的 HTTP 请求,服务端返回 101 Switching Protocols 状态码才算升级成功。这一步失败,后续所有双向通信都无从谈起。
常见错误现象:
- 浏览器控制台报
WebSocket connection to 'ws://...' failed: Error during WebSocket handshake—— 通常是服务端没正确响应101,或反向代理(如 Nginx)未透传升级头 - 请求卡在
Pending状态 —— 可能是服务端未读取完客户端的握手请求体,或 TLS 配置不匹配(wss://要求证书有效)
关键点:
- Nginx 需显式配置:
proxy_http_version 1.1;、proxy_set_header Upgrade $http_upgrade;、proxy_set_header Connection "upgrade"; - Node.js 的
http.Server实例不能直接监听upgrade事件,要用server.on('upgrade', ...)拦截并手动处理握手
onmessage 和 send() 不是“配对调用”,而是完全解耦
WebSocket 的双向性体现在 API 设计上:客户端调用 socket.send() 不需要等待服务器响应;服务端调用 session.getBasicRemote().sendText()(Java)或 ws.send()(Node)也不依赖客户端是否正在监听。双方的收发逻辑各自独立运行。
使用场景差异:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 聊天室里,用户 A 发消息时,服务端会同时调用
send()给所有在线成员(包括 A 自己),这不是“回显”,而是广播 - 实时协作编辑中,服务端收到光标移动事件后,可能只推送给同文档的其他协作者,不发给源客户端
容易踩的坑:
- 在
onmessage回调里直接嵌套send()并假设对方立刻收到 —— 实际网络有延迟,且send()是异步非阻塞的,返回不代表已送达 - 用
setInterval频繁调用send()而不检查socket.readyState === WebSocket.OPEN,连接断开时会静默失败
帧格式决定了低开销和全双工能力
HTTP 每次请求都要携带几百字节头部,而 WebSocket 数据帧最小只有 2 字节(不含掩码)。一个文本帧结构包括 FIN(是否结束)、opcode(类型)、payload length、mask key(客户端强制)、payload data。这种轻量设计让高频小包通信成为可能。
性能影响明显:
- 1000 个用户每秒各发 1 条 50 字节消息,HTTP 轮询约需 1000×(头部+body) ≈ 数 MB/s 带宽;WebSocket 实际传输仅约 100 KB/s
- 服务端内存占用差异更大:HTTP 轮询需为每个请求维持完整上下文;WebSocket 每个连接只存一个
Session或WebSocket对象
注意兼容性细节:
- 客户端发帧必须启用掩码(mask=1),否则服务端应拒绝(RFC 6455 强制要求),这是安全机制,不是可选项
- 服务端发帧 mask 必须为 0,否则现代浏览器直接关闭连接
真正难的不是“怎么写 send/onmessage”,而是理解连接生命周期中哪些状态允许发数据、哪些错误必须重连、以及如何在分布式环境下同步 session 状态 —— 这些不在协议规范里,得靠业务层兜底。










