Połączenia peer to część specyfikacji WebRTC, która dotyczy łączenia dwóch aplikacji na różnych komputerach w celu komunikowania się przy użyciu protokołu peer-to-peer. Komunikacja między użytkownikami może odbywać się za pomocą wideo, audio lub dowolnych danych binarnych (w przypadku klientów obsługujących interfejs RTCDataChannel
API). Aby dowiedzieć się, jak dwa połączenia równorzędne mogą się połączyć, obaj klienci muszą udostępnić konfigurację serwera ICE. Jest to STUN lub serwer TURN Jest to często sygnał sygnalizacyjny.
Sygnalizacja
Specyfikacja WebRTC zawiera interfejsy API do komunikowania się z serwerem ICE (Internet Connectivity Instytucja), ale komponent sygnalizacyjny nie jest częścią tego interfejsu. Sygnały są niezbędne, by dwóch partnerów mogło się dowiedzieć, w jaki sposób powinni się połączyć. Zwykle te problemy rozwiązuje zwykły interfejs API oparty na HTTP (np. usługa REST lub inny mechanizm RPC), w którym aplikacje internetowe mogą przekazywać niezbędne informacje, zanim rozpocznie się połączenie równorzędne.
Fragment kodu poniżej pokazuje, w jaki sposób ta fikcyjna usługa do sygnalizowania może służyć do wysyłania i odbierania wiadomości asynchronicznie. Będzie ona potrzebna w pozostałych przykładach tego przewodnika.
// Set up an asynchronous communication channel that will be
// used during the peer connection setup
const signalingChannel = new SignalingChannel(remoteClientId);
signalingChannel.addEventListener('message', message => {
// New message from remote client received
});
// Send an asynchronous message to the remote client
signalingChannel.send('Hello!');
Sygnalizowanie można wdrożyć na wiele różnych sposobów. Specyfikacja WebRTC nie preferuje żadnego konkretnego rozwiązania.
Inicjowanie połączeń równorzędnych
Każde połączenie równorzędne jest obsługiwane przez obiekt RTCPeerConnection
. Konstruktor tej klasy przyjmuje jako pojedynczy obiekt RTCConfiguration
obiekt. Ten obiekt określa sposób konfiguracji połączenia równorzędnego i powinien zawierać informacje o używanych serwerach ICE.
Po utworzeniu RTCPeerConnection
musimy utworzyć ofertę SDP lub odpowiedź, w zależności od tego, czy dzwonimy, czy odbieramy połączenie. Po utworzeniu oferty lub odpowiedzi SDP należy ją wysłać do zdalnego peera przez inny kanał. Przekazywanie obiektów SDP do zdalnych równorzędnych połączeń równorzędnych jest nazywane sygnałami i nie jest objęte specyfikacją WebRTC.
Aby zainicjować konfigurację połączenia równorzędnego po stronie wywołania, tworzy obiekt RTCPeerConnection
, a następnie wywołujemy obiekt createOffer()
, by utworzyć obiekt RTCSessionDescription
. Ten opis sesji jest ustawiany jako lokalny opis za pomocą setLocalDescription()
, a następnie wysyłany przez nasz kanał sygnałowy do odbiorcy. Ponadto tworzymy detektor dla naszego kanału sygnalizacyjnego, na którym odbierane są odpowiedzi na proponowany opis sesji po stronie odbiorcy.
async function makeCall() {
const configuration = {'iceServers': [{'urls': 'stun:stun.l.google.com:19302'}]}
const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
if (message.answer) {
const remoteDesc = new RTCSessionDescription(message.answer);
await peerConnection.setRemoteDescription(remoteDesc);
}
});
const offer = await peerConnection.createOffer();
await peerConnection.setLocalDescription(offer);
signalingChannel.send({'offer': offer});
}
Po stronie odbiorcy czekamy na pojawienie się oferty przed utworzeniem instancji RTCPeerConnection
. Gdy to zrobisz, ustawimy otrzymaną ofertę za pomocą setRemoteDescription()
. Następnie łączy się z createAnswer()
, aby utworzyć odpowiedź na otrzymaną ofertę. Odpowiedź jest ustawiana jako opis lokalny przy użyciu setLocalDescription()
, a następnie wysyłana do serwera połączeń przez nasz serwer sygnalizacji.
const peerConnection = new RTCPeerConnection(configuration);
signalingChannel.addEventListener('message', async message => {
if (message.offer) {
peerConnection.setRemoteDescription(new RTCSessionDescription(message.offer));
const answer = await peerConnection.createAnswer();
await peerConnection.setLocalDescription(answer);
signalingChannel.send({'answer': answer});
}
});
Po wyznaczeniu przez obie osoby opisów zarówno sesji lokalnej, jak i zdalnej, zna umiejętności oferowane w grupie zdalnej. Nie oznacza to, że połączenie z innymi aplikacjami jest gotowe. Aby to osiągnąć, musimy zebrać kandydatów ICE na każdym peerze i przesłać (za pomocą kanału sygnalizacyjnego) do drugiej grupy.
Kandydaci do ICE
Zanim 2 osoby będą mogły łączyć się przez WebRTC, muszą wymienić się informacjami o połączeniach. Sytuacja sieciowa może być różna w zależności od wielu czynników, dlatego zwykle do wykrywania potencjalnych kandydatów do połączenia z peerem używana jest usługa zewnętrzna. Ta usługa nazywa się ICE i używa serwera STUN lub TURN. STUN to skrót od sesji sesji NAT-s (Nawigacja sesji) dla NAT. Jest on używany pośrednio w większości aplikacji WebRTC.
TURN (Traversal Using Relay NAT) to bardziej zaawansowane rozwiązanie, które obejmuje protokoły STUN i większość komercyjnych usług WebRTC korzysta z serwera TURN do nawiązywania połączeń między peerami. Interfejs API WebRTC obsługuje zarówno STUN, jak i TURN i jest gromadzony w ramach pełniejszego cyklu połączeń internetowych. Podczas tworzenia połączenia WebRTC zwykle dostarczamy co najmniej 1 serwer ICE w konfiguracji obiektu RTCPeerConnection
.
Trick Trice
Po utworzeniu obiektu RTCPeerConnection
bazowa platforma używa dostępnych serwerów ICE do gromadzenia kandydatów do utworzenia łączności (kandydatów ICE). Zdarzenie icegatheringstatechange
na RTCPeerConnection
wskazuje,
jak wygląda zjazd ICE (new
, gathering
lub complete
).
Choć jest możliwe, że peer rozpocznie oczekiwanie na zakończenie zgromadzenia ICE, zazwyczaj lepiej jest zastosować technikę &trickingu lodu, a następnie przesyłać każdego kandydata do ICE po odkryciu. To znacznie zmniejszy czas konfiguracji połączenia peer-to-peer i pozwoli rozpocząć rozmowę wideo z mniejszym opóźnieniem.
Aby zebrać kandydatów ICE, dodaj detektor zdarzeń icecandidate
.
RTCPeerConnectionIceEvent
wyemitowany w tym detektorze będzie zawierał właściwość candidate
, która reprezentuje nowego kandydata, który należy wysłać do zdalnego peera (zobacz Signinging).
// Listen for local ICE candidates on the local RTCPeerConnection
peerConnection.addEventListener('icecandidate', event => {
if (event.candidate) {
signalingChannel.send({'new-ice-candidate': event.candidate});
}
});
// Listen for remote ICE candidates and add them to the local RTCPeerConnection
signalingChannel.addEventListener('message', async message => {
if (message.iceCandidate) {
try {
await peerConnection.addIceCandidate(message.iceCandidate);
} catch (e) {
console.error('Error adding received ice candidate', e);
}
}
});
Połączenie zostało nawiązane
Po otrzymaniu kandydatów ICE należy spodziewać się, że stan naszego połączenia równorzędnego zmieni się w stan połączenia. Aby tego uniknąć, dodajemy do listy RTCPeerConnection
listę odbiorców nasłuchującą zdarzeń connectionstatechange
.
// Listen for connectionstatechange on the local RTCPeerConnection
peerConnection.addEventListener('connectionstatechange', event => {
if (peerConnection.connectionState === 'connected') {
// Peers connected!
}
});