Tworzenie usług backendu niezbędnych dla aplikacji WebRTC

Co to jest sygnalizacja?

Sygnalizowanie to proces koordynowania komunikacji. Aby aplikacja WebRTC mogła skonfigurować wywołanie, jej klienci muszą przekazać te informacje:

  • komunikaty dotyczące kontroli sesji używane do nawiązywania i zamykania komunikacji;
  • Komunikaty o błędach
  • metadane multimediów, takie jak kodeki, ustawienia kodeka, przepustowość i typy multimediów;
  • Kluczowe dane używane do nawiązywania bezpiecznych połączeń
  • dane sieci, takie jak adres IP i port hosta, z zewnątrz;

Proces sygnalizacji musi umożliwiać klientom przekazywanie wiadomości tam i z powrotem. Mechanizm ten nie jest zaimplementowany przez interfejsy API WebRTC. Musisz ją zbudować samodzielnie. W dalszej części tego artykułu dowiesz się, jak utworzyć usługę sygnalową. Najpierw jednak potrzeba dodatkowego kontekstu.

Dlaczego WebRTC nie definiuje sygnalizacji?

Aby uniknąć nadmiarowości i zmaksymalizować zgodność z ugruntowanymi technologiami, w standardach WebRTC nie są określone metody i protokoły sygnalizacji. Ta zasada jest opisana w JavaScript Session Embedment Protocol (JSEP):

Architektura JSEP eliminuje też konieczność zapisywania przez przeglądarkę stanu, tzn. funkcjonowania jako maszyny stanu sygnałów. Może to być problematyczne, jeśli na przykład dane sygnałowe zostaną utracone przy każdym ponownym wczytaniu strony. Zamiast tego można zapisać stan sygnalizacji na serwerze.

Schemat architektury JSEP
Architektura JSEP

