Was ist Signaling?
Signalisierung ist der Prozess der Koordination der Kommunikation. Damit eine WebRTC-App einen Anruf einrichten kann, müssen ihre Clients die folgenden Informationen austauschen:
- Sitzungssteuerungsnachrichten zum Öffnen oder Schließen der Kommunikation
- Fehlermeldungen
- Medienmetadaten wie Codecs, Codec-Einstellungen, Bandbreite und Medientypen
- Schlüsseldaten, die zum Herstellen sicherer Verbindungen verwendet werden
- Netzwerkdaten wie die IP-Adresse und der Port eines Hosts, wie sie von außen gesehen werden
Für diesen Signalisierungsprozess benötigen Clients eine Möglichkeit, Nachrichten hin und her zu senden. Dieser Mechanismus wird von den WebRTC-APIs nicht implementiert. Sie müssen ihn selbst erstellen. Weiter unten in diesem Artikel erfahren Sie, wie Sie einen Signalisierungsdienst erstellen können. Zuerst benötigen Sie jedoch etwas Kontext.
Warum wird die Signalisierung nicht von WebRTC definiert?
Um Redundanz zu vermeiden und die Kompatibilität mit etablierten Technologien zu maximieren, werden Signalmethoden und ‑protokolle nicht durch WebRTC-Standards festgelegt. Dieser Ansatz wird im JavaScript Session Establishment Protocol (JSEP) beschrieben:
Die Architektur von JSEP verhindert außerdem, dass ein Browser den Status speichern muss, d. h. als Signalisierungszustandsautomat fungieren muss. Das wäre problematisch, wenn beispielsweise jedes Mal, wenn eine Seite neu geladen wird, Signaldaten verloren gingen. Stattdessen kann der Signalstatus auf einem Server gespeichert werden.

