| 👁️ 阅读预计:15分钟 | 🏷️ 技术深度:进阶
🎯 核心结论前置:WebSocket不是HTTP的替代品,而是HTTP的"进化形态"。它让浏览器和服务器之间建立了一条"专线电话",从此告别"每隔5分钟去前台问一次快递到了没"的尴尬。
想象一下这个场景:你在等一个重要的快递,但快递员没有你的电话。于是你每5分钟下楼问一次前台:"我的快递到了吗?"前台每次都摇头。你问了100次,终于在第101次,前台说:“到了!”——这就是传统轮询。
后来你学聪明了,站在前台旁边不走,每隔几秒就问一次。虽然响应快了,但你累,前台也烦——这就是长轮询。
直到有一天,前台说:“留个电话吧,到了我直接打给你。”——这就是WebSocket。
一、通信方式的"三代同堂"
在WebSocket出现之前,前端想要获取服务器最新数据,只有两条路可走。这两条路的共同点是:都是客户端主动问,服务器被动答。
1.1 传统轮询(Polling):最原始的"敲门问话"
💡 生活类比: 你每隔固定时间(比如5秒)发送一次HTTP请求,问服务器:"有新消息吗?"服务器不管有没有,都立即回答:“没有"或者"有,这是数据”。
客户端 服务器
| |
|---- HTTP Request -------->| "有新消息吗?"
| |
|<--- HTTP Response --------| "没有"
| |
| [等待5秒] |
| |
|---- HTTP Request -------->| "有新消息吗?"
| |
|<--- HTTP Response --------| "没有"
| |
| [重复N次...] |
| |
|---- HTTP Request -------->| "有新消息吗?"
| |
|<--- HTTP Response --------| "有!这是数据"
致命缺陷:
- 大量请求都是"空跑",浪费带宽和服务器资源
- 延迟不可控,最坏情况下消息要等一个轮询周期才能收到
- HTTP头部开销巨大(每次都要带Cookie、User-Agent等)
1.2 长轮询(Long Polling):"站岗等信"模式
长轮询是对传统轮询的优化。客户端发起请求后,服务器不会立即响应,而是挂起连接,等到有数据时才返回。
💡 生活类比: 你问前台:"快递到了叫我。"然后你站在前台旁边等。前台也不会让你干等,而是真的去盯着快递,一到就喊你。但问题是,如果快递一直不来,连接会超时,你得重新问一次。
客户端 服务器
| |
|---- HTTP Request -------->| "有新消息就告诉我"
| |
| [服务器挂起连接] |
| [保持等待...] |
| |
|<--- HTTP Response --------| "有消息了!" (30秒后)
| |
|---- HTTP Request -------->| "继续等下一个"
| |
| [挂起...] |
|<--- HTTP Response --------| (超时,重新连接)
改进与局限:
- ✅ 减少了无效请求的次数
- ❌ 服务器需要维护大量挂起的连接,消耗资源
- ❌ 连接超时后需要重新建立,仍有延迟
- ❌ 每次响应后都要重新发起请求,不够优雅
1.3 WebSocket:建立"专线电话"
WebSocket的核心思想很简单:既然HTTP协议不支持服务器主动推送,那我们就升级它。
💡 生活类比: WebSocket就像你和前台之间建立了一条专线电话。一旦接通,双方可以随时说话,不需要再敲门。快递到了前台主动打给你,你有问题也可以随时问前台。
客户端 服务器
| |
|---- HTTP Upgrade ------->| "咱们升级成WebSocket吧"
| (带特殊Header) |
|<--- 101 Switching -------| "好的,升级成功"
| |
|==== WebSocket连接建立 ====|
| |
|<--- "快递到了!" ---------| 服务器主动推送
| |
|---- "好的,我马上下楼" --->| 客户端随时发送
| |
|<--- "还有一个包裹" -------| 双向实时通信
| |
|==== 保持连接,随时通信 ====|
WebSocket的杀手锏:
- ✅ 全双工通信:双方可以同时发送和接收
- ✅ 低延迟:建立连接后,数据可以立即传输
- ✅ 低开销:头部极小(2-14字节),不像HTTP那样臃肿
- ✅ 服务器主动推送:真正的"实时"体验
1.4 三种方式对比一览
| 特性 | 传统轮询 | 长轮询 | WebSocket |
|---|---|---|---|
| 通信模式 | 客户端主动拉取 | 客户端主动拉取 | 全双工(双向主动) |
| 延迟 | 高(轮询间隔决定) | 中(接近实时) | 极低(毫秒级) |
| 服务器压力 | 极高(大量无效请求) | 高(维护挂起连接) | 低(长连接复用) |
| 带宽效率 | 极低(HTTP头重复) | 低 | 极高(帧头仅2-14字节) |
| 实现复杂度 | 简单 | 中等 | 中等(需处理状态) |
| 兼容性 | 所有浏览器 | 所有浏览器 | IE10+,现代浏览器全支持 |
二、WebSocket握手:HTTP的"华丽转身"
WebSocket最巧妙的地方在于:它借用了HTTP协议来完成自己的握手。这意味着它不需要新的端口(默认80/443),也能穿透大多数防火墙。
2.1 握手过程详解
WebSocket连接建立分为两个阶段:
- HTTP Upgrade阶段:用HTTP协议"协商升级"
- WebSocket数据传输阶段:升级成功后,切换到WebSocket协议
┌─────────────────────────────────────────────────────────────┐
│ WebSocket握手流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 客户端 服务器 │
│ │ │ │
│ │ ① TCP三次握手 │ │
│ │═══════════════════════════════►│ │
│ │ │ │
│ │ ② HTTP Upgrade请求 │ │
│ │ GET /chat HTTP/1.1 │ │
│ │ Host: server.example.com │ │
│ │ Upgrade: websocket │ │
│ │ Connection: Upgrade │ │
│ │ Sec-WebSocket-Key: dGhlIHNhbXBsZQ== │ │
│ │ Sec-WebSocket-Version: 13 │ │
│ │───────────────────────────────►│ │
│ │ │ │
│ │ ③ 服务器验证并响应 │ │
│ │ │ 计算Accept密钥 │
│ │ HTTP/1.1 101 Switching │ GUID + Key + SHA1 │
│ │ Upgrade: websocket │ + Base64 │
│ │ Connection: Upgrade │ │
│ │ Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= │
│ │◄───────────────────────────────│ │
│ │ │ │
│ │ ④ WebSocket连接建立! │ │
│ │◄══════════════════════════════►│ │
│ │ 双向数据传输通道就绪 │ │
│ │
└─────────────────────────────────────────────────────────────┘
2.2 关键Header解析
握手请求中有几个Header至关重要:
| Header | 说明 | 示例值 |
|---|---|---|
Upgrade: websocket | 表明客户端希望升级到WebSocket协议 | websocket |
Connection: Upgrade | 告诉服务器,这个连接要升级,不要关闭 | Upgrade |
Sec-WebSocket-Key | 客户端生成的16字节随机数,Base64编码 | dGhlIHNhbXBsZQ== |
Sec-WebSocket-Version | WebSocket协议版本,目前主流是13 | 13 |
Sec-WebSocket-Accept | 服务器响应,证明它理解WebSocket协议 | s3pPLMBiTxaQ9kYGzzhZRbK+xOo= |
2.3 Sec-WebSocket-Accept的计算
这个字段是WebSocket协议的安全校验机制。服务器收到Key后,按以下步骤计算Accept值:
// 1. 取客户端传来的Key
key = "dGhlIHNhbXBsZQ=="
// 2. 拼接固定的GUID(RFC 6455规定)
magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
// 3. 拼接后做SHA1哈希
hash = SHA1(key + magic)
// 结果: 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6
// 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea
// 4. Base64编码得到Accept值
accept = Base64(hash)
// 结果: "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
💡 为什么要有这个校验? 这是为了防止缓存代理服务器误将WebSocket握手响应当成普通HTTP响应缓存。因为这个计算需要知道固定的GUID,只有真正支持WebSocket的服务器才能算出正确的Accept值。
三、WebSocket帧结构:数据包的"解剖学"
HTTP协议传输的是明文文本,而WebSocket传输的是二进制帧。这种设计让WebSocket既能传输文本,也能传输二进制数据(图片、音频、视频),而且帧头极小,效率极高。
3.1 帧结构总览
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - -+
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
┌─────────────────────────────────────────────────────────────┐
│ 最小帧头: 2字节 │ 最大帧头: 14字节 │ 对比HTTP: 几百字节 │
└─────────────────────────────────────────────────────────────┘
3.2 字段详解
| 字段 | 位数 | 说明 |
|---|---|---|
FIN | 1 bit | 是否为最后一帧。1表示这是消息的最后一个片段 |
RSV1-3 | 3 bits | 保留位,用于扩展。一般全为0 |
Opcode | 4 bits | 帧类型:0x1=文本,0x2=二进制,0x8=关闭,0x9=心跳ping,0xA=心跳pong |
MASK | 1 bit | 是否掩码。客户端→服务器必须掩码(1),服务器→客户端不掩码(0) |
Payload length | 7 bits | 负载长度:0-125=直接表示,126=后面2字节是长度,127=后面8字节是长度 |
Masking-key | 32 bits | 掩码密钥(仅当MASK=1时存在),用于异或解码 |
Payload Data | 变长 | 实际传输的数据(文本或二进制) |
3.3 Opcode类型速查
// WebSocket Opcode定义
const OPCODES = {
0x0: "CONTINUATION", // 延续帧(分片消息的中间帧)
0x1: "TEXT", // 文本帧(UTF-8编码)
0x2: "BINARY", // 二进制帧
0x8: "CLOSE", // 关闭连接
0x9: "PING", // 心跳检测请求
0xA: "PONG" // 心跳检测响应
};
3.4 掩码机制:为什么客户端必须掩码?
WebSocket协议规定:客户端发送的帧必须掩码,服务器发送的帧不能掩码。这个设计是为了解决一个安全问题——缓存投毒攻击。
⚠️ 攻击场景: 假设没有掩码机制,恶意网页可以通过JavaScript构造一个特殊的WebSocket帧,内容看起来像HTTP请求。如果用户处于代理服务器后,这个帧可能被代理服务器误解为有效的HTTP请求,从而导致缓存污染或安全漏洞。
掩码算法很简单,就是**异或(XOR)**操作:
// 掩码解码/编码(客户端和服务器都用同样的算法)
function unmask(payload, maskingKey) {
const result = Buffer.alloc(payload.length);
for (let i = 0; i < payload.length; i++) {
// 循环使用4字节的maskingKey
result[i] = payload[i] ^ maskingKey[i % 4];
}
return result;
}
// 示例:掩码密钥是 [0x12, 0x34, 0x56, 0x78]
// 原始数据: "Hi" = [0x48, 0x69]
// 掩码后: [0x48^0x12, 0x69^0x34] = [0x5A, 0x5D]
四、心跳与断线重连:让连接"永生"
WebSocket连接可能因为各种原因断开:网络波动、服务器重启、防火墙超时、手机切换WiFi/4G等。一个健壮的WebSocket应用必须具备心跳检测和自动重连机制。
4.1 心跳机制:“你还活着吗?”
心跳就像两个人打电话时偶尔问一句"喂,还在吗?"——如果得不到回应,就知道对方可能掉线了。
客户端 服务器
| |
|◄════════════════════════►| 正常通信
| |
|---- PING frame --------->| "在吗?"(心跳请求)
| |
|<--- PONG frame ----------| "在!"(心跳响应)
| |
| [等待心跳间隔] |
| |
|---- PING frame --------->| "在吗?"
| |
| [超时未响应...] |
| |
| [判定连接已死] │
| │
| [触发重连逻辑] │
WebSocket协议原生支持心跳:
- PING帧(Opcode 0x09):一方发送,询问对方是否存活
- PONG帧(Opcode 0x0A):收到PING后必须回复PONG
💡 实际开发建议: 虽然协议层有心跳帧,但很多开发者更喜欢在应用层实现心跳(比如每隔30秒发送一个
{"type":"ping"}的文本消息)。这样更灵活,可以在心跳包中携带额外信息(如客户端状态统计)。
4.2 断线重连策略
重连不是简单地"断了就连",需要考虑多种情况:
class ReconnectingWebSocket {
constructor(url) {
this.url = url;
this.ws = null;
this.reconnectInterval = 1000; // 初始重连间隔1秒
this.maxReconnectInterval = 30000; // 最大重连间隔30秒
this.reconnectAttempts = 0; // 重连次数
this.maxReconnectAttempts = 10; // 最大重连次数
this.heartbeatInterval = 30000; // 心跳间隔30秒
this.heartbeatTimer = null;
this.connect();
}
connect() {
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('连接成功');
this.reconnectAttempts = 0; // 重置重连计数
this.startHeartbeat();
};
this.ws.onclose = () => {
console.log('连接关闭');
this.stopHeartbeat();
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('连接错误:', error);
};
} catch (e) {
this.reconnect();
}
}
reconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('重连次数超限,放弃重连');
return;
}
this.reconnectAttempts++;
const interval = Math.min(
this.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1),
this.maxReconnectInterval
);
console.log(`第${this.reconnectAttempts}次重连,${interval}ms后尝试...`);
setTimeout(() => this.connect(), interval);
}
startHeartbeat() {
this.heartbeatTimer = setInterval(() => {
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type: 'ping', time: Date.now() }));
}
}, this.heartbeatInterval);
}
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
}
}
4.3 重连时的消息补偿
重连后最大的问题:断线期间的消息怎么补?
常见的解决方案:
- 消息ID + 去重:每条消息带唯一ID,客户端记录已接收的最大ID,重连后请求"ID大于X的消息"
- 消息队列:服务器为每个用户维护一个消息队列,直到确认客户端已接收
- 增量同步:重连后发送当前状态版本号,服务器返回差异部分
五、实战案例:WebSocket的三大主战场
5.1 在线聊天室:WebSocket的"Hello World"
聊天室是WebSocket最经典的场景。核心需求:消息实时送达、在线状态显示、已读回执。
┌─────────────────────────────────────────────────────────────┐
│ 聊天室架构示意 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 用户A(浏览器) 服务器(Node.js) 用户B │
│ │ │ │ │
│ │── "大家好!"─────────────►│────────────►│ │
│ │ │ │ │
│ │◄── "A说:大家好!"────────│◄─────────────│ │
│ │ │ │ │
│ │ │── "大家好!"►│ │
│ │ │ │ │
│ │◄── "B说:欢迎!"─────────│◄──────────────│ │
│ │
│ 关键技术点: │
│ • 房间(Room)概念:用户加入不同频道 │
│ • 广播(Broadcast):消息发给房间内所有用户 │
│ • 私聊(DM):点对点消息传输 │
│ │
└─────────────────────────────────────────────────────────────┘
5.2 股票行情:毫秒级的数据推送
股票行情对延迟极其敏感,价格变动需要在毫秒级推送到所有客户端。
💡 为什么不用轮询? 假设有10万用户在看行情,每秒轮询一次,就是10万QPS。而WebSocket只需要维护10万个长连接,数据变化时才推送,服务器压力降低90%以上。
股票行情系统的特殊挑战:
- 数据量大:全量推送带宽不够,需要增量更新
- 优先级不同:自选股优先,大盘指数次之
- 客户端性能:高频更新需要节流(throttle)或防抖(debounce)
// 股票行情推送示例(节流优化)
class StockPusher {
constructor(ws) {
this.ws = ws;
this.buffer = new Map(); // 待推送数据缓冲区
this.flushInterval = 100; // 100ms批量推送一次
this.startFlush();
}
// 收到行情变化,先放入缓冲区
update(stockCode, price) {
this.buffer.set(stockCode, {
code: stockCode,
price: price,
time: Date.now()
});
}
// 定时批量推送,减少网络开销
startFlush() {
setInterval(() => {
if (this.buffer.size === 0) return;
const updates = Array.from(this.buffer.values());
this.ws.send(JSON.stringify({
type: 'batch_update',
data: updates
}));
this.buffer.clear();
}, this.flushInterval);
}
}
5.3 多人协作编辑:OT算法的舞台
Google Docs、腾讯文档、Notion这类产品的核心技术是**Operational Transformation(OT)**算法,而WebSocket是其实时同步的传输层。
用户A编辑:"Hello" 服务器(OT引擎) 用户B编辑:"World"
│ │ │
│── op: insert(0,"H") ───►│ │
│ │── 转换 ───────────►│
│ │ │
│◄── 确认 ────────────────│ │
│ │ │
│ │◄── op: insert(0,"W")│
│◄── 转换 ────────────────│ │
│ │ │
最终文档: "HWorldello" 或 "WHelloorld" (取决于操作顺序)
协作编辑的核心难点:
- 操作冲突:两个人同时修改同一处,需要OT算法解决
- 光标同步:显示其他用户的光标位置
- 离线编辑:断网期间的操作,重连后合并
六、性能对比:用数据说话
6.1 延迟对比
| 通信方式 | 理论延迟 | 实际场景延迟 | 适用场景 |
|---|---|---|---|
| 传统轮询(5秒间隔) | 0-5000ms | 平均2500ms | 对实时性要求极低的场景 |
| 长轮询 | 接近0ms(有消息时) | 50-500ms | 中等实时性需求 |
| WebSocket | 网络RTT | 10-100ms | 高实时性需求(聊天、游戏、行情) |
6.2 服务器资源占用对比
假设场景:10万并发用户,每秒产生1条消息
| 指标 | 轮询(1秒间隔) | 长轮询 | WebSocket |
|---|---|---|---|
| 每秒HTTP请求数 | 10万 | 约10万(超时重建) | 0(已建立连接) |
| 并发连接数 | 瞬时1万(持续重建) | 10万(挂起) | 10万(长连接) |
| 每秒带宽消耗 | ~500MB(HTTP头开销) | ~100MB | ~5MB(仅数据) |
| CPU占用 | 极高(频繁创建/销毁连接) | 高(维护挂起连接) | 低(事件驱动) |
6.3 并发连接数实测
不同服务器配置下的WebSocket并发能力(仅供参考):
- 单核2GB内存(Node.js):约1-2万连接
- 4核8GB内存(Node.js + Cluster):约5-8万连接
- 8核16GB内存(uWebSockets.js):约20-50万连接
- 专业方案(Erlang/Elixir + 分布式):百万级连接
💡 性能优化建议:
- 使用uWebSockets.js替代原生ws库,性能提升5-10倍
- 启用gzip压缩减少传输数据量
- 合理设置心跳间隔(30-60秒),平衡及时性与开销
- 使用Redis Pub/Sub实现多服务器消息广播
七、代码实战:Node.js WebSocket全栈实现
7.1 服务端实现(原生ws库)
// server.js - WebSocket服务器
const WebSocket = require('ws');
const http = require('http');
// 创建HTTP服务器
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('WebSocket Server Running\n');
});
// 创建WebSocket服务器
const wss = new WebSocket.Server({ server });
// 存储连接和用户信息
const clients = new Map();
wss.on('connection', (ws, req) => {
const clientId = generateId();
console.log(`客户端 ${clientId} 已连接`);
clients.set(ws, { id: clientId, lastPing: Date.now() });
// 发送欢迎消息
ws.send(JSON.stringify({
type: 'system',
message: '欢迎连接WebSocket服务器!',
clientId: clientId
}));
// 广播新用户加入
broadcast({
type: 'user_joined',
clientId: clientId,
userCount: clients.size
}, ws);
// 处理消息
ws.on('message', (data) => {
try {
const message = JSON.parse(data);
handleMessage(ws, message);
} catch (e) {
ws.send(JSON.stringify({ type: 'error', message: 'Invalid JSON' }));
}
});
// 处理关闭
ws.on('close', () => {
console.log(`客户端 ${clientId} 已断开`);
clients.delete(ws);
broadcast({
type: 'user_left',
clientId: clientId,
userCount: clients.size
});
});
// 处理错误
ws.on('error', (err) => {
console.error(`客户端 ${clientId} 错误:`, err);
});
});
// 消息处理器
function handleMessage(ws, message) {
const client = clients.get(ws);
switch (message.type) {
case 'chat':
// 广播聊天消息
broadcast({
type: 'chat',
clientId: client.id,
content: message.content,
timestamp: Date.now()
});
break;
case 'ping':
// 心跳响应
client.lastPing = Date.now();
ws.send(JSON.stringify({ type: 'pong', time: Date.now() }));
break;
default:
ws.send(JSON.stringify({ type: 'error', message: 'Unknown message type' }));
}
}
// 广播函数(排除发送者)
function broadcast(data, excludeWs = null) {
const message = JSON.stringify(data);
wss.clients.forEach((client) => {
if (client !== excludeWs && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
// 生成唯一ID
function generateId() {
return Math.random().toString(36).substr(2, 9);
}
// 启动服务器
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
console.log(`WebSocket服务器运行在端口 ${PORT}`);
});
// 定时清理死连接(每30秒)
setInterval(() => {
const now = Date.now();
const timeout = 60000; // 60秒无心跳视为死连接
clients.forEach((client, ws) => {
if (now - client.lastPing > timeout) {
console.log(`清理死连接: ${client.id}`);
ws.terminate();
clients.delete(ws);
}
});
}, 30000);
7.2 运行步骤
# 1. 初始化项目
mkdir websocket-chat && cd websocket-chat
npm init -y
# 2. 安装依赖
npm install ws
# 3. 创建 server.js(复制上面的服务端代码)
# 4. 创建 index.html(客户端代码)
# 5. 启动服务器
node server.js
# 输出: WebSocket服务器运行在端口 8080
# 6. 打开浏览器访问 http://localhost:8080
# 打开多个标签页测试多人聊天
🎉 恭喜! 你现在拥有了一个完整的WebSocket聊天室。可以在此基础上添加更多功能:私聊、表情包、图片发送、历史消息记录等。
📦 【源码获取】
完整代码已开源,包含:
- ✅ 原生ws库实现的WebSocket服务器
- ✅ 带心跳和自动重连的客户端
- ✅ 多房间聊天室扩展版本
- ✅ Docker部署配置
GitHub地址: https://github.com/example/websocket-chat-demo(示例链接)
🤔 【思考题】
- WebSocket的掩码机制为什么只要求客户端掩码,而服务器不掩码?如果服务器也掩码会有什么后果?
- 在高并发场景下,如何设计WebSocket服务器的水平扩展方案?(提示:Redis Pub/Sub、消息队列)
- WebSocket连接在移动端容易断开,除了心跳重连,还有哪些优化手段?(提示:App保活、智能重连策略)
- Socket.io相比原生WebSocket有哪些优势和劣势?什么情况下应该选择Socket.io?
📚 【系列文章预告】
网络协议系列持续更新中:
- 第11篇: 《Socket.io深度解析——WebSocket的"瑞士军刀"》
- 第12篇: 《WebRTC实战——浏览器里的视频会议系统》
- 第13篇: 《gRPC vs REST——微服务通信协议选型指南》
- 第14篇: 《QUIC协议——HTTP/3背后的革命性传输协议》
如果这篇文章对你有帮助,欢迎:
👍 点赞 | ⭐ 收藏 | 💬 评论 | 📤 分享
标签: WebSocket | 实时通信 | 全双工 | Node.js | 即时通讯
881

被折叠的 条评论
为什么被折叠?



