基于y-websocket实现Ping-Pong机制:客户端未收到服务端Ping消息
问题:y-websocket客户端无法接收服务端Ping消息,如何正确实现Ping-Pong机制?
我使用y-websocket作为服务端实现在线编辑器消息同步,尝试在收到服务端Ping消息时回复Pong消息,编写了对应客户端代码,但客户端始终未收到服务端发送的Ping消息。服务端Ping消息发送逻辑已确认正常,请问我遗漏了什么?该如何正确实现Ping-Pong机制?
客户端代码
const doWsConn = (ydoc: Y.Doc, docId: string): WebsocketProvider => { const wsProvider: WebsocketProvider = new WebsocketProvider(readConfig("wssUrl"), docId, ydoc, { maxBackoffTime: 1000000, params: { access_token: localStorage.getItem(WheelGlobal.ACCESS_TOKEN_NAME) ?? "" } }); const uInfo = localStorage.getItem("userInfo"); if (!uInfo) { console.error("user info is null", uInfo); return; }; const user: UserModel = JSON.parse(uInfo); const ydocUser = { name: user.nickname, color: userColor.color, colorLight: userColor.light }; const permanentUserData = new Y.PermanentUserData(ydoc); permanentUserData.setUserMapping(ydoc, ydoc.clientID, ydocUser.name) wsProvider.awareness.setLocalStateField('user', ydocUser); wsProvider.on('auth', (event: any) => { handleWsAuth(event, wsProvider, ydoc, docId); }); wsProvider.on('connection-error', (event: any) => { wsProvider.shouldConnect = false; wsProvider.ws?.close() }); wsProvider.on('message', (event: MessageEvent) => { console.log(event.data); }); wsProvider.on('ping', (event: MessageEvent) => { console.warn("recived ping message"); wsProvider.ws.pong(); }); wsProvider.on('status', (event: any) => { if (event.status === 'connected') { if (wsProvider.ws) { } } else if (event.status === 'disconnected' && wsRetryCount < wsMaxRetries) { wsRetryCount++; setTimeout(() => { wsProvider.connect(); }, 2000); } else { wsProvider.destroy(); return; } }); return wsProvider; }
服务端Ping发送逻辑
const pingInterval = setInterval(() => { if (!pongReceived) { if (doc.conns.has(conn)) { logger.warn('close connection pong, pong:' + pongReceived) closeConn(doc, conn) } clearInterval(pingInterval) } else if (doc.conns.has(conn)) { pongReceived = false try { conn.ping() } catch (e) { logger.error('close connection when ping,' + e) closeConn(doc, conn) clearInterval(pingInterval) } } }, pingTimeout) conn.on('close', () => { closeConn(doc, conn) clearInterval(pingInterval) })
问题分析与解决方法
核心问题
你直接给WebsocketProvider绑定ping事件是无效的——WebsocketProvider是y-websocket对原生WebSocket的封装,它本身并不暴露原生WebSocket的ping事件,只有原生WebSocket实例才会触发该事件。
正确实现步骤
在连接建立后绑定原生WebSocket的
ping事件
监听WebsocketProvider的status事件,当状态变为connected时,获取内部的原生WebSocket实例(wsProvider.ws),然后给这个实例绑定ping事件。处理重连场景
每次连接断开重连后,wsProvider.ws会生成新的实例,因此需要在每次connected状态触发时重新绑定事件,避免事件失效。
修改后的客户端关键代码
wsProvider.on('status', (event: any) => { if (event.status === 'connected') { if (wsProvider.ws) { // 绑定原生WebSocket的ping事件 wsProvider.ws.addEventListener('ping', () => { console.warn("收到ping消息"); // 回复Pong wsProvider.ws.pong(); }); } } else if (event.status === 'disconnected' && wsRetryCount < wsMaxRetries) { wsRetryCount++; setTimeout(() => { wsProvider.connect(); }, 2000); } else { wsProvider.destroy(); return; } }); // 移除原来给WebsocketProvider绑定的ping事件 // wsProvider.on('ping', ...) // 这行要删掉
额外注意事项
- 浏览器端原生WebSocket的
ping事件需要通过addEventListener绑定(或直接赋值onping属性),不能通过WebsocketProvider的on方法绑定。 - 服务端使用的ws库
conn.ping()方法会正常触发客户端的ping事件,无需修改服务端逻辑。
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