JSEP wymaga wymiany między elementami offer i answer (metadane multimediów wymienione powyżej). Oferty i odpowiedzi są przekazywane przy użyciu protokołu SIP (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 się dowiedzieć, co to właściwie znaczy ten bzdurny lasek SDP? Przyjrzyj się przykładom IETF (Internet Engineering Task Force).

Pamiętaj, że WebRTC został zaprojektowany tak, aby ofertę lub odpowiedź można było modyfikować przed ustawieniem jej jako opis lokalny lub zdalny przez edycję wartości w tekście SDP. Na przykład funkcji preferAudioCodec() w pliku appr.tc można użyć do ustawienia domyślnego kodeka i szybkości transmisji bitów. Obsługa SDP jest dość trudna w obsłudze i dyskutuje o tym, czy przyszłe wersje WebRTC powinny zamiast tego używać formatu JSON. Jednak zastosowanie SDP ma pewne korzyści.

Interfejs API i sygnały usługi RTCPeerConnection: oferta, odpowiedź i kandydat

RTCPeerConnection to interfejs API używany przez aplikacje WebRTC do budowania połączenia między aplikacjami i przekazywania dźwięku i obrazu.

Aby zainicjować ten proces, RTCPeerConnection ma 2 zadania:

  • Określ lokalne warunki multimediów, takie jak rozdzielczość czy możliwości kodeka. To są metadane używane na potrzeby mechanizmu ofert i odpowiedzi.
  • Uzyskiwanie potencjalnych adresów sieciowych hosta aplikacji, tzw. kandydatów.

Po sprawdzeniu tych danych lokalnych należy je wymienić ze zdalnym peerem za pomocą mechanizmu sygnalizacji.

Wyobraź sobie, że Alicja próbuje do Ewy zadzwonić. Oto pełny mechanizm oferty i odpowiedzi:

  1. Alicja tworzy obiekt RTCPeerConnection.
  2. Alicja tworzy ofertę (opis sesji SDP) za pomocą metody RTCPeerConnection createOffer().
  3. Alicja dzwoni do firmy setLocalDescription(), przedstawiając jej ofertę.
  4. Alicja łączy ofertę i używa mechanizmu sygnału, aby wysłać ją do Ewy.
  5. Ewa dzwoni pod numer setRemoteDescription(), proponując Alicję, aby jej RTCPeerConnection wiedział o konfiguracji Alicji.
  6. Ewa dzwoni pod numer createAnswer(), a oddzwonienie, które zakończyło się sukcesem, przekazuje opis sesji lokalnej – odpowiedź Ewy.
  7. Ewa ustawia swoją odpowiedź jako opis lokalny, dzwoniąc pod numer setLocalDescription().
  8. Następnie Eve wykorzystuje mechanizm sygnałów, aby wysłać Alicję sprecyzowaną odpowiedź.
  9. Alicja ustawia odpowiedź Ewy jako opis sesji zdalnej za pomocą funkcji setRemoteDescription().

Alicja i Ewa muszą też wymienić się informacjami o sieci. Wyrażenie „znajdowanie kandydatów” odnosi się do procesu znajdowania interfejsów sieci i portów za pomocą platformy ICE.

  1. Alicja tworzy obiekt RTCPeerConnection z modułem obsługi onicecandidate.
  2. Moduł obsługi jest wywoływany, gdy dostępne są kandydaci sieci.
  3. W module obsługi Alicja wysyła do Ewy ciąg znaków w postaci ciągów danych kandydata za pomocą kanału sygnałów.
  4. Gdy Alicja otrzymuje wiadomość kandydującą od Alicji, dzwoni pod numer addIceCandidate(), aby dodać kandydata do opisu zdalnego współuczestnika.

JSEP obsługuje narzędzie ICE Candidate Trickling, które pozwala rozmówcy na stopniowe przekazywanie kandydatów po otrzymaniu oferty wstępnej, a rozmówca może rozpocząć rozmowę i nawiązać połączenie bez oczekiwania na pojawienie się wszystkich kandydatów.

Kod WebRTC do sygnalizacji

Poniższy fragment kodu to przykładowy kod W3C, który podsumowuje cały proces sygnalizacji. Kod zakłada, że istnieje mechanizm sygnalizacyjny SignalingChannel. Sygnalizowanie zostało szczegółowo omówione 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ła oferta i odpowiedź oraz procesy wymiany kandydatów, otwórz stronę simpl.info RTCPeerConnection i przejrzyj dziennik konsoli, gdzie znajdziesz przykładowy czat wideo na jednej stronie. Jeśli chcesz dowiedzieć się więcej, pobierz pełny zrzut statystyk i sygnałów WebRTC ze strony about://webrtc-internals w Google Chrome lub strony opera://webrtc-internals w Operze.

Odkrywanie w ramach grupy porównawczej

W ten sposób możesz w fantazyjny sposób zapytać „Jak znaleźć kogoś, z kim mogę porozmawiać?”

W przypadku połączeń telefonicznych dostępne są numery telefonów i informacje telefoniczne. Do korzystania z czatu wideo i przesyłania wiadomości online potrzebne są systemy zarządzania tożsamością i obecnością oraz narzędzia umożliwiające użytkownikom inicjowanie sesji. Aplikacje WebRTC muszą mieć sposób, aby klienci mogli zasygnalizować sobie, że chcą rozpocząć rozmowę lub dołączyć do niej.

Mechanizmy wykrywania peera nie są definiowane przez WebRTC i nie przejdziesz do tych opcji. Może to być tak proste jak wysłanie e-maila lub SMS-a na adres URL. W przypadku aplikacji do obsługi czatu wideo, takich jak Talky, tawk.to i Browser Meeting, możesz zapraszać osoby do rozmowy, udostępniając im niestandardowy link. Programista Chris Ball stworzył intrygujący eksperyment typu serverless-webrtc, który umożliwia uczestnikom korzystającym z WebRTC wymianę metadanych z dowolną usługą do przesyłania wiadomości, taką jak komunikator, poczta e-mail czy gołąb-homing.

Jak można utworzyć usługę sygnalową?

Dla przypomnienia: protokoły i mechanizmy sygnałów nie są definiowane w standardach WebRTC. Niezależnie od wybranej opcji potrzebujesz serwera pośredniego do wymiany wiadomości wskazujących na sygnały i danych aplikacji między klientami. Niestety aplikacja internetowa nie może po prostu krzyczeć do internetu: „Połącz mnie z przyjacielem!”.

Na szczęście komunikaty te są niewielkie i przeważnie są wymieniane na początku rozmowy. Podczas testów z użyciem pliku appr.tc w ramach sesji czatu wideo usługa sygnałowa przeanalizowała łącznie około 30–45 wiadomości. Łączny rozmiar wszystkich wiadomości wyniósł około 10 KB.

Poza tym, że usługi sygnalizacyjne WebRTC są stosunkowo mało wymagające pod względem przepustowości, nie wykorzystują dużo przetwarzania ani pamięci, ponieważ muszą tylko przekazywać wiadomości i zachowywać niewielką ilość danych o stanie sesji, na przykład o tym, którzy klienci są połączeni.

Przekazywanie komunikatów z serwera do klienta

Usługa wiadomości na potrzeby sygnalizacji musi być dwukierunkowa: klient–serwer i serwer–klient. Dwukierunkowa komunikacja jest sprzeczna z modelem żądania/odpowiedzi HTTP/klient/serwer, ale od wielu lat są stosowane metody wykorzystujące długie ankiety, by przesyłać dane z usługi działającej na serwerze WWW do aplikacji internetowej działającej w przeglądarce.

Niedawno interfejs API EventSource został wdrożony na dużą skalę. Umożliwia to rejestrowanie zdarzeń wysyłanych przez serwer, czyli danych wysyłanych z serwera WWW do klienta przeglądarki przez HTTP. Urządzenie EventSource zostało zaprojektowane do komunikacji jednokierunkowej, ale można go używać w połączeniu z technologią XHR do stworzenia usługi wymiany komunikatów sygnałowych. Usługa sygnalizacji przekazuje wiadomość od rozmówcy, dostarczaną przez żądanie XHR, przekazując ją do dzwoniącego przez EventSource.

WebSocket to bardziej naturalne rozwiązanie zaprojektowane z myślą o w pełni duplikatowej komunikacji klient-serwer – wiadomości, które mogą być przesyłane w obu kierunkach jednocześnie. Jedną z zalet usługi sygnalizacji utworzonej wyłącznie na podstawie protokołu WebSocket lub zdarzeń wysyłanych przez serwer (EventSource) jest to, że backend dla tych interfejsów API można wdrożyć na różnych platformach sieciowych typowych dla większości pakietów hostingu WWW dla języków takich jak PHP, Python czy Ruby.

Wszystkie nowoczesne przeglądarki z wyjątkiem Opera Mini obsługują protokół WebSocket, a co ważniejsze – wszystkie przeglądarki, które go obsługują, również obsługują WebSocket, zarówno na komputerach, jak i na urządzeniach mobilnych. W przypadku wszystkich połączeń należy używać protokołu TLS, aby zapewnić, że wiadomości nie mogą być przechwycone w postaci niezaszyfrowanej, oraz aby zmniejszyć problemy z przemierzaniem serwera proxy. (Więcej informacji o protokole WebSocket i przemierzaniu przez serwer proxy można znaleźć w rozdziale dotyczącym WebRTC w dokumencie Ilya Grigorik o nazwie High Performance Browser Networking (Sieć o wysokiej wydajności w przeglądarce) Ilyi Grigorik).

Można też obsługiwać sygnalizację, zachęcając klientów WebRTC do ciągłego odpytywania serwera komunikatów przez Ajax, ale prowadzi to do dużej liczby nadmiarowych żądań sieciowych, co jest szczególnie problematyczne w przypadku urządzeń mobilnych. Nawet po rozpoczęciu sesji inne grupy równorzędne muszą przeprowadzać ankietę w celu sygnalizowania wiadomości w przypadku zmian lub zakończenia sesji przez inne grupy. W przykładzie aplikacji WebRTC Book użyto tej opcji z pewnymi zmianami w częstotliwości odpytywania.

Sygnalizacja skalowania

Mimo że usługa sygnałowa zużywa stosunkowo małą przepustowość i zużywa mniej procesorów na klienta, serwery sygnałowe popularnej aplikacji mogą być zmuszone do obsługi wielu wiadomości z różnych lokalizacji przy wysokim poziomie równoczesności. Aplikacje WebRTC, które otrzymują duży ruch, potrzebują serwerów sygnałów, które będą w stanie obsłużyć znaczne obciążenie. Nie zagłębiasz się w tę historię. Poniżej znajdziesz kilka opcji, które pozwolą Ci dotrzeć do wielu odbiorców jednocześnie. Są to między innymi:

  • eXtensible Messaging and Presence Protocol (XMPP), który pierwotnie nosił nazwę Jabber-a i został opracowany na potrzeby komunikatorów internetowych, które można wykorzystać do sygnalizacji (implementacje serwerów to ejabberd i Openfire). Klienty JavaScript, np. Strophe.js, używają BOSH do emulacji dwukierunkowej transmisji strumieniowej, ale z różnych powodów BOSH może nie być tak wydajna jak WebSocket i z tych samych powodów może nie być dobrze skalowana). (W tangensach Jingle to rozszerzenie XMPP, które umożliwia obsługę głosu i wideo. Projekt WebRTC korzysta z komponentów sieci i transportu z biblioteki libjingle – czyli implementacji Jingle w języku C++).

  • Biblioteki open source, takie jak ZeroMQ (który używa TokBox na potrzeby usługi Rumour) i OpenMQ (NullMQ stosuje koncepcje ZeroMQ do platform internetowych przy użyciu protokołu STOMP zamiast WebSocket).

  • Komercyjne platformy komunikacyjne w chmurze, które używają WebSocket (chociaż mogą w przyszłości używać długich odpytań), np. Pusher, Kaazing i PubNub (PubNub posiada też interfejs API dla WebRTC).

  • Komercyjne platformy WebRTC, takie jak vLine