JSEP erfordert den Austausch von Angebot und Antwort zwischen Peers, also den oben genannten Medienmetadaten. Angebote und Antworten werden im Session Description Protocol-Format (SDP) übermittelt, das so aussieht:
v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2
…
Möchten Sie wissen, was das alles bedeutet? Sehen Sie sich die Beispiele der Internet Engineering Task Force (IETF) an.
WebRTC ist so konzipiert, dass das Angebot oder die Antwort angepasst werden kann, bevor sie als lokale oder Remote-Beschreibung festgelegt wird. Dazu müssen die Werte im SDP-Text bearbeitet werden. Mit der Funktion preferAudioCodec()
in appr.tc können Sie beispielsweise den Standardcodec und die Standardbitrate festlegen. SDP ist mit JavaScript etwas umständlich zu bearbeiten. Es wird diskutiert, ob in zukünftigen Versionen von WebRTC stattdessen JSON verwendet werden sollte. Es gibt jedoch einige Vorteile, bei SDP zu bleiben.
RTCPeerConnection
API und Signalisierung: Angebot, Antwort und Kandidat
RTCPeerConnection
ist die API, die von WebRTC-Apps verwendet wird, um eine Verbindung zwischen Peers herzustellen und Audio- und Videodaten zu übertragen.
Zur Initialisierung dieses Prozesses hat RTCPeerConnection
zwei Aufgaben:
- Stellen Sie die lokalen Medienbedingungen fest, z. B. Auflösung und Codec-Funktionen. Dies sind die Metadaten, die für den Offer-and-Answer-Mechanismus verwendet werden.
- Rufen Sie potenzielle Netzwerkadressen für den Host der App ab, die als Kandidaten bezeichnet werden.
Sobald diese lokalen Daten ermittelt wurden, müssen sie über einen Signalisierungsmechanismus mit dem Remote-Peer ausgetauscht werden.
Stellen Sie sich vor, Alice versucht, Eve anzurufen. Hier ist der vollständige Offer/Answer-Mechanismus in allen seinen Details:
- Anne erstellt ein
RTCPeerConnection
-Objekt. - Anne erstellt mit der Methode
RTCPeerConnection
createOffer()
ein Angebot (eine SDP-Sitzungsbeschreibung). - Anne ruft
setLocalDescription()
mit ihrem Angebot an. - Alice wandelt das Angebot in einen String um und sendet es über einen Signalisierungsmechanismus an Eve.
- Eve ruft
setRemoteDescription()
mit dem Angebot von Alice an, damit ihrRTCPeerConnection
über die Einrichtung von Alice informiert ist. - Eve ruft
createAnswer()
auf und der Erfolgs-Callback dafür erhält eine lokale Sitzungsbeschreibung – Eves Antwort. - Eve legt ihre Antwort als lokale Beschreibung fest, indem sie
setLocalDescription()
aufruft. - Eve verwendet dann den Signalisierungsmechanismus, um ihre in einen String umgewandelte Antwort an Alice zu senden.
- Alice legt die Antwort von Eve mit
setRemoteDescription()
als Beschreibung der Remotesitzung fest.
Außerdem müssen Alice und Eve Netzwerkinformationen austauschen. Der Begriff „Kandidaten für Ergebnisse“ bezieht sich auf den Prozess der Suche nach Netzwerkschnittstellen und Ports mithilfe des ICE-Frameworks.
- Anne erstellt ein
RTCPeerConnection
-Objekt mit einemonicecandidate
-Handler. - Der Handler wird aufgerufen, wenn Netzwerkkandidaten verfügbar werden.
- Im Handler sendet Alice stringifizierte Kandidatendaten über den Signalisierungskanal an Eve.
- Wenn Eve eine Kandidatennachricht von Alice erhält, ruft sie
addIceCandidate()
auf, um den Kandidaten der Beschreibung des Remote-Peers hinzuzufügen.
JSEP unterstützt ICE Candidate Trickling. Dadurch kann der Anrufer dem Angerufenen nach dem ersten Angebot inkrementell Kandidaten zur Verfügung stellen und der Angerufene kann mit dem Anruf beginnen und eine Verbindung herstellen, ohne auf alle Kandidaten warten zu müssen.
WebRTC für die Signalisierung codieren
Das folgende Code-Snippet ist ein W3C-Codebeispiel, das den gesamten Signalisierungsprozess zusammenfasst. Im Code wird das Vorhandensein eines Signalisierungsmechanismus, SignalingChannel
, vorausgesetzt. Das Signaling wird später noch genauer erläutert.
// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);
// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});
// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
// send the offer to the other peer
signaling.send({desc: pc.localDescription});
} catch (err) {
console.error(err);
}
};
// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
// Don't set srcObject again if it is already set.
if (remoteView.srcObject) return;
remoteView.srcObject = event.streams[0];
};
// Call start() to initiate.
async function start() {
try {
// Get local stream, show it in self-view, and add it to be sent.
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
selfView.srcObject = stream;
} catch (err) {
console.error(err);
}
}
signaling.onmessage = async ({desc, candidate}) => {
try {
if (desc) {
// If you get an offer, you need to reply with an answer.
if (desc.type === 'offer') {
await pc.setRemoteDescription(desc);
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
await pc.setLocalDescription(await pc.createAnswer());
signaling.send({desc: pc.localDescription});
} else if (desc.type === 'answer') {
await pc.setRemoteDescription(desc);
} else {
console.log('Unsupported SDP type.');
}
} else if (candidate) {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
};
Um die Prozesse für Angebot/Antwort und Kandidatenaustausch in Aktion zu sehen, rufen Sie simpl.info RTCPeerConnection auf und sehen Sie sich das Konsolenprotokoll für ein Beispiel für einen Video-Chat auf einer einzelnen Seite an. Wenn Sie mehr Informationen benötigen, können Sie einen vollständigen Dump der WebRTC-Signalisierung und -Statistiken von der Seite „about://webrtc-internals“ in Google Chrome oder „opera://webrtc-internals“ in Opera herunterladen.
Peer-Erkennung
Das ist eine elegante Art zu fragen: „Wie finde ich jemanden, mit dem ich sprechen kann?“
Für Telefonanrufe gibt es Telefonnummern und Telefonbücher. Für Online-Videochats und ‑Nachrichten benötigen Sie Systeme zur Identitäts- und Präsenzverwaltung sowie eine Möglichkeit für Nutzer, Sitzungen zu starten. WebRTC-Apps benötigen eine Möglichkeit für Clients, sich gegenseitig zu signalisieren, dass sie einen Anruf starten oder daran teilnehmen möchten.
Die Mechanismen zur Peer-Erkennung werden nicht von WebRTC definiert und die Optionen werden hier nicht behandelt. Das kann so einfach sein wie das Senden einer URL per E-Mail oder Nachricht. Bei Videochat-Apps wie Talky, tawk.to und Browser Meeting laden Sie Personen zu einem Anruf ein, indem Sie einen benutzerdefinierten Link teilen. Der Entwickler Chris Ball hat ein interessantes serverless-webrtc-Experiment entwickelt, mit dem WebRTC-Anrufteilnehmer Metadaten über einen beliebigen Messaging-Dienst wie Instant Messaging, E-Mail oder Brieftaube austauschen können.
Wie kann ich einen Signalisierungsdienst erstellen?
Nochmal zur Erinnerung: Signalprotokolle und ‑mechanismen sind nicht durch WebRTC-Standards definiert. Unabhängig davon, für welche Option Sie sich entscheiden, benötigen Sie einen Vermittlungsserver, um Signalisierungsnachrichten und App-Daten zwischen Clients auszutauschen. Leider kann eine Web-App nicht einfach ins Internet rufen: „Verbinde mich mit meinem Freund!“
Glücklicherweise sind Signalisierungsnachrichten klein und werden hauptsächlich zu Beginn eines Anrufs ausgetauscht. Bei Tests mit appr.tc für eine Videochat-Sitzung wurden insgesamt etwa 30–45 Nachrichten vom Signalisierungsdienst verarbeitet. Die Gesamtgröße aller Nachrichten betrug etwa 10 KB.
WebRTC-Signalisierungsdienste sind nicht nur relativ bandbreitenfreundlich, sondern verbrauchen auch nicht viel Rechenleistung oder Speicher, da sie nur Nachrichten weiterleiten und eine geringe Menge an Sitzungsstatusdaten wie die verbundenen Clients speichern müssen.
Push-Nachrichten vom Server an den Client senden
Ein Nachrichtendienst für die Signalisierung muss bidirektional sein: Client zu Server und Server zu Client. Die bidirektionale Kommunikation widerspricht dem HTTP-Client-/Server-Anfrage-/Antwortmodell. Im Laufe der Jahre wurden jedoch verschiedene Hacks wie Long Polling entwickelt, um Daten von einem Dienst, der auf einem Webserver ausgeführt wird, an eine Web-App zu senden, die in einem Browser ausgeführt wird.
In letzter Zeit wurde die EventSource
API weitgehend implementiert. Dadurch werden serverseitig gesendete Ereignisse ermöglicht – Daten, die über HTTP von einem Webserver an einen Browserclient gesendet werden. EventSource
ist für die Einwegkommunikation konzipiert, kann aber in Kombination mit XHR verwendet werden, um einen Dienst für den Austausch von Signalisierungsnachrichten zu erstellen. Ein Signalisierungsdienst leitet eine Nachricht von einem Anrufer, die per XHR-Anfrage gesendet wurde, über EventSource
an den Empfänger weiter.
WebSocket ist eine natürlichere Lösung, die für die Vollduplex-Client-Server-Kommunikation entwickelt wurde. Nachrichten können also gleichzeitig in beide Richtungen fließen. Ein Vorteil eines Signalisierungsdiensts, der mit reinen WebSockets oder Server-Sent Events (EventSource
) erstellt wurde, besteht darin, dass das Backend für diese APIs in einer Vielzahl von Webframeworks implementiert werden kann, die für die meisten Webhosting-Pakete für Sprachen wie PHP, Python und Ruby üblich sind.
Alle modernen Browser mit Ausnahme von Opera Mini unterstützen WebSocket. Wichtiger ist jedoch, dass alle Browser, die WebRTC unterstützen, auch WebSocket unterstützen, sowohl auf dem Computer als auch auf Mobilgeräten. TLS sollte für alle Verbindungen verwendet werden, damit Nachrichten nicht unverschlüsselt abgefangen werden können und Probleme mit der Proxy-Übertragung reduziert werden. Weitere Informationen zu WebSocket und Proxy-Traversal finden Sie im WebRTC-Kapitel in High Performance Browser Networking von Ilya Grigorik.
Es ist auch möglich, die Signalisierung zu verarbeiten, indem WebRTC-Clients einen Messaging-Server wiederholt über Ajax abfragen. Dies führt jedoch zu vielen redundanten Netzwerkanfragen, was insbesondere bei Mobilgeräten problematisch ist. Auch nach dem Aufbau einer Sitzung müssen Peers nach Signalisierungsnachrichten suchen, falls andere Peers Änderungen vornehmen oder die Sitzung beenden. Im App-Beispiel WebRTC Book wird diese Option mit einigen Optimierungen für die Abfragehäufigkeit verwendet.
Skalierungssignalisierung
Obwohl ein Signalisierungsdienst pro Client relativ wenig Bandbreite und CPU-Leistung benötigt, müssen Signalisierungsserver für eine beliebte App möglicherweise viele Nachrichten von verschiedenen Standorten mit hoher Parallelität verarbeiten. WebRTC-Apps mit hohem Traffic benötigen Signalisierungsserver, die eine erhebliche Last bewältigen können. Sie gehen hier nicht ins Detail, aber es gibt eine Reihe von Optionen für Messaging mit hohem Volumen und hoher Leistung, darunter die folgenden:
eXtensible Messaging and Presence Protocol (XMPP), ursprünglich als Jabber bekannt, ist ein Protokoll, das für Instant Messaging entwickelt wurde und für die Signalisierung verwendet werden kann. Zu den Serverimplementierungen gehören ejabberd und Openfire. JavaScript-Clients wie Strophe.js verwenden BOSH, um bidirektionales Streaming zu emulieren. Aus verschiedenen Gründen ist BOSH jedoch möglicherweise nicht so effizient wie WebSocket und lässt sich aus denselben Gründen möglicherweise nicht gut skalieren.) Jingle ist eine XMPP-Erweiterung, mit der Sprach- und Videoanrufe möglich sind. Das WebRTC-Projekt verwendet Netzwerk- und Transportkomponenten aus der libjingle-Bibliothek, einer C++-Implementierung von Jingle.
Open-Source-Bibliotheken wie ZeroMQ (wird von TokBox für den Rumour-Dienst verwendet) und OpenMQ (NullMQ wendet ZeroMQ-Konzepte auf Webplattformen an, indem das STOMP-Protokoll über WebSocket verwendet wird).
Kommerzielle Cloud-Messaging-Plattformen, die WebSocket verwenden (obwohl sie möglicherweise auf Long Polling zurückgreifen), z. B. Pusher, Kaazing und PubNub (PubNub hat auch eine API für WebRTC).
Kommerzielle WebRTC-Plattformen wie vLine
Eine umfassende Liste von Messaging-Diensten und ‑Bibliotheken finden Sie im Real-Time Web Technologies Guide des Entwicklers Phil Leggetter.
Signalisierungsdienst mit Socket.io auf Node erstellen
Im Folgenden finden Sie den Code für eine einfache Webanwendung, die einen mit Socket.io auf Node erstellten Signalisierungsdienst verwendet. Socket.io ist so konzipiert, dass sich damit ganz einfach ein Dienst zum Austausch von Nachrichten erstellen lässt. Aufgrund des integrierten Konzepts von Räumen eignet sich Socket.io besonders gut für die WebRTC-Signalisierung. Dieses Beispiel ist nicht für die Skalierung als Signalisierungsdienst in der Produktion konzipiert, sondern für eine relativ kleine Anzahl von Nutzern leicht verständlich.
Socket.io verwendet WebSocket mit Fallbacks: AJAX-Long-Polling, AJAX-Multipart-Streaming, Forever Iframe und JSONP-Polling. Es wurde auf verschiedene Back-Ends portiert, ist aber vielleicht am besten für seine Node-Version bekannt, die in diesem Beispiel verwendet wird.
In diesem Beispiel wird WebRTC nicht verwendet. Es soll nur zeigen, wie die Signalisierung in eine Web-App eingebaut wird. Im Konsolenlog sehen Sie, was passiert, wenn Clients einem Raum beitreten und Nachrichten austauschen. In diesem WebRTC-Codelab finden Sie eine Schritt-für-Schritt-Anleitung zur Integration in eine vollständige WebRTC-Videochat-App.
Hier ist der Client index.html
:
<!DOCTYPE html>
<html>
<head>
<title>WebRTC client</title>
</head>
<body>
<script src='/socket.io/socket.io.js'></script>
<script src='js/main.js'></script>
</body>
</html>
Hier ist die JavaScript-Datei main.js
, auf die im Client verwiesen wird:
const isInitiator;
room = prompt('Enter room name:');
const socket = io.connect();
if (room !== '') {
console.log('Joining room ' + room);
socket.emit('create or join', room);
}
socket.on('full', (room) => {
console.log('Room ' + room + ' is full');
});
socket.on('empty', (room) => {
isInitiator = true;
console.log('Room ' + room + ' is empty');
});
socket.on('join', (room) => {
console.log('Making request to join room ' + room);
console.log('You are the initiator!');
});
socket.on('log', (array) => {
console.log.apply(console, array);
});
Hier ist die vollständige Server-App:
const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
file.serve(req, res);
}).listen(2013);
const io = require('socket.io').listen(app);
io.sockets.on('connection', (socket) => {
// Convenience function to log server messages to the client
function log(){
const array = ['>>> Message from server: '];
for (const i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', (message) => {
log('Got message:', message);
// For a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', (room) => {
const numClients = io.sockets.clients(room).length;
log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room ' + room);
if (numClients === 0){
socket.join(room);
socket.emit('created', room);
} else if (numClients === 1) {
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room);
} else { // max two clients
socket.emit('full', room);
}
socket.emit('emit(): client ' + socket.id +
' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id +
' joined room ' + room);
});
});
Sie müssen sich damit nicht auseinandersetzen. Sie wird nur in diesem Beispiel verwendet.)
Wenn Sie diese App auf localhost ausführen möchten, müssen Sie Node, Socket.IO und node-static installiert haben. Node kann von Node.js heruntergeladen werden. Die Installation ist einfach und schnell. Wenn Sie Socket.IO und node-static installieren möchten, führen Sie den Node Package Manager über ein Terminal in Ihrem App-Verzeichnis aus:
npm install socket.io
npm install node-static
Führen Sie den folgenden Befehl in einem Terminal in Ihrem App-Verzeichnis aus, um den Server zu starten:
node server.js
Öffnen Sie localhost:2013
in Ihrem Browser. Öffnen Sie in einem beliebigen Browser einen neuen Tab oder ein neues Fenster und rufen Sie localhost:2013
noch einmal auf. Sehen Sie in der Konsole nach, was passiert. In Chrome und Opera können Sie über die Google Chrome-Entwicklertools mit Ctrl+Shift+J
(oder Command+Option+J
auf dem Mac) auf die Konsole zugreifen.
Unabhängig davon, welchen Ansatz Sie für die Signalisierung wählen, müssen Ihr Backend und Ihre Client-App mindestens Dienste wie in diesem Beispiel bereitstellen.
Fallstricke bei der Signalisierung
RTCPeerConnection
beginnt erst mit dem Sammeln von Kandidaten, wennsetLocalDescription()
aufgerufen wird. Dies ist im JSEP-IETF-Entwurf vorgeschrieben.- Trickle ICE nutzen Rufen Sie
addIceCandidate()
an, sobald die Kandidaten eintreffen.
Sofort einsatzbereite Signalserver
Wenn Sie nicht selbst einen entwickeln möchten, sind mehrere WebRTC-Signalisierungsserver verfügbar, die wie im vorherigen Beispiel Socket.IO verwenden und in WebRTC-Client-JavaScript-Bibliotheken integriert sind:
- webRTC.io ist eine der ersten Abstraktionsbibliotheken für WebRTC.
- Signalmaster ist ein Signalisierungsserver, der für die Verwendung mit der SimpleWebRTC-JavaScript-Clientbibliothek erstellt wurde.
Wenn Sie überhaupt keinen Code schreiben möchten, sind vollständige kommerzielle WebRTC-Plattformen von Unternehmen wie vLine, OpenTok und Asterisk verfügbar.
Ericsson hat in den frühen Tagen von WebRTC einen Signalisierungsserver mit PHP auf Apache erstellt. Das ist jetzt etwas veraltet, aber es lohnt sich, den Code anzusehen, wenn Sie etwas Ähnliches in Erwägung ziehen.
Signalisierungssicherheit
„Sicherheit ist die Kunst, dafür zu sorgen, dass nichts passiert.“
Salman Rushdie
Die Verschlüsselung ist für alle WebRTC-Komponenten erforderlich.
Signalisierungsmechanismen sind jedoch nicht durch WebRTC-Standards definiert. Es liegt also an Ihnen, die Signalisierung zu sichern. Wenn es einem Angreifer gelingt, die Signalisierung zu manipulieren, kann er Sitzungen beenden, Verbindungen umleiten und Inhalte aufzeichnen, ändern oder einfügen.
Der wichtigste Faktor für die Sicherung der Signalisierung ist die Verwendung sicherer Protokolle wie HTTPS und WSS (z. B. TLS), die dafür sorgen, dass Nachrichten nicht unverschlüsselt abgefangen werden können. Achten Sie außerdem darauf, dass Sie keine Signalisierungsnachrichten so senden, dass andere Anrufer, die denselben Signalisierungsserver verwenden, darauf zugreifen können.
Nach der Signalisierung: ICE verwenden, um NATs und Firewalls zu umgehen
Für die Metadatensignalisierung verwenden WebRTC-Apps einen Vermittlungsserver. Für das eigentliche Streamen von Medien und Daten versucht RTCPeerConnection
jedoch, Clients direkt oder Peer-to-Peer zu verbinden, sobald eine Sitzung eingerichtet ist.
In einer einfacheren Welt hätte jeder WebRTC-Endpunkt eine eindeutige Adresse, die er mit anderen Peers austauschen könnte, um direkt zu kommunizieren.

