断线重连后,怎样补消息且不丢草稿
只讨论重连后的两件事:用已确认序号请求补推,避免消息空洞;用快照保存输入草稿,避免会话列表预览把未发送内容覆盖掉。
- WebSocket
- 消息补推
- 草稿
- 断线重连
关联案例:企业沟通与售后协作平台。文章依据本地 Socket 重连与会话草稿逻辑整理;示例接口字段已泛化,短重连阈值与缓存键名仅作说明。本文不声称消息在任何网络切换下都零丢失。
重连成功不等于消息已对齐
聊天里 Socket 断开再连上很常见:切网、休眠、网关重启。客户端若只把“连接状态”改成在线,界面可能已经缺了一段消息;用户看到的是空洞,而不是“正在补齐”。
需要分开回答两个问题:
- 消息空洞:从哪一条之后开始补?
- 输入草稿:断线期间用户敲的字,以谁为准?
用已确认序号表达“我收到哪了”
本地做法是为会话维护 last_ack_seq:客户端处理并确认消息后更新;重连成功时带上该序号请求服务端补推后续消息。
在线收消息 ─→ 更新 last_ack_seq ─→ 断开
│
重连成功 ─→ 对每个已记录会话请求 replay(last_ack_seq)
│
服务端补推后续 ─→ 再更新 ack
两个容易做错的点:
- 不能只补当前选中会话。后台会话同样可能缺消息,只补激活会话会让列表预览继续错位。
- 短重连与长断开要分流。很短的断开适合立即 replay;更长的离线往往应走历史分页或全量对齐,而不是无界补推。
本地实现用重连耗时阈值区分:低于阈值才触发按 last_ack_seq 的补推,更长的断开交给上层错误/刷新路径。阈值是产品与协议的折中,不是通用常数。
草稿不能写在“列表预览字符串”上就结束
会话列表常显示 send_content 作为预览。若把草稿直接覆盖进去,会遇到:
- 清空输入时无法还原真正的上一条消息;
- 多次编辑后丢失“草稿前的原始内容”;
- 图片等富文本直接进列表字段,预览语义被破坏。
本地实现把草稿当成带前缀的展示态,并保留一份原始快照:
// 示意:列表字段同时承载“原消息”与“草稿展示”
const DRAFT_PREFIX = '[草稿]'
// 记录草稿时
item.send_old_content = wasDraft ? item.send_old_content : item.send_content
item.send_content = DRAFT_PREFIX + draftText
// 清空输入时
item.send_content = wasDraft ? (item.send_old_content || '') : item.send_content
要点:
send_old_content只在首次进入草稿时写入,避免草稿叠草稿把快照冲掉;- 空内容或仅
<br>视为“清空草稿”,走还原分支; - 当前正在编辑的会话不把草稿前缀写回预览,避免输入框与列表互相干扰。
补消息与草稿可以并行,但都要以会话为边界
replay 按会话维度请求;草稿也按会话键缓存。身份切换或强制重连时,应先销毁旧连接,避免不同账号复用同一条 Socket,把 ack 与草稿都写到错误的人身上。
重连成功
├─ replay: 对每个 clientInstance 的 last_ack_seq 补推
└─ drafts: 输入框恢复当前会话缓存;列表只展示草稿态预览
验证建议
至少覆盖:断开 2 秒内重连(应补推)、断开较久(应走历史对齐)、编辑草稿后切会话再切回、清空输入还原原消息、强制换身份重连。断言不只看“Socket 已连接”,还要看消息序号是否连续、草稿前缀是否只出现在非激活会话的预览里。
把“补空洞”和“保草稿”拆成会话级的两条恢复路径,重连才不会变成另一种形式的数据错乱。