(Przewodnik po technologiach internetowych w czasie rzeczywistym dla dewelopera Phila Leggettera zawiera obszerną listę bibliotek i usług do przesyłania wiadomości).

Tworzenie usługi sygnalizacji za pomocą Socket.io w Node

Poniżej znajduje się kod prostej aplikacji internetowej, która korzysta z usługi sygnałowej utworzonej za pomocą Socket.io w węźle. Układ Socket.io ułatwia zbudowanie usługi wymiany wiadomości, a ze względu na wbudowaną koncepcję pomieszczeń szczególnie dobrze sprawdza się sygnalizacja WebRTC. Ten przykład nie został opracowany z myślą o skalowaniu jako usługi sygnalizacji klasy produkcyjnej, ale jest prosty do zrozumienia dla stosunkowo niewielkiej liczby użytkowników.

Socket.io korzysta z WebSocket z opcjami zastępczymi: odpytywanie długie AJAX, wieloczęściowe strumieniowe przesyłanie danych AJAX, Forever Iframe i odpytywanie JSONP. Został on przeniesiony do różnych backendów, ale najbardziej znany jest z wersji węzła użytej w tym przykładzie.

W tym przykładzie nie ma WebRTC. Szkolenie zawiera jedynie pokazanie, jak wbudować sygnalizację w aplikację internetową. Wyświetl dziennik konsoli, aby dowiedzieć się, co klienci dołączają do pokoju i wymieniają się wiadomościami. To laboratorium w Codelabs zawiera szczegółowe instrukcje integracji tego interfejsu z pełną aplikacją 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 JavaScript 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 pełna 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 to zrobić, nie musisz znać się na temat statycznego węzła Tak się składa, że jest ono używane w tym przykładzie).

