WebSocket ws.onmessage合并消息解析错误致断连问题求助
WebSocket合并JSON消息的前端解析解决方案
问题背景
原WebSocket消息处理逻辑如下:
ws.onmessage = function (event) { let message = event.data; let data = JSON.parse(message); parseWSData(data); }
当服务器短时间内发送多条消息时,前端会收到合并后的消息(如'{"aa":11,"bb":22}{"cc":33,"bb":22}'),直接调用JSON.parse会抛出错误,最终导致WebSocket以错误码1006断开连接。即便用try-catch捕获错误,连接仍会断开。
尝试通过拆分"}{"处理合并消息,但该方案存在缺陷:当JSON字符串中包含"}{"子串时会错误拆分,且运行一段时间后仍会出现解析}或空字符串的错误。最终通过将后端改为异步发送解决了问题,但仍需前端层面的可靠处理方案。
前端可靠处理方案
方案1:使用消息分隔符(推荐)
和后端约定,每条JSON消息末尾添加一个不会出现在JSON内容中的分隔符(比如换行符\n,或自定义标记如|||),后端发送每条消息时都追加该分隔符。前端维护一个缓冲区,每次收到消息后追加到缓冲区,再按分隔符拆分出完整消息解析:
// 全局缓冲区存储未处理的消息片段 let wsBuffer = ''; ws.onmessage = function (event) { wsBuffer += event.data; // 以换行符作为分隔符示例,可根据约定修改 const delimiter = '\n'; let messageList = wsBuffer.split(delimiter); // 最后一个元素可能是不完整的消息,放回缓冲区 wsBuffer = messageList.pop(); for (const msg of messageList) { if (!msg.trim()) continue; // 跳过空消息 try { const data = JSON.parse(msg); parseWSData(data); } catch (e) { console.error('JSON解析失败:', e, '消息内容:', msg); // 可根据业务需求选择记录错误后继续,或触发重连逻辑 } } };
这种方式通过明确的分隔符区分消息,完全避免了JSON内容干扰拆分逻辑,是最稳定可靠的方案。
方案2:渐进式JSON解析(无需后端配合)
如果无法修改后端,可采用渐进式解析逻辑:维护缓冲区,从开头尝试解析完整JSON,解析成功则处理并截断缓冲区,失败则尝试定位最后一个可能的闭合位置,逐步处理:
let wsBuffer = ''; ws.onmessage = function (event) { wsBuffer += event.data; while (true) { let parsedData = null; let validEndIndex = 0; // 尝试解析整个缓冲区 try { parsedData = JSON.parse(wsBuffer); validEndIndex = wsBuffer.length; } catch (e) { // 解析失败,尝试定位最后一个闭合大括号 const lastCloseBrace = wsBuffer.lastIndexOf('}'); if (lastCloseBrace === -1) break; // 无完整JSON结构,退出循环 // 尝试解析到最后一个闭合大括号的位置 try { parsedData = JSON.parse(wsBuffer.slice(0, lastCloseBrace + 1)); validEndIndex = lastCloseBrace + 1; } catch (e2) { // 到该位置仍无法解析,丢弃错误片段 wsBuffer = wsBuffer.slice(lastCloseBrace + 1); continue; } } if (parsedData) { parseWSData(parsedData); wsBuffer = wsBuffer.slice(validEndIndex); } else { break; } } };
该方案无需后端修改,但逻辑较复杂,极端场景下(如嵌套多层JSON)可使用成熟的流式JSON解析库(如oboe.js)简化开发。
原有方案的问题分析
之前使用split("}{")的方案存在核心缺陷:
- 若JSON字符串中包含
"}{"(比如{"desc":"示例}{文本"}),会错误拆分消息,导致后续解析失败。 - 无法处理半完整消息(如收到
'{"aa":11,"bb":22}{"cc":'),拆分后最后一段无法解析,且错误内容会残留到后续处理中。
内容的提问来源于stack exchange,提问作者GainLos
相关产品推荐
相关产品推荐