In der Realität befinden sich die meisten Geräte hinter einer oder mehreren NAT, einige haben Antivirensoftware, die bestimmte Ports und Protokolle blockiert, und viele befinden sich hinter Proxys und Unternehmensfirewalls. Eine Firewall und NAT können tatsächlich vom selben Gerät implementiert werden, z. B. von einem WLAN-Router für zu Hause.

WebRTC-Apps können das ICE-Framework verwenden, um die Komplexität der realen Vernetzung zu bewältigen. Dazu muss Ihre App ICE-Server-URLs an RTCPeerConnection
übergeben, wie in diesem Artikel beschrieben.
ICE versucht, den besten Pfad für die Verbindung von Peers zu finden. Es werden alle Möglichkeiten parallel ausprobiert und die effizienteste Option ausgewählt, die funktioniert. ICE versucht zuerst, eine Verbindung über die Hostadresse herzustellen, die vom Betriebssystem und der Netzwerkkarte eines Geräts abgerufen wird. Wenn das fehlschlägt (was bei Geräten hinter NATs der Fall ist), ruft ICE eine externe Adresse über einen STUN-Server ab. Wenn auch das fehlschlägt, wird der Traffic über einen TURN-Relay-Server weitergeleitet.
Mit anderen Worten: Ein STUN-Server wird verwendet, um eine externe Netzwerkadresse zu erhalten, und TURN-Server werden verwendet, um Traffic weiterzuleiten, wenn eine direkte (Peer-to-Peer-)Verbindung fehlschlägt.
Jeder TURN-Server unterstützt STUN. Ein TURN-Server ist ein STUN-Server mit zusätzlicher integrierter Relay-Funktionalität. ICE kommt auch mit den Komplexitäten von NAT-Einrichtungen zurecht. In der Realität kann es sein, dass für NAT-Hole-Punching mehr als nur eine öffentliche IP-Adresse und ein Port erforderlich sind.
URLs für STUN- und/oder TURN-Server werden (optional) von einer WebRTC-App im iceServers
-Konfigurationsobjekt angegeben, das das erste Argument für den RTCPeerConnection
-Konstruktor ist. Für appr.tc sieht der Wert so aus:
{
'iceServers': [
{
'urls': 'stun:stun.l.google.com:19302'
},
{
'urls': 'turn:192.158.29.39:3478?transport=udp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
},
{
'urls': 'turn:192.158.29.39:3478?transport=tcp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
}
]
}
Sobald RTCPeerConnection
diese Informationen hat, wird die ICE-Magie automatisch ausgelöst. RTCPeerConnection
verwendet das ICE-Framework, um den besten Pfad zwischen Peers zu ermitteln, und arbeitet bei Bedarf mit STUN- und TURN-Servern zusammen.
STUN
NATs stellen einem Gerät eine IP-Adresse zur Verfügung, die in einem privaten lokalen Netzwerk verwendet werden kann. Diese Adresse kann jedoch nicht extern verwendet werden. Ohne öffentliche Adresse können WebRTC-Peers nicht kommunizieren. Um dieses Problem zu umgehen, verwendet WebRTC STUN.
STUN-Server befinden sich im öffentlichen Internet und haben eine einfache Aufgabe:Sie prüfen die IP‑Adresse und den Port einer eingehenden Anfrage (von einer App, die hinter einem NAT ausgeführt wird) und senden diese Adresse als Antwort zurück. Mit anderen Worten:Die App verwendet einen STUN-Server, um ihre IP-Adresse und ihren Port aus öffentlicher Sicht zu ermitteln. So kann ein WebRTC-Peer eine öffentlich zugängliche Adresse für sich selbst abrufen und sie dann über einen Signalisierungsmechanismus an einen anderen Peer übergeben, um eine direkte Verbindung herzustellen. In der Praxis funktionieren verschiedene NATs unterschiedlich und es kann mehrere NAT-Ebenen geben, aber das Prinzip ist dasselbe.
STUN-Server müssen nicht viel tun oder sich viel merken. Daher können STUN-Server mit relativ niedrigen Spezifikationen eine große Anzahl von Anfragen verarbeiten.
Bei den meisten WebRTC-Anrufen wird eine Verbindung über STUN hergestellt – laut Webrtcstats.com in 86% der Fälle. Bei Anrufen zwischen Peers hinter Firewalls und komplexen NAT-Konfigurationen kann dieser Wert jedoch niedriger sein.

