You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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实例才会触发该事件。

正确实现步骤

  1. 在连接建立后绑定原生WebSocket的ping事件
    监听WebsocketProvider的status事件,当状态变为connected时,获取内部的原生WebSocket实例(wsProvider.ws),然后给这个实例绑定ping事件。

  2. 处理重连场景
    每次连接断开重连后,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:44:55