Realizacja komunikacji dwustronnej na przykładzie zastosowania WebSocket + Play Framework
ProgramowanieWebSocket to technologia będąca częścią specyfikacji HTML5, która mocno uproszczając w założeniu ma rozwiązać problemy stosowania nagminnie technologii AJAX wszędzie tam, gdzie się do tego po prostu nie nadaje. Jednakże do tej pory nie było innej alternatywy - wydaje się że świat webu jest już w pełni gotowy, aby nowa technologia w pełni zagościła w przeglądarkach swoich użytkowników.
WebSocket to technologia będąca częścią specyfikacji HTML5, która mocno uproszczając w założeniu ma rozwiązać problemy stosowania nagminnie technologii AJAX wszędzie tam, gdzie się do tego po prostu nie nadaje. Jednakże do tej pory nie było innej alternatywy - wydaje się że świat webu jest już w pełni gotowy, aby nowa technologia w pełni zagościła w przeglądarkach swoich użytkowników.
Technologia została wprowadzona do przeglądarek Google Chrome już od wersji 4, Firefox 4, Opera 11 czy IE 10, lecz każda nowinka musi dojrzeć do stosowania produkcyjnego, jak i sami deweloperzy dobierając odpowiednie rozwiązania do potrzeb. Aktualnie z powodzeniem możemy korzystać z tej technologii bez obaw o kompatybilność co możemy sprawdzić tutaj: http://caniuse.com/#feat=websockets
WebSocket zapewnia dwukierunkowy kanał komunikacji za pośrednictwem tylko jednego gniazda TCP, co umożliwia na otworzenie interaktywnej sesji komunikacyjnej pomiędzy użytkownikiem (jego przeglądarką), a serwerem. Co więcej specyfikacja API pozwala posługiwać się zdarzeniami podczas wysyłania wiadomości do serwera oraz jej odbierania. Należy zaznaczyć, że nie jest konieczne odpytywanie serwera o wiadomość, co należy robić posługując się technologią AJAX.
Specyfikacja WebSocket do celów komunikacji definiuje nowe URI:
- ws: dla połączeń nieszyfrowanych
- wss: dla połączeń szyfrowanych
Poniżej przedstawiamy przykład zastosowania API WebSocket w duecie z frameworkiem Play Framework na serwerze. Play sam w sobie bardzo dobrze wpisuje się w to połączenie swoją bezstanowością oraz natywnym wsparciem dla omawianej technologii.
Część odpowiadająca za nawiązanie połączenia i komunikację po stronie klienta przy założeniu że wysyłane wiadomości są w formacie JSON.
var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket; var socket = new WS('ws:/' + location.host + '/messenger/chat'); // zdarzenie otwarcia połączenia socket.onopen = function() { console.log('Socket Status: ' + socket.readyState + ' (open)'); } // zdarzenie otrzymania wiadomości socket.onmessage = function(event) { var message = JSON.parse(event.data); console.log(message); } // zdarzenie zamknięcia połączenia socket.onclose = function() { console.log('Socket Status: ' + socket.readyState + ' (Closed)'); } ... // koniecznie wykonaj jeśli chcesz zamknąć połączenie socket.close(); socket = null; ... // wysyłanie wiadomości socket.send(JSON.stringify({ messageText: 'Hello WebSocket!' }));
API po stronie JavaScriptu jest naprawdę przystępne i ciężko w powyższym fragmencie kodu tłumaczyć coś więcej. Myślę że komentarze mówią same za siebie. Pierwsza linijka jest bardzo preferowana i zabezpiecza nas na wypadek kłopotów po stronie przeglądarki Firefoxa.
Część serwerowa jest nieco bardziej skomplikowana, jednakże nie powinna absolutnie sprawiać kłopotów. Na pierwszy rzut definiujemy kwestię routingu:
GET /messenger/chat controllers.Messenger.chat()
Metoda statyczna chat w kontrolerze Messenger wygląda tak:
/** * Handle the chat websocket. */ public static WebSocketchat() { return new WebSocket (){ // called when websocket handshake is done public void onReady(WebSocket.In in, WebSocket.Out out){ MessengerConnector.connect(in, out); } }; }
Klasa MessengerConnector pełni tutaj rolę pomocniczą, porządkuje kod oraz robi go czytelniejszym.
package classes; import com.fasterxml.jackson.databind.JsonNode; import play.libs.F.Callback; import play.libs.F.Callback0; import play.libs.Json; import play.mvc.WebSocket; public class MessengerConnector { public static void connect(WebSocket.Inin, WebSocket.Out out){ in.onMessage(new Callback () { @Override public void invoke(JsonNode message) throws Throwable { String messageText = message.get("messageText").asText(); out.write("Hello, your message: " + message); } }); in.onClose(new Callback0() { public void invoke() { // zrób coś, oczyść zasoby etc. } }); } }
Tak wygląda bardzo uproszczona do celów prezentacji idei klasa obsługi WebSocket w frameworku Play. Oczywiście należało by ją doposażyć w zmienną przechowującą połączenia, identyfikację użytkownika, metodę notifyAll, czy writeTo, sprawdzanie czy dany użytkownik jest połączony itd. wg potrzeb ;)
Mamy nadzieję, że wielu nowicjuszom w tej dziedzinie ten wpis pomorze ogarnąć połączenie jakie zaprezentowano w tym duecie. Nic nie stoi na przeszkodzie, aby dowolnie rozbudować część kodu po stronie klienckiej jak i serwerowej.
Polecamy stronę dokumentacji Play Framework dotyczącą WebSocket - https://www.playframework.com/documentation/2.4.x/JavaWebSockets