You need to enable JavaScript to run this app. WebRTC断开后如何与同一对等端重连 需符合官方文档的代码示例
优惠活动
大模型
产品
解决方案
定价
更多

WebRTC断开后如何与同一对等端重连 需符合官方文档的代码示例

WebRTC 同一对等端断开重连实现

核心改造点

  • 持久化存储房间ID、目标用户、本地媒体流等重连必需的上下文数据
  • 新增重连锁、重连次数限制、防抖逻辑,避免重复触发重连
  • 断开时先销毁旧的RTCPeerConnection实例,再复用原有上下文重新发起连接

完整可运行代码

// 全局上下文存储,重连时复用
let peerConnection = null;
let localStream = null; // 提前通过getUserMedia/getDisplayMedia获取,存在此处复用
let remoteStream = null;
let currentRoomId = null; // 加入房间时存储
let targetUser = null; // 发起连接时存储目标对等端信息
let reconnectTimer = null;
let isReconnecting = false;
const MAX_RECONNECT_ATTEMPTS = 5;
let reconnectCount = 0;

const configuration = {
  iceServers: [
    {
      urls: ["stun:stun1.l.google.com:19302", "stun:stun2.l.google.com:19302"],
    },
  ],
  iceCandidatePoolSize: 20,
};

// 发起方创建连接
const createPeer = async (user, ls, rs) => {
  if (isReconnecting) return;
  isReconnecting = true;
  targetUser = user;
  localStream = ls;
  remoteStream = rs;

  // 销毁旧实例
  if (peerConnection) {
    peerConnection.close();
    peerConnection = null;
  }

  try {
    peerConnection = new RTCPeerConnection(configuration);
    // 添加本地流轨道
    localStream.getTracks().forEach(track => {
      peerConnection.addTrack(track, localStream);
    });
    // 接收远端轨道
    peerConnection.ontrack = (event) => {
      event.streams[0].getTracks().forEach(track => {
        remoteStream.addTrack(track);
      });
    };

    // 注册监听器
    registerPeerConnectionListeners();

    // 创建offer
    const offer = await peerConnection.createOffer();
    await peerConnection.setLocalDescription(offer);
    
    // ---------- 此处对接你的信令服务,将offer发送给目标对等端 ----------
    // sendToSignalingServer({ type: 'offer', data: offer, targetUser: user })

    reconnectCount = 0;
  } catch (e) {
    console.error('创建连接失败:', e);
    isReconnecting = false;
  }
};

// 接收方加入连接
const joinPeer = async (roomId, ls, rs) => {
  if (isReconnecting) return;
  isReconnecting = true;
  currentRoomId = roomId;
  localStream = ls;
  remoteStream = rs;

  if (peerConnection) {
    peerConnection.close();
    peerConnection = null;
  }

  try {
    peerConnection = new RTCPeerConnection(configuration);
    localStream.getTracks().forEach(track => {
      peerConnection.addTrack(track, localStream);
    });
    peerConnection.ontrack = (event) => {
      event.streams[0].getTracks().forEach(track => {
        remoteStream.addTrack(track);
      });
    };

    registerPeerConnectionListeners();
    
    // ---------- 此处对接你的信令服务,获取发起方发送的offer ----------
    // const offer = await getOfferFromSignalingServer(roomId)
    // await peerConnection.setRemoteDescription(new RTCSessionDescription(offer))
    // const answer = await peerConnection.createAnswer()
    // await peerConnection.setLocalDescription(answer)
    // ---------- 此处对接你的信令服务,将answer发送给发起方 ----------
    // sendToSignalingServer({ type: 'answer', data: answer, roomId: roomId })

    reconnectCount = 0;
  } catch (e) {
    console.error('加入连接失败:', e);
    isReconnecting = false;
  }
};

// 注册连接状态监听器
const registerPeerConnectionListeners = () => {
  // 收集ICE候选并发送
  peerConnection.addEventListener('icecandidate', (event) => {
    if (event.candidate) {
      // ---------- 此处对接你的信令服务,发送ICE候选到对等端 ----------
      // sendToSignalingServer({ type: 'icecandidate', data: event.candidate })
    }
  });

  peerConnection.addEventListener("connectionstatechange", () => {
    const state = peerConnection.connectionState;
    console.log(`Connection state change: ${state}`);
    
    if (state === "connected") {
      isReconnecting = false;
      reconnectCount = 0;
      if (reconnectTimer) {
        clearTimeout(reconnectTimer);
        reconnectTimer = null;
      }
      return;
    }

    if (state === "disconnected" || state === "failed") {
      remoteStream?.getTracks().forEach(track => track.stop());
      remoteStream = null;
      isReconnecting = false;

      // 触发重连逻辑,加2秒防抖避免频繁触发
      if (reconnectCount < MAX_RECONNECT_ATTEMPTS && !reconnectTimer) {
        reconnectTimer = setTimeout(() => {
          reconnectCount++;
          console.log(`发起第${reconnectCount}次重连`);
          // 发起方调用createPeer,接收方调用joinPeer
          if (targetUser) {
            createPeer(targetUser, localStream, new MediaStream());
          } else if (currentRoomId) {
            joinPeer(currentRoomId, localStream, new MediaStream());
          }
          reconnectTimer = null;
        }, 2000);
      } else if (reconnectCount >= MAX_RECONNECT_ATTEMPTS) {
        console.error('重连次数已达上限,停止重连');
        reconnectCount = 0;
      }
    } else {
      remoteStream = new MediaStream();
    }
  });

  peerConnection.addEventListener("iceconnectionstatechange", () => {
    console.log(`ICE connection state change: ${peerConnection.iceConnectionState}`);
  });
};

// 信令服务收到answer后的处理逻辑(你需要在信令回调中调用此方法)
const handleReceiveAnswer = async (answer) => {
  if (!peerConnection) return;
  await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
};

// 信令服务收到ICE候选后的处理逻辑(你需要在信令回调中调用此方法)
const handleReceiveIceCandidate = async (candidate) => {
  if (!peerConnection) return;
  await peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
};

注意事项

  • 代码中标记了信令对接的位置,替换成项目中现有信令传输逻辑即可正常运行
  • 本地媒体流可以直接复用,不需要重新调用getUserMedia申请权限,不会重复触发用户授权弹窗
  • 如果需要调整重连次数和重连间隔,修改MAX_RECONNECT_ATTEMPTS和setTimeout的时间参数即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.11 22:51:02