Signatur
Beschreibung
Die Eigenschaft candidate eines RTCPeerConnectionIceEvent enthält den ICE-Kandidaten, den die lokale RTCPeerConnection gerade gefunden hat. Ein ICE-Kandidat beschreibt einen möglichen Netzwerkweg (Host-Adresse, über STUN ermittelte öffentliche Adresse oder TURN-Relay), über den der entfernte Partner die Verbindung aufbauen kann.
Man wertet die Eigenschaft fast immer im Handler des Ereignisses icecandidate aus. Jeder gefundene Kandidat muss über den eigenen Signalisierungskanal (z. B. WebSocket) an die Gegenseite geschickt werden. Dort wird er mit RTCPeerConnection.addIceCandidate() hinzugefügt. Dieses Vorgehen heißt Trickle ICE: Kandidaten werden verschickt, sobald sie gefunden sind, und nicht erst nach Abschluss der Suche.
Ist candidate gleich null, hat der Browser die Kandidatensuche (ICE Gathering) abgeschlossen. Dieses Signal kann man an die Gegenseite weitergeben, damit sie ebenfalls weiß, dass keine Kandidaten mehr kommen. Außerdem gibt es in manchen Implementierungen am Ende einer Generation einen Kandidaten mit leerem candidate-String. Er bedeutet „Ende der Kandidaten“ für diese Generation und sollte wie ein normaler Kandidat weitergeleitet werden.
Rückgabewert
RTCIceCandidate-Objekt mit dem gefundenen Kandidaten oder null, wenn die Kandidatensuche abgeschlossen ist.Beispiele
Kandidaten per WebSocket an die Gegenseite senden (addEventListener)
const signaling = new WebSocket('wss://signal.example.com');
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
pc.addEventListener('icecandidate', (event) => {
if (event.candidate) {
// toJSON() liefert candidate, sdpMid, sdpMLineIndex, usernameFragment
signaling.send(JSON.stringify({ type: 'candidate', candidate: event.candidate.toJSON() }));
} else {
// null: Kandidatensuche beendet
signaling.send(JSON.stringify({ type: 'end-of-candidates' }));
}
});
pc.createDataChannel('chat');
const offer = await pc.createOffer();
await pc.setLocalDescription(offer); // startet die Kandidatensuche
Kandidaten auf der Gegenseite hinzufügen
signaling.addEventListener('message', async ({ data }) => {
const msg = JSON.parse(data);
if (msg.type === 'candidate') {
try {
await pc.addIceCandidate(msg.candidate);
} catch (err) {
console.error('Kandidat abgelehnt:', err);
}
} else if (msg.type === 'end-of-candidates') {
// Ohne Argument signalisiert addIceCandidate das Ende
await pc.addIceCandidate();
}
});
Kandidatentypen protokollieren (onicecandidate-Property)
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
pc.onicecandidate = ({ candidate }) => {
if (!candidate) {
console.log('Suche abgeschlossen');
return;
}
console.log(candidate.type, candidate.protocol, candidate.address, candidate.port);
};
pc.createDataChannel('test');
await pc.setLocalDescription(await pc.createOffer());
// Wichtig · Fallstricke
- Immer auf
nullprüfen: Das letzteicecandidate-Ereignis liefertcandidate === null. Wer ohne Prüfungevent.candidate.toJSON()aufruft, bekommt einenTypeError. - Beim Versand über JSON lieber
candidate.toJSON()verwenden (oderJSON.stringifydirekt), damitsdpMidundsdpMLineIndexerhalten bleiben. Nur dencandidate-String zu schicken reicht nicht. - Kandidaten, die auf der Gegenseite ankommen, bevor
setRemoteDescription()aufgerufen wurde, lösen beiaddIceCandidate()einen Fehler aus. Solche Kandidaten sollte man puffern und erst danach hinzufügen. - Datenschutz: Kandidaten können lokale und öffentliche IP-Adressen verraten. Moderne Browser verschleiern Host-Adressen deshalb mit mDNS-Namen (
*.local). Die Eigenschaftaddressist also nicht immer eine echte IP. - Die Eigenschaft ist schreibgeschützt. Eigene Ereignisse lassen sich mit
new RTCPeerConnectionIceEvent('icecandidate', { candidate })erzeugen. - Wird in allen aktuellen Browsern unterstützt. Ältere Implementierungen (vor 2018) lösten das Ende der Suche teils nur über
icegatheringstatechangeaus.