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