本文从协议原理、握手、帧结构、连接生命周期、安全、工程实践到嵌入式落地,系统介绍 WebSocket(RFC 6455)。


目录

  1. 概述
  2. 历史与由来
  3. 与 HTTP / 轮询 / SSE / gRPC 的对比
  4. 协议栈与 URL
  5. 握手过程(Opening Handshake)
  6. 数据帧格式详解
  7. 掩码(Masking)机制
  8. 控制帧:Ping / Pong / Close
  9. 分片与消息边界
  10. 连接生命周期与状态机
  11. 扩展与子协议
  12. 客户端实现示例
  13. 服务端实现与常见框架
  14. 反向代理与负载均衡
  15. 安全实践
  16. 性能与工程实践
  17. 调试与抓包
  18. 嵌入式 / 物联网场景实践
  19. 常见问题 FAQ
  20. 参考资料

1. 概述

WebSocket 是一种在单个 TCP 连接上实现全双工(full-duplex)通信的应用层协议,由 HTML5 规范引入,后由 IETF 标准化为 RFC 6455(2011 年)。

核心特征:

  • 全双工:连接建立后,客户端与服务器可随时、独立地互相发送数据。
  • 长连接:一次握手后连接保持,不随每次消息重建。
  • 低开销:帧头最小仅 2 字节,远小于 HTTP 每请求的完整头部。
  • 基于 HTTP 握手:借助 Upgrade 机制完成协议升级,天然穿透大多数防火墙与代理。
  • 双向对等:客户端和服务器在数据层面地位对等,均可主动推送。
  • 消息导向:协议本身保留消息边界(对比 TCP 是字节流),无需应用层处理"粘包/半包"。

URL 形式:

ws://host:port/path      # 明文(默认端口 80)
wss://host:port/path     # 基于 TLS(默认端口 443)

2. 历史与由来

HTTP 是"客户端发起、服务器响应"的单向模型,浏览器无法被服务器主动推送。在 WebSocket 出现之前,业界用各种方式模拟实时通信:

  • 短轮询(Polling):客户端定时发请求,大量无效请求、延迟高。
  • 长轮询(Comet / Long Polling):服务器 hold 住请求直到有数据,连接频繁重建、并发开销大。
  • XHR Streaming / multipart streaming:利用不结束的 HTTP 响应流式下发,兼容性差、代理易截断。
  • Flash Socket / Silverlight:依赖插件,已淘汰。

2008 年,Michael Carter 等人开始推动 TCP 级别的浏览器双向通信;2009 年协议草案提出;2011 年 RFC 6455 正式发布,浏览器统一支持 new WebSocket()。此后成为实时 Web 的事实标准,并被广泛用于 IM、推送、协同、游戏、IoT 等领域。


3. 与 HTTP / 轮询 / SSE / gRPC 的对比

方案通信方向连接头部开销延迟复杂度典型场景
HTTP 短连接请求—响应短高高低普通资源请求
HTTP Keep-Alive请求—响应复用长连接高中低REST API
短轮询客户端拉短高高低简单状态刷新
长轮询服务器推(伪)反复重建中中中兼容性要求高的推送
SSE服务器→客户端单向长低低低日志流、行情、通知
WebSocket双向长极低极低中实时双向交互
gRPC (HTTP/2)双向(streaming)长中(HPACK)低高微服务、强类型 RPC
MQTT双向(经 broker)长极低低中IoT 海量设备

选型建议:

  • 只需服务器→客户端单向推送:SSE 往往更简单。
  • 需要真正的双向、低延迟、细粒度消息:WebSocket。
  • 内部服务强类型 RPC、多路复用:gRPC。
  • 海量设备、弱网、QoS 分级:MQTT。

4. 协议栈与 URL

