WebSocket断线重连后消息重复发送怎么办?

司空利芹 阅读 48

在做在线协作编辑器时用WebSocket同步数据,但发现客户端断线重连后,之前发送的半条消息会和重连后的新消息拼在一起。比如输入”hello”时断线,重连后继续输入” world”,服务端收到的是”hello worldhello”。试过在重连时清空本地缓存队列,但用户刚恢复输入时又会出现重复。控制台显示连接状态是”open”,但消息体里带上了上次未完成的草稿内容。

以下是消息容器的样式代码,可能和状态显示有关:


.message-container {
  position: relative;
  max-height: 200px;
  overflow-y: auto;
}
.message {
  display: flex;
  align-items: center;
  padding: 8px;
  margin: 4px 0;
  border-radius: 4px;
}
.message.error {
  background-color: #fee;
  border-left: 4px solid #f00;
}

检查过连接状态判断逻辑,发现reconnect时虽然关闭了旧连接,但消息发送函数可能在断线期间被多次调用了。有没有标准的重连时消息重发策略,既能保证消息可靠又不重复?

我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
南宫慧利
最简单的办法是给每条消息加个唯一ID,服务端用个集合存已经收到的ID,重复的直接丢掉。客户端这边重连时清空发送队列,只保留当前正在输入的内容。

let msgId = 0;
const sentMessages = new Set();

function sendMessage(content) {
const id = ++msgId;
const message = { id, content };
ws.send(JSON.stringify(message));
sentMessages.add(id);
}

// 服务端伪代码
const receivedIds = new Set();
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (!receivedIds.has(msg.id)) {
receivedIds.add(msg.id);
processMessage(msg.content);
}
});


实在懒得改的话,就让后端去重,简单粗暴但有效。
点赞 4
2026-02-15 17:03
设计师东宸
这个问题挺常见的,关键在于如何在重连期间控制消息发送的时机和队列管理。我的做法是这样的:

在客户端维护一个待发送的消息队列,每次发送前检查 WebSocket 状态:
const sendQueue = [];
let isConnecting = false;

function sendMessage(msg) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(msg);
} else {
sendQueue.push(msg);
if (!isConnecting) {
reconnect();
}
}
}


重连时清空旧连接的回调事件,避免重复触发:
function reconnect() {
isConnecting = true;

const oldWs = ws;
oldWs.onopen = oldWs.onmessage = oldWs.onerror = oldWs.onclose = null;
oldWs.close();

// 重置状态后重发队列消息
const interval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
clearInterval(interval);
isConnecting = false;
sendQueue.splice(0).forEach(sendMessage);
}
}, 100);
}


消息体需要添加唯一ID,服务端进行幂等校验:
function createMessage(content) {
return JSON.stringify({
id: Math.random().toString(36).substr(2, 9),
content,
timestamp: Date.now()
});
}


具体原理是:
通过切断旧连接所有事件回调,避免重连期间收到历史消息
用独立队列管理待发消息,而不是依赖浏览器缓存
消息ID确保即使重复发送服务端也能识别
轮询检测连接状态比单纯监听onopen更可靠,因为onopen触发后连接可能还没完全就绪

这样处理后消息丢失和重复的概率基本为0了。建议同时在服务端做消息去重,形成双重保险。
点赞 3
2026-02-06 14:00