Data publikacji: 4 listopada 2013 r.
WebRTC umożliwia komunikację typu peer-to-peer. Wymaga serwerów, aby klienci mogli wymieniać metadane i koordynować komunikację w ramach procesu zwanego sygnalizacją. Serwery pomagają też klientom radzić sobie z translacją adresów sieciowych (NAT) i zaporami sieciowymi.
Dowiesz się, jak utworzyć usługę sygnalizacyjną i jak radzić sobie z problemami z łącznością w rzeczywistych warunkach przy użyciu serwerów STUN i TURN. Dowiesz się też, jak aplikacje WebRTC mogą obsługiwać połączenia wielostronne i wchodzić w interakcje z usługami takimi jak VoIP i PSTN (czyli telefonia).
Co to jest sygnalizacja?
Sygnalizacja to proces koordynowania komunikacji. Aby aplikacja WebRTC mogła nawiązać połączenie, jej klienci muszą wymienić te informacje:
- wiadomości kontroli sesji używane do otwierania i zamykania komunikacji;
- Komunikaty o błędach
- metadane multimediów, takie jak kodeki, ustawienia kodeków, przepustowość i typy multimediów;
- Kluczowe dane używane do nawiązywania bezpiecznych połączeń
- dane sieciowe, takie jak adres IP hosta i port widoczny dla świata zewnętrznego;
Ten proces sygnalizacji wymaga, aby klienci mogli przekazywać wiadomości między sobą. Ten mechanizm nie jest implementowany przez interfejsy WebRTC API. Zamiast tego musisz zbudować go samodzielnie.
Dlaczego sygnalizacja nie jest zdefiniowana przez WebRTC?
Aby uniknąć nadmiarowości i zmaksymalizować zgodność z ustalonymi technologiami, metody i protokoły sygnalizacji nie są określone przez standardy WebRTC. To podejście jest opisane w protokole JavaScript Session Establishment Protocol (JSEP):
Architektura JSEP pozwala też uniknąć konieczności zapisywania stanu przez przeglądarkę, czyli działania jako maszyna stanu sygnalizacji. Byłoby to problematyczne, gdyby na przykład dane sygnalizacyjne były tracone przy każdym ponownym załadowaniu strony. Zamiast tego stan sygnalizacji może być zapisywany na serwerze.
JSEP wymaga wymiany między węzłami oferty i odpowiedzi, czyli metadanych multimediów. Oferty i odpowiedzi są przekazywane w formacie Session Description Protocol (SDP), który wygląda tak:
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
...
Chcesz wiedzieć, co oznaczają te wszystkie skomplikowane terminy związane z SDP? Zapoznaj się z przykładami IETF (Internet Engineering Task Force).
Pamiętaj, że WebRTC zaprojektowano tak, aby można było dostosować ofertę lub odpowiedź przed ustawieniem jej jako opisu lokalnego lub zdalnego, edytując wartości w tekście SDP. Na przykład funkcja preferAudioCodec() w appr.tc może służyć do ustawiania domyślnego kodeka i szybkości transmisji bitów. Manipulowanie SDP za pomocą JavaScriptu jest nieco uciążliwe i trwa dyskusja, czy w przyszłych wersjach WebRTC nie należy zamiast tego używać JSON, ale pozostanie przy SDP ma pewne zalety.
Interfejs API RTCPeerConnection i sygnalizacja: oferta, odpowiedź i kandydat
RTCPeerConnection to interfejs API używany przez aplikacje WebRTC do tworzenia połączenia między urządzeniami oraz przesyłania dźwięku i obrazu.
Aby zainicjować ten proces, RTCPeerConnection musi wykonać 2 zadania:
- ustalanie warunków multimediów lokalnych, takich jak rozdzielczość i możliwości kodeków; Są to metadane używane w mechanizmie oferty i odpowiedzi.
- pobrać potencjalne adresy sieciowe hosta aplikacji, nazywane kandydatami;
Po ustaleniu tych danych produktów dostępnych lokalnie należy je wymienić za pomocą mechanizmu sygnalizacyjnego ze zdalnym węzłem równorzędnym.
Wyobraź sobie, że Alicja próbuje zadzwonić do Ewy. Oto pełny mechanizm odpowiedzi ze wszystkimi szczegółami:
- Alicja tworzy obiekt
RTCPeerConnection. - Alicja tworzy ofertę (opis sesji SDP) za pomocą metody
RTCPeerConnectioncreateOffer(). - Alicja dzwoni do
setLocalDescription(), aby przedstawić swoją ofertę. - Alicja przekształca ofertę w ciąg znaków i używa mechanizmu sygnalizacji, aby wysłać ją do Ewy.
- Ewa dzwoni do
setRemoteDescription()z ofertą Alicji, aby jejRTCPeerConnectionwiedział o konfiguracji Alicji. - Eve wywołuje
createAnswer(), a wywołanie zwrotne powodzenia otrzymuje opis sesji lokalnej – odpowiedź Eve. - Ewa ustawia swoją odpowiedź jako opis lokalny, wywołując metodę
setLocalDescription(). - Następnie Eve używa mechanizmu sygnalizacyjnego, aby wysłać do Alice swoją odpowiedź w postaci ciągu znaków.
- Alicja ustawia odpowiedź Ewy jako opis sesji zdalnej za pomocą
setRemoteDescription().
Alicja i Ewa muszą też wymienić się informacjami o sieci. Wyrażenie „znajdowanie kandydatów” odnosi się do procesu znajdowania interfejsów sieciowych i portów za pomocą platformy ICE.
- Alicja tworzy obiekt
RTCPeerConnectionz modułem obsługionicecandidate. - Moduł obsługi jest wywoływany, gdy dostępne są kandydaty sieciowe.
- W programie obsługi Alicja wysyła do Ewy dane kandydata w postaci ciągu znaków przez kanał sygnalizacyjny.
- Gdy Ewa otrzyma od Alicji wiadomość z kandydatem, wywołuje funkcję
addIceCandidate(), aby dodać kandydata do opisu zdalnego peera.
JSEP obsługuje ICE Candidate Trickling, co pozwala dzwoniącemu na stopniowe przekazywanie kandydatów do rozmówcy po początkowej ofercie, a rozmówcy na rozpoczęcie rozmowy i nawiązanie połączenia bez czekania na wszystkich kandydatów.
Kod WebRTC do sygnalizacji
Poniższy fragment kodu to przykład kodu W3C, który podsumowuje cały proces sygnalizacji. Kod zakłada istnienie pewnego mechanizmu sygnalizacyjnego, SignalingChannel. Sygnalizacja zostanie omówiona bardziej szczegółowo później.
// 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);
}
};
Aby zobaczyć, jak działają procesy wymiany ofert i odpowiedzi oraz kandydatów, wejdź na stronę simpl.info RTCPeerConnection i sprawdź dziennik konsoli w przykładowym czacie wideo na jednej stronie. Możesz pobrać pełny zrzut sygnalizacji i statystyk WebRTC ze strony about://webrtc-internals w Google Chrome lub ze strony opera://webrtc-internals w Operze.
Wykrywanie innych urządzeń
To tylko inny sposób na zadanie pytania: „Jak znaleźć kogoś, z kim można porozmawiać?”.
W przypadku połączeń telefonicznych masz numery telefonów i książki telefoniczne. W przypadku czatu wideo i wiadomości online potrzebne są systemy zarządzania tożsamością i obecnością oraz sposób, w jaki użytkownicy mogą inicjować sesje. Aplikacje WebRTC potrzebują sposobu, aby klienci mogli sygnalizować sobie nawzajem, że chcą rozpocząć rozmowę lub do niej dołączyć.
Mechanizmy wykrywania połączeń równorzędnych nie są definiowane przez WebRTC i nie będziemy tu omawiać tych opcji. Może to być tak proste jak wysłanie adresu URL e-mailem lub w wiadomości. W przypadku aplikacji do czatu wideo, takich jak Talky, tawk.to i Browser Meeting, zapraszasz osoby do rozmowy, udostępniając im niestandardowy link. Programista Chris Ball stworzył ciekawy eksperyment serverless-webrtc, który umożliwia uczestnikom rozmowy WebRTC wymianę metadanych za pomocą dowolnej usługi przesyłania wiadomości, np. komunikatora, poczty e-mail czy gołębia pocztowego.
Jak utworzyć usługę sygnalizacyjną?
Przypominamy, że protokoły i mechanizmy sygnalizacyjne nie są zdefiniowane przez standardy WebRTC. Niezależnie od wybranej opcji potrzebujesz serwera pośredniczącego do wymiany wiadomości sygnalizacyjnych i danych aplikacji między klientami. Niestety aplikacja internetowa nie może krzyknąć w internecie: „Połącz mnie z moim znajomym!”.
Na szczęście wiadomości sygnalizacyjne są niewielkie i są wymieniane głównie na początku rozmowy. Podczas testowania sesji czatu wideo za pomocą appr.tc usługa sygnalizacyjna obsłużyła łącznie około 30–45 wiadomości o łącznym rozmiarze około 10 KB.
Usługi sygnalizacyjne WebRTC nie tylko są stosunkowo mało wymagające pod względem przepustowości, ale też nie zużywają dużo mocy obliczeniowej ani pamięci, ponieważ muszą jedynie przekazywać wiadomości i przechowywać niewielką ilość danych o stanie sesji, takich jak informacje o tym, którzy klienci są połączeni.
Wysyłanie wiadomości push z serwera do klienta
Usługa wiadomości do sygnalizacji musi być dwukierunkowa: klient-serwer i serwer-klient. Komunikacja dwukierunkowa jest sprzeczna z modelem żądania/odpowiedzi klienta/serwera HTTP, ale przez wiele lat opracowano różne obejścia, takie jak długie sondowanie, aby przesyłać dane z usługi działającej na serwerze WWW do aplikacji internetowej działającej w przeglądarce.
Interfejs API EventSource umożliwia wysyłanie zdarzeń przez serwer, czyli danych wysyłanych z serwera WWW do klienta przeglądarki przez HTTP. EventSource jest przeznaczony do przesyłania wiadomości jednokierunkowych, ale można go używać w połączeniu z XHR do tworzenia usługi wymiany wiadomości sygnalizacyjnych. Usługa sygnalizacyjna przekazuje wiadomość od dzwoniącego, dostarczoną przez żądanie XHR, przesyłając ją przez EventSource do odbiorcy.
WebSocket umożliwia komunikację klient–serwer w trybie pełnego dupleksu, co oznacza, że wiadomości mogą być przesyłane w obu kierunkach jednocześnie. Jedną z zalet usługi sygnalizacyjnej utworzonej przy użyciu czystego protokołu WebSocket lub zdarzeń wysyłanych przez serwer (EventSource) jest to, że backend tych interfejsów API można zaimplementować w różnych platformach internetowych, które są wspólne dla większości pakietów hostingu WWW, w językach takich jak PHP, Python i Ruby.
Wszystkie przeglądarki obsługujące WebRTC obsługują też WebSocket. W przypadku wszystkich połączeń należy używać protokołu TLS, aby uniemożliwić przechwytywanie niezaszyfrowanych wiadomości, a także ograniczyć problemy z przechodzeniem przez serwer proxy. Więcej informacji o WebSocket i przechodzeniu przez serwer proxy znajdziesz w rozdziale o WebRTC w książce Ilyi Grigorika High Performance Browser Networking.
Sygnalizację można obsługiwać, zmuszając klientów WebRTC do wielokrotnego odpytywania serwera wiadomości za pomocą technologii Ajax, ale prowadzi to do wielu zbędnych żądań sieciowych, co jest szczególnie problematyczne w przypadku urządzeń mobilnych. Nawet po nawiązaniu sesji równorzędne urządzenia muszą sprawdzać, czy nie pojawiły się komunikaty sygnalizacyjne, na wypadek zmian lub zakończenia sesji przez inne urządzenia równorzędne. Przykładem aplikacji, w której zastosowano tę opcję z pewnymi optymalizacjami częstotliwości odpytywania, jest WebRTC Book.
Sygnały dotyczące skali
Chociaż usługa sygnalizacji zużywa stosunkowo mało przepustowości i mocy procesora na klienta, serwery sygnalizacji popularnej aplikacji mogą obsługiwać wiele wiadomości z różnych lokalizacji przy wysokim poziomie równoczesności. Aplikacje WebRTC, które generują duży ruch, wymagają serwerów sygnalizacyjnych, które są w stanie obsłużyć znaczne obciążenie. Nie będziemy tu wchodzić w szczegóły, ale istnieje wiele opcji przesyłania wiadomości o dużej objętości i wysokiej wydajności, w tym:
eXtensible Messaging and Presence Protocol (XMPP) to protokół opracowany na potrzeby czatu, który może być używany do sygnalizacji. Implementacje serwera obejmują ejabberd i Openfire. Klienty JavaScript, takie jak Strophe.js, używają BOSH do emulowania strumieniowania dwukierunkowego, ale BOSH może być nieefektywny i słabo się skalować w porównaniu z WebSocket.
Biblioteki open source, takie jak ZeroMQ (używana przez TokBox w usłudze Rumour) i OpenMQ (NullMQ stosuje koncepcje ZeroMQ do platform internetowych przy użyciu protokołu STOMP przez WebSocket).
Komercyjne platformy do komunikacji w chmurze, które używają protokołu WebSocket (choć mogą też korzystać z długiego sondowania), takie jak Pusher i Kaazing.
komercyjne platformy WebRTC, takie jak vLine;
Tworzenie usługi sygnalizacyjnej za pomocą Socket.io w Node
Poniżej znajduje się kod aplikacji internetowej, która korzysta z usługi sygnalizacyjnej utworzonej za pomocą Socket.io w Node. Konstrukcja Socket.io ułatwia tworzenie usługi do wymiany wiadomości, a Socket.io jest szczególnie odpowiedni do sygnalizacji WebRTC ze względu na wbudowaną koncepcję pokojów. Ten przykład jest przeznaczony dla stosunkowo niewielkiej liczby użytkowników, a nie do użytku produkcyjnego.
Socket.io używa protokołu WebSocket z mechanizmami zastępczymi: długim odpytywaniem AJAX, strumieniowaniem wieloczęściowym AJAX, Forever Iframe i odpytywaniem JSONP. Został przeniesiony do różnych backendów, ale prawdopodobnie najbardziej znany jest ze swojej wersji Node używanej w tym przykładzie.
W tym przykładzie nie ma WebRTC. Pokazuje, jak wbudować sygnalizację w aplikację internetową. Wyświetl dziennik konsoli, aby zobaczyć, co się dzieje, gdy klienci dołączają do pokoju i wymieniają wiadomości. To ćwiczenie z WebRTC zawiera szczegółowe instrukcje, jak zintegrować tę funkcję z pełną aplikacją do obsługi czatu wideo WebRTC.
Oto klient 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>
Oto plik JavaScriptu main.js, do którego odwołuje się klient:
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);
});
Oto kompletna aplikacja serwera:
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);
});
});
Aby uruchomić tę aplikację na hoście lokalnym, musisz mieć Node.js, Socket.IO i node-static. Aby zainstalować Socket.IO i node-static, uruchom npm z terminala w katalogu aplikacji:
npm install socket.io
npm install node-static
Aby uruchomić serwer, wykonaj to polecenie w terminalu w katalogu aplikacji:
node server.js
W przeglądarce otwórz localhost:2013. Otwórz nową kartę lub nowe okno w dowolnej przeglądarce i ponownie otwórz localhost:2013. Aby zobaczyć, co się dzieje, sprawdź konsolę. W Chrome i Operze możesz uzyskać dostęp do konsoli za pomocą Narzędzi dla deweloperów w Google Chrome, naciskając Ctrl+Shift+J (lub Command+Option+J na Macu).
Niezależnie od wybranego podejścia do sygnalizowania backend i aplikacja kliencka muszą zapewniać usługi podobne do tych w tym przykładzie.
Pułapki związane z sygnałami
RTCPeerConnectionnie zacznie zbierać kandydatów, dopóki nie zostanie wywołana metodasetLocalDescription(). Jest to wymagane w projekcie JSEP IETF.- Skorzystaj z funkcji Trickle ICE. Zadzwoń do
addIceCandidate(), gdy tylko pojawią się kandydaci.
Gotowe serwery sygnałów
Jeśli nie chcesz tworzyć własnego serwera, możesz skorzystać z kilku dostępnych serwerów sygnalizacyjnych WebRTC, które używają Socket.IO, tak jak w poprzednim przykładzie, i są zintegrowane z bibliotekami JavaScriptu klienta WebRTC:
- webRTC.io to jedna z pierwszych bibliotek abstrakcji dla WebRTC.
- Signalmaster to serwer sygnalizacyjny utworzony do użytku z biblioteką klienta JavaScriptu SimpleWebRTC.
Jeśli nie chcesz pisać żadnego kodu, możesz skorzystać z komercyjnych platform WebRTC oferowanych przez takie firmy jak vLine, OpenTok czy Asterisk.
Warto wspomnieć, że na początku istnienia WebRTC firma Ericsson utworzyła serwer sygnalizacyjny przy użyciu PHP na Apache. Jest to już nieco przestarzałe, ale warto przyjrzeć się kodowi, jeśli rozważasz coś podobnego.
Bezpieczeństwo sygnału
Szyfrowanie jest obowiązkowe w przypadku wszystkich komponentów WebRTC.
Mechanizmy sygnalizacji nie są jednak zdefiniowane przez standardy WebRTC, więc to Ty musisz zadbać o bezpieczeństwo sygnalizacji. Jeśli atakującemu uda się przejąć sygnalizację, może zatrzymać sesje, przekierować połączenia oraz nagrywać, zmieniać lub wstrzykiwać treści.
Najważniejszym czynnikiem w zabezpieczaniu sygnalizacji jest używanie bezpiecznych protokołów: HTTPS i WSS, takich jak TLS. Dzięki temu wiadomości nie mogą zostać przechwycone w postaci niezaszyfrowanej. Uważaj też, aby nie transmitować wiadomości sygnalizacyjnych w sposób, który umożliwia dostęp do nich innym rozmówcom korzystającym z tego samego serwera sygnalizacyjnego.
Radzenie sobie z NAT i zaporami sieciowymi
W przypadku sygnalizowania metadanych aplikacje WebRTC używają serwera pośredniczącego, ale w przypadku rzeczywistego strumieniowania multimediów i danych po nawiązaniu sesji RTCPeerConnection próbuje połączyć klientów bezpośrednio lub w trybie peer-to-peer.
W prostszym świecie każdy punkt końcowy WebRTC miałby unikalny adres, który mógłby wymieniać z innymi punktami końcowymi, aby komunikować się bezpośrednio.
W rzeczywistości większość urządzeń znajduje się za co najmniej jedną warstwą NAT, niektóre mają oprogramowanie antywirusowe, które blokuje określone porty i protokoły, a wiele z nich znajduje się za serwerami proxy i firmowymi zaporami sieciowymi. Zapora sieciowa i NAT mogą być wdrożone na tym samym urządzeniu, np. domowym routerze Wi-Fi.
Aplikacje WebRTC mogą używać platformy ICE, aby radzić sobie ze złożonością rzeczywistych sieci. Aby to umożliwić, aplikacja musi przekazywać adresy URL serwerów ICE do RTCPeerConnection.
ICE próbuje znaleźć najlepszą ścieżkę do połączenia równorzędnych urządzeń. Próbuje równolegle wszystkich możliwości i wybiera najbardziej efektywną opcję, która działa. ICE najpierw próbuje nawiązać połączenie przy użyciu adresu hosta uzyskanego z systemu operacyjnego urządzenia i karty sieciowej. Jeśli to się nie powiedzie (co ma miejsce w przypadku urządzeń za NAT), ICE uzyskuje adres zewnętrzny za pomocą serwera STUN, a jeśli to się nie powiedzie, ruch jest kierowany przez serwer przekaźnikowy TURN.
Innymi słowy, serwer STUN służy do uzyskiwania zewnętrznego adresu sieciowego, a serwery TURN są używane do przekazywania ruchu, jeśli bezpośrednie połączenie (peer-to-peer) nie powiedzie się.
Każdy serwer TURN obsługuje STUN. Serwer TURN to serwer STUN z dodatkową wbudowaną funkcją przekazywania. ICE radzi sobie również ze złożonymi konfiguracjami NAT. W rzeczywistości NAT hole punching może wymagać więcej niż tylko publicznego adresu IP:port.
Adresy URL serwerów STUN i TURN są (opcjonalnie) określane przez aplikację WebRTC w obiekcie konfiguracji iceServers, który jest pierwszym argumentem konstruktora RTCPeerConnection. W przypadku appr.tc ta wartość wygląda tak:
{
'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'
}
]
}
Gdy RTCPeerConnection otrzyma te informacje, automatycznie wykona magię ICE. RTCPeerConnection używa platformy ICE do określenia najlepszej ścieżki między urządzeniami, współpracując w razie potrzeby z serwerami STUN i TURN.
STUN
NAT zapewnia urządzeniu adres IP do użytku w prywatnej sieci lokalnej, ale nie można go używać na zewnątrz. Bez adresu publicznego urządzenia równorzędne WebRTC nie mogą się komunikować. Aby rozwiązać ten problem, WebRTC używa STUN.
Serwery STUN działają w publicznym internecie i mają jedno zadanie: sprawdzić adres IP:port przychodzącego żądania (z aplikacji działającej za NAT) i odesłać ten adres jako odpowiedź. Innymi słowy, aplikacja używa serwera STUN do wykrywania swojego adresu IP i portu z perspektywy publicznej. Ten proces umożliwia węzłowi WebRTC uzyskanie publicznie dostępnego adresu i przekazanie go do innego węzła za pomocą mechanizmu sygnalizacji w celu skonfigurowania bezpośredniego połączenia.
Serwery STUN nie muszą wykonywać wielu zadań ani zapamiętywać wielu informacji, więc serwery STUN o stosunkowo niskich parametrach mogą obsługiwać dużą liczbę żądań.
Większość połączeń WebRTC jest nawiązywana za pomocą protokołu STUN, ale może być ich mniej w przypadku połączeń między urządzeniami za zaporami sieciowymi i złożonymi konfiguracjami NAT.
TURN
RTCPeerConnection próbuje skonfigurować bezpośrednią komunikację między urządzeniami równorzędnymi przez UDP.
Jeśli to się nie uda, RTCPeerConnection przełącza się na TCP. Jeśli to się nie uda, można użyć serwerów TURN jako rozwiązania zastępczego, które przekazuje dane między punktami końcowymi.
Przypominamy, że TURN służy do przekazywania strumieniowego dźwięku, obrazu i danych między węzłami, a nie do sygnalizowania danych.
Serwery TURN mają adresy publiczne, więc mogą się z nimi kontaktować urządzenia równorzędne, nawet jeśli znajdują się za zaporami sieciowymi lub serwerami proxy. Serwery TURN mają jedno zadanie: przekazywanie strumienia. W przeciwieństwie do serwerów STUN zużywają one dużo przepustowości.
Ten diagram przedstawia działanie TURN. Samo STUN nie zadziałało, więc każdy węzeł korzysta z serwera TURN.
Wdrażanie serwerów STUN i TURN
Do celów testowych Google udostępnia publiczny serwer STUN stun.l.google.com:19302, który jest używany przez appr.tc. W przypadku usługi STUN/TURN w środowisku produkcyjnym użyj serwera rfc5766-turn-server. Kod źródłowy serwerów STUN i TURN jest dostępny na GitHub, gdzie znajdziesz też linki do kilku źródeł informacji o instalacji serwera. Dostępny jest też obraz maszyny wirtualnej dla Amazon Web Services.
Alternatywnym serwerem TURN jest restund, dostępny jako kod źródłowy, a także dla AWS. Możesz skonfigurować restund na Compute :
- W razie potrzeby otwórz zaporę sieciową dla tcp=443, udp/tcp=3478.
- Utwórz 4 instancje, po jednej dla każdego publicznego adresu IP, ze standardowym obrazem Ubuntu 12.06.
- Skonfiguruj lokalną zaporę sieciową (zezwól na WSZYSTKO z WSZYSTKICH).
- Zainstaluj narzędzia:
shell sudo apt-get install make sudo apt-get install gcc - Zainstaluj libre ze strony creytiv.com/re.html.
- Pobierz restund ze strony creytiv.com/restund.html i rozpakuj.
wgethancke.name/restund-auth.patch i zastosuj za pomocąpatch -p1 < restund-auth.patch.- Uruchom
make,sudo make installdla libre i restund. - Dostosuj plik
restund.confdo swoich potrzeb (zastąp adresy IP i upewnij się, że zawiera on ten sam wspólny klucz tajny), a następnie skopiuj go do/etc. - Skopiuj
restund/etc/restunddo/etc/init.d/. - Skonfiguruj restund:
- Ustaw
LD_LIBRARY_PATH. - Skopiuj
restund.confdo/etc/restund.conf. - Ustaw
restund.conf, aby używać właściwej dziesiątki. adres IP;
- Ustaw
- Uruchamianie restund
- Testowanie za pomocą klienta stund z maszyny zdalnej:
./client IP:port
WebRTC z wieloma uczestnikami
Możesz też zapoznać się z proponowanym przez Justina Ubertiego standardem IETF dla interfejsu API REST umożliwiającego dostęp do usług TURN.
Istnieje wiele przypadków użycia strumieniowania multimediów, które wykraczają poza rozmowy 1:1. Może to być na przykład rozmowa wideo między grupą współpracowników lub wydarzenie publiczne z jednym prelegentem i setkami lub milionami widzów.
Aplikacja WebRTC może używać wielu obiektów RTCPeerConnection, dzięki czemu każdy punkt końcowy łączy się z każdym innym punktem końcowym w konfiguracji siatki. Takie podejście jest stosowane w aplikacjach takich jak talky.io i sprawdza się bardzo dobrze w przypadku niewielkiej liczby połączeń równorzędnych. Powyżej tej wartości przetwarzanie i zużycie przepustowości stają się nadmierne, zwłaszcza w przypadku klientów mobilnych.
Aplikacja WebRTC może też wybrać jeden punkt końcowy, który będzie rozpowszechniać strumienie do wszystkich pozostałych w konfiguracji gwiazdy. Można też uruchomić punkt końcowy WebRTC na serwerze i utworzyć własny mechanizm redystrybucji (przykładową aplikację kliencką udostępnia webrtc.org).
MediaStream z jednego RTCPeerConnection może być używany jako dane wejściowe dla innego. Może to umożliwić tworzenie bardziej elastycznych architektur, ponieważ pozwala aplikacji internetowej obsługiwać przekierowywanie połączeń przez wybór innego peera, z którym ma się połączyć. Aby zobaczyć, jak to działa, zapoznaj się z przykładami WebRTC dotyczącymi przekazywania połączeń równorzędnych i przykładami WebRTC dotyczącymi wielu połączeń równorzędnych.
Jednostka sterująca połączeniami wielopunktowymi
W przypadku dużej liczby punktów końcowych lepszym rozwiązaniem jest użycie jednostki sterującej połączeniami wielopunktowymi (MCU). Jest to serwer, który działa jako most do dystrybucji multimediów między dużą liczbą uczestników. Jednostki MCU mogą obsługiwać różne rozdzielczości, kodeki i liczby klatek podczas rozmowy wideo, a także transkodowanie, selektywne przekazywanie strumieni oraz miksowanie i nagrywanie dźwięku i obrazu. W przypadku rozmów z wieloma uczestnikami należy wziąć pod uwagę kilka kwestii, zwłaszcza sposób wyświetlania wielu wejść wideo i miksowania dźwięku z wielu źródeł. Platformy chmurowe, takie jak vLine, również próbują optymalizować routing ruchu.
Możesz kupić kompletny pakiet sprzętu MCU lub utworzyć własny.
Dostępnych jest kilka opcji oprogramowania MCU typu open source. Na przykład Licode (wcześniej Lynckia) tworzy MCU typu open source dla WebRTC. OpenTok ma Mantis.
Poza przeglądarkami: VoIP, telefony i wiadomości
Standaryzacja WebRTC umożliwia nawiązywanie komunikacji między aplikacją WebRTC działającą w przeglądarce a urządzeniem lub platformą działającą na innej platformie komunikacyjnej, np. telefonem lub systemem do rozmów wideo.
SIP to protokół sygnalizacyjny używany przez systemy VoIP i systemy do obsługi rozmów wideo. Aby umożliwić komunikację między aplikacją internetową WebRTC a klientem SIP, takim jak system do rozmów wideo, WebRTC potrzebuje serwera proxy do pośredniczenia w sygnalizacji. Sygnalizacja musi przechodzić przez bramę, ale po nawiązaniu komunikacji ruch SRTP (wideo i audio) może odbywać się w trybie peer-to-peer.
Publiczna komutowana sieć telefoniczna (PSTN) to sieć komutowana wszystkich „starych” telefonów analogowych. W przypadku połączeń między aplikacjami internetowymi WebRTC a telefonami ruch musi przechodzić przez bramę PSTN. Podobnie aplikacje internetowe WebRTC potrzebują pośredniczącego serwera XMPP do komunikacji z punktami końcowymi Jingle, takimi jak klienci komunikatorów. Protokół Jingle został opracowany przez Google jako rozszerzenie XMPP, aby umożliwić przesyłanie głosu i obrazu w usługach do obsługi wiadomości. Obecne implementacje WebRTC są oparte na bibliotece C++ libjingle, czyli implementacji Jingle opracowanej początkowo na potrzeby Google Talk.
Wiele aplikacji, bibliotek i platform korzysta z możliwości WebRTC do komunikacji ze światem zewnętrznym. Na przykład:
- jsSIP: biblioteka SIP JavaScript.
- Phono: interfejs API telefonu w języku JavaScript o otwartym kodzie źródłowym, utworzony jako wtyczka.
- Twilio: połączenia głosowe i wiadomości,
Więcej informacji
- Zapoznaj się z dokumentacją WebRTC API.
- Ćwiczenie z WebRTC zawiera szczegółowe instrukcje tworzenia aplikacji do obsługi czatu wideo i tekstowego przy użyciu usługi sygnalizacyjnej Socket.io działającej w Node.
- Prezentacja Chrisa Wilsona na SFHTML5: Wprowadzenie do aplikacji WebRTC
350-stronicowa książka WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web zawiera wiele szczegółowych informacji o ścieżkach danych i sygnalizacji oraz wiele szczegółowych diagramów topologii sieci.