dal 2015 - visita n. 1635
php socket parte 6
php socket parte 6

 

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:

ProtocolloIndirizzoPortaServer
wslocalhost5000chat-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:


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:

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.


















Menù
Introduzione
Ambiente di sviluppo
intro php-01
tag e commenti php-02
variabili e tipi php-03
operatori php-04
php IF esercizi
php FOR esercizi
php objects intro
php objects parte 1
php objects parte 2
php objects parte 3
php objects parte 4
php objects parte 5
CLIL
php socket intro
php socket parte 1
php socket parte 2
php socket parte 3
php socket parte 4
php socket parte 5
php socket parte 6
php socket extra
linux netcat


MiniGuida PHP
Guida Ufficiale PHP (en)
Form Base
Flat DataBase
Pagine Protette
mySQL



Aliena vitia in oculis habemus, a tergo nostra sunt.
(I difetti altrui li abbiamo davanti agli occhi, i nostri dietro le spalle.
Seneca

Valid CSS!
pagina generata in 0.001 secondi