┌─────────────────────────────┐
│         应用数据             │
├─────────────────────────────┤
│  WebSocket(RFC 6455)      │  ← 帧、握手、控制帧
├─────────────────────────────┤
│  TLS(可选,wss://)         │
├─────────────────────────────┤
│  TCP                         │  ← 字节流、可靠、有序
├─────────────────────────────┤
│  IP                          │
└─────────────────────────────┘
  • WebSocket 位于 TCP 之上,不依赖 HTTP 传输数据,仅借用 HTTP 完成握手。
  • 握手成功后,连接是纯 TCP 字节流,双方按帧格式解析。
  • HTTP/2 场景可通过 RFC 8441(Extended CONNECT)承载 WebSocket。

5. 握手过程(Opening Handshake)

WebSocket 复用 HTTP 协议完成"协议升级",因此能穿透大部分代理和防火墙。

5.1 客户端请求

GET /chat?room=1 HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: chat, mqtt
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Origin: https://example.com
Cookie: session=abc

关键字段:

头含义
Upgrade: websocket请求升级为 WebSocket
Connection: Upgrade通知中间件保持连接、勿缓存
Sec-WebSocket-Key客户端随机 16 字节的 Base64 编码
Sec-WebSocket-Version: 13协议版本,当前唯一有效值
Sec-WebSocket-Protocol子协议列表(可选,应用层自定义)
Sec-WebSocket-Extensions扩展协商,如压缩(可选)
Origin来源,用于服务端 CSWSH 防护

Sec-WebSocket-Key 生成示例:

import os, base64, hashlib
key = base64.b64encode(os.urandom(16)).decode()
accept = base64.b64encode(hashlib.sha1(
    (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").encode()
).digest()).decode()

5.2 服务器响应

成功升级:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

Sec-WebSocket-Accept 计算:

Accept = Base64( SHA1( Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ) )

这个 GUID 是 RFC 6455 固定的"魔术字符串",用于防止普通的 HTTP 客户端被误升级。

失败时服务器返回普通 HTTP 错误,例如:

HTTP/1.1 401 Unauthorized
HTTP/1.1 403 Forbidden
HTTP/1.1 426 Upgrade Required

5.3 握手要点

  • 握手是一次性的,之后不再有 HTTP 语义。
  • Sec-WebSocket-Protocol 用于协商应用层子协议(如 chat、mqtt),服务端从中选一个返回。
  • 无法在浏览器 WebSocket API 中自定义任意请求头(如 Authorization),常用替代:URL 查询参数、子协议、Cookie、或在连接后发送首帧鉴权。

6. 数据帧格式详解

帧是 WebSocket 的最小传输单位。

 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 ...                |
+---------------------------------------------------------------+

字段说明:

字段位宽含义
FIN1是否为消息最后一帧;分片时首/中帧为 0,末帧为 1
RSV1~33保留位;未协商扩展时必须为 0
opcode4帧类型
MASK1负载是否加掩码;客户端→服务器必须为 1
Payload len7负载长度;0~125 直接表示,126/127 表示后跟扩展长度
Extended payload length16 / 6416 位最大 65535;64 位最高位必须为 0
Masking-key324 字节掩码,仅当 MASK=1
Payload Data变长实际数据,若加掩码则已异或

opcode 取值:

值类型说明
0x0Continuation分片续帧
0x1TextUTF-8 文本(必须是合法 UTF-8)
0x2Binary二进制数据
0x3~0x7保留(非控制)未定义
0x8Close关闭连接
0x9Ping心跳探测
0xAPong心跳应答
0xB~0xF保留(控制)未定义

长度编码规则:

  • 0 ~ 125:负载长度直接写在 7 位字段。
  • 126:后跟 2 字节无符号整数(网络字节序)表示长度。
  • 127:后跟 8 字节无符号整数,最高位必须为 0。

控制帧(0x8~0xF)限制:

  • 负载长度不得超过 125 字节。
  • 不得分片(FIN 必须为 1)。

7. 掩码(Masking)机制

RFC 6455 规定:

  • 客户端 → 服务器:所有帧必须加掩码(MASK=1)。
  • 服务器 → 客户端:必须不加掩码(MASK=0)。
  • 若违反,接收方必须关闭连接(1002)。

掩码算法:对负载第 i 字节,与 masking-key[i % 4] 异或:

for (i = 0; i < payload_len; i++) {
    payload[i] ^= masking_key[i % 4];
}

示例(4 字节负载 0x12 0x34 0x56 0x78,掩码 0xDE 0xAD 0xBE 0xEF):

0x12 ^ 0xDE = 0xCC
0x34 ^ 0xAD = 0x99
0x56 ^ 0xBE = 0xE8
0x78 ^ 0xEF = 0x97
→ 发送 0xCC 0x99 0xE8 0x97

掩码的目的不是加密,而是防止中间代理缓存污染攻击(早期代理可能把 WebSocket 帧误当 HTTP 缓存),同时让攻击者无法预测字节流以此构造可被缓存解析的内容。掩码是对称的,解掩码用同样操作。


8. 控制帧:Ping / Pong / Close

8.1 Ping / Pong

  • 任意一方可发送 Ping,负载可选(≤125 字节)。
  • 收到 Ping 的一方必须尽快回一个 Pong,负载与 Ping 相同。
  • 用途:
    • 保活:定时 Ping,检测对端存活。
    • 探测半开连接:客户端断电、NAT 超时后,TCP 可能长时间不报错,Ping 可及时发现问题。
    • 延迟测量:Ping 发出到 Pong 返回的时间约等于 RTT。
  • 协议层 Ping/Pong 对应用层通常透明(库自动处理),但长连接仍建议应用层心跳以覆盖应用级假死。

8.2 Close

关闭是一次"双向握手":

  1. 主动方发送 Close 帧(含可选关闭码 + UTF-8 原因)。
  2. 被动方收到后回一个 Close 帧。
  3. 双方各自关闭底层 TCP。

常用关闭码:

码含义
1000正常关闭
1001端点离开(如页面关闭/服务重启)
1002协议错误
1003收到不可接受的数据类型
1007文本非合法 UTF-8
1008违反策略
1009消息过大
1010客户端期望扩展未协商
1011服务器内部错误
3000~4999库/应用自定义(如 4001 鉴权失败)

状态码约定:

  • 1000~2999:RFC 保留。
  • 3000~3999:IANA 注册,需公开。
  • 4000~4999:私有应用使用。

关闭细节:

  • 已发送 Close 后,不应再发送数据帧,但仍需处理接收到的帧直到对端关闭。
  • 关闭时若仍有未发送完的数据,应先发完再 Close("优雅关闭")。

9. 分片与消息边界

一条"消息"可由一个或多个帧组成:

Text  FIN=0 opcode=0x1  "Hel"      ← 首帧
Cont  FIN=0 opcode=0x0  "lo, "     ← 续帧
Cont  FIN=1 opcode=0x0  "world"    ← 末帧

规则:

  • 首帧携带真实 opcode(0x1/0x2),FIN=0。
  • 后续帧 opcode=0x0(Continuation)。
  • 最后一帧 FIN=1。
  • 控制帧可以插在两个分片之间,接收方需妥善处理。
  • 分片是可选优化:对于大消息可先发送前几字节再流式发送剩余(如流式 JSON、大文件)。

工程建议:

  • 接收方必须支持重组分片;对超长消息设置上限(防 DoS)。
  • 发送方除流式场景外通常不需要分片。

10. 连接生命周期与状态机

    ┌──────────┐   TCP 连接 + HTTP Upgrade   ┌──────────┐
    │ CONNECTING├──────────────────────────►│  OPEN    │
    └────┬─────┘                            └────┬─────┘
         │ 失败                                   │ 任一方发 Close / 出错 / 超时
         ▼                                        ▼
    ┌──────────┐                            ┌──────────┐
    │ CLOSED   │◄───────────────────────────│ CLOSING  │
    └──────────┘                            └──────────┘

浏览器 WebSocket.readyState 对应:

常量值状态
CONNECTING0连接中(握手未完成)
OPEN1已连接,可收发
CLOSING2关闭中(已发/收到 Close)
CLOSED3已关闭

连接建立后,需要关注:

  • 保活:定时 Ping / 应用心跳,避免中间设备(NAT、LB)回收空闲连接。
  • 超时:读写超时、握手超时(通常 5~10s)。
  • 重连:断线后指数退避重连,并做会话恢复。

11. 扩展与子协议

11.1 子协议(Subprotocol)

应用层协议协商,服务于业务语义(如 chat、mqtt、graphql-ws)。

  • 客户端在 Sec-WebSocket-Protocol 列出支持项。
  • 服务端从中选择一个放入响应;未选中则不带该头。
  • 协商结果对双方可见,便于同一端点承载不同协议。

11.2 扩展(Extensions)

协议级能力协商,如:permessage-deflate、x-webkit-deflate-frame。

permessage-deflate(RFC 7692)对每条消息用 DEFLATE 压缩:

  • 文本/JSON 压缩率通常 60%~90%,显著省带宽。
  • 代价:CPU 占用、上下文内存、轻微延迟;小消息可能"越压越大"。

协商示例:

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits=15

服务端可接受、拒绝或调整参数。


12. 客户端实现示例

12.1 浏览器 / Node.js

// 二进制用 ArrayBuffer;文本为字符串
const ws = new WebSocket('wss://example.com/chat', ['chat']);
ws.binaryType = 'arraybuffer';

ws.onopen = () => {
  console.log('connected');
  ws.send(JSON.stringify({ type: 'hello', ts: Date.now() }));
  ws.send(new Uint8Array([0x01, 0x02, 0x03])); // 二进制
};

ws.onmessage = (ev) => {
  if (typeof ev.data === 'string') {
    console.log('text:', ev.data);
  } else {
    console.log('binary:', new Uint8Array(ev.data));
  }
};

ws.onerror = (e) => console.error('ws error', e);

ws.onclose = (e) => {
  console.log(`closed code=${e.code} reason=${e.reason} clean=${e.wasClean}`);
  // 触发重连(见第 16 节)
};

注意:浏览器 API 无法自定义握手请求头;需要 Authorization 时可用 Cookie、URL 参数或子协议携带。

12.2 Python(asyncio + websockets)

import asyncio
import websockets

async def main():
    async with websockets.connect(
        "wss://example.com/chat",
        subprotocols=["chat"],
        ping_interval=20,      # 自动 Ping 保活
        ping_timeout=20,
    ) as ws:
        await ws.send("hello")
        async for msg in ws:
            print("recv:", msg)

asyncio.run(main())

12.3 C(简化的帧发送/接收骨架)

#include <stdint.h>
#include <string.h>

/* 发送一个未分片的 Binary/Text 帧;客户端必须加掩码 */
int ws_send_frame(int fd, uint8_t opcode, const uint8_t *data, size_t len)
{
    uint8_t header[14];
    size_t hlen = 0;
    uint8_t mask[4] = { 0x11, 0x22, 0x33, 0x44 };

    header[0] = 0x80 | (opcode & 0x0F); /* FIN=1 */
    if (len <= 125) {
        header[1] = 0x80 | (uint8_t)len;
        hlen = 2;
    } else if (len <= 65535) {
        header[1] = 0x80 | 126;
        header[2] = (len >> 8) & 0xFF;
        header[3] = len & 0xFF;
        hlen = 4;
    } else {
        header[1] = 0x80 | 127;
        for (int i = 0; i < 8; i++) {
            header[2 + i] = (len >> (56 - 8 * i)) & 0xFF;
        }
        hlen = 10;
    }
    memcpy(&header[hlen], mask, 4);
    hlen += 4;

    /* 发送 header,再逐字节异或后发送 payload */
    /* ... write(fd, header, hlen); ... */
    return 0;
}

真实项目中建议直接使用成熟库(libwebsockets、mongoose、WebSocket++、uWebSockets),除非有极致资源约束。


13. 服务端实现与常见框架

语言框架/库
Node.jsws、socket.io(带降级与房间)、uWebSockets.js
Pythonwebsockets、aiohttp、FastAPI(WebSocket)、Tornado
Gogorilla/websocket、nhooyr.io/websocket
JavaNetty、Spring WebSocket、Jakarta WebSocket
C/C++libwebsockets、mongoose、WebSocket++、Boost.Beast
Rusttokio-tungstenite、axum(ws)
Erlang/ElixirCowboy、Phoenix Channels

服务端要点:

  • 每个连接占一个 socket 与一定内存;高并发需事件驱动(epoll/kqueue/IOCP)。
  • 维护连接表(用户→连接),支持广播、房间、定向推送。
  • 心跳检测与僵尸连接清理。
  • 优雅关闭:服务重启时先发 Close(1001) 并等待。

14. 反向代理与负载均衡

WebSocket 需要中间件显式支持 Upgrade 透传。

Nginx

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    location /ws/ {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # 长连接超时需显著放大,否则空闲会被断开
        proxy_read_timeout  3600s;
        proxy_send_timeout  3600s;
        proxy_buffering off;
    }
}

其他

  • HAProxy:timeout tunnel 1h + option http-server-connection / mode http。
  • Envoy / Traefik / API 网关:原生支持 WebSocket upgrade。
  • HTTP/2 与 HTTP/3:WebSocket over HTTP/2(RFC 8441);HTTP/3 需 QUIC 扩展支持。

粘性会话(Sticky Session):WebSocket 是有状态长连接,如果服务端有多实例且维护内存态,需要四层/七层粘性,或把状态外置到 Redis 等。


15. 安全实践

  1. **始终使用 wss://**:ws:// 明文可被窃听、篡改、注入。
  2. 鉴权:
    • 握手中用 Cookie / URL token / 子协议携带凭据;
    • 或在连接建立后发送首帧鉴权,未通过则立即 Close(4001)。
    • Token 应短期有效、可撤销。
  3. **校验 Origin**:防止跨站 WebSocket 劫持(CSWSH)。服务端应维护允许的来源白名单。
  4. 输入校验:所有消息都视为不可信,做长度、格式、类型校验。
  5. 限制资源:
    • 单条消息大小上限、单连接发送速率;
    • 单 IP 连接数上限;
    • 空闲超时自动踢出。
  6. 防 SSRF:若服务端接受客户端提供的 URL 进行反向 WebSocket 连接,需严格校验。
  7. TLS 配置:使用现代密码套件、HSTS;避免旧协议。
  8. 日志与审计:记录连接建立/关闭、关闭码、异常,便于排查与风控。
  9. 不要信任 Sec-WebSocket-Key 之外的内容:握手头可能被伪造。
  10. 子协议/扩展白名单:只接受已知的子协议和扩展。

16. 性能与工程实践

16.1 心跳与保活

let pingTimer = null;
function startHeartbeat(ws) {
  pingTimer = setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: 'ping', ts: Date.now() }));
    }
  }, 25000);
}
  • 协议层 Ping/Pong 只能探测链路,应用层心跳还能探测"服务假死"。
  • 服务端同样应检测长时间无数据/无 Pong 的连接并清理。

