Przedstawiamy WebSockets – wdrażamy gniazda w internecie

Problem: krótkie połączenia klient-serwer i serwer-klient

Sieć opiera się w dużej mierze na tzw. paradygmencie żądań i odpowiedzi stosowanych w protokole HTTP. Klient wczytuje stronę internetową i nic się nie dzieje, dopóki użytkownik nie przejdzie do następnej strony. Około 2005 roku technologia AJAX sprawiła, że internet stał się bardziej dynamiczny. Jednak całą komunikacją HTTP zarządzał klient, co wymagało interakcji użytkowników lub okresowego odpytywania w celu wczytania nowych danych z serwera.

Technologie, które umożliwiają serwerowi wysyłanie danych do klienta w momencie, gdy wie, że są dostępne nowe dane, istnieją już od jakiegoś czasu. Są to na przykład „Push” lub „Comet”. Jednym z najpowszechniejszych sposobów na „stworzenie iluzji połączenia inicjowanego przez serwer” jest długie sondowanie. W przypadku długiego odpytywania klient nawiązuje połączenie HTTP z serwerem, które pozostaje otwarte do czasu wysłania odpowiedzi. Gdy serwer faktycznie posiada nowe dane, wysyła odpowiedź (inne techniki obejmują żądania Flash, XHR multipart zwane htmlfiles). Długie ankiety i inne techniki działają dość dobrze. Codziennie używasz ich w aplikacjach, takich jak czat w Gmailu.

Jednak wszystkie te obejścia wiążą się z jednym problemem: wiążą się z narzutem protokołu HTTP, co sprawia, że nie są odpowiednie do stosowania w aplikacjach z krótkim czasem oczekiwania. Pomyśl o wieloosobowych strzelankach FPP w przeglądarce lub innych grach online z komponentem czasu rzeczywistego.

Przedstawiamy technologię WebSocket: dostęp do gniazdek w internecie

Specyfikacja WebSocket definiuje interfejs API określający połączenia „socket” między przeglądarką a serwerem. Krótko mówiąc: między klientem a serwerem istnieje trwałe połączenie i obie strony mogą w dowolnym momencie zacząć wysyłać dane.

Pierwsze kroki

Połączenie WebSocket otwiera się, wywołując konstruktor WebSocket:

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

Zwróć uwagę na ws:. To jest nowy schemat adresu URL dla połączeń WebSocket. Istnieje też protokół wss: do bezpiecznego połączenia WebSocket w taki sam sposób, w jaki https: służy do bezpiecznych połączeń HTTP.

Jeśli dodasz niektóre moduły obsługi zdarzeń natychmiast do połączenia, będziesz wiedzieć, kiedy połączenie jest otwarte, odebrane wiadomości i wystąpił błąd.

Drugi argument akceptuje opcjonalne protokoły podrzędne. Może to być ciąg lub tablica ciągów tekstowych. Każdy ciąg znaków powinien reprezentować nazwę protokołu podrzędnego, a serwer akceptuje w tablicy tylko jeden z przekazanych protokołów podrzędnych. Zaakceptowany protokół podrzędny można określić, uzyskując dostęp do właściwości protocol obiektu WebSocket.

Nazwy protokołów podrzędnych muszą być jedną z zarejestrowanych nazw podprotokołów w rejestrze IANA. Obecnie w lutym 2012 roku jest zarejestrowana tylko jedna nazwa podprotokołu (mydło).

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

Komunikacja z serwerem

Gdy tylko nawiążesz połączenie z serwerem (po uruchomieniu zdarzenia open), możemy zacząć wysyłać dane na serwer za pomocą metody send('your message') w obiekcie połączenia. Dawniej obsługiwał on tylko ciągi tekstowe, ale obecnie może też wysyłać wiadomości binarne. Aby wysyłać dane binarne, możesz użyć obiektu Blob lub ArrayBuffer.

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

Serwer może też wysyłać nam wiadomości w dowolnej chwili. W takim przypadku uruchamia się wywołanie zwrotne onmessage. Wywołanie zwrotne odbiera obiekt zdarzenia, a rzeczywista wiadomość jest dostępna przez właściwość data.

WebSocket może również odbierać wiadomości binarne zgodnie z najnowszą specyfikacją. Ramki binarne mogą być odbierane w formacie Blob lub ArrayBuffer. Aby określić format odebranego pliku binarnego, ustaw właściwość binarneType obiektu WebSocket na „blob” lub „arraybuffer”. Domyślny format to „blob”. (Nie musisz dopasowywać parametru binarnego podczas wysyłania).

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

