Start · Sprachen · JavaScript · Referenz · RTCIceTransport.getSelectedCandidatePair()

RTCIceTransport.getSelectedCandidatePair()

Methode

Gibt das aktuell ausgewählte Paar aus lokalem und entferntem ICE-Kandidaten als <code>RTCIceCandidatePair</code> zurück, oder <code>null</code>, solange noch keins feststeht.

seit JavaScript Chromium-Browser und Safari; Firefox: nicht unters Kategorie: webapi

Signatur

RTCIceTransport.prototype.getSelectedCandidatePair()

Beschreibung

getSelectedCandidatePair() zeigt, über welchen Netzwerkpfad eine WebRTC-Verbindung tatsächlich läuft. Beim ICE-Verfahren (Interactive Connectivity Establishment) sammeln beide Seiten Kandidaten, also mögliche Adressen und Ports, und prüfen, welche Kombinationen funktionieren. Das Paar, das der ICE-Agent am Ende auswählt, gibt diese Methode als RTCIceCandidatePair mit den Eigenschaften local und remote zurück. Beide sind RTCIceCandidate-Objekte.

Typische Einsatzfälle sind Diagnose und Überwachung. Man kann zum Beispiel feststellen, ob eine Verbindung direkt läuft (host), über NAT-Traversal (srflx/prflx) oder über einen TURN-Server (relay). Relay-Verbindungen kosten Serverbandbreite und erhöhen meist die Latenz. Deshalb ist diese Information für Qualitätsanzeigen, Logs und Kostenauswertungen nützlich.

Ein RTCIceTransport wird nicht direkt erzeugt. Man erreicht ihn über den DTLS-Transport eines Senders oder Empfängers (sender.transport.iceTransport) oder über pc.sctp.transport.iceTransport bei Datenkanälen. Das ausgewählte Paar kann sich während einer Sitzung ändern, etwa bei einem Netzwechsel oder einem ICE-Restart. Dann wird das Ereignis selectedcandidatepairchange ausgelöst.

Rückgabewert

Typ
RTCIceCandidatePair | null
Beschreibung
Ein Objekt mit den Eigenschaften local und remote (jeweils RTCIceCandidate), das das aktuell ausgewählte Kandidatenpaar beschreibt. Ist noch kein Paar gewählt, zum Beispiel weil die ICE-Prüfungen noch laufen, lautet der Rückgabewert null.

Beispiele

Verbindungsart nach dem Aufbau ermitteln

const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });

pc.addEventListener('connectionstatechange', () => {
  if (pc.connectionState !== 'connected') return;

  const sender = pc.getSenders().find((s) => s.transport);
  const iceTransport = sender?.transport?.iceTransport;
  const pair = iceTransport?.getSelectedCandidatePair?.();

  if (!pair) {
    console.log('Kein ausgewähltes Kandidatenpaar verfügbar');
    return;
  }

  const { local, remote } = pair;
  console.log(`Lokal:  ${local.type} ${local.protocol} ${local.address}:${local.port}`);
  console.log(`Remote: ${remote.type} ${remote.protocol} ${remote.address}:${remote.port}`);
  console.log(local.type === 'relay' || remote.type === 'relay'
    ? 'Verbindung läuft über TURN-Relay'
    : 'Direkte bzw. NAT-traversierte Verbindung');
});
Lokal: srflx udp 203.0.113.5:54321 Remote: host udp 192.168.1.20:61000 Direkte bzw. NAT-traversierte Verbindung

Auf Wechsel des Kandidatenpaars reagieren

const watchCandidatePair = (pc) => {
  const iceTransport = pc.sctp?.transport?.iceTransport
    ?? pc.getSenders()[0]?.transport?.iceTransport;
  if (!iceTransport) return;

  const logPair = () => {
    const pair = iceTransport.getSelectedCandidatePair();
    if (pair) {
      console.log('Neues Paar:', pair.local.type, '↔', pair.remote.type);
    }
  };

  // addEventListener-Variante
  iceTransport.addEventListener('selectedcandidatepairchange', logPair);
  // alternativ: iceTransport.onselectedcandidatepairchange = logPair;

  return () => iceTransport.removeEventListener('selectedcandidatepairchange', logPair);
};

// Aufruf, nachdem Tracks oder Datenkanal ausgehandelt wurden:
// const stopWatching = watchCandidatePair(pc);
Neues Paar: host ↔ host Neues Paar: relay ↔ srflx

Browserübergreifender Ersatz über getStats()

const getSelectedPairFromStats = async (pc) => {
  const stats = await pc.getStats();
  let pairId;
  stats.forEach((report) => {
    if (report.type === 'transport' && report.selectedCandidatePairId) {
      pairId = report.selectedCandidatePairId;
    }
  });
  // Firefox: Fallback über das nominierte Paar
  if (!pairId) {
    stats.forEach((r) => {
      if (r.type === 'candidate-pair' && r.nominated && r.state === 'succeeded') pairId = r.id;
    });
  }
  const pair = stats.get(pairId);
  if (!pair) return null;
  return {
    local: stats.get(pair.localCandidateId),
    remote: stats.get(pair.remoteCandidateId),
    rtt: pair.currentRoundTripTime,
  };
};

const info = await getSelectedPairFromStats(pc);
console.log(info?.local.candidateType, info?.remote.candidateType, info?.rtt);
relay host 0.042

// Wichtig · Fallstricke

  • Browser-Kompatibilität: In Chromium-basierten Browsern (Chrome, Edge, Opera) und in Safari verfügbar. Firefox stellt RTCIceTransport samt dieser Methode nicht bereit. Deshalb vorher die Unterstützung prüfen (typeof iceTransport?.getSelectedCandidatePair === 'function') oder die Werte über getStats() ermitteln (siehe Beispiel).
  • Kann null liefern: Vor Abschluss der ICE-Prüfungen gibt es kein ausgewähltes Paar. Deshalb erst bei connectionState === 'connected' oder im Ereignis selectedcandidatepairchange abfragen.
  • Zugriff auf den Transport: sender.transport ist null, bis die SDP-Aushandlung abgeschlossen ist. Bei Bundling (Standard) teilen sich alle Sender einen gemeinsamen Transport.
  • Momentaufnahme: Das zurückgegebene Objekt aktualisiert sich nicht. Nach einem Netzwechsel oder restartIce() muss man die Methode erneut aufrufen.
  • Datenschutz: Wegen mDNS-Verschleierung erscheinen lokale Host-Adressen oft als *.local-Hostname statt als echte IP. IP-Adressen gelten als personenbezogen und sollten nicht ungefiltert protokolliert werden.