Aby uruchomić tę aplikację na hoście lokalnym, musisz mieć zainstalowane Node, Socket.IO i node-static. Węzeł można pobrać z Node.js (instalacja jest prosta i szybka). Aby zainstalować Socket.IO i node-static, uruchom Menedżera pakietów węzłów z terminala w katalogu aplikacji:

npm install socket.io
npm install node-static

Aby uruchomić serwer, uruchom następujące polecenie z terminala 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 aplikację localhost:2013. Aby sprawdzić, co się dzieje, sprawdź konsolę. W przeglądarkach Chrome i Opera możesz uzyskać dostęp do konsoli za pomocą Narzędzi dla deweloperów w Google Chrome, używając pakietu Ctrl+Shift+J (lub Command+Option+J na Macu).

Niezależnie od wybranej metody sygnalizacji Twoja aplikacja backendu i klienta – a przynajmniej – muszą zapewniać usługi podobne do przedstawionego w tym przykładzie.

Zaburzenia sygnału

  • RTCPeerConnection nie rozpocznie zbierania kandydatów, dopóki nie zostanie wywołana funkcja setLocalDescription(). Jest to wymagane w wersji roboczej JSEP IETF.
  • Skorzystaj z Trickle ICE. Zadzwoń do firmy addIceCandidate(), gdy tylko pojawią się kandydaci.

