Skip to content
jimilucio edited this page Nov 26, 2014 · 2 revisions

Websocket API

In questa esercitazione vedremo come utilizzare le connessioni Websocket di HTML5 realizzando una chat.

Step 1. Predisponiamo il nostro ambiente

# git checkout websocket-dev

https://github.com/marcocasario/codemotion2014/tree/websocket-dev

facciamo partire il server!

per fermare l'istanza precedente, premi 'CTRL+C'.

Step 2. Il modulo nodejs-websocket

La versione precedente del server realizzato in node js non espone connessioni WebSocket. In questo esercizio ci serve una nuova versione.

La nuova versione apre una connessione websocket all'indirizzo: localhost:8890 Se la porta è occupata, potete cambiarla aprendo il file server.js che trovate nella root directory

Installiamo il nuovo modulo

Per poter funzionare, il server websocket realizzato con nodejs ha bisogno di un nuovo modulo, che non viene installato di default da node.js. Installiamolo direttamente da command line, utilizzando sempre npm:

# npm install nodejs-websocket

Per verificare che tutto sia andato a buon fine, all'interno del nostro progetto è stata creata una nuova cartella node_modules. Se esploriamo il contenuto di questa cartella possiamo vedere al suo interno una cartella nodejs-websocket.

node_modules

Step 3. Il nuovo server nel dettaglio

Di seguito è possibile dare uno sguardo al server che utilizzeremo in questo esercizio:

//includiamo il nuvo modulo websocket installato
ws = require("nodejs-websocket"),

/* websocket server */
//creiamo una nuova istanza websocket
var server = ws.createServer(function (connection) {
  //valorizziamo le variabili nowTime per avere on oggetto di tipo data sempre a disposizione 
  var nowTime = new Date();
  // new message per ospitare l'oggetto message
  var newMessage = {status: "", message: "", time: nowTime.toJSON() };
  
  //impostiamo il nickname alla connessione a null
  connection.nickname = null;

  //ad ogni messaggio ricevuto ne inviamo uno in broadcast a tutti gli utenti collegati
  connection.on("text", function (str) {
    if(connection.nickname === null) {
      connection.nickname = str;
      newMessage.status="in"
      newMessage.message= str +" è entrato";
      broadcast(JSON.stringify(newMessage));
    }else{
      newMessage.status="msg"
      newMessage.message= connection.nickname +": "+ str;
      broadcast(JSON.stringify(newMessage));
    }
  });
  //mettiamo il server in ascolto su eventuali chiusure di connessione
  connection.on("close", function () {
    newMessage.status="out"
    newMessage.message= connection.nickname +":  è uscito";
    broadcast(JSON.stringify(newMessage));
  });
  //mettiamo il server in ascolto su eventuali errori generici
  connection.on("error", function (e) {
    console.error(nowTime + "# Error:" + connection.nickname, e);
    connection.close();
  });
});
//mettiamo in ascolto il server sulla porta 8890
server.listen(8890);

//questa funzione ci permette di ciclare per tutti gli utenti connessi ed inviare il relativo messaggio in broadcase
function broadcast(str) {
  server.connections.forEach(function (connection) {
    connection.sendText(str);
  });
}

Step 4. Il codice HTML

Creiamo una nuova area dove saranno visualizzati i messaggi, sostituiamo il codice attuale:

<section class="banner_section"></section>

con il seguente codice:

<section id="support_section" class="support_section text_style">
    <div class="welcome_message_area">Benvenuto, il supporto tecnico ti fornirà le informazioni necessarie per ogni tua esigenza.</div>
    <div class="title_message_area hide">Dashboard</div>
    <ul class="message_area"></ul>
</section>

Possiamo notare come siano cambiati gli id e le classi degli oggetti. Il primo ospita l'intera area, il secondo visualizzerà tutti i messaggi che saranno inviati dal server.

All'interno del nostro

inseriremo invece le informazioni necessarie per dare la possibilità all'utente di scegliere un nickname, ed una volta effettuato il "login" di inviare un nuovo messaggio.

Ecco il codice per fare il login:

<article class="article clearfix nickname_area">
<form id="form_nickname" class="form_box">
    <fieldset class="fieldset">
        <ol>
            <li class="left">
                <label class="label" for="nickname">Nickname *</label><br>
                <input class="input" id="nickname" name="nickname" type="text" placeholder="Inserisci il tuo nickname" required autofocus>
            </li>
            <li><br><button class="submit" type=submit>Send</button></li>
        </ol>
    </fieldset>
</form>
</article>

Aggiungiamo subito dopo anche il codice per scrivere un nuovo messaggio:

<article class="article clearfix send_message_area hide">
    <form id="form_message" class="form_box">
        
        <fieldset class="fieldset">
            <ol>
                <li class="left">
                    <label class="label" for="message">Inserisci il messaggio</label><br>
                    <input class="input" id="message" name="message" type="text" placeholder="nuovo messaggio" required autofocus>
                </li>
                <li><br><button class="submit" type=submit>Send</button></li>
            </ol>
        </fieldset>
    </form>
</article>

I moduli appena inseriti sono dei semplici input type:text con relativo submit per inviare i dati al server.

Effettuati questi passaggi il risultato ottenuto sarà molto simile a questo:

websocket_1

Step 5. Modifichiamo i css

