Protokół WebSocket daje nam dwukierunkową komunikację serwer-przeglądarka. Nie pojedyncze wywołania GET, POST itp ale stałe połączenie. Możemy przesyłać komunikaty, dane binarne itp. Można np. zbudować czat, który nie będzie musiał co chwilę odpytywać serwera o nowe wiadomości.
Jak działa WebSocket
Aby otworzyć połączenie korzystamy z url w postaci ws: lub wss: (zamiast http: lub https:). Przeglądarka wysyła do serwera nagłówki w postaci:
GET /connection HTTP/1.1 Host: localhost Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Origin: http://localhost
To minimalny zestaw nagłówków, które trzeba wysłać. Ważny jest nagłówek z kluczem, który serwer przelicza i odsyła:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Serwer może przyjąć połączenie wysyłając kod 101 – zmieniamy protokół albo odrzucić odsyłając kod błędu. Możemy odrzucić połączenie z kilku powodów – serwer nie obsługuje WebSocket, nie zgadza się na przyjęcie połączenia z adresu podanego w origin, nie przyjmuje klucza itp.
Klucz jest kluczowy w nawiązaniu połączenia. Do tego, co przysłał klient trzeba dokleić stały kod 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
. Następnie obliczyć SHA-1 z całości i odesłać w kodowaniu base64. Inaczej połączenie się nie powiedzie.
Jeśli udało się nawiązać połączenie, od tego momentu serwer i klient mogą sobie wysyłać pakiety. Połączenie jest otwarte dopóki jedna ze stron go nie zamknie.
WebSocket w PHP i JavaScript
W sieci można znaleźć bardzo dobrą bibliotekę PHP-Websockets a także przykłady prostego czatu. Korzystając z tego drugiego źródła stworzymy minimalny kod łączący przeglądarkę z serwerem.
Na początek kod serwera w PHP:
<?php require 'class.PHPWebSocket.php'; // Funkcja będzie wywoływana przy każdej przychodzącej wiadomości function wsOnMessage($clientID, $message, $messageLength, $binary) { global $Server; // wypisujemy w konsoli to, co przyszło printf("Client %s sent: %s\n",$clientID,$message); // odsyłamy wiadomość z przedrostkiem "Re:" $Server->wsSend($clientID, "Re: $message"); } // Tworzymy klasę, podłączamy naszą funckję i uruchamiamy serwer $Server = new PHPWebSocket(); $Server->bind('message', 'wsOnMessage'); $Server->wsStartServer('localhost', 9000);
To wszystko. Cały serwer, który przyjmuje wiadomości, wypisuje je w konsoli i odsyła z powrotem mieści się w powyższym kodzie. Jeśli zapisaliśmy ten kod np. w pliku serwer.php, to w konsoli możemy go uruchomić:
$ php serwer.php
Od tego momentu na porcie 9000 nasz serwer nasłuchuje czekając na połączenia. Czas na klienta. W JavaScript tworzymy równie prosty kod:
<script type="text/javascript"> var socket; function start() { var host = "ws://127.0.0.1:9000/"; // nasz adres serwera socket = new WebSocket(host); // zaraz po nawiązaniu połączenia wysyłamy wiadomość socket.onopen = function(msg) { console.log("Connected, status "+this.readyState); socket.send("My message to the server"); }; // po otrzymaniu wiadomości można zamknąć połączenie socket.onmessage = function(msg) { console.log("Received: "+msg.data); socket.close(); }; // logujemy zamknięcie połączenia socket.onclose = function(msg) { console.log("Disconnected, status "+this.readyState); }; } </script>
Wystarczy tylko uruchomić naszą funkcję przy starcie strony:
<body onload="start()"></body>
Teraz konsola w przeglądarce powie nam, że połączyliśmy się, otrzymaliśmy wiadomość i zakończyliśmy połączenie:
Connected, status 1 Received: Re: My message to the server Disconnected, status 3
Zajrzyjmy do konsoli, gdzie uruchomiliśmy serwer. Faktycznie, będzie tu informacja o otrzymanej wiadomości:
Client 1 sent: My message to the server
Przykład czatu zawiera więcej kodu – w pełni funkcjonalny czat, z którym może połączyć się wielu klientów. Na tej bazie możemy budować dowolne serwisy oparte o WebSockety.
Co dalej?
Warto przeczytać specyfikację WebSocket w RFC 6455. Na sieci jest też dużo bibliotek PHP tworzących serwery WebSocket oraz równie dużo kodu JavaScript wspomagającego obsługę protokołu w przeglądarkach.
WebSocekt jest obsługiwany przez większość nowoczesnych przeglądarek. Aktualna lista jest dostępna w angielskiej Wikipedii.
Na bazie protokołu możemy tworzyć nie tylko czaty. Możemy na bieżąco aktualizować dane np. w wykresach giełdowych i inne dane przesyłane na bieżąco. W kolejnym artykule opiszę też protokół MQTT, z którym można współpracować właśnie za pośrednictwem WebSocket.
W skrypcie jest błąd. Zamiast:
$Server->bind(‚message’, ‚onMessage’);
powinno być
$Server->bind(‚message’, ‚wsOnMessage’);
Dzięki za zauważenie!
Świetnie tego potrzebowałem. Wielkie dzieki