Gotowe serwery sygnałowe

Jeśli nie chcesz robić tego samodzielnie, dostępnych jest kilka serwerów sygnałów WebRTC, które korzystają z Socket.IO tak jak w poprzednim przykładzie i są zintegrowanych z bibliotekami JavaScript klienta WebRTC:

  • webRTC.io to jedna z pierwszych bibliotek abstrakcji dla WebRTC.
  • Signalmaster to serwer sygnałów utworzony do użytku z biblioteką klienta JavaScript SimpleWebRTC.

Jeśli nie chcesz pisać żadnego kodu, możesz skorzystać z oferowanych przez firmy takie jak vLine, OpenTok czy Asterisk pełne komercyjne platformy WebRTC.

Dla przypomnienia, w pierwszych latach pracy WebRTC na platformie Apache Ericsson zbudował serwer sygnałowy z wykorzystaniem języka PHP w Apache. Są one już nieaktualne, ale jeśli zastanawiasz się nad podobnymi zmianami, warto przyjrzeć się kodowi.

Bezpieczeństwo sygnalizacji

„Bezpieczeństwo to sztuka, żeby nic nie stało”.

Salman Rushdie

Szyfrowanie jest obowiązkowe w przypadku wszystkich komponentów WebRTC.

Jednak mechanizmy sygnałów nie są zdefiniowane w standardach WebRTC, dlatego to od Ciebie zależy, czy sygnalizacja będzie bezpieczna. Jeśli haker zdoła przechwycić sygnały, może zatrzymać sesje, przekierować połączenia oraz nagrywać, zmieniać lub wstrzykiwać treści.

Najważniejszym czynnikiem w zabezpieczaniu sygnałów jest korzystanie z bezpiecznych protokołów – HTTPS i WSS (np. TLS), które zapewniają, że wiadomości nie zostaną przechwycone w postaci niezaszyfrowanej. Uważaj też, aby nie przesyłać komunikatów sygnalizacyjnych w taki sposób, aby inni rozmówcy nie mieli do nich dostępu za pomocą tego samego serwera sygnałowego.

Po sygnalizacji: używaj ICE do radzenia sobie z sieciami NAT i zaporami sieciowymi

Do sygnalizowania metadanych aplikacje WebRTC korzystają z serwera pośredniego, ale w przypadku rzeczywistych multimediów i strumieniowego przesyłania danych po nawiązaniu sesji RTCPeerConnection próbuje połączyć klientów bezpośrednio lub peer-to-peer.

W prostszym świecie każdy punkt końcowy WebRTC miałby unikalny adres, który mógłby wymieniać z innymi elementami równorzędnymi w celu bezpośredniej komunikacji.

Proste połączenie peer-to-peer
Świat bez NAT i zapór sieciowych

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 korporacyjnymi zaporami sieciowymi. Zapora sieciowa i NAT mogą być wdrożone na tym samym urządzeniu, na przykład w domowym routerze Wi-Fi.

Połączenia równorzędne za NATami i zaporami sieciowymi
Świat rzeczywisty

Aplikacje WebRTC mogą korzystać z platformy ICE, aby radzić sobie z złożonymi sieciami w świecie rzeczywistym. Aby było to możliwe, aplikacja musi przekazywać adresy URL serwera ICE do usługi RTCPeerConnection zgodnie z opisem w tym artykule.

ICE próbuje znaleźć najlepszą ścieżkę do łączenia aplikacji z grupy porównawczej. Próbuje równolegle wypróbowywać wszystkie możliwości i wybiera tę, która sprawdza się najlepiej. ICE najpierw próbuje nawiązać połączenie przy użyciu adresu hosta uzyskanego z systemu operacyjnego i karty sieciowej urządzenia. Jeśli to się nie uda (co zrobi to w przypadku urządzeń obsługujących NAT), ICE uzyska zewnętrzny adres przy użyciu serwera STUN. Jeśli to się nie uda, ruch będzie kierowany przez serwer przekazujący TURN.

