WebSocket 实时通信
校招考察频度:☆ 中频(实时通信与长连接·应用拓展) 生产导向主线:HTTP 传统的“一问一答”模式无法实现低延迟的服务端主动推送。WebSocket 通过一次标准的 HTTP 请求升级(
101 Switching Protocols)将 TCP 连接转变为全双工双向长连接。面试重点:握手升级流程与 Sec-WebSocket-Key 校验、NAT 超时与应用层 Ping/Pong 心跳保活机制、以及 WebSocket vs SSE(Server-Sent Events)在生产中的选型。
一、握手升级机制(HTTP 101)
WebSocket 握手: 标准 HTTP 协议升级为全双工双向通信
- 握手核心请求头:
Connection: UpgradeUpgrade: websocketSec-WebSocket-Key:客户端随机生成的 Base64 字符串。
- 服务端响应:
- 服务端将该 Key 与固定的 GUID 字符串拼接后计算 SHA-1 哈希,返回
Sec-WebSocket-Accept。 - 状态码返回
101 Switching Protocols。 - 防误升级:该握手机制用于确认服务端确实理解 WebSocket 协议,防止老旧的透明 HTTP 缓存代理服务器误将长连接当成普通请求缓存。
- 服务端将该 Key 与固定的 GUID 字符串拼接后计算 SHA-1 哈希,返回
二、生产痛点:为什么必须有应用层心跳?
在生产环境下,如果长时间没有消息交互,连接很可能会被防火墙、云厂商负载均衡器(ALB)或家用 NAT 路由器静默关闭(Dead Connection)。
- 静默断开的危害:TCP 连接在中间设备已经被抹掉了路由表项,但客户端和服务端都不知道。当有新消息发送时,数据包被路由静默丢弃,最终触发长达数分钟的 TCP 超时重传,用户体验极其恶劣。
- 生产心跳黄金法则:
- Ping/Pong 控制帧:WebSocket 协议原生支持
0x9(Ping)和0xA(Pong)控制帧。 - 心跳间隔小于中间件空闲超时:例如云厂商 Nginx / ALB 默认空闲超时为 60 秒,生产心跳间隔通常配置为 25~30 秒。
- 双向保活与超时主动剔除:若连续 3 次未收到心跳回包,客户端主动重连,服务端主动断开释放文件描述符(fd)。
- Ping/Pong 控制帧:WebSocket 协议原生支持
三、生产选型:WebSocket vs SSE vs 长轮询
在大模型(LLM)打字机流式输出、股票行情推送等场景中,面试官最爱问:“你们的 AI 对话为什么选 SSE 而不是 WebSocket?”
| 技术方案 | 通信方向 | 协议基础 | 优势 | 痛点与选型定位 |
|---|---|---|---|---|
| HTTP 长轮询 | 客户端向服务端伪推送 | HTTP 短请求挂起 | 兼容性最好,无防火墙阻碍 | 频繁建连开销大,延迟高,已被淘汰 |
| SSE (Server-Sent Events) | 仅服务端向客户端单向推送 | 标准 HTTP/1.1 或 HTTP/2 文本流 (text/event-stream) | 极度轻量,天然支持断线重连与事件 ID,完美穿越企业防火墙,原生支持 HTTP/2 多路复用 | 只支持单向推送。大模型流式输出(ChatGPT/DeepSeek)的最优解! |
| WebSocket | 真正的全双工双向通信 | 独立于 HTTP 的独立长连接协议 | 双向极低延迟,支持自定义二进制与文本帧 | 状态有状态、跨域与 LB 配置繁琐,连接维护成本高。多人在线游戏、IM 即时通讯的最优解! |
面试高频追问
- WebSocket 握手成功后,底层还在用 TCP 吗? 还在用。WebSocket 是应用层协议,复用握手时建立的那条底层 TCP 连接,只是后续的数据收发不再遵守 HTTP 报文格式,而是使用轻量级的二进制数据帧(Frame)。
- 大模型流式输出(Chat)为什么优先选 SSE 而不是 WebSocket?
- 场景本质是“用户发送一个 Query(客户端到服务端),大模型持续吐出几百个 Token(服务端到客户端单向流式推送)”,并不需要双向全双工。
- SSE 基于标准 HTTP,零学习成本,自带断线重连,完美兼容已有的 API 网关鉴权、限流、日志监控和 HTTP/2 多路复用,架构复杂度远低于 WebSocket。
- 线上大量 WebSocket 连接如何做集群扩展与负载均衡?
- 单台服务器支持万级长连接,集群部署时需借助 Redis Pub/Sub 或消息队列(Kafka/RocketMQ)作为分布式会话广播总线。
- 用户 A 连在 Server-1,用户 B 连在 Server-2;Server-1 将 A 发送的消息发布到 Redis 频道,Server-2 订阅后下发给用户 B。