TURN
RTCPeerConnection
versucht, eine direkte Kommunikation zwischen Peers über UDP einzurichten. Wenn das fehlschlägt, greift RTCPeerConnection
auf TCP zurück. Wenn das nicht funktioniert, können TURN-Server als Fallback verwendet werden, um Daten zwischen Endpunkten weiterzuleiten.
Zur Erinnerung: TURN wird verwendet, um Audio-, Video- und Datenstreams zwischen Peers weiterzuleiten, nicht für Signalisierungsdaten.
TURN-Server haben öffentliche Adressen, sodass sie von Peers kontaktiert werden können, auch wenn sich die Peers hinter Firewalls oder Proxys befinden. TURN-Server haben eine konzeptionell einfache Aufgabe: Sie leiten einen Stream weiter. Im Gegensatz zu STUN-Servern verbrauchen sie jedoch von Natur aus viel Bandbreite. TURN-Server müssen also leistungsfähiger sein.

Dieses Diagramm zeigt TURN in Aktion. Pure STUN hat nicht funktioniert, daher greift jeder Peer auf einen TURN-Server zurück.
STUN- und TURN-Server bereitstellen
Für Tests betreibt Google einen öffentlichen STUN-Server, stun.l.google.com:19302, der von appr.tc verwendet wird. Verwenden Sie für einen STUN/TURN-Produktionsdienst den rfc5766-turn-Server. Der Quellcode für STUN- und TURN-Server ist auf GitHub verfügbar. Dort finden Sie auch Links zu verschiedenen Informationsquellen zur Serverinstallation. Ein VM-Image für Amazon Web Services ist ebenfalls verfügbar.
Ein alternativer TURN-Server ist restund, der als Quellcode und auch für AWS verfügbar ist. Hier finden Sie eine Anleitung zum Einrichten von restund in Compute Engine.
- Öffnen Sie die Firewall nach Bedarf für tcp=443 und udp/tcp=3478.
- Erstellen Sie vier Instanzen, eine für jede öffentliche IP-Adresse, mit dem Standard-Ubuntu 12.06-Image.
- Richten Sie die lokale Firewallkonfiguration ein (ALLE von BELIEBIG zulassen).
- Tools installieren:
shell sudo apt-get install make sudo apt-get install gcc
- Installieren Sie libre über creytiv.com/re.html.
- Rufe den Restund von creytiv.com/restund.html ab und entpacke ihn./
wget
hancke.name/restund-auth.patch und mitpatch -p1 < restund-auth.patch
anwenden.- Führen Sie
make
undsudo make install
für libre und restund aus. - Passen Sie
restund.conf
an Ihre Anforderungen an (ersetzen Sie IP-Adressen und achten Sie darauf, dass es dasselbe gemeinsame Secret enthält) und kopieren Sie es nach/etc
. - Kopieren Sie
restund/etc/restund
nach/etc/init.d/
. - „restund“ konfigurieren:
- Legen Sie dazu
LD_LIBRARY_PATH
fest. - Kopieren Sie
restund.conf
nach/etc/restund.conf
. - Legen Sie
restund.conf
fest, um die richtige 10 zu verwenden. IP-Adresse
- Legen Sie dazu
- restund ausführen
- Mit dem stund-Client von einem Remotecomputer aus testen:
./client IP:port
Über die Eins-zu-eins-Kommunikation hinaus: WebRTC für mehrere Teilnehmer
Möglicherweise möchten Sie sich auch den von Justin Uberti vorgeschlagenen IETF-Standard für eine REST API für den Zugriff auf TURN-Dienste ansehen.
Es gibt viele Anwendungsfälle für Media-Streaming, die über einen einfachen Anruf zwischen zwei Personen hinausgehen. Das kann z. B. eine Videokonferenz zwischen einer Gruppe von Kollegen oder eine öffentliche Veranstaltung mit einem Redner und Hunderten oder Millionen von Zuschauern sein.
Eine WebRTC-App kann mehrere RTCPeerConnections verwenden, sodass jeder Endpunkt in einer Mesh-Konfiguration eine Verbindung zu jedem anderen Endpunkt herstellt. Dieser Ansatz wird von Apps wie talky.io verwendet und funktioniert für eine kleine Anzahl von Teilnehmern sehr gut. Darüber hinaus werden die Verarbeitung und der Bandbreitenverbrauch übermäßig hoch, insbesondere für mobile Clients.