Inaczej mówiąc, serwer STUN służy do pobierania zewnętrznego adresu sieciowego, a serwery TURN są używane do przekazywania ruchu w przypadku niepowodzenia połączenia bezpośredniego (peer-to-peer).

Każdy serwer TURN obsługuje STUN. Serwer TURN to serwer STUN z dodatkowymi wbudowanymi funkcjami przekazywania. ICE radzi sobie też ze złożonością konfiguracji NAT. W rzeczywistości dziurkowanie NAT może wymagać czegoś więcej niż tylko publicznego adresu IP:port.

Adresy URL serwerów STUN lub TURN są (opcjonalnie) określane przez aplikację WebRTC w obiekcie konfiguracji iceServers, który jest pierwszym argumentem konstruktora RTCPeerConnection. W przypadku parametru 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, działanie ICE stanie się automatyczne. RTCPeerConnection używa platformy ICE do opracowania najlepszej ścieżki między aplikacjami równorzędnymi, współpracując w razie potrzeby z serwerami STUN i TURN.

STUN

NAT zapewniają urządzenie z adresem IP, którego można używać w prywatnej sieci lokalnej. Tego adresu nie można używać zewnętrznie. Bez adresu publicznego połączenia równorzędne WebRTC nie mogą się ze sobą komunikować. Aby obejść ten problem, WebRTC korzysta z metody STUN.

Serwery STUN działają w publicznym internecie i mają jedno proste zadanie: sprawdzić adres IP:port przychodzącego żądania (z aplikacji działającej w sieci NAT) i wysłać ten adres w odpowiedzi. Inaczej mówiąc, aplikacja wykorzystuje serwer STUN do wykrywania adresu IP:port z perspektywy publicznej. Ten proces umożliwia peerowi WebRTC uzyskanie do siebie publicznie dostępnego adresu, a następnie przekazanie go do innego połączenia za pomocą mechanizmu sygnałów w celu utworzenia bezpośredniego linku. W praktyce różne NAT działają na różne sposoby i może istnieć wiele warstw NAT, ale zasada jest taka sama.

Serwery STUN nie muszą za wiele robić ani zapamiętywać, więc te mniej wydajne serwery mogą obsłużyć dużą liczbę żądań.

Większość wywołań WebRTC realizuje połączenia za pomocą metody STUN – 86% według strony Webrtcstats.com, ale może to być mniej w przypadku wywołań między elementami równorzędnymi za zaporami sieciowymi i złożonymi konfiguracjami NAT.

Połączenia peer-to-peer za pomocą serwera STUN
Korzystanie z serwerów STUN do pobierania publicznych adresów IP:port

OKRÓT

RTCPeerConnection próbuje skonfigurować bezpośrednią komunikację między peerami przez UDP. Jeśli to nie pomoże, RTCPeerConnection użyje TCP. Jeśli to się nie uda, możesz używać serwerów TURN jako kreacji zastępczych, które przekazują dane między punktami końcowymi.

Dla przypomnienia – tryb TURN służy do przekazywania dźwięku, obrazu i strumieni danych między aplikacjami równorzędnymi, a nie danych.

Serwery TURN mają adresy publiczne, więc sieci równorzędne mogą kontaktować się z nimi, nawet jeśli znajdują się za zaporą sieciową lub serwerami proxy. Serwery TURN mają pozornie proste zadanie – przekazywanie strumienia. Jednak w przeciwieństwie do serwerów STUN zużywają one dużą przepustowość. Innymi słowy, serwery TURN muszą być wydajniejsze.

Połączenia peer-to-peer za pomocą serwera STUN
Monty: STUN, TURN i sygnalizacja

Ten diagram przedstawia działanie TURN. Prawdziwa STUN się nie udała, więc każdy z innych zespołów używa serwera TURN.

Wdrażanie serwerów STUN i TURN

Do celów testowych Google używa publicznego serwera STUN o nazwie stun.l.google.com:19302, który jest używany przez appr.tc. W przypadku produkcyjnej usługi STUN/TURN użyj serwera turniejowego rfc5766-turn-server. Kod źródłowy serwerów STUN i TURN jest dostępny na stronie GitHub. Znajdziesz tam też linki do różnych źródeł informacji o instalacji serwera. Dostępny jest też obraz maszyny wirtualnej dla Amazon Web Services.