16.2 断线重连(指数退避 + 抖动)

function connect() {
  const ws = new WebSocket(URL);
  let attempt = 0;

  const retry = () => {
    const base = Math.min(30000, 500 * 2 ** attempt++);
    const jitter = Math.random() * base * 0.3;   // 防止惊群
    setTimeout(connect, base + jitter);
  };

  ws.onopen = () => { attempt = 0; };
  ws.onclose = retry;
  ws.onerror = () => ws.close();
}

16.3 发送队列与背压

  • bufferedAmount(浏览器)反映未发送字节;过大时应暂停生产或丢弃。
  • 服务端同理:对慢消费者要有队列上限与丢弃策略,避免内存膨胀。
function safeSend(ws, data, limit = 1 << 20) {
  if (ws.bufferedAmount > limit) {
    console.warn('backpressure, drop message');
    return false;
  }
  ws.send(data);
  return true;
}

16.4 消息设计

  • 优先使用二进制(MessagePack / Protobuf / 自定义 TLV)替代 JSON,省带宽与解析开销。
  • 协议需带版本号、消息类型、请求 ID(便于请求-响应配对)。
  • 大消息考虑分片或分块传输。

16.5 其他

  • 多路复用:单连接承载多种业务(用消息类型区分),减少连接数。
  • 连接数控制:移动端/嵌入式注意内存与 FD 上限。
  • 优雅重启:滚动发布时先 Close(1001),客户端按重连策略切换。

