WebSocket需在应用层实现可靠消息确认:每条消息带唯一ID并存入待确认队列,设超时机制;接收方立即回ACK;发送方匹配后清理状态;支持断连恢复与幂等去重。

WebSocket 本身不提供消息送达确认机制,所谓“发送成功”只代表数据写入了浏览器的发送缓冲区,并不代表对方收到、解析或处理完成。要实现可靠的消息确认与应答(ACK),必须在应用层设计一套轻量但严谨的协议逻辑。
消息必须带唯一 ID 并维护待确认队列
每条需要确认的业务消息都要附带一个全局唯一标识(如 crypto.randomUUID() 或时间戳+随机字符串),发送前生成 ID 并存入本地待确认 Map:
- 用 ID 作为键,保存原始消息内容、发送时间、超时回调或 Promise resolve 方法
- 设置超时定时器(例如 10 秒),未收到 ACK 就触发失败处理
- 页面刷新或标签页切换时,需结合 localStorage 或 IndexedDB 持久化未确认 ID,避免丢失状态
接收方收到后立即回发标准 ACK 包
客户端或服务端收到消息后,不能等业务逻辑执行完再响应,而应第一时间提取 ID,构造最小化 ACK 消息返回:
- 约定统一结构,例如
{ "type": "ack", "id": "xxx" } - 确保解析逻辑健壮:先尝试 JSON.parse,捕获异常并丢弃非法消息,避免阻塞 ACK 发送
- 若消息含敏感字段或需校验签名,应在 ACK 前完成基础校验(如 ID 格式、必填字段),但不等待数据库写入等耗时操作
发送方监听并匹配 ACK 清理状态
WebSocket 的 onmessage 回调中需区分普通消息和 ACK:
立即学习“Java免费学习笔记(深入)”;
- 判断
data.type === "ack"且pendingMessages.has(data.id) - 找到对应条目后,调用保存的 resolve 或成功回调,并立刻从 Map 中删除该 ID
- 务必清除引用,否则待确认队列持续增长会导致内存泄漏
支持断连恢复与幂等处理
真实场景中网络不稳定,需兼顾重连和重复消息:
- 连接断开时,未确认消息保留在本地或由服务端按会话 ID(如 JWT 中的 sid)暂存
- 重连成功后,服务端主动补发未确认消息,客户端需识别重复 ID 并跳过 UI 更新(但依然回复 ACK)
- 服务端建议用 Redis 缓存最近 N 条已处理 ID,实现跨进程去重;客户端可用 Set 记录已处理 ID,有效期可设为 5–10 分钟


