Alternatywny serwer TURN jest dostępny jako kod źródłowy oraz dla AWS. Oto instrukcje konfigurowania ograniczeń w Compute Engine.

  1. W razie potrzeby otwórz zaporę sieciową dla tcp=443, udp/tcp=3478.
  2. Utwórz 4 instancje, po jednej dla każdego publicznego adresu IP, obraz Standard Ubuntu 12.06.
  3. Skonfiguruj lokalną konfigurację zapory sieciowej (zezwól na DOWOLNE z DOWOLNYCH).
  4. Narzędzia do instalacji: shell sudo apt-get install make sudo apt-get install gcc
  5. Zainstaluj libre ze strony creytiv.com/re.html.
  6. Pobierz plik Restund ze strony creytiv.com/restund.html i rozpakuj./
  7. wget hancke.name/restund-auth.patch i prześlij zgłoszenie w patch -p1 < restund-auth.patch.
  8. Uruchom make, sudo make install, aby pobrać libre i resund.
  9. Dostosuj usługę restund.conf do swoich potrzeb (zastąp adresy IP i upewnij się, że zawiera ten sam wspólny obiekt tajny) i skopiuj plik do /etc.
  10. Skopiuj restund/etc/restund do /etc/init.d/.
  11. Skonfiguruj ustawienie:
    1. Ustaw LD_LIBRARY_PATH.
    2. Skopiuj restund.conf do /etc/restund.conf.
    3. Ustaw w polu restund.conf właściwą 10. Adres IP.
  12. Bieganie po treningu
  13. Przeprowadź test za pomocą klienta kaskadowego na komputerze zdalnym: ./client IP:port

Więcej niż jeden do jednego: wielostronny WebRTC

Możesz też zapoznać się z proponowanym przez Justina Uberti standardem IETF dla interfejsu API typu REST zapewniającego dostęp do usług TURN.

Łatwo sobie wyobrazić przypadki użycia strumieniowania multimediów, które wykraczają poza proste połączenie indywidualne. 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 połączeń RTCPeerConnection, aby każdy punkt końcowy łączył się z każdym innym punktem końcowym w konfiguracji sieci typu mesh. Takie podejście jest stosowane w aplikacjach, takich jak talky.io. Ten sposób sprawdza się u niewielu aplikacji porównawczych. Poza tym przetwarzanie i wykorzystanie przepustowości staje się nadmierne, zwłaszcza w przypadku klientów korzystających z urządzeń mobilnych.

Sieć typu mesh: małe połączenie N-way
Topologia sieci typu mesh: wszyscy mają połączenie ze wszystkimi

Aplikacja WebRTC może też wybrać jeden punkt końcowy, aby rozprowadzać strumienie do wszystkich pozostałych w ramach konfiguracji gwiazdy. Można też uruchomić punkt końcowy WebRTC na serwerze i zbudować własny mechanizm redystrybucji (przykładowa aplikacja kliencka jest dostarczana przez webrtc.org).

Od wersji Chrome 31 i Opera 18 MediaStreamz jednego RTCPeerConnection mogą być używane jako dane wejściowe dla drugiego. Może to zapewnić większą elastyczność architektury, ponieważ aplikacja internetowa może obsługiwać kierowanie połączeń przez wybór innego peera, z którym ma się połączyć. Aby zobaczyć, jak to działa, zapoznaj się z artykułami na temat przykładów połączeń równorzędnych z WebRTC i przykładów użycia protokołu WebRTC na wielu połączeniach równorzędnych.

Element sterujący wielopunktowego

W przypadku dużej liczby punktów końcowych lepiej jest użyć jednostki sterującej wielopunktowej (MCU). Jest to serwer służący jako pomost do dystrybucji multimediów między dużą liczbą uczestników. Jednostki MCU poradzą sobie z różnymi rozdzielczościami, kodekami i liczbą klatek podczas wideokonferencji podczas wideokonferencji. Obsługują transkodowanie, selektywne przekazywanie strumienia oraz miksują lub nagrywają dźwięk i obraz. W przypadku rozmów z wieloma osobami trzeba się zastanowić, jak to zrobić, szczególnie w przypadku wyświetlania wielu wejść wideo i miksowania dźwięku z różnych źródeł. Platformy działające w chmurze, takie jak vLine, również próbują zoptymalizować kierowanie ruchu.

