Signatur
Beschreibung
RTCIceCandidateStats ist ein Wörterbuch (Dictionary) der WebRTC-API. Es beschreibt einen einzelnen ICE-Kandidaten, den die Verbindung gesammelt (lokal) oder von der Gegenseite erhalten hat (entfernt). Solche Objekte bekommt man nicht über einen Konstruktor, sondern als Einträge des RTCStatsReport, den RTCPeerConnection.getStats() zurückgibt. Ihr type ist entweder "local-candidate" oder "remote-candidate".
Wichtige Felder sind unter anderem:
address/port: IP-Adresse (oder mDNS-Hostname) und Port des Kandidaten.protocol:"udp"oder"tcp".candidateType:"host","srflx"(über STUN ermittelt),"prflx"(peer-reflexiv) oder"relay"(TURN).priority,foundation,transportId,url(STUN/TURN-Server-URL bei lokalen Kandidaten),relayProtocol(Protokoll zwischen Client und TURN-Server),tcpType,usernameFragment.- Geerbt von
RTCStats:id,timestamp,type.
In der Praxis nutzt man diese Statistiken vor allem zur Fehlersuche und Überwachung: Man findet heraus, ob eine Verbindung direkt (host/srflx) oder über einen TURN-Relay läuft, was sich auf Latenz und Serverkosten auswirkt. Dazu verknüpft man das aktive RTCIceCandidatePairStats (Felder localCandidateId/remoteCandidateId) mit den passenden Kandidaten-Einträgen.
Rückgabewert
RTCStatsReport (einer Map-ähnlichen Struktur), mit type "local-candidate" oder "remote-candidate".Beispiele
Alle lokalen und entfernten Kandidaten auflisten
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
pc.createDataChannel('probe');
await pc.setLocalDescription(await pc.createOffer());
// Kurz warten, bis ICE-Kandidaten gesammelt sind
await new Promise((r) => setTimeout(r, 2000));
const report = await pc.getStats();
report.forEach((stat) => {
if (stat.type === 'local-candidate' || stat.type === 'remote-candidate') {
console.log(stat.type, stat.candidateType, stat.protocol, `${stat.address}:${stat.port}`);
}
});
Prüfen, ob die aktive Verbindung über TURN läuft
const getConnectionType = async (pc) => {
const report = await pc.getStats();
let pair;
report.forEach((s) => {
if (s.type === 'transport' && s.selectedCandidatePairId) {
pair = report.get(s.selectedCandidatePairId);
}
});
// Fallback für Browser ohne selectedCandidatePairId (z. B. Firefox)
if (!pair) {
report.forEach((s) => {
if (s.type === 'candidate-pair' && s.state === 'succeeded' && s.nominated) pair = s;
});
}
if (!pair) return 'unbekannt';
const local = report.get(pair.localCandidateId);
const remote = report.get(pair.remoteCandidateId);
return {
lokal: local?.candidateType,
entfernt: remote?.candidateType,
relay: local?.candidateType === 'relay' || remote?.candidateType === 'relay',
relayProtokoll: local?.relayProtocol
};
};
// Aufruf nach erfolgreichem Verbindungsaufbau
pc.addEventListener('iceconnectionstatechange', async () => {
if (pc.iceConnectionState === 'connected') {
console.log(await getConnectionType(pc));
}
});
Kandidaten nach Typ zählen (Monitoring)
const countCandidates = async (pc) => {
const counts = {};
(await pc.getStats()).forEach((s) => {
if (s.type === 'local-candidate') {
counts[s.candidateType] = (counts[s.candidateType] ?? 0) + 1;
}
});
return counts;
};
console.log(await countCandidates(pc));
// Wichtig · Fallstricke
- Kein Konstruktor:
RTCIceCandidateStatsist ein Wörterbuch, keine globale Klasse – Objekte entstehen nur übergetStats().instanceof-Prüfungen funktionieren nicht; stattdessenstat.typeauswerten. - Datenschutz: Browser verschleiern lokale Host-Adressen oft per mDNS (
xxxx.local).addresskann außerdemnullsein, etwa wenn die Seite keine Mediengerät-Berechtigung hat. IP-Adressen gelten als personenbezogene Daten – beim Versand an Monitoring-Server beachten. - Veraltete Felder: Ältere Implementierungen nutzten
ipstattaddress,deletedundnetworkType; diese wurden aus der Spezifikation entfernt. Für Kompatibilitätstat.address ?? stat.ipverwenden. - Browser-Unterschiede: Die Menge der gelieferten Felder variiert (z. B.
url,relayProtocol,tcpType).selectedCandidatePairIdauf dem Transport-Eintrag fehlt in Firefox – dort übernominated/selecteddes Kandidatenpaars suchen. - Zeitpunkt: Kandidaten tauchen erst nach dem ICE-Gathering im Bericht auf;
getStats()direkt nachcreateOffer()liefert eventuell noch keine Einträge.