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

WebRTC公网测试:RTCPeerConnection无因触发disconnected状态求助

WebRTC公网连接20-30秒后触发disconnected状态的原因及解决办法

核心原因分析

  1. NAT穿透失效:公网环境下未配置STUN/TURN服务器,或仅用STUN无法穿透对称NAT类型,导致ICE连接超时后触发disconnected状态。局域网无需NAT穿透,因此连接稳定。
  2. ICE候选动态失效:公网IP/端口因网络波动动态变化,未及时重新收集、交换ICE候选,现有连接路径失效。
  3. UDP连接被路由器回收:公网路由器会主动释放长时间无活跃流量的UDP连接,即使有视频流,若丢包严重或流量间隙过大,也会被判定为闲置连接。
  4. 浏览器资源竞争:同一浏览器的不同标签页共享资源,部分标签页的WebRTC连接可能因资源优先级被标记为disconnected。

你的现有代码问题

你用transport类型的bytesReceived判断连接状态存在明显缺陷:

  • 统计字段不准确:transport统计的是传输层总字节数(含ICE控制包),无法反映媒体流的真实接收状态,应监听inbound-rtp类型的bytesReceived;
  • 浏览器兼容性差:Chrome、Firefox的WebRTC Stats格式差异大,transport字段的统计逻辑不一致,导致b值被重置或获取失败,出现b2-b1为0/负数的情况;
  • 判断逻辑不合理:短暂网络波动也会导致bytesReceived停止增长,直接断开连接属于误判。

修正后的状态判断逻辑示例:

this.confirmPeerDisconnectionFunc = async function(connectionObject) {
    let bytesReceived = 0;
    const stats = await connectionObject.getStats();
    stats.forEach(report => {
        // 仅统计视频媒体流的入站字节数
        if (report.type === 'inbound-rtp' && report.kind === 'video') {
            bytesReceived = parseInt(report.bytesReceived || 0);
        }
    });
    return bytesReceived;
}

解决办法

1. 配置STUN/TURN服务器

必须添加STUN获取公网候选,对称NAT环境需部署TURN中继:

const rtcConfig = {
    iceServers: [
        { urls: 'stun:stun.l.google.com:19302' }, // 公共STUN服务器
        { urls: 'turn:your-turn-server.com:3478', username: 'your-user', credential: 'your-pass' } // 自建TURN服务器
    ]
};
const peerConnection = new RTCPeerConnection(rtcConfig);

2. 启用ICE重连机制

当触发disconnected时,优先尝试重启ICE而非直接断开:

peerConnection.onconnectionstatechange = async () => {
    if (peerConnection.connectionState === 'disconnected') {
        // 重启ICE连接
        await peerConnection.restartIce();
        // 重新生成Offer并通过信令服务器发送给对方
        const offer = await peerConnection.createOffer();
        await peerConnection.setLocalDescription(offer);
        sendSignalingMsg(offer); // 替换为你的信令发送逻辑
    }
};

3. 添加数据通道心跳

通过DataChannel定时发送心跳包,保持UDP连接活跃:

const heartbeatChannel = peerConnection.createDataChannel('heartbeat');
heartbeatChannel.onopen = () => {
    setInterval(() => {
        if (heartbeatChannel.readyState === 'open') {
            heartbeatChannel.send('ping');
        }
    }, 5000); // 每5秒发送一次心跳
};

4. 优化状态判断逻辑

结合iceConnectionState和媒体流统计综合判断:

  • 监听iceConnectionStatechange事件,当状态变为disconnected时先尝试重连;
  • 用inbound-rtp/outbound-rtp的字节统计判断媒体流是否活跃,避免误判。

内容的提问来源于stack exchange,提问作者SuperYegorius

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 02:05:27