Można kupić kompletny pakiet sprzętowy MCU lub stworzyć własny.

Widok z tyłu urządzenia Cisco MCU5300
Tylna część Cisco MCU

Dostępnych jest kilka opcji oprogramowania open source MCU. Na przykład Licode (wcześniej Lynckia) tworzy MCU typu open source dla WebRTC. OpenTok obsługuje Mantis.

Więcej niż przeglądarki: VoIP, telefony i wiadomości

Ustandaryzowany charakter WebRTC pozwala nawiązać komunikację między aplikacją WebRTC działającą w przeglądarce a urządzeniem lub platformą działającą na innej platformie komunikacyjnej, takiej jak telefon czy system do wideokonferencji.

SIP to protokół sygnału używany w systemach VoIP i wideokonferencjach. Aby umożliwić komunikację między aplikacją internetową WebRTC a klientem SIP, takim jak system do wideokonferencji, WebRTC potrzebuje serwera proxy do zapośredniczenia sygnału. Sygnalizacja musi przechodzić przez bramę, ale po nawiązaniu komunikacji ruch SRTP (wideo i audio) może odbywać się bezpośrednio przez połączenie peer-to-peer.

Publiczna komutowana sieć telefoniczna (PSTN) to sieć z przełącznikiem obwodu dla wszystkich „starych” analogowych telefonów. W przypadku połączeń między aplikacjami internetowymi i telefonami WebRTC ruch musi przechodzić przez bramę PSTN. Podobnie aplikacje internetowe WebRTC potrzebują pośredniego serwera XMPP do komunikacji z punktami końcowymi Jingle, takimi jak klienty czatu. Jingle został opracowany przez Google jako rozszerzenie XMPP w celu umożliwienia obsługi wiadomości głosowych i wideo w usługach do przesyłania wiadomości. Obecne implementacje WebRTC oparte są na bibliotece libjingle w C++, czyli implementacji Jingle opracowanej na początku dla Talk.

Wiele aplikacji, bibliotek i platform korzysta z funkcji WebRTC do komunikowania się ze światem zewnętrznym:

  • sipML5: klient SIP korzystający z JavaScriptu typu open source
  • jsSIP: biblioteka SIP JavaScript.
  • Phono: interfejs API telefonu JavaScript typu open source stworzony jako wtyczka
  • Zingaya: widżet na telefonie, który można umieścić w witrynie;
  • Twilio: połączenia głosowe i wiadomości.
  • Uberconference: konferencja

Programiści sipML5 stworzyli też bramę webrtc2sip. Tethr i Tropo zademonstrowały strukturę komunikacji dotyczącej katastrof „w aktce” przy użyciu komórki OpenBTS, która umożliwia komunikację między telefonami z internetem a komputerami przez WebRTC. To komunikacja telefoniczna bez operatora.

Więcej informacji

Ćwiczenie z programowania WebRTC zawiera szczegółowe instrukcje tworzenia aplikacji do obsługi czatu wideo i tekstowego przy użyciu usługi sygnalizacji Socket.io działającej w węźle.

Prezentacja Google I/O WebRTC z 2013 roku z Justinem Uberti, kierownikiem technicznym WebRTC

Prezentacja SFHTML5 Chrisa Wilsona: Wprowadzenie do aplikacji WebRTC

350-stronicowa książka WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web zawiera wiele szczegółów na temat ścieżek danych i sygnałów oraz zawiera wiele szczegółowych diagramów topologii sieci.

WebRTC i sygnalizacja: czego nauczyło nas dwuletnie doświadczenie – post na blogu TokBox na temat tego, dlaczego rezygnacja ze specyfikacji sygnału była dobrym pomysłem

Przewodnik praktyczny tworzenia aplikacji WebRTC autorstwa Ben Stronga zawiera wiele informacji o topologii i infrastrukturze WebRTC.

Rozdział WebRTC w opisie Ilyi Grigorik o sieci wysokiej wydajności w przeglądarce szczegółowo opisuje architekturę, przypadki użycia i wydajność WebRTC.