17. 调试与抓包

  • 浏览器 DevTools:Network → WS,可查看帧内容、方向、长度。
  • 命令行:
    npm i -g wscat
    wscat -c wss://example.com/chat
    
    websocat wss://example.com/chat
    
  • 抓包:Wireshark 支持 WebSocket dissector;明文 ws:// 可直接看帧,wss:// 需提供 TLS 密钥日志(SSLKEYLOGFILE)。
  • 握手排查:
    • 若返回 200 而非 101:代理未透传 Upgrade。
    • 若卡在 CONNECTING:防火墙/证书/跨域问题。
    • 若频繁断开:检查空闲超时、proxy_read_timeout、心跳配置。

18. 嵌入式 / 物联网场景实践

在资源受限设备(MCU + RTOS/Linux)上使用 WebSocket,需注意:

  1. 内存:TLS 握手与缓冲会占较多 RAM;考虑精简的 TLS 实现(如 mbedTLS 裁剪)与固定缓冲区。
  2. 断线重连:弱网下需健壮的指数退避与状态恢复;建议设备侧维护会话令牌。
  3. 心跳:设备常经 NAT/移动网络,必须定时心跳,否则连接被回收。
  4. 半双工约束:若设备音频/外设同一时间只能收或发,需在应用层协调收发时序。
  5. TLS 证书:内置根证书、SNI、时间同步(证书校验依赖正确时钟)。
  6. 可靠性:WebSocket 只保证传输层可靠,业务级"恰好一次/至少一次"需在应用层实现(ACK、序号、去重)。
  7. 二进制上行:音频/传感器数据用 Binary 帧,按固定帧长切片,配合服务端流式解析。
  8. 看门狗:网络栈阻塞要兜底,避免拖垮实时任务。

