Realizzazione

di un

servizio scommesse affidabile


Davide Vampiri

Vittorio Teglia


Reti di Calcolatori

Bologna marzo '99


Analisi e specifica dei requisiti:

Si vuole fornire un sistema che implementa un SERVIZIO in grado di accettare, da parte di clienti esterni scommesse a loro già note a priori, ed eventualmente acconsentire il pagamento di una vincita ai clienti che ne hanno diritto. Si presuppone che i clienti siano già a priori a conoscenza delle scommesse (in particolare di un loro codice identificativo UNICO) ad esempio, esso può essere stato reperito da un tabellone supposto sempre correttamente aggiornato. I clienti in questo caso rappresentano i terminali delle varie agenzie attraverso i quali le persone comuni richiedono di accedere al servizio di gestione delle scommesse.

N.B. In particolare, dovendo in questa sede sviluppare un progetto di tipo accademico, in particolare un progetto di reti di calcolatori, si sono tralasciate parti che in un problema reale si sarebbero dovute tenere in altra considerazione, quale ad esempio la gestione del sistema informativo gestito dal servizio che è stato brutalmente semplificato con un banale array, ipotesi inaccettabile in un sistema reale, ma ai fini di un progetto di reti a nostro avviso poco penalizzante.


Progettazione:

Il sistema:

 

