WebSocket – dwukierunkowa komunikacja z przeglądarką

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.

3 myśli na temat “WebSocket – dwukierunkowa komunikacja z przeglądarką”

  1. W skrypcie jest błąd. Zamiast:
    $Server->bind(‘message’, ‘onMessage’);
    powinno być
    $Server->bind(‘message’, ‘wsOnMessage’);

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *