第 16.1~16.4 节介绍的网络请求,无论是 XHR、Fetch 还是 Axios,本质上都遵循 HTTP 协议的“请求-响应”模型:客户端主动发起,服务器被动应答。这种模式在多数业务中工作良好,但遇到需要服务器主动向客户端推送数据的场景时,就显得捉襟见肘。轮询(Polling)可以模拟实时效果,但浪费资源且延迟高。WebSocket 正是为解决这一问题而生,它在客户端和服务器之间建立一条持久的、全双工的通信通道,让双方随时可以自由发送数据。
16.5.1 WebSocket 与 HTTP 的关系
WebSocket 并不是 HTTP 的替代品,而是对 HTTP 的一种升级。连接建立过程是这样:
- 客户端发起一个 HTTP 请求,请求头中包含
Upgrade: websocket和Connection: Upgrade等特殊字段,表明希望升级协议。 - 如果服务器支持 WebSocket,会返回 101 状态码(Switching Protocols),同意升级。
- 之后,这条 TCP 连接上的协议就切换为 WebSocket,不再使用 HTTP 文本格式,转而采用高效的二进制帧进行双向通信。
一次握手,持久连接,此后双方地位对等。整个握手过程依然依赖 HTTP,确保了与现有 Web 基础设施的兼容性(例如可以复用 HTTP 的 80/443 端口,通过代理和防火墙)。
16.5.2 全双工双向通信
HTTP 的通信是单向的(客户端发起,服务器响应),而 WebSocket 是全双工的:任何一端都可以直接向另一端发送数据,无需等待对方先发请求。这意味着:
- 服务器可以主动推送:股价变化、消息通知、协同编辑中的他人操作,可以瞬间触达前端。
- 客户端可以随时发送:用户输入可以实时传输,而不需要每个操作都单独发起 HTTP 请求。
这种双向实时能力,用传统的 HTTP 轮询(每隔几秒去查询一次服务器)来实现的话,要么延迟高,要么开销大(大量空轮询消耗带宽和服务器资源)。WebSocket 建立连接后,双方只在有数据时才发送帧,且头部开销极小(最少仅 2 字节),省流又极速。
16.5.3 浏览器端 API 基础用法
浏览器原生提供了 WebSocket 构造函数,使用起来非常直观:
const ws = new WebSocket('wss://example.com/socket');
// 连接建立时触发
ws.onopen = () => {
console.log('连接已建立');
ws.send('Hello, server!'); // 发送消息(字符串、ArrayBuffer、Blob 等)
};
// 收到服务器消息时触发
ws.onmessage = (event) => {
console.log('收到消息:', event.data);
// data 可以是文本,也可能是二进制数据
};
// 连接出错时触发
ws.onerror = (error) => {
console.error('连接错误:', error);
};
// 连接关闭时触发
ws.onclose = (event) => {
console.log('连接关闭', event.code, event.reason);
};
// 主动关闭
// ws.close(1000, '用户主动断开');
API 只有几个事件回调,开发者无需关心帧的拆包与重组,使用成本极低。
16.5.4 心跳保活与断线重连
WebSocket 连接并非“永生不死”。网络波动、代理超时、服务器重启等都会导致连接断开。实际项目中,必须加入健壮性机制:
- 心跳机制:客户端或服务器定时发送一个简单消息(通常叫
ping/pong),检测连接是否存活。如果一段时间内没有收到对方回复,就认为连接已断开,主动重连。一些 WebSocket 库和代理支持框架自带ping/pong帧,用来维持连接并识别假死。 - 断线重连:在
onclose回调中,根据关闭码判断是否为异常断开,然后等待一定时间(指数退避算法可避免频繁重连冲击服务器)后重新发起new WebSocket。重连后可能需要重新订阅、同步状态等。
16.5.5 安全考虑
WebSocket 同样受同源策略约束(握手时的 Origin 头会被服务器检查),默认无法跨域建立连接(除非服务器在握手响应中明确允许)。生产环境必须使用 wss://(WebSocket over TLS),就像 HTTPS 一样加密传输,防止中间人攻击。
此外,WebSocket 连接可以携带 Cookie(默认行为,也可通过 Access-Control-Allow-Credentials 控制),因此也需要注意 CSRF 等问题。在设计 WebSocket 协议层时,应当对消息进行认证与鉴权,例如首次连接后要求客户端发送 token 进行身份校验。
16.5.6 实际适用场景
WebSocket 最适合高实时性、低延迟、双向数据交换频繁的场景:
- 即时通讯:网页版微信、钉钉、在线客服,一对一或群聊消息的实时收发。
- 实时数据推送:股票行情、体育比分、航班动态、服务器监控指标,数据需要秒级更新。
- 协同编辑:在线文档多人同时编辑,必须将每个人的操作实时同步给所有参与者。
- 网络游戏:对实时性要求较高的多人在线游戏,需要不断同步玩家位置、状态。
- 物联网:设备与服务器间需要常连接来上报数据、接收指令(如智能家居控制)。
- 异步任务状态通知:大文件导入导出、CI/CD 构建进度等,后端处理完成后主动推送结果。
16.5.7 什么场景不适合 WebSocket
虽然 WebSocket 强大,但并非所有实时场景的默认选择。如果数据更新频率不高(如每小时推送一次),使用简单的 HTTP 轮询或者 Server-Sent Events(SSE)可能更简单。SSE 是单向的服务器推送,浏览器原生支持,自动重连,实现更轻量。如果并非强实时需求,HTTP 长轮询(Comet)也能解决问题。技术选型时,根据双向需求、兼容性、运维复杂度综合决策。
16.5.8 小结
WebSocket 为 Web 应用提供了真正的实时双向通信能力,把“客户端等服务器响应”的模式改写为“双方随时对话”。掌握 WebSocket,也就掌握了一类关键交互模式的技术基础。实际项目中,将其与心跳、重连、认证等机制结合,就能构建出稳定可靠的实时应用。后续第 30 章还会涉及 Service Worker 等高级 API,进一步扩展 Web 应用的离线与后台能力;而在与 Node.js 配合时,WebSocket 服务端实现也同样简洁高效,事件循环的特性使其天生擅长维护大量长连接。