Kolejną nowo dodaną funkcją WebSocket są rozszerzenia. Dzięki rozszerzeniom możliwe będzie wysyłanie ramek skompresowanych, multipleksowych itp. Aby znaleźć rozszerzenia akceptowane przez serwer, sprawdź właściwość rozszerzeń obiektu WebSocket po zdarzeniu otwartym. Od lutego 2012 r. nie jest jeszcze dostępna żadna oficjalnie opublikowana specyfikacja rozszerzeń.

// Determining accepted extensions
console.log(connection.extensions);

Komunikacja między domenami

To nowoczesny protokół, który jest wbudowany bezpośrednio w WebSocket. Mimo że warto komunikować się tylko z klientami i serwerami, którym ufasz, WebSocket umożliwia komunikację między stronami w dowolnej domenie. Serwer decyduje, czy udostępnić swoją usługę wszystkim klientom, czy tylko tym, które znajdują się w zbiorze dobrze zdefiniowanych domen.

Serwery proxy

Każda nowa technologia wiąże się z nowym zestawem problemów. W przypadku WebSocket jest to zgodność z serwerami proxy pośredniczącymi w połączeniach HTTP w większości sieci firmowych. Protokół WebSocket wykorzystuje system uaktualniania HTTP (zwykle używany w przypadku protokołów HTTP/SSL) do „uaktualnienia” połączenia HTTP do połączenia WebSocket. Nie podoba się to niektórym serwerom proxy, przez co połączenie zostanie przerwane. Dlatego nawet jeśli dany klient używa protokołu WebSocket, ustanowienie połączenia może nie być możliwe. To sprawia, że kolejna sekcja jest jeszcze ważniejsza :)

Zacznij korzystać z WebSockets już dziś

Technologia WebSocket jest wciąż młoda i nie została w pełni wdrożona we wszystkich przeglądarkach. Obecnie możesz używać WebSocket z bibliotekami, które korzystają z jednej z wymienionych wyżej wartości zastępczych, gdy WebSocket nie jest dostępny. Biblioteka, która stała się bardzo popularna w tej domenie, to socket.io, która zawiera klienta i implementację protokołu na serwerze, a także wartości zastępcze (od lutego 2012 r. socket.io nie obsługuje jeszcze przesyłania wiadomości binarnych). Istnieją też rozwiązania komercyjne, takie jak PusherApp, które można łatwo zintegrować z dowolnym środowiskiem internetowym za pomocą interfejsu API HTTP do wysyłania wiadomości WebSocket do klientów. Dodatkowe żądanie HTTP zawsze wiąże się z dodatkowym obciążeniem w porównaniu z protokołem WebSocket.

Po stronie serwera

Zastosowanie WebSocket powoduje utworzenie zupełnie nowego wzorca wykorzystania aplikacji po stronie serwera. Tradycyjne stosy serwerów, takie jak LAMP, są zaprojektowane pod kątem cyklu żądań/odpowiedzi HTTP, ale często nie radzą sobie dobrze z dużą liczbą otwartych połączeń WebSocket. Utrzymywanie dużej liczby otwartych połączeń w tym samym czasie wymaga architektury, która otrzymuje wysoką równoczesność przy niskich kosztach wydajności. Tego typu architektury są zwykle zaprojektowane z myślą o wątkach lub tak zwanych nieblokujących operacjach wejścia-wyjścia.

Implementacje po stronie serwera

Wersje protokołu

Protokół przewodowy (uzgadnianie połączenia oraz transfer danych między klientem a serwerem) WebSocket to teraz RFC6455. Najnowsze przeglądarki Chrome i Chrome na Androida są w pełni zgodne ze standardem RFC6455, w tym z komunikatami binarnymi. Oprócz tego przeglądarka Firefox będzie zgodna z wersją 11, a Internet Explorer w wersji 10. Nadal możesz używać starszych wersji protokołów, ale nie jest to zalecane, ponieważ są one często podatne na atak. Jeśli masz implementacje serwera dla starszych wersji protokołu WebSocket, zalecamy uaktualnienie go do najnowszej wersji.

Przykłady zastosowań

Używaj WebSocket wtedy, gdy potrzebujesz połączeń między klientem a serwerem o bardzo niewielkich opóźnieniach, niemal w czasie rzeczywistym. Pamiętaj, że może to wymagać ponownego zastanowienia się nad sposobem tworzenia aplikacji po stronie serwera pod kątem nowych technologii takich jak kolejki zdarzeń. Oto kilka przykładów:

  • Gry online wieloosobowe
  • Aplikacje do obsługi czatu
  • Pasek aktywności na żywo
  • Aktualizacje w czasie rzeczywistym strumieni danych społecznościowych

Przykłady

Odniesienia