典型数据流:

设备传感器/麦克风 ──(Binary 帧, 固定帧长)──► 云端
云端结果/指令      ◄──(Text/Binary 帧)───── 设备

19. 常见问题 FAQ

Q1:WebSocket 和 Socket 是一回事吗?
不是。Socket 是操作系统提供的网络编程接口(TCP/UDP);WebSocket 是建立在 TCP 之上的应用层协议。日常说"用 Socket 长连接"通常指自研 TCP 协议,与 WebSocket 不同。

Q2:WebSocket 会自动重连吗?
不会。断线后 onclose 触发,重连需应用自己实现(含退避、抖动、会话恢复)。

Q3:为什么客户端必须加掩码?
防止中间代理缓存污染,并让攻击者无法预测字节流。服务器帧不得加掩码。

Q4:101 之外还能返回什么?
普通 HTTP 错误码。401/403 鉴权失败,426 Upgrade Required 提示需要升级,404 路径不存在。

Q5:WebSocket 能传二进制吗?
能,用 Binary 帧(opcode=0x2),适合音频、图片、Protobuf 等。

Q6:为什么连接一段时间后自己断了?
多因 NAT/负载均衡/代理的空闲超时。解决:协议层 Ping + 应用层心跳,且调大 proxy_read_timeout。

Q7:WebSocket 和 SSE 怎么选?
只需服务器推送 → SSE 更简单;需要双向、低延迟 → WebSocket。

Q8:WebSocket 消息会"粘包/半包"吗?
不会。帧头含长度,协议保留消息边界;但若用 TCP 自研协议才需要处理粘包。

Q9:如何做请求-响应式调用?
在应用协议里加 requestId,服务端回带同一 requestId,客户端用 map 配对 Promise。

Q10:permessage-deflate 该开吗?
文本/JSON 多、带宽敏感时建议开;小消息多、CPU 紧张时可关。


20. 参考资料


Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