Client Javascript e Server PHP
WebSocket è una funzionalità , introdotta alcuni anni fa da HTML5, che consente di stabilire delle connessioni socket tra un browser web ed un server. Oggi WebSocket può essere usato, tramite Javascript, per stabilire una connessione con il server, con la conseguenza che tutti i dati del websocket (frame) vengono inviati direttamente sul socket invece di funzionare come richiesta in attesa di una risposta HTTP, fornendo quindi una comunicazione permanente molto veloce tra un browser web ed un server.
I canali di comunicazione sono basati su una singola connessione TCP. Il protocollo WebSocket inizia la comunicazione in modalità HTTP, durante una fase iniziale chiamata handshake, e continua, dopo, con l'attivazione del suo protocollo WS. Grazie ad HTML5 che utilizza la tecnologia WebSocket, ora possiamo predisporre il client usando Javascript. La figura seguente illustra le modalità della comunicazione con protocollo WebSocket.
Vediamo di creare, allora, un semplice sistema di chat con questa nuova tecnologia: la parte client realizzata con i WebSocket di HTML5 e la parte server con i socket PHP. La figura seguente illustra una tipica sessione, lato client, dell'attività della chat.
I vecchi browser non riconoscono WebSocket, quindi per provare l'esempio ci vuole uno degli ultimi browser con supporto delle funzioni WebSocket di HTML5. E' meglio consultare, a tale scopo, caniuse.com per avere informazioni dettagliate.
Client Javascript
La realizzazione del Client con WebSocket è molto semplice, l'intero codice consiste di alcuni eventi e qualche metodo. Cominciamo col dare un'occhiata al codice seguente.
// Creazione di un nuovo oggetto WebSocket websocket = new WebSocket("ws://localhost:5000/chat-server.php"); websocket.onopen = function(ev) { /* fa qualcosa */ }; // Evento on open websocket.onclose = function(ev) { /* fa qualcosa */ }; // Evento on close websocket.onmessage = function(ev) { /* fa qualcosa */ }; // Evento on message websocket.onerror = function(ev) { /* fa qualcosa */ }; // Evento on error websocket.send(message); // Metodo send websocket.close(); // Metodo close
Per aprire una connessione socket, semplicemente, chiamiamo new WebSocket(ws://SERVER URL)
, notando che WebSocket utilizza un protocollo diverso per le connessioni: ws:// invece di http://. Il resto rimane uguale: indirizzo, porta, nome del server.
Il tutto si tradurrà in una assegnazione del tipo:
var wsUri = "ws://localhost:5000/chat-server.php";
che rispecchia lo schema:
Protocollo | Indirizzo | Porta | Server |
---|---|---|---|
ws | localhost | 5000 | chat-server.php |
Subito dopo l'apertura della connessione occorre attivare alcuni gestori di eventi per conoscere lo stato della connessione, eventuali errori e messaggi in arrivo:
WebSocket() - crea un nuovo oggetto WebSocket.
.onopen - Evento che si verifica quando si stabilisce una connessione.
.onclose - Evento che si verifica alla chiusura di una connessione.
.onmessage - Evento che si verifica quando il client riceve dei dati dal server.
.onerror - Evento che si verifica quando c'è un errore.
.send(message) - Metodo per trasmettere dati al server.
.close() - Metodo per chiudere una connessione.
La pagina per realizzare il WebSocket, con il supporto di jQuery, sarà simile a questa:
$(document).ready( function() { // Apertura connessione WebSocket var wsUri = "ws://localhost:5000/chat-server.php"; websocket = new WebSocket(wsUri); websocket.onopen = function(ev) { // Connessione al server alert('Connesso al server '); } websocket.onclose = function(ev) { // Chiusura connessione alert('Disconnesso'); }; websocket.onmessage = function(ev) { // Messaggio ricevuto alert('Messaggio '+ev.data); }; websocket.onerror = function(ev) { // Errore alert('Errore '+ev.data); }; $('#send').click( function() { // Invio messaggio var mymessage = 'Testo del messaggio'; websocket.send(mymessage); } ); } );
Come spiegato nei frammenti di codice precedenti, si parte con la creazione di un oggetto WebSocket, allegando quindi i gestori di eventi ed utilizzando infine il metodo websocket.send()
per inviare i dati.
Poiché dobbiamo trasmettere un insieme di valori per la chat (nome utente, messaggio, colore, ecc) convertiremo i nostri dati in formato JSON prima di inviarli al server.
Nota:
JSON, acronimo di JavaScript Object Notation, è un formato adatto per lo scambio dei dati in applicazioni client-server. È basato sul linguaggio JavaScript Standard ECMA-262 3ª edizione dicembre 1999, ma ne è indipendente, e viene usato in AJAX come alternativa a XML/XSLT.
La semplicità di JSON ne ha decretato un rapido utilizzo specialmente nella programmazione in AJAX. Il suo uso tramite JavaScript è particolarmente semplice, infatti l'interprete è in grado di eseguirne il parsing tramite una semplice chiamata alla funzione eval(). Questo fatto lo ha reso velocemente molto popolare a causa della diffusione della programmazione in JavaScript nel mondo del Web.
$(document).ready(function() { // creazione oggetto WebSocket var usrcolor = getcolor(); var wsUri = "ws://localhost:5000/chat-server.php"; websocket = new WebSocket(wsUri); websocket.onopen = function(ev) { // apertura connessione $('#message_box').append("<div class=\"system_msg\">Connesso!</div>"); } $('#send-btn').click(function() { // gestione pulsante send var mymessage = $('#message').val(); // testo del messaggio var myname = $('#name').val(); // nome utente if (myname == "") { // nome vuoto ? alert("Digita il tuo nome, per favore!"); return; } if (mymessage == "") { // messaggio vuoto ? alert("Digita il messaggio, per favore!"); return; } var msg = { // dati in formato JSON message: mymessage, name: myname, color: usrcolor }; websocket.send(JSON.stringify(msg)); // conversione ed invio dati }); websocket.onmessage = function(ev) { // messaggio ricevuto dal server ? var msg = JSON.parse(ev.data); // PHP invia i dati JSON var type = msg.type; // tipo del messaggio var umsg = msg.message; // testo del messaggio var uname = msg.name; // nome utente var ucolor = msg.color; // colore if (type == 'usermsg') { st1 = "<div><span class=\"user_name\" style=\"color:#" + ucolor + "\">"; st1 = st1 + uname + "</span> : <span class=\"user_message\">"; st1 = st1 + umsg + "</span></div>"; $('#message_box').append(st1); } if (type == 'system') { $('#message_box').append("<div class=\"system_msg\">"+umsg+"</div>"); } $('#message').val(''); //reset del testo }; websocket.onerror = function(ev) { $('#message_box').append("<div class=\"system_error\">Errore - "+ev.data+"</div>"); }; websocket.onclose = function(ev) { $('#message_box').append("<div class=\"system_msg\">Connessione Chiusa</div>"); }; });
Di seguito viene indicato il codice base HTML per la chat, ma non viene riportato il codice CSS che è, invece, incluso nel listato definitivo.
<div class="chat_wrapper"> <div class="message_box" id="message_box"></div> <div class="panel"> <input type="text" name="name" id="name" placeholder="Nome Utente" maxlength="10" style="width:20%" /> <input type="text" name="message" id="message" placeholder="Messaggio" maxlength="80" style="width:60%" /> <button id="send-btn">Invia</button> </div> </div>
Client Chat
Ed infine ecco il listato base definitivo del client per la chat.
Salvataggio del client
Salvare il codice precedente nella cartella public_html
dandogli il nome chat-client.html
ed utilizzarlo con un browser (compatibile WebSocket) solo dopo aver avviato il server descritto qui di seguito.
Server PHP
Adesso che abbiamo finito di preparare la pagina client, è arrivato il momento di attivare un server, che rimanga continuamente in attesa e che possa rispondere alle richieste dei client.
Il nostro server dovrà , in particolare, essere in grado di: eseguire l'handshake al WebSocket, ricevere i dati della chat, inviare messaggi, gestire più client. Sostanzialmente sarà molto simile ai vari esempi di socket visti in precedenza, ma con qualche caratterizzazione legata al particolare lavoro che dovrà svolgere:
Aprire un socket
Realizzare il Bind dell'indirizzo
Mettersi in ascolto per le connessioni
Accettare le connessioni
Eseguire l'handshake di WebSocket
Codificare/Decodificare i frame dei dati
Le fasi iniziali sono le solite, già viste in precedenza, ma alleggerite della gestione degli errori e con l'introduzione di alcune variabili di supporto.
$host = 'localhost'; // Indirizzo dell'host $port = '5000'; // Numero di porta $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // Creazione socket TCP/IP socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1); // Impostazione porto riusabile socket_bind($socket, 0, $port); // Bind del socket socket_listen($socket); // Socket in ascolto banner(); // Messaggio di Server pronto
L'introduzione della funzione socket_set_option serve a modificare le impostazioni predefinite dei vari parametri di un socket. Per funzionare ha bisogno di 4 informazioni:
1 - Identificatore del socket
2 - Livello di azione
3 - Nome del parametro da modificare
4 - Valore del parametro da modificare
L'aspetto che caratterizza questo server è, però, costituito dalla fase di handshake che si attiva all'arrivo di ogni nuovo client. Per praticità questo lavoro viene realizzato da una funzione apposita.
function handshake($receved_header,$client_conn, $host, $port) { $headers = array(); $lines = preg_split("/\r\n/", $receved_header); foreach($lines as $line) { $line = chop($line); if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) { $headers[$matches[1]] = $matches[2]; } } $secKey = $headers['Sec-WebSocket-Key']; $secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))); // Header dell'handshake $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "WebSocket-Origin: $host\r\n" . "WebSocket-Location: ws://$host:$port/chat-server.php\r\n". "Sec-WebSocket-Accept:$secAccept\r\n\r\n"; socket_write($client_conn,$upgrade,strlen($upgrade)); }
Un altro aspetto che vale la pena notare è l'introduzione di alcuni comandi (date, time, random, port, help, quit) che possono essere digitati nell'area messaggio, questi comandi vengono esaminati nel seguente frammento di codice:
switch($user_message) { case "date": $user_message = "<b>date</b><br>Oggi è il ".date('d.m.Y'); break; case "time": $user_message = "<b>time</b><br>Sono le ".date('H.i.s'); break; case "random": $user_message = "<b>random</b><br>Il tuo numero fortunato è il ".rand(1,100); break; case "port": $user_message = "<b>port</b><br>Sei collegato alla porta n. ".$port; break; case "help": $st = "<b>help</b>"; $st .= "<br>Nel campo <b>Messaggio</b> si possono inserire:"; $st .= "<br>- messaggi per la chat,"; $st .= "<br>- comandi per il server.<br>"; $user_message = $st; break; case "quit": $st = "<b>quit</b>"; $st .= "<br>Disconnesso su richiesta del Client."; $user_message = $st; $response_text = mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message, 'color'=>$user_color))); send_message($response_text); // Invio dei dati $found_socket = array_search($changed_socket, $clients); socket_getpeername($changed_socket, $ip); unset($clients[$found_socket]); // Notifica a tutti la disconnessione $response = mask(json_encode(array('type'=>'system', 'message'=>$ip.' disconnesso'))); send_message($response); break; }
Server Chat
Il listato completo del server PHP è riportato qui di seguito.
Esecuzione dello script
Dopo aver salvato il codice precedente nella cartella public_html
dandogli il nome chat-server.php
, lo possiamo mandare in esecuzione, come al solito, direttamente dalla riga di comando di un terminale:
$ php -q chat-server.php
A questo punto possiamo aprire una serie di finestre del browser (compatibile con WebSocket), avviare in ognuna di esse il client chat-client.html e sperimentare le funzionalità del sistema di comunicazione.
Colui che prova gioia al servizio di Dio vedrà ogni cosa provare la stessa gioia al suo servizio, e colui che trova la freschezza dei suoi occhi in Dio vedrà che ogni cosa troverà la stessa freschezza posando semplicemente lo sguardo su di lui.
abu 'Abd ar-Rahman as-Sulamì