Ogni cliente si rivolge al SERVIZIO: in particolare si rivolge ad un preciso server in maniera indiretta (per mantenere la trasparenza del servizio) tramite la consultazione di una propria tabella_server in grado di indicare un server preferenziale a cui fare riferimento (in fase di implementazione è stata anche estesa all'utente la possibilità di scegliere a quale server rivolgersi nel primo tentativo di connessione). In caso di mancata risposta (es. dopo tot.tempo) la tabella_server indica l’altro server che per l’ipotesi fatta è attivo. Una opportuna implementazione di tale tabella permetterà di scegliere fra due diverse politiche di distribuzione delle richieste: una di bilanciamento (distribuendo equamente le richieste alle due macchine) l’altra indirizzando le richieste preferenzialmente sullo stesso server ed usando l’altro solo in caso di caduta del primo.

La parte di progetto che è stata realizzata è quella che riguarda la realizzazione di un protocollo affidabile fra i servitori, non ci siamo occupati di autenticazione per motivi di tempo; potrebbe essere questa una parte del progetto per ulteriori sviluppi.

Si rende ora necessario progettare e realizzare un protocollo di trasferimento dati fra i due server che garantisca sempre l'invio di una corretta risposta al/ai cliente/i che interrogano il servizio.

Assumiamo come ipotesi di progetto che:

in qualsiasi istante almeno uno tra i due server (S1 ed S2) sia attivo

Volendo inoltre realizzare il servizio con due server ed un numero indefinito di clienti è necessario realizzare un protocollo che garantisca per quel che riguarda lo scambio di informazioni fra i due server la mutua esclusione, fra i vari processi coinvolti, sulle risorse comuni.

Protocolli:

Protocollo di Giocata di una scommessa: (lato client)

il Cliente C consulta la propria tabella_server determina il server_a_cui_rivolgersi

while not(riceve risposta){

invia messaggio (giocata_scommessa) al server_a_cui_rivolgersi

attendi risposta per un tempo T

// il server è caduto;

if <non ho ricevuto risposta >

{// il server è caduto;

server_a_cui_rivolgersi =server secondario

}

}

 


Protocollo di Giocata di una scommessa: (lato server)

... ready.....

accetta messaggio dal cliente

legge il record_scommessa dal cliente e ne verifica autenticità

if (invia il record_scommessa all’altro server) >0

{

if (ricevi risposta altro server)>0 {

invia al client la conferma accettazione (record_ giocata)

scrive nella sua tabella }

altrimenti{

scrive nella sua tabella

invia al client la conferma accettazione (record_ giocata)

}

}

altrimenti //l'altro server è caduto

{

scrive nella sua tabella

invia al cliente la conferma dell’accettazione (record_giocata)

}

 

 


Protocollo di Incasso scommessa: (lato client)

 

il Cliente C consulta la propria tabella_server determina il server_a_cui_rivolgersi

while not(riceve risposta){

invia messaggio (incasso_scommessa) al server_a_cui_rivolgersi

attendi risposta per un tempo T

// il server è caduto;

if <non ho ricevuto risposta >

{// il server è caduto;

server_a_cui_rivolgersi =server secondario

}

attendi risposta per un tempo T

}

 


Protocollo di Giocata di una scommessa: (lato server)

... ready.....

accetta messaggio dal cliente

legge il record_scommessa dal cliente e ne verifica autenticità

if (invia il record_scommessa all’altro server) >0

{

if (ricevi risposta altro server)>0 {

invia al client l'esito della richiesta (record_ giocata)

scrive nella sua tabella }

altrimenti{

scrive nella sua tabella

invia al client l'esito della richiesta (record_ giocata)

}

}

altrimenti //l'altro server è caduto

{

scrive nella sua tabella

invia al cliente laesito della richiesta(record_giocata)

}

 

Utilizzando all'interno del protocollo stesso opportune strutture di sincronizzazione sul Dbase sottostante si è in grado di garantire al cliente una risposta sempre coerente con lo stato del DBase stesso.

In particolare questo protocollo garantisce l'affidabilità del servizio cioè a fronte della caduta di uno dei due servitori, l'altro è comunque in grado di fornire ancora il servizio, senza che il cliente non si accorga di nulla, al massimo egli può rilevare ritardi nel tempo di risposta ma non gli verrà mai negato il servizio..


Procedura avvio server

crea tabella_scommesse locale VUOTA

se (crea connessione altro server)>0

{

richiedi tabella_scommessa all’altro server

ricevi tabella_scommesse chiesta

tabella_scommesse locale= tabella_scommesse ricevuta

}

// fino a questo istante il server è giù quindi per hp l’altro non può cadere

....ready...

 


Procedura avvio client

carica in memoria la tabella_server (ad esempio da file)

 

 


 

Fase di Implementazione

Si è scelto di realizzare il sistema in Java (Jdk 1.2) per la sua portabilità, per la sua facilità d'uso nell'ambito delle reti e per le innumerevoli classi .net e .io che mette a disposizione.

Per quel che riguarda i vari canali di comunicazione tra le varie macchine del sistema si è pensato a un protocollo senza connessione di tipo UDP per quel che riguarda la comunicazione client-server, in particolare il cliente incapsula la scommessa in un Datagramma che poi spedisce al servitore; si è operata una scelta di questo tipo, in quanto, è parso troppo oneroso stabilire una connessione preventiva fra tutti i clienti (che possono raggiungere un numero elevato ~100) e i servitori per spedire, un solo messaggio per ogni cliente e ricevere poi una risposta in seguito. Per ciò che concerne le comunicazioni server-server si è usato il protocollo TCP/IP un protocollo con connessione in quanto la comunicazione fra i due servitori è frequente e deve essere comunque sicura, non è ammessa la perdita di alcun messaggio senza che chi lo ha spedito se ne accorga. Si è anche usato il TCP/IP per il trasferimento di tabella tra il server up e il down che si sta avviando.

 

L'applicazione

E' costituita dal modulo server che commenteremo ampiamente .

Tale modulo e' costituito dalle classi AscoltaThread1 , AscoltaThread2 , Thread1 , Thread2 , Thread3 , e ovviamente il modulo di esecuzione principale ( il main dell'applicazione ) . Vediamo piu' in dettaglio il funzionamento : il main del programma non fa altro che fare partire AscoltaThread1 , AscoltaThread2 e Thread3 ( vediamo in seguito che cosa fanno ) , crea una tabella per la registrazione delle scommesse ( inizialmente vuota ) e richiede all'altro server la tabella aggiornata . Se non riceve risposta vuol dire che l'altro server e' down , allora lui non fa altro che lavorare e predisporsi ad inviare all'altro server ( quando si svegliera' ) la sua tabella . Passiamo ora a illustrare il funzionamento degli altri threads . AscoltaThread1 inizialmente prova a connettersi all'altro server , dopodiche ' esegue un loop infinito : riceve scommesse ( datagrammi UDP ) dai clienti e crea ed avvia un'istanza di Thread1 ( una per ogni datagramma ricevuto ) che si occupa di svolgere il protocollo di giocata/incasso della scommessa . Thread1 invia sulla connessione stabilita con l'altro server la scommessa , riceve sempre sulla stessa connessione , la risposta dall'altro server , risponde al cliente e aggiorna la propria tabella . Avendo usato un'unica connessione TCP puo' capitare che un'istanza di Thread1 invii una scommessa all'altro server e riceva la risposta di un'altra scommessa ; questo non e' un problema in quanto la scommessa che inviamo all'altro server e che ritorna indietro ( confermata o rifiutata ) contiene l'indicazione di chi l'ha giocata ( il client la porta sulla quale si dovra' inviare la risposta ) . Il thread che si occupa di ricevere le scommesse dall'altro server e' Ascoltahread2 . AscoltaThread2 riceve la richiesta di connessione dall'altro server , dopodiche' legge dal canale stabilito una scommessa e genera un thread per ogni scommessa letta ( Thread2 ) che si occupa di inviare all'altro server la risposta e se non accadono eventi anomali ( l'altro server cade ) di registrare la scommessa nella propria tabella .

 

La scommessa che il cliente invia al server dentro al datagramma, è in questo caso una stringa i cui primi tre caratteri sono l'id UNICO della scommessa; il vero oggetto scommessa è creato dentro al server al primo che lo riceve, questo perchè e stato ritenuto importante ridurre al minimo il contenuto informativo nel datagram essendo esso un protocollo non sicuro. La signature della classe scommessa è la seguente:

class scommessa implements Serializable{
String id,testo;
String resto;
boolean tipo ;//true giocata false incasso
public int port ; //porta che id. il client
public InetAddress client; //indi cliente
String message; }

di cui si fa notare in particolare la variabile tipo settata se è una giocata e resettata se la richiesta del cliente è per un incasso (anche questa è settata sul server mentre sulla datagram viaggia un carttere speciale sulla stringa). La scommessa è stata definita in questo modo perchè tale struttura è ritenuta sufficente per un buon test del nostro protocollo, ma nulla vieta per sviluppi futuri di creare una classe più complessa contenente infromazioni di altro genere; inoltre tale classe mette a disposizione due metodi isEqual() e toString() le cui funzioni sono ovvie ma che comunque possono essere modificate e la classe arrichita con altri metodi.

Altra classe è tabella; in un sistema efficente, tale classe deve essere un dbase, nel nostro caso è un vettore di oggetti scommessa, scelta del tutto contestabile ma estendibile con un dbase reale cambiando la classe tabella e riimplementando i metodi:

insert(scommessa), delete (scommessa), presente(scommessa), show(scommessa)

in quanto le classi server succitate usano solo questi ultimi per manipolare la tabella. Si è comunque tentato di realizzare queste due classi in maniera semplice ma allo stesso tempo di garantire un minimo di adattabilità del sistema verso un problema reale.

 

Per quel che riguarda il modulo client, si è realizzato un Thread (class clientsingolo) che viene fatto partire da una applicazione stand-alone esterna (lettorem.class) ogni qual volta si deve inviare una scommessa; tale thread implementa il protocollo del cliente sopra descritto, in particolare riceve in ingresso una scommessa, costruisce il datagramma, lo invia e attende risposta, rispettando comunque il protocollo succitato.

Testing dell'applicazione


L'applicazione e' costituita da due programmi ( praticamente identici ) lanciati da due console con :
../server1/java server
../server2/java server
le due versioni del programma differiscono solamente per la porta sulla quale ascoltano i messaggi dei clienti e per la porta dalla quale ricevono dall'altro server la richiesta di invio tabella . Osservando le due console si può osservare l'evoluzione dei servitori.
Dopodiche' l'applicazione :
../java lettorem
avvia un programma la cui interfaccia permette di creare un numero prestabilito di scommesse da inviare ad un server preferenziale ( selezionabile ) ; lanciando uno o piu' di questi moduli possiamo simulare il comportamento dei due server in regime di lavoro . Possiamo anche volutamente fare cadere uno dei due server e vedere come quello rimasto su' provvede all' incasso/giocata delle scommesse .

L'interfaccia lettorem:


Vittorio Teglia

Davide Vampiri