aggiungiamo una nuova regola per il contenitore ed il box che ospiterà i messaggi che arrivano dal server.

.support_section { width: 880px; height: 200px; padding: 0 40px; background: #fff; }
.message_area{ height: 150px }

Aggiungiamo gli stili che devono essere associati ai form nickname e new message:

.support_section .form_box { padding: 0 10px; }
.support_section .form_box .input { width: 268px; }
.support_section .form_box .submit { width: 298px; float: none; }

websocket_2

infine aggiungiamo tre semplici classi per cambiare il colore dei messaggi che appariranno in base al tipo di informazione visualizzata.

.user_message{ list-style: none; margin-bottom: 10px; border-bottom: 1px solid #e8e8e8;}
.in{color: green;}
.out{color: red;}
.msg{ color: #888888; }

Step 6. Implementiamo il nostro codice javascript

Apriamo il file js/helpdesk.js

Cominciamo con il definire l'oggetto che si occuperà di gestire la nostra micro applicazione help-desk:

//inizializziamo app
var app = {
    connection: {}, 
    nickname: '',
    status: 'closed'
};

Utilizzeremo Modernizr per verigicare se il browser ha le funzioni websocket. Creiamo quindi una nuova parte di codice che sarà istanziata non appena il documento è pronto, utilizzando jQuery:

$(document).ready( function (){

    if (!Modernizr.websockets) {
      alert("No WebSocket here");
    }

});

Aggiungiamo al suo interno anche il controllo per intercettare l'evento al submit del form con il relativo al nickname:

//form event submit
$('#form_nickname').submit(function (event) {
  //evitiamo il submit del form
  event.preventDefault();

  if ( setNickname() ){
    hide( $('.nickname_area') );
    show( $('.send_message_area') );
    setWSConnection();
  }

  //evitiamo il submit del form
  return false;
});

Fatto questo, aggiungiamo l'evento al submit del form per l'invio del messaggio:

$('#form_message').submit(function (event) {
   //evitiamo il submit del form
   event.preventDefault();
   sendWsMessage();
   //evitiamo il submit del form
   return false;
});

Se analizziamo il codice appena scritto per l'invio del form relativo al nickname, possiamo notare come prima della funzione setWSConnection() venga richiamata la setNickname(), vediamo nel dettaglio di cosa si occupa:

//valorizza l'oggetto app.nickname
function setNickname(){
  var newNickname = $('#nickname').val();
  if ( isNotNull( newNickname ) && newNickname.length > 3){
    app.nickname = newNickname;
    return true;
  }
  return false;
}

Valorizza l' oggetto app.nickname, con il nickname inserito dall'utente.

Passiamo ora alla funzione setWSConnection():

// si occupa dell'invio dei messaggi al server websocket.
function setWSConnection(){
  if (isNotNull( app.nickname )){
    
    clearDomMessages();

    app.connection = new WebSocket("ws://"+window.location.hostname+":8890")
    app.connection.onopen = function () {
        console.log("Connection opened");
        app.connection.send(app.nickname);
        app.connection.status = 'open';
    }
    app.connection.onclose = function () {
        var newMessage = {"status":"out", "msg": "connection closed", "time": new Date()};
        setDomMessage(newMessage);
        app.connection.status = 'close';
    }
    app.connection.onerror = function () {
        
        var newMessage = {"status":"out", "msg": "Connection error", "time": new Date()};
        setDomMessage(newMessage);
        app.connection.status = 'close';

    }
    app.connection.onmessage = function (event) {
        setDomMessage(event.data);
    }
  }   
}

Step 7. Completiamo il nostro script con le funzioni richiamate dalla setWSConnection()

//ripulisce l'area messaggi alla prima connessione dell'utente.
function clearDomMessages(){
  
  $('.welcome_message_area').hide();
  $('.title_message_area').show();
  $('.message_area').empty();

}

Il codice di seguito prende tutti i messaggi che arrivano dalla connessione websocket e li scrive all'interno del nostro oggetto dom.

//scrive i messaggi che arrivano dal server mentre la connessione websocket resta in ascolto
function setDomMessage( message ){

    var newMessage = jQuery.parseJSON( message ),
    element = document.createElement('li'),
    domElement = $('.message_area'),
    time = '';

    function getTime(){
      var newTime = new Date(newMessage.time);
      return newTime.getUTCHours()+":"+newTime.getUTCMinutes();
    }

    time = getTime();

    element.className = 'user_message'+' ' + newMessage.status;
    
    element.appendChild (document.createTextNode("["+time+"] " + newMessage.message) );

    domElement.append( element );
    domElement.animate({'scrollTop': domElement[0].scrollHeight});

}

Inviamo i messaggi al server:

// funzione per inviare i messaggi al server WS
function sendWsMessage(){
    
    var message = $('#message').val();
    if ( isNotNull( message )){
        console.log('Invio del messaggio al server: ' + message);
        app.connection.send(message);
        $('#message').val('');
    }

}

Chiudiamo la connessione:

//chiude la connessione al server webSocket
function closeWSConnection(){
  if (app.connection.status === 'open'){
    app.connection.close();
    app.connection.status = 'closed';
  }
}

Di seguito alcuni screenshot con il rusultato ottenuto.

Login degli utenti

websocket_login

Nuovo messaggio

new_message

Logout degli utenti

websocket_logout