当包含冰服务器时,WebRTC 卡在连接状态(远程候选者甚至通过 LAN 导致问题) [英] WebRTC stuck in connecting state when ice servers are included (remote candidates causing issues even over LAN)

查看:62
本文介绍了当包含冰服务器时,WebRTC 卡在连接状态(远程候选者甚至通过 LAN 导致问题)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在尝试解决 上一期.

let peer = new RTCPeerConnection();

这在我的本地网络上运行良好.

This has been working perfectly on my local network.

但是,不在同一网络上的设备(例如,使用 4G 的手机)将无法连接.我记得我必须将一些 iceServers 添加回 RTCPeerConnection 构造函数.

However device that's not on the same network (for example, a mobile on 4G) would not connect. I remembered that I had to add back some iceServers to the RTCPeerConnection constructor.

let peer = RTCPeerConnection(
  {
    iceServers: [
      {
        urls: [
          "stun:stun1.l.google.com:19302",
          "stun:stun2.l.google.com:19302",
        ],
      },
      {
        urls: [
          "stun:global.stun.twilio.com:3478?transport=udp",
        ],
      },
    ],
    iceCandidatePoolSize: 10,
  }
);

这样做之后,我的 WebRTC 连接一直停留在连接状态.即使在我的本地网络上,也没有一个连接成功.(不再是这种情况,请参阅下面的编辑 2)

After doing so, my WebRTC connections have been stuck in the connecting state ever since. Not a single connection has succeeded, even on my local network. (no longer the case, see edit 2 below)

这是连接的状态:

  • 冰上候选者齐聚一堂.
  • 提议/答案已创建.
  • offer/answer 和 ice 候选人已通过我的信令服务成功发送.
  • 我成功设置了远程和本地描述,并在两端添加了 ice 候选.
  • 连接保持连接状态.
  • 大约 30 秒后,连接超时并失败.

EDIT:看起来比当我将 iceServers 留空时,连接仍然收集了一个 ice 候选,所以我假设我的浏览器(chrome)提供了一个默认的 ice服务器.在这种情况下,只有我的自定义 ice 服务器(如上所示)导致了问题,而不是浏览器的默认设置.

EDIT: It appears than when I leave the iceServers blank, the connection still gathers an ice candidate, so I am assuming my browser (chrome) provided a default ice server. In that case, it's only my custom ice servers (shown above) that are causing a problem, and not the browser defaults.

编辑 2:新观察

我添加了大量的日志记录,每当我包含 iceServers 时,我都会注意到一些事情:

I've added loads and loads of logging and I just noticed something whenever I do have the iceServers included:

每当对等方 A 在一段时间内第一次发起与对等方 B 的连接时,对等方 B 会收集两个冰候选:1 个本地主机候选和 1 个远程候选.正如我上面已经说过的,连接失败.

Whenever peer A initiates a connection with peer B for the first time in a while, peer B gathers two ice candidates: 1 local host candidate and 1 remote candidate. As I've already stated above, the connection fails.

但是当我快速尝试再次连接时……对等点 B 只收集一个冰候选者:本地主机候选者.不收集远程候选人.我的第一个假设是我正在使用的 STUN 服务器(在这种情况下可能是谷歌的)对其服务有某种形式的速率限制.这个场景真正有趣的是,连接成功了!!

But when I quickly attempt to connect again... peer B only gathers a single ice candidate: a local host candidate. The remote candidate is not gathered. My first assumption is that the STUN server I'm using (in this case it was likely google's) has some form of rate limiting on their service. What's really funny about this scenario, is that the connection is successful!!

远程候选人搞乱了连接有点神秘……我希望这些新的细节会有所帮助.我被困在这几个月了!而且两台设备都在我的局域网上,所以我希望远程候选人完全没有影响.

There's something mysterious about a remote candidate messing up the connection... and I hope these new details will help. I'm been stuck at this for months! And both devices are on my LAN so I would expect a remote candidate to have absolutely no effect.

Peer A 代码(发起方):

Peer A code (initiator):

export class WebRTCConnection {
  private _RTCPeerConnection: any;
  private _fetch: any;
  private _crypto: any;

  private _entity: any;
  private _hostAddress: any;
  private _eventHandlers: ConnectionEventHandlers;
  private _peer: any;
  private _peerChannel: any;

  constructor({
    entity,
    hostAddress,
    eventHandlers,
    RTCPeerConnection,
    fetch,
    crypto,
  }: {
    entity: any,
    hostAddress: any,
    eventHandlers: ConnectionEventHandlers,
    RTCPeerConnection: any,
    fetch: any,
    crypto: any,
  }) {
    this._RTCPeerConnection = RTCPeerConnection;
    this._fetch = fetch;
    this._crypto = crypto;

    this._entity = entity;
    this._hostAddress = hostAddress;
    this._eventHandlers = eventHandlers;

    this._initPeer();
  }

