Veröffentlicht: 4. November 2013
WebRTC ermöglicht die Peer-to-Peer-Kommunikation. Es sind Server erforderlich, damit Clients Metadaten austauschen und die Kommunikation über einen Prozess namens Signalisierung koordinieren können. Server helfen Clients auch bei der Bewältigung von Network Address Translators (NATs) und Firewalls.
Hier erfahren Sie, wie Sie einen Signalisierungsdienst erstellen und mit den Besonderheiten der realen Konnektivität mit STUN- und TURN-Servern umgehen. Außerdem erfahren Sie, wie WebRTC-Anwendungen Anrufe mit mehreren Teilnehmern verarbeiten und mit Diensten wie VoIP und PSTN (auch als Telefonie bezeichnet) interagieren können.
Was ist Signalisierung?
Signalisierung ist der Prozess der Koordinierung der Kommunikation. Damit eine WebRTC-Anwendung einen Anruf einrichten kann, müssen die Clients die folgenden Informationen austauschen:
- Sitzungssteuerungsnachrichten, die zum Öffnen oder Schließen der Kommunikation verwendet werden
- Fehlermeldungen
- Medienmetadaten wie Codecs, Codec-Einstellungen, Bandbreite und Medientypen
- Schlüsseldaten, die zum Herstellen sicherer Verbindungen verwendet werden
- Netzwerkdaten, z. B. die IP-Adresse und der Port eines Hosts, wie sie von außen sichtbar sind
Für diesen Signalisierungsprozess ist eine Möglichkeit erforderlich, mit der Clients Nachrichten hin- und hersenden können. Dieser Mechanismus wird von den WebRTC APIs nicht implementiert. Stattdessen müssen Sie es selbst erstellen.
Warum wird die Signalisierung nicht von WebRTC definiert?
Um Redundanz zu vermeiden und die Kompatibilität mit etablierten Technologien zu maximieren, werden Signalisierungsmethoden und -protokolle nicht durch WebRTC-Standards festgelegt. Dieser Ansatz wird im JavaScript Session Establishment Protocol (JSEP) beschrieben:
Die Architektur von JSEP verhindert auch, dass ein Browser den Status speichern muss, d. h. als Signalisierungs-Zustandsautomat fungieren muss. Das wäre problematisch, wenn beispielsweise bei jedem Neuladen einer Seite Signalisierungsdaten verloren gingen. Stattdessen kann der Signalisierungsstatus auf einem Server gespeichert werden.
JSEP erfordert den Austausch von Angebot und Antwort, den Medienmetadaten, zwischen Peers. Angebote und Antworten werden im SDP-Format (Session Description Protocol) kommuniziert, 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 ganze SDP-Kauderwelsch eigentlich bedeutet? Sehen Sie sich die Beispiele der Internet Engineering Task Force (IETF) an.
Denken Sie daran, dass WebRTC so konzipiert ist, dass das Angebot oder die Antwort angepasst werden kann, bevor es als lokale oder Remote-Beschreibung festgelegt wird. Dazu werden die Werte im SDP-Text bearbeitet. Beispielsweise kann die Funktion preferAudioCodec() in appr.tc verwendet werden, um den Standardcodec und die Standardbitrate festzulegen. SDP ist mit JavaScript etwas mühsam zu bearbeiten und es wird diskutiert, ob zukünftige Versionen von WebRTC stattdessen JSON verwenden sollten. 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 Videoinhalte zu übertragen.
Um diesen Prozess zu initialisieren, hat RTCPeerConnection zwei Aufgaben:
- Ermitteln Sie die lokalen Medienbedingungen, z. B. die Auflösung und die Codec-Funktionen. Dies sind die Metadaten, die für den Offer-and-Answer-Mechanismus verwendet werden.
- Potenzielle Netzwerkadressen für den Host der App abrufen, die als Kandidaten bezeichnet werden.
Nachdem 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 Antwortmechanismus im Detail:
- Alice erstellt ein
RTCPeerConnection-Objekt. - Alice erstellt mit der Methode
RTCPeerConnectioncreateOffer()ein Angebot (eine SDP-Sitzungsbeschreibung). - Alice ruft
setLocalDescription()mit ihrem Angebot auf. - Alice wandelt das Angebot in einen String um und sendet es über einen Signalisierungsmechanismus an Eve.
- Eve ruft
setRemoteDescription()mit dem Angebot von Alice auf, damit ihreRTCPeerConnectionüber die Einrichtung von Alice informiert ist. - Eve ruft
createAnswer()auf und der Erfolgs-Callback dafür erhält eine lokale Sitzungsbeschreibung – die Antwort von Eve. - Eve legt ihre Antwort als lokale Beschreibung fest, indem sie
setLocalDescription()aufruft. - Eve verwendet dann den Signalisierungsmechanismus, um ihre Antwort als String an Alice zu senden.
- Alice legt die Antwort von Eve mit
setRemoteDescription()als Beschreibung der Remote-Sitzung fest.
Alice und Eve müssen auch Netzwerkinformationen austauschen. Der Ausdruck „Kandidaten finden“ bezieht sich auf den Prozess, bei dem Netzwerkschnittstellen und Ports mithilfe des ICE-Frameworks gefunden werden.
- Alice erstellt ein
RTCPeerConnection-Objekt mit einemonicecandidate-Handler. - Der Handler wird aufgerufen, wenn Netzwerkkandidaten verfügbar werden.
- Im Handler sendet Alice über ihren Signalisierungskanal stringifizierte Kandidatendaten 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 schrittweise Kandidaten zur Verfügung stellen. Der Angerufene kann dann auf den Anruf reagieren und eine Verbindung herstellen, ohne darauf warten zu müssen, dass alle Kandidaten eintreffen.
WebRTC für die Signalisierung programmieren
Das folgende Code-Snippet ist ein W3C-Codebeispiel, das den gesamten Signalisierungsprozess zusammenfasst. Der Code setzt voraus, dass ein Signalisierungsmechanismus SignalingChannel vorhanden ist. Die Signalisierung wird später ausführlicher 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);
}
};
Wenn Sie die Prozesse für den Austausch von Angeboten und Antworten sowie von Kandidaten in Aktion sehen möchten, rufen Sie simpl.info RTCPeerConnection auf und sehen Sie sich das Konsolenprotokoll für ein Beispiel eines Videoanrufs auf einer einzelnen Seite an. Sie können einen vollständigen Dump der WebRTC-Signalisierung und -Statistiken von der Seite about://webrtc-internals in Google Chrome oder der Seite opera://webrtc-internals in Opera herunterladen.
Peer-Erkennung
Das ist eine schicke Art zu fragen: „Wie finde ich jemanden, mit dem ich sprechen kann?“
Für Telefonanrufe gibt es Telefonnummern und Verzeichnisse. Für Online-Videoanrufe und -Messaging benötigen Sie Systeme zur Identitäts- und Präsenzverwaltung sowie eine Möglichkeit für Nutzer, Sitzungen zu starten. WebRTC-Anwendungen benötigen eine Möglichkeit, mit der Clients einander signalisieren können, dass sie einen Anruf starten oder daran teilnehmen möchten.
Mechanismen zur Peer-Erkennung werden nicht von WebRTC definiert und die Optionen werden hier nicht behandelt. Der Vorgang kann so einfach sein wie das Senden einer URL per E-Mail oder Nachricht. Bei Videoanruf-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 serverloses WebRTC-Experiment entwickelt, mit dem WebRTC-Anrufteilnehmer Metadaten über einen beliebigen Messaging-Dienst wie IM, E-Mail oder Brieftaube austauschen können.
Wie können Sie einen Signalisierungsdienst erstellen?
Noch einmal: Signalisierungsprotokolle und -mechanismen werden nicht durch WebRTC-Standards definiert. Unabhängig davon, wofür Sie sich entscheiden, benötigen Sie einen Zwischenserver, um Signalisierungsnachrichten und App-Daten zwischen Clients auszutauschen. Leider kann eine Web-App nicht ins Internet rufen: „Verbinde mich mit meinem Freund!“
Glücklicherweise sind Signalisierungsnachrichten klein und werden meist zu Beginn eines Anrufs ausgetauscht. Beim Testen mit appr.tc für eine Videoanrufsitzung wurden insgesamt etwa 30–45 Nachrichten vom Signalisierungsdienst verarbeitet, wobei die Gesamtgröße aller Nachrichten etwa 10 KB betrug.
WebRTC-Signalisierungsdienste sind nicht nur relativ anspruchslos in Bezug auf die Bandbreite, sondern verbrauchen auch nicht viel Rechenleistung oder Arbeitsspeicher, da sie nur Nachrichten weiterleiten und eine kleine Menge an Sitzungsstatusdaten speichern müssen, z. B. welche Clients verbunden sind.
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/Antwort-Modell. Es wurden jedoch im Laufe der Jahre verschiedene Hacks wie Long Polling entwickelt, um Daten von einem Dienst, der auf einem Webserver ausgeführt wird, an eine Webanwendung zu übertragen, die in einem Browser ausgeführt wird.
Die EventSource API ermöglicht vom Server gesendete Ereignisse, also Daten, die von einem Webserver über HTTP an einen Browserclient gesendet werden. EventSource ist für die unidirektionale Nachrichtenübermittlung 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 Angerufenen weiter.
WebSocket ist für die Vollduplex-Client-Server-Kommunikation konzipiert. Das bedeutet, dass Nachrichten gleichzeitig in beide Richtungen übertragen werden können. Ein Vorteil eines Signalisierungsdienstes, der mit reinem WebSocket oder Server-Sent Events (EventSource) erstellt wurde, besteht darin, dass das Backend für diese APIs auf einer Vielzahl von Web-Frameworks implementiert werden kann, die in den meisten Webhosting-Paketen für Sprachen wie PHP, Python und Ruby enthalten sind.
Alle Browser, die WebRTC unterstützen, unterstützen auch WebSocket. Für alle Verbindungen sollte TLS verwendet werden, damit Nachrichten nicht unverschlüsselt abgefangen werden können und um Probleme mit der Proxy-Durchquerung zu reduzieren. Weitere Informationen zu WebSocket und Proxy-Traversal finden Sie im WebRTC-Kapitel in High Performance Browser Networking von Ilya Grigorik.
Sie können die Signalisierung verarbeiten, indem Sie WebRTC-Clients wiederholt über Ajax einen Messaging-Server abfragen lassen. Dies führt jedoch zu vielen redundanten Netzwerkanfragen, was insbesondere für Mobilgeräte problematisch ist. Auch nach dem Aufbau einer Sitzung müssen Peers nach Signalisierungsnachrichten suchen, falls Änderungen vorgenommen werden oder die Sitzung von anderen Peers beendet wird. In der Beispiel-App WebRTC Book wird diese Option mit einigen Optimierungen für die Abfragehäufigkeit verwendet.
Signalisierung skalieren
Obwohl ein Signalisierungsdienst pro Client relativ wenig Bandbreite und CPU verbraucht, müssen Signalisierungsserver für eine beliebte App möglicherweise viele Nachrichten von verschiedenen Standorten mit hoher Gleichzeitigkeit verarbeiten. WebRTC-Apps mit hohem Traffic benötigen Signalisierungsserver, die eine erhebliche Last bewältigen können. Wir gehen hier nicht ins Detail, aber es gibt eine Reihe von Optionen für Nachrichten mit hohem Volumen und hoher Leistung, darunter:
eXtensible Messaging and Presence Protocol (XMPP) 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. BOSH kann jedoch im Vergleich zu WebSocket ineffizient sein und schlecht skalieren.
Open-Source-Bibliotheken wie ZeroMQ (wie von TokBox für den Rumour-Dienst verwendet) und OpenMQ (NullMQ wendet ZeroMQ-Konzepte auf Webplattformen an, die das STOMP-Protokoll über WebSocket verwenden.)
Kommerzielle Cloud-Messaging-Plattformen, die WebSocket verwenden (obwohl sie möglicherweise auf Long Polling zurückgreifen), z. B. Pusher und Kaazing.
Kommerzielle WebRTC-Plattformen wie vLine
Signalisierungsdienst mit Socket.io auf Node erstellen
Der folgende Code ist für eine Webanwendung, die einen Signalisierungsdienst verwendet, der mit Socket.io auf Node erstellt wurde. Das Design von Socket.io erleichtert die Erstellung eines Dienstes zum Austausch von Nachrichten. Socket.io eignet sich aufgrund des integrierten Konzepts von Räumen besonders für die WebRTC-Signalisierung. Dieses Beispiel ist für eine relativ kleine Anzahl von Nutzern und nicht für die Produktion gedacht.
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 die in diesem Beispiel verwendete Node-Version bekannt.
In diesem Beispiel wird kein WebRTC verwendet. Sie soll zeigen, wie die Signalisierung in eine Webanwendung integriert wird. Im Konsolenprotokoll können Sie sehen, was passiert, wenn Clients einem Raum beitreten und Nachrichten austauschen. In diesem WebRTC-Codelab finden Sie eine detaillierte Anleitung, wie Sie dies in eine vollständige WebRTC-Videoanruf-App einbinden.
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);
});
});
Wenn Sie diese App auf localhost ausführen möchten, benötigen Sie Node.js, Socket.IO und node-static. Führen Sie in einem Terminal im Verzeichnis Ihrer Anwendung npm aus, um Socket.IO und node-static zu installieren:
npm install socket.io
npm install node-static
Führen Sie den folgenden Befehl in einem Terminal in Ihrem Anwendungsverzeichnis 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 sich die Konsole an, um zu sehen, 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 Dienste bereitstellen, die diesem Beispiel ähneln.
Fallstricke bei Signalen
RTCPeerConnectionbeginnt erst mit der Erfassung von Kandidaten, wennsetLocalDescription()aufgerufen wird. Dies ist im JSEP-IETF-Entwurf vorgeschrieben.- Nutzen Sie Trickle ICE. Rufen Sie
addIceCandidate()an, sobald Kandidaten eintreffen.
Vorgefertigte Signal-Server
Wenn Sie keinen eigenen Server erstellen möchten, stehen Ihnen mehrere WebRTC-Signalisierungsserver zur Verfügung, 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 JavaScript-Clientbibliothek SimpleWebRTC erstellt wurde.
Wenn Sie überhaupt keinen Code schreiben möchten, können Sie kommerzielle WebRTC-Plattformen von Unternehmen wie vLine, OpenTok und Asterisk verwenden.
In den Anfängen von WebRTC hat Ericsson einen Signalisierungsserver mit PHP auf Apache erstellt. Das ist zwar etwas veraltet, aber es lohnt sich, den Code anzusehen, wenn Sie etwas Ähnliches planen.
Signalsicherheit
Die Verschlüsselung ist für alle WebRTC-Komponenten obligatorisch.
Signalisierungsmechanismen sind jedoch nicht durch WebRTC-Standards definiert. Sie müssen also selbst für die Sicherheit der Signalisierung sorgen. Wenn es einem Angreifer gelingt, die Signalisierung zu kapern, kann er Sitzungen beenden, Verbindungen umleiten und Inhalte aufzeichnen, ändern oder einschleusen.
Der wichtigste Faktor bei der Sicherung der Signalisierung ist die Verwendung sicherer Protokolle: HTTPS und WSS, z. B. TLS. So wird sichergestellt, dass Nachrichten nicht unverschlüsselt abgefangen werden können. Achten Sie außerdem darauf, dass Signalisierungsnachrichten nicht so übertragen werden, dass andere Anrufer, die denselben Signalisierungsserver verwenden, darauf zugreifen können.
Umgang mit NATs und Firewalls
Für die Metadatensignalisierung verwenden WebRTC-Anwendungen einen Zwischenserver. Für das eigentliche Medien- und Datenstreaming nach dem Aufbau einer Sitzung versucht RTCPeerConnection jedoch, Clients direkt oder über Peer-to-Peer zu verbinden.
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-Ebenen, einige haben Antivirensoftware, die bestimmte Ports und Protokolle blockiert, und viele befinden sich hinter Proxys und Unternehmens-Firewalls. Eine Firewall und NAT können tatsächlich vom selben Gerät implementiert werden, z. B. von einem Wifi-Router zu Hause.
WebRTC-Anwendungen können das ICE-Framework verwenden, um die Komplexität realer Netzwerke zu bewältigen. Dazu muss Ihre Anwendung ICE-Server-URLs an RTCPeerConnection übergeben.
ICE versucht, den besten Weg zu finden, um Peers zu verbinden. 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 dies fehlschlägt (was bei Geräten hinter NATs der Fall ist), erhält ICE eine externe Adresse über einen STUN-Server. Wenn dies fehlschlägt, wird der Traffic über einen TURN-Relay-Server weitergeleitet.
Mit anderen Worten: Ein STUN-Server wird verwendet, um eine externe Netzwerkadresse abzurufen, und TURN-Server werden verwendet, um Traffic weiterzuleiten, wenn die 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-Funktion. ICE bewältigt auch die Komplexität von NAT-Einrichtungen. In der Praxis ist für NAT-Hole-Punching möglicherweise mehr als nur eine öffentliche IP-Adresse und ein Port erforderlich.
URLs für STUN- und TURN-Server werden (optional) von einer WebRTC-Anwendung im Konfigurationsobjekt iceServers angegeben, das das erste Argument für den Konstruktor RTCPeerConnection ist. Für appr.tc sieht dieser 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 angewendet. 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 Verwendung in einem privaten lokalen Netzwerk bereit. Diese Adresse kann jedoch nicht extern verwendet werden. Ohne eine ö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 Aufgabe: die IP:Port-Adresse einer eingehenden Anfrage (von einer App, die hinter einem NAT ausgeführt wird) zu prüfen und diese Adresse als Antwort zurückzusenden. Mit anderen Worten:Die App verwendet einen STUN-Server, um ihre IP-Adresse und ihren Port aus einer öffentlichen Perspektive zu ermitteln. Durch diesen Prozess kann ein WebRTC-Peer eine öffentlich zugängliche Adresse für sich selbst abrufen und diese dann über einen Signalisierungsmechanismus an einen anderen Peer weitergeben, um eine direkte Verbindung einzurichten.
STUN-Server müssen nicht viel tun oder sich viel merken, sodass relativ einfache STUN-Server eine große Anzahl von Anfragen verarbeiten können.
Bei den meisten WebRTC-Anrufen wird eine Verbindung über STUN hergestellt. Bei Anrufen zwischen Peers hinter Firewalls und komplexen NAT-Konfigurationen kann dies jedoch seltener der Fall 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.
Noch einmal: TURN wird verwendet, um Audio-, Video- und Datenstreaming zwischen Peers weiterzuleiten, nicht 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 nur eine Aufgabe: einen Stream weiterleiten. Im Gegensatz zu STUN-Servern verbrauchen sie von Natur aus viel Bandbreite.
Dieses Diagramm zeigt TURN in Aktion. STUN allein war nicht erfolgreich, daher greift jeder Peer auf einen TURN-Server zurück.
STUN- und TURN-Server bereitstellen
Zu Testzwecken betreibt Google einen öffentlichen STUN-Server, stun.l.google.com:19302, der von appr.tc verwendet wird. Verwenden Sie für einen STUN/TURN-Dienst in der Produktion 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. Sie können einen Restund auf Compute einrichten :
- Öffnen Sie die Firewall nach Bedarf für tcp=443, udp/tcp=3478.
- Erstellen Sie vier Instanzen, eine für jede öffentliche IP-Adresse, Standard-Ubuntu-12.06-Image.
- Lokale Firewallkonfiguration einrichten (ANY von ANY zulassen)
- Installieren Sie die Tools:
shell sudo apt-get install make sudo apt-get install gcc - Installieren Sie libre von creytiv.com/re.html.
- Rufen Sie restund von creytiv.com/restund.html ab und entpacken Sie es.
wgethancke.name/restund-auth.patch und wenden Sie ihn mitpatch -p1 < restund-auth.patchan.- Führen Sie
make,sudo make installfür libre und restund aus. - Passen Sie
restund.confan Ihre Anforderungen an (ersetzen Sie die IP-Adressen und achten Sie darauf, dass es dasselbe gemeinsame Secret enthält) und kopieren Sie es nach/etc. - Kopieren Sie
restund/etc/restundnach/etc/init.d/. - Konfigurieren Sie restund:
- Legen Sie dazu
LD_LIBRARY_PATHfest. - Kopieren Sie
restund.confnach/etc/restund.conf. - Legen Sie
restund.confso fest, dass die richtigen 10 verwendet werden. ohne dass eine externe IP-Adresse erforderlich ist.
- Legen Sie dazu
- restund ausführen
- Testen Sie mit dem stund-Client von einem Remote-Computer aus:
./client IP:port
WebRTC mit mehreren Teilnehmern
Vielleicht 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 zahlreiche Anwendungsfälle für Medienstreaming, die über einen Einzelanruf hinausgehen. Beispiele hierfür sind Videokonferenzen zwischen einer Gruppe von Kollegen oder eine öffentliche Veranstaltung mit einem Sprecher und Hunderten oder Millionen von Zuschauern.
Eine WebRTC-Anwendung kann mehrere RTCPeerConnections verwenden, sodass jeder Endpunkt in einer Mesh-Konfiguration mit jedem anderen Endpunkt verbunden ist. Dies ist der Ansatz, der von Apps wie talky.io verwendet wird und für eine kleine Anzahl von Peers bemerkenswert gut funktioniert. Darüber hinaus werden die Verarbeitung und der Bandbreitenverbrauch übermäßig hoch, insbesondere für mobile Clients.
Alternativ kann eine WebRTC-Anwendung 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 zur Weiterverteilung zu erstellen. Eine Beispiel-Client-App wird von webrtc.org bereitgestellt.
Ein MediaStream aus einem RTCPeerConnection kann als Eingabe für einen anderen verwendet werden. Dies kann flexiblere Architekturen ermöglichen, da eine Web-App das Anrufrouting verarbeiten kann, indem sie auswählt, mit welchem anderen Peer eine Verbindung hergestellt werden soll. Beispiele hierfür finden Sie unter WebRTC-Beispiele: Peer-Verbindungsweiterleitung und WebRTC-Beispiele: Mehrere Peer-Verbindungen.
Multipoint Control Unit
Für eine große Anzahl von Endpunkten ist die Verwendung einer Multipoint Control Unit (MCU) die bessere Option. Dies ist ein Server, der als Brücke dient, um Medien zwischen einer großen Anzahl von Teilnehmern zu verteilen. MCUs können mit verschiedenen Auflösungen, Codecs und Frameraten in einer Videokonferenz umgehen, Transcodierung durchführen, selektive Streamweiterleitung durchführen und Audio und Video mischen oder aufzeichnen. Bei Anrufen mit mehreren Teilnehmern gibt es eine Reihe von Problemen, die berücksichtigt werden müssen, insbesondere wie mehrere Videoeingaben angezeigt und Audio von mehreren Quellen gemischt werden. Cloud-Plattformen wie vLine versuchen ebenfalls, das Traffic-Routing zu optimieren.
Sie können ein komplettes MCU-Hardwarepaket kaufen oder ein eigenes erstellen.
Es sind mehrere Open-Source-MCU-Softwareoptionen verfügbar. Licode (früher Lynckia) bietet beispielsweise eine Open-Source-MCU für WebRTC an. OpenTok hat Mantis.
Über Browser hinaus: VoIP, Telefone und Messaging
Die Standardisierung von WebRTC ermöglicht die Kommunikation zwischen einer WebRTC-Anwendung, die in einem Browser ausgeführt wird, und einem Gerät oder einer Plattform, die auf einer anderen Kommunikationsplattform ausgeführt wird, z. B. einem Telefon oder einem Videokonferenzsystem.
SIP ist ein Signalisierungsprotokoll, das von VoIP- und Videokonferenzsystemen verwendet wird. Damit die Kommunikation zwischen einer WebRTC-Web-App und einem SIP-Client wie einem Videokonferenzsystem möglich ist, benötigt WebRTC einen Proxyserver, der die Signalisierung vermittelt. Die Signalisierung muss über das Gateway erfolgen. Sobald die Kommunikation hergestellt ist, kann der SRTP-Traffic (Video und Audio) jedoch Peer-to-Peer übertragen werden.
Das öffentliche Fernsprechnetz (PSTN) ist das leitungsvermittelte Netzwerk aller „normalen“ analogen Telefone. Für Anrufe zwischen WebRTC-Webanwendungen und Telefonen muss der Traffic über ein PSTN-Gateway geleitet werden. Ebenso benötigen WebRTC-Webanwendungen einen XMPP-Zwischenserver, um mit Jingle-Endpunkten wie IM-Clients zu kommunizieren. Jingle wurde von Google als Erweiterung für XMPP entwickelt, um Sprach- und Videoanrufe 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 Fähigkeit von WebRTC, mit der Außenwelt zu kommunizieren. Beispiel:
- jsSIP: JavaScript-SIP-Bibliothek
- Phono: Open-Source-JavaScript-Telefon-API, die als Plug-in erstellt wurde
- Twilio: Sprach- und Messaging-Dienste
Weitere Informationen
- WebRTC API-Dokumentation lesen
- Das WebRTC-Codelab enthält eine detaillierte Anleitung zum Erstellen einer Video- und Textchat-App mit einem Socket.io-Signalisierungsdienst, der auf Node ausgeführt wird.
- SFHTML5-Präsentation von Chris Wilson: Einführung in WebRTC-Apps
Das 350-seitige Buch WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web bietet viele Details zu Daten- und Signalisierungspfaden und enthält eine Reihe detaillierter Netzwerktopologiediagramme.