Alternativ kann eine WebRTC-App einen Endpunkt auswählen, um Streams in einer Sternkonfiguration an alle anderen zu verteilen. Es wäre auch möglich, einen WebRTC-Endpunkt auf einem Server auszuführen und einen eigenen Mechanismus für die Weiterleitung zu erstellen (eine Beispiel-Client-App wird von webrtc.org bereitgestellt).
Seit Chrome 31 und Opera 18 kann ein MediaStream
aus einem RTCPeerConnection
als Eingabe für ein anderes verwendet werden. Dies kann flexiblere Architekturen ermöglichen, da eine Web-App das Anruf-Routing übernehmen kann, indem sie auswählt, mit welchem anderen Peer sie sich verbinden soll. Beispiele finden Sie unter WebRTC-Beispiele: Peer-Verbindungs-Relay und WebRTC-Beispiele: Mehrere Peer-Verbindungen.
Multipoint Control Unit
Bei einer großen Anzahl von Endpunkten ist es besser, eine Multipoint Control Unit (MCU) zu verwenden. Dies ist ein Server, der als Brücke dient, um Medien an eine große Anzahl von Teilnehmern zu verteilen. MCUs können unterschiedliche Auflösungen, Codecs und Frameraten in einer Videokonferenz verarbeiten, Transcodierung durchführen, Streams selektiv weiterleiten und Audio und Video mischen oder aufzeichnen. Bei Anrufen mit mehreren Teilnehmern sind einige Probleme zu berücksichtigen, insbesondere wie mehrere Videoeingaben angezeigt und Audio aus mehreren Quellen gemischt werden soll. Cloud-Plattformen wie vLine versuchen ebenfalls, das Traffic-Routing zu optimieren.
Sie können ein komplettes MCU-Hardwarepaket kaufen oder Ihr eigenes zusammenstellen.