  async _initPeer() {
    this._peer = new this._RTCPeerConnection(/* as shown in question */);

    let resolveOfferPromise: (value: any) => void;
    let resolveIceCandidatesPromise: (value: any[]) => void;
    
    let iceCandidatesPromise: Promise<any[]> = new Promise((resolve, _reject) => {
      resolveIceCandidatesPromise = resolve;
    });

    let offerPromise: Promise<any> = new Promise((resolve, _reject) => {
      resolveOfferPromise = resolve;
    });

    this._peer.onnegotiationneeded = async () => {
      let offer = await this._peer.createOffer();
      await this._peer.setLocalDescription(offer);
      resolveOfferPromise(this._peer.localDescription);
    };

    this._peer.onicecandidateerror = () => {
      // log error
    };

    let iceCandidates: any[] = [];

    this._peer.onicecandidate = async (evt: any) => {
      if (evt.candidate) {
        // Save ice candidate
        iceCandidates.push(evt.candidate);
      } else {
        resolveIceCandidatesPromise(iceCandidates);
      }
    };

    (async () => {
      // No more ice candidates, send on over signaling service
      let offer: any = await offerPromise;
      let iceCandidates: any[] = await iceCandidatesPromise;

      let sigData = // reponse after sending offer and iceCandidates over signaling service

      let answer = sigData.answer;
      await this._peer.setRemoteDescription(answer);

      for (let candidate of sigData.iceCandidates) {
        await this._peer.addIceCandidate(candidate);
      }
    })();

    this._peer.onicegatheringstatechange = (evt: any) => {
      // log state
    };

    this._peer.onconnectionstatechange = async () => {
      // log state
    };

    this._peerChannel = this._peer.createDataChannel("...", {
      id: ...,
      ordered: true,
    });

    this._peerChannel.onopen = () => {
      // log this
    };

    this._peerChannel.onmessage = (event: any) => {
      // do something
    };
  }

  send(msg: any) {
    this._peerChannel.send(
      new TextEncoder().encode(JSON.stringify(msg)).buffer,
    );
  }

  close() {
    if (this._peer) {
      this._peer.destroy();
    }
  }
}

对等 B 代码:

export class WebRTCConnection {
  constructor({ signalData, eventHandlers, RTCPeerConnection }) {
    this._eventHandlers = eventHandlers;

    this._peer = new RTCPeerConnection(/* as seen above */);

    this._isChannelOpen = false;

    this._peer.ondatachannel = (event) => {
      event.channel.onopen = () => {
        this._mainDataChannel = event.channel;
        event.channel.onmessage = async (event) => {
          // do something
        };
        this._isChannelOpen = true;
      };
    };

    this._peer.onicecandidateerror = () => {
      // log error
    };

    this._iceCandidates = [];
    this._isIceCandidatesFinished = false;
    this._iceCandidatesPromise = new Promise((resolve, _reject) => {
      this._resolveIceCandidatesPromise = resolve;
    });
    this._isAnswerFinished = false;
    this._isSignalDataSent = false;

    this._peer.onicecandidate = async (evt) => {
      if (evt.candidate) {
        // Save ice candidate
        this._iceCandidates.push(evt.candidate);
      } else {
        // No more ice candidates, send on over signaling service when ready
        this._isIceCandidatesFinished = true;
        this._resolveIceCandidatesPromise();
        this._sendSignalData();
      }
    };

    (async () => {
      let sigData = JSON.parse(signalData);

      let offer = sigData.offer;
      await this._peer.setRemoteDescription(offer);

      this._answer = await this._peer.createAnswer();
      await this._peer.setLocalDescription(this._answer);

      for (let candidate of sigData.iceCandidates) {
        await this._peer.addIceCandidate(candidate);
      }

      this._isAnswerFinished = true;
      this._sendSignalData();
    })();

    this._peer.onconnectionstatechange = async () => {
      // log state
    };
  }

  _sendSignalData() {
    if (false
      || !this._isIceCandidatesFinished
      || !this._isAnswerFinished
      || this._isSignalDataSent
    ) {
      return;
    }

    this._isSignalDataSent = true;

    this._eventHandlers.onSignal(JSON.stringify({
      answer: {
        type: this._answer.type,
        sdp: this._answer.sdp,
      },
      iceCandidates: this._iceCandidates,
    }));
  }

  send(msg) {
    this._mainDataChannel.send(new TextEncoder().encode(JSON.stringify(msg)));
  }

  close() {
    this._peer.destroy();
  }
}

推荐答案

问题是我使用的是 Brave 浏览器.

The problem was that I was using the Brave browser.

使用 Chrome 解决了所有问题.

Using Chrome solved all the issues.

这篇关于当包含冰服务器时,WebRTC 卡在连接状态(远程候选者甚至通过 LAN 导致问题)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