Web 实时通信:从短轮询到 WebSocket

在互联网早期,网页像一本翻页杂志:用户点击,浏览器请求,服务器响应,页面刷新。当产品经理提出"实时聊天""股价闪动""库存秒变"这些需求时,HTTP 的"请求---响应"模型立刻显得力不从心。于是,开发者经历了一场从"笨拙轮询"到"优雅全双工"的技术进化。今天,就让我们再走一遍这条路。

一、短轮询

思路简单到极致:

客户端每隔 X 秒发一次 Ajax,问"有新消息吗?"

服务器答"没有"------循环往复。

sequenceDiagram 客户端->>服务器: 有新消息吗? 服务器->>客户端: 没 Note over 客户端,服务器: 一段时间后... 客户端->>服务器: 有新消息吗? 服务器->>客户端: 没 Note over 客户端,服务器: 一段时间后... 客户端->>服务器: 有新消息吗? 服务器->>客户端: 有,user1对你说:你好 Note over 客户端,服务器: 一段时间后... 客户端->>服务器: 有新消息吗? 服务器->>客户端: 没 Note over 客户端,服务器: 一段时间后... 客户端->>服务器: 有新消息吗? 服务器->>客户端: 没

伪代码

js 复制代码
setInterval(() => fetch('/msg'), 1000);

痛点

  • 空包占比高:99% 的请求是无用往返。
  • 连接反复建立:TCP 三次握手 + 四次挥手成了"日常运动"。
  • 延迟不可控:最快也要等到下一个轮询周期。

短轮询就像给服务器打"骚扰电话",简单,却低效。

二、长轮询

聪明的工程师想到:

把电话"挂起不挂线"。客户端发起 Ajax,服务器夯住这个连接,直到真的有消息才返回;客户端收到后立即再挂一个,如此循环。

sequenceDiagram 客户端->>+服务器: 有新消息吗? Note right of 服务器: 没有消息不会响应 Note right of 服务器: 一段时间后... 服务器->>-客户端: user1对你说:你好 客户端->>+服务器: 有新消息吗? Note right of 服务器: 没有消息不会响应 Note right of 服务器: 一段时间后... 服务器->>-客户端: user1对你说:你吃了没 客户端->>服务器: ......

优势

  • 零空包:每次响应都携带数据。
  • 减少连接数:TCP 复用率提高。

新问题

  • 超时风险:浏览器/代理可能粗暴切断长连接。
  • 资源悬挂:服务器需要为大量空闲连接维持线程或内存。
  • 实时性仍受"挂起窗口"限制。

长轮询把"骚扰电话"变成了"占线等待",但线路资源依旧吃紧。

三、WebSocket

HTML5 带来了 WebSocket------基于 TCP 的全双工通道

握手阶段仍用 HTTP,升级协议后,双方可随时推送数据帧。

1. 握手:HTTP 的极限操作

客户端:

makefile 复制代码
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13

服务器返回 101:

makefile 复制代码
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

状态码 101 表示:协议升级完成。此后数据帧不再走 HTTP。

2. 通信:帧级协议,双向飞驰

  • **帧(frame)**是最小单位,携带 payload、opcode、掩码。
  • **流(stream)**由帧组成,任何一方都可主动发送。
  • 无队头阻塞:不同消息并行交错,靠帧头 id 区分归属。

3. 代码示例(Node + 前端)

前端

js 复制代码
const ws = new WebSocket('wss://example.com/chat');

ws.onopen   = () => ws.send('Hello Server!');
ws.onmessage = e => console.log('New msg:', e.data);

Node 后端(ws 库)

js 复制代码
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', ws => {
  ws.on('message', msg => wss.clients.forEach(c => c.send(msg)));
});

四、使用场景与权衡

场景 建议方案
高频实时(聊天、游戏) WebSocket
低频通知(后台弹窗) SSE 或长轮询
IE8 及以下 只能降级轮询

资源成本:长连接需心跳保活、内存占用。低并发或弱网环境需评估收益。

相关推荐
EndingCoder1 小时前
调试技巧:Chrome DevTools 与 Node.js Inspector
javascript·网络·electron·node.js·vim·chrome devtools
知识分享小能手1 小时前
React学习教程,从入门到精通, React 入门指南:React JSX 语法知识点详解及案例代码(8)
前端·javascript·vue.js·学习·react.js·前端框架·anti-design-vue
卓码软件测评1 小时前
第三方web测评机构:【WEB安全测试中HTTP方法(GET/POST/PUT)的安全风险检测】
前端·网络协议·安全·web安全·http·xss
学习3人组1 小时前
React 组件基础与事件处理
前端·javascript·react.js
qczg_wxg5 小时前
React Native的动画系统
javascript·react native·react.js
漂流瓶jz6 小时前
解锁Babel核心功能:从转义语法到插件开发
前端·javascript·typescript
周小码7 小时前
shadcn-table:构建高性能服务端表格的终极解决方案 | 2025最新实践
前端·react.js
大怪v7 小时前
老乡,别走!Javascript隐藏功能你知道吗?
前端·javascript·代码规范
ERP老兵-冷溪虎山7 小时前
Python/JS/Go/Java同步学习(第三篇)四语言“切片“对照表: 财务“小南“纸切片术切凭证到崩溃(附源码/截图/参数表/避坑指南/老板沉默术)
java·javascript·python·golang·中医编程·四语言同步学习·职场生存指南
webYin7 小时前
vue2 打包生成的js文件过大优化
前端·vue.js·webpack