Es sind mehrere Open-Source-Optionen für die MCU-Software verfügbar. Licode (früher Lynckia) bietet beispielsweise eine Open-Source-MCU für WebRTC. OpenTok hat Mantis.
Über Browser hinaus: VoIP, Telefone und Messaging
Durch die Standardisierung von WebRTC ist es möglich, die Kommunikation zwischen einer WebRTC-App, die in einem Browser ausgeführt wird, und einem Gerät oder einer Plattform herzustellen, die auf einer anderen Kommunikationsplattform wie einem Telefon oder einem Videokonferenzsystem ausgeführt wird.
SIP ist ein Signalisierungsprotokoll, das von VoIP- und Videokonferenzsystemen verwendet wird. Damit die Kommunikation zwischen einer WebRTC-Web-App und einem SIP-Client, z. B. einem Videokonferenzsystem, möglich ist, benötigt WebRTC einen Proxyserver, der die Signalisierung übernimmt. Die Signalisierung muss über das Gateway erfolgen. Sobald die Kommunikation hergestellt wurde, kann der SRTP-Traffic (Video und Audio) jedoch direkt zwischen den Peers übertragen werden.
Das öffentliche Fernsprechnetz (Public Switched Telephone Network, PSTN) ist das leitungsvermittelte Netzwerk aller analogen Standardtelefone. Bei Anrufen zwischen WebRTC-Web-Apps und Telefonen muss der Traffic über ein PSTN-Gateway geleitet werden. WebRTC-Web-Apps benötigen ebenfalls einen zwischengeschalteten XMPP-Server, um mit Jingle-Endpunkten wie IM-Clients zu kommunizieren. Jingle wurde von Google als Erweiterung von XMPP entwickelt, um Sprach- und Videofunktionen für Messaging-Dienste zu ermöglichen. Aktuelle WebRTC-Implementierungen basieren auf der C++-Bibliothek libjingle, einer Implementierung von Jingle, die ursprünglich für Talk entwickelt wurde.
Eine Reihe von Apps, Bibliotheken und Plattformen nutzen die Möglichkeit von WebRTC, mit der Außenwelt zu kommunizieren:
- sipML5: ein Open-Source-JavaScript-SIP-Client
- jsSIP: JavaScript SIP-Bibliothek
- Phono: Open-Source-JavaScript-Telefon-API, die als Plug-in entwickelt wurde
- Zingaya: ein einbettbares Telefon-Widget
- Twilio: Sprach- und Messaging-Dienste
- Uberconference: Konferenzen
Die sipML5-Entwickler haben auch das webrtc2sip-Gateway entwickelt. Tethr und Tropo haben ein Framework für die Katastrophenkommunikation „im Koffer“ demonstriert, das eine OpenBTS-Zelle verwendet, um die Kommunikation zwischen einfachen Mobiltelefonen und Computern über WebRTC zu ermöglichen. Das ist Telefonkommunikation ohne Mobilfunkanbieter.
Weitere Informationen
Im WebRTC-Codelab finden Sie eine Schritt-für-Schritt-Anleitung zum Erstellen einer Video- und Text-Chat-App mit einem Socket.io-Signalisierungsdienst, der auf Node ausgeführt wird.
Google I/O-WebRTC-Präsentation von 2013 mit Justin Uberti, dem technischen Leiter von WebRTC
SFHTML5-Präsentation von Chris Wilson: Introduction to WebRTC Apps
Das 350 Seiten umfassende Buch WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web enthält viele Details zu Daten- und Signalwegen sowie eine Reihe detaillierter Diagramme zur Netzwerktopologie.
WebRTC and Signaling: What Two Years Has Taught Us – TokBox-Blogpost darüber, warum es eine gute Idee war, die Signalisierung aus der Spezifikation herauszulassen
Ben Strongs A Practical Guide to Building WebRTC Apps enthält viele Informationen zu WebRTC-Topologien und ‑Infrastruktur.
Das WebRTC-Kapitel in Ilya Grigoriks High Performance Browser Networking befasst sich ausführlich mit der WebRTC-Architektur, Anwendungsfällen und Leistung.