Signatur
Beschreibung
Das Ereignis datachannel vom Typ RTCDataChannelEvent wird auf der empfangenden Seite einer WebRTC-Verbindung ausgelöst, sobald der entfernte Peer einen Datenkanal per RTCPeerConnection.createDataChannel() angelegt hat und dieser ausgehandelt wurde. Über die Eigenschaft event.channel erhält man das zugehörige RTCDataChannel-Objekt, mit dem man anschließend Nachrichten senden und empfangen kann.
Einen Handler registriert man entweder mit pc.addEventListener("datachannel", handler) oder über die Eigenschaft pc.ondatachannel = handler. Das Ereignis lässt sich nicht abbrechen und steigt nicht auf (kein Bubbling).
Wichtig: Die Seite, die createDataChannel() selbst aufruft, erhält kein datachannel-Ereignis – sie besitzt den Kanal bereits als Rückgabewert. Auch bei ausgehandelten Kanälen (negotiated: true mit fester id) wird das Ereignis nicht ausgelöst, da beide Seiten den Kanal selbst erzeugen.
Typische Einsatzgebiete sind Peer-to-Peer-Chats, Dateiübertragung, Synchronisation von Spielzuständen oder kollaborativen Editoren mit niedriger Latenz.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "datachannel". |
|
| $handler Pflicht | (event: RTCDataChannelEvent) => void | Callback, der ein RTCDataChannelEvent erhält. Wichtigste Eigenschaft: event.channel (der neue RTCDataChannel). |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Listener-Optionen wie once oder signal (zum Entfernen per AbortController). |
Rückgabewert
addEventListener() gibt nichts zurück. Der Handler erhält ein RTCDataChannelEvent mit der Eigenschaft channel.Beispiele
Eingehenden Datenkanal mit addEventListener empfangen
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
pc.addEventListener('datachannel', (event) => {
const channel = event.channel;
console.log(`Neuer Kanal: ${channel.label}`);
channel.addEventListener('open', () => channel.send('Hallo zurück!'));
channel.addEventListener('message', (e) => {
console.log('Nachricht empfangen:', e.data);
});
channel.addEventListener('close', () => console.log('Kanal geschlossen'));
});
// ... Signalisierung (Offer/Answer, ICE) erfolgt über eigenen Server
Vollständiges lokales Beispiel mit zwei Peers
const caller = new RTCPeerConnection();
const callee = new RTCPeerConnection();
// ICE-Kandidaten direkt austauschen (lokal, ohne Server)
caller.onicecandidate = (e) => e.candidate && callee.addIceCandidate(e.candidate);
callee.onicecandidate = (e) => e.candidate && caller.addIceCandidate(e.candidate);
// Empfängerseite: on-Property statt addEventListener
callee.ondatachannel = ({ channel }) => {
channel.onmessage = (e) => console.log(`[${channel.label}]`, e.data);
};
// Senderseite erzeugt den Kanal
const chat = caller.createDataChannel('chat');
chat.onopen = () => chat.send('Hallo vom Anrufer');
const connect = async () => {
await caller.setLocalDescription(await caller.createOffer());
await callee.setRemoteDescription(caller.localDescription);
await callee.setLocalDescription(await callee.createAnswer());
await caller.setRemoteDescription(callee.localDescription);
};
connect();
Mehrere Kanäle nach Label unterscheiden und Listener entfernen
const pc = new RTCPeerConnection();
const controller = new AbortController();
const handlers = {
chat: (msg) => console.log('Chat:', msg),
file: (chunk) => console.log('Dateiblock, Bytes:', chunk.byteLength)
};
pc.addEventListener('datachannel', ({ channel }) => {
if (channel.label === 'file') channel.binaryType = 'arraybuffer';
const handle = handlers[channel.label];
if (!handle) {
channel.close(); // unbekannte Kanäle ablehnen
return;
}
channel.onmessage = (e) => handle(e.data);
}, { signal: controller.signal });
// Später: keine weiteren Kanäle mehr annehmen
const stopAccepting = () => controller.abort();
// Alternativ bei on-Property: pc.ondatachannel = null;
// Wichtig · Fallstricke
- Nur auf der Gegenseite: Der Peer, der
createDataChannel()aufruft, erhält keindatachannel-Ereignis. Beinegotiated: truewird es auf keiner Seite ausgelöst. - Handler früh registrieren: Den Listener vor
setRemoteDescription()bzw. vor Abschluss der Aushandlung setzen, damit kein Kanal verpasst wird. - Kanal ist evtl. noch nicht offen:
event.channel.readyStatekann"connecting"sein – vor dem erstensend()auf dasopen-Ereignis warten, sonst wirftsend()einenInvalidStateError. - Sicherheit: Empfangene Daten stammen von einem fremden Peer und sind nicht vertrauenswürdig. Niemals ungeprüft per
innerHTMLeinfügen (XSS); JSON-Nachrichten validieren und Größen begrenzen. - Binärdaten: Standardmäßig ist
binaryType"arraybuffer"(in älteren Implementierungen"blob") – bei Bedarf explizit setzen. - Kompatibilität: In allen aktuellen Browsern unterstützt; in Node.js nur über Zusatzbibliotheken (z. B.
wrtc/node-datachannel).