Progetto  di   Reti di Calcolatori

                                                                             Stefano Giovannini


Titolo del Progetto:

   Gestione aziendale di un servizio commerciale distribuito in rete.
 

Introduzione:

   Si vuole realizzare un sistema di gestione aziendale tramite Java.

   Un cliente che vuole usufruire di questo sistema deve prima farsi riconoscere tramite una specie di login in cui digita il suo nome e il suo codice. Se l'utente è riconosciuto può effettuare un ordine presso una delle ditte  cui è convenzionato oppure ricevere informazioni su eventuali ordini da lui effettuati in precedenza.
   Una volta creato l'ordine l'utente può inviarlo a un database in cui sarà registrato.

Per ricevere o spedire informazioni il cliente si avvale di server che a loro volta si metteranno in contatto con i rispettivi database.
   Per avere affidabilità dei server si utilizza la replicazione: sono presenti due programmi server su due nodi diversi che sono in attesa di richieste da parte di eventuali clienti. I server, ricevute le richieste dai  client si mettono in contatto con un altro server , detto di banca , che gestisce tutti i dati.
   Il cliente conosce entrambi gli indirizzi dei server replicati ed effettua le richieste su uno dei due : selezionandolo , quando gli si presenta un menù di scelta , oppure in altri casi la scelta sarà quella di default.


 
 
 
 

Le ipotesi fatte sono:

  1. Ipotesi di guasto singolo per i server di gestione richieste, cioè nel caso in cui uno dei due cada l’ altro funziona correttamente finché non    viene ripristinato il primo server,
  2. Ipotesi di affidabilità del server banca dati, cioè si assume che tale nodo non cada mai,
  3. Ipotesi di traffico delle richieste non elevato, in modo che non ci sia congestione dei nodi.

Descrizione del progetto:

Client

   Quando viene fatto partire un processo client, ci si trova prima di tutto a dover effettuare una scelta sul tipo di scambio di informazioni che si vuole effettuare. Le possibili scelte sono due :o effettuare degli ordini o ricevere informazioni sugli ordini già fatti.
   Per creare le varie finestre di dialogo , posso scegliere diverse alternative : posso realizzare le finestre iniziali con delle applet e passare poi con dei link di pagine HTML , a successive pagine con altre applet che iniziano ad eseguire codice , oppure realizzarle con delle applicazioni java che devono essere fatte partire dal prompt di MS-DOS.

Ordine

   Avviato il client , e scelta l' opzione di fare degli ordini , viene presentata una pagina che rappresenta la commissione che un cliente può effettuare , la quale consiste in un elenco di pezzi che produce una specifica ditta , ogni pezzo ha un codice , una descrizione che ne delinea le caratteristiche fondamentali , un campo che ne determina la quantità ed un prezzo. Viene inoltre calcolato un totale della spesa che si sta effettuando man mano che si richiedono pezzi ; l' ordine è definito anche ,  oltre che dal nome del cliente e da quello della ditta , dalla data in cui viene effettuata la richiesta e dalla ragione sociale del cliente la quale dovrebbe identificarlo più specificatamente.
 
 


 
 

   Prima di poter iniziare a definire l'ordine e prima di inviarlo , il client  deve farsi identificare tramite un'apposita finestra che  richiede  nome e  codice con cui lo stesso client è stato registrato nel database del sistema aziendale.

   Ogni tentativo di invio di ordini senza essere stati identificati è rifiutato , allo stesso tempo viene ripresentata la finestra di identificazione.
   Prima dell' identificazione possono comunque essere riempiti i vari campi dell' ordine , ma questi non sono interattivi :il cliente cioè non è in grado di scambiare alcuna informazione con le ditte in quanto non è ancora capace di effettuare connessioni con i loro database.
   Quando i campi della finestra di identificazione vengono riempiti e  viene premuto il tasto OK il client può scegliere con quale server connettersi come primo tentativo. Se il server prescelto non risponde alla richiesta entro un certo tempo (timeout), poiché potrebbe essere guasto o non attivo, il client prova a collegarsi con l'altro e così via alternativamente , di time out in time out, finche non ce n'è uno che risponde alla richiesta.
   A questo server viene spedito il nome ed il codice del cliente.
   A questo punto il client si mette in attesa di una risposta che gli dovrà fornire il server con cui si è messo in contatto. Se la risposta ricevuta è negativa allora il client dovrà provare a riconnettersi per farsi identificare correttamente altrimenti non può effettuare ancora alcun tipo di operazione. Al contrario , se la risposta è positiva il client riceve informazioni sulle ditte con le quali ha stabilito un rapporto commerciale che quindi possono ricevere i suoi ordini. Infatti il cliente si trova a dover scegliere quale ditta  vuole contattare , tra quelle a cui è convenzionato.
   Scelta la ditta il client può ora riempire i vari campi dell' ordine ricevendo informazioni dai database delle ditte. Il campo che definisce il nome del cliente e della ditta non sono ormai più modificabili e viene inoltre chiesto con una finestra apposita , se non lo è stato fatto in precedenza , di definire la propria ragione sociale. Il cliente , a questo punto, trova il cursore attivo nel textfield del codice del primo pezzo della videata che rappresenta l'ordine.
   Ogni volta che viene scritto un codice e poi si tenta di lasciare quel campo , il client chiede informazioni al database della ditta che ha precedentemente scelto , stavolta però  non gli viene chiesto di fare una scelta su quale server della azienda contattare per primo perché ci si può rendere conto che riempiendo tutti i campi dei codici , il cliente si troverebbe a dover scegliere tutte le volte quale server contattare per primo , in questo caso allora , viene effettuata una scelta di default. Se il codice è riconosciuto dalla ditta , vengono fornite anche le altre informazioni relative a quel pezzo , cioè la sua descrizione ed il  prezzo ; allo stesso tempo viene reso attivo il campo che definisce la quantità  voluta dell' oggetto. In base alla quantità desiderata viene ricalcolato in tempo reale il totale dell' ordine ; se viene digitata una quantità non corretta , ad esempio non viene scritto un numero valido , rimane attivo lo stesso campo di quantità in attesa che venga inserito un valore giusto , se invece si tenta di andare al codice successivo senza aver scritto nulla , questa azione viene intesa come azione di default : la quantità desiderata sarà di un' unità.
   Se al contrario il codice non esisteva o non è stato inserito correttamente rimarrà attivo sempre lo stesso campo di codice finché il pezzo non verrà riconosciuto. Descritto correttamente codice e quantità viene automaticamente reso attivo il campo del codice successivo.
   Quando il cliente ha completato l'ordine può inviarlo alla banca dati del sistema principale che provvederà a registrarlo. Il client si mette sempre in contatto con uno dei due server che gestiscono le richieste inviate al database generale del sistema.
   Se l' ordine è stato registrato correttamente il cliente avrà la convalida di questa operazione altrimenti sarà informato del cattivo esito della registrazione e sarà invitato a rieffettuare l' ordine , in quanto si potrebbero essere verificati errori nei server atti alla memorizzazione della commissione oppure qualche macchina non era ancora attiva. Infine viene presentata una finestra di opzione in cui si può scegliere se fare un altro ordine oppure uscire dall' applicazione , se si decide di farne un altro si ripresenta il menù di scelta delle ditte con cui si può interagire.
   Per realizzare questa applicazione devo effettuare varie scelte : ad esempio posso scegliere in che modo fare le connessioni con i vari server e per quanto tempo stabilirle. In effetti posso scegliere tra connessione affidabili TCP/IP oppure connectionless con il protocollo UDP. Un' altra scelta riguarda la durata della connessione : posso scegliere di stabilire una connessione con il primo dei due server agenti attivo e mantenerla per tutti i possibili invii di ordini o di codici di identificazione che può fare un client e chiuderla solo al momento in cui cessa l'applicazione oppure posso preferire di stabilire delle connessioni provvisorie attive solo per il tempo necessario a trasferire o ricevere dati. Lo stesso discorso vale per le connessioni che si devono creare con i server delle ditte.
 

Informazioni

   Per quanto riguarda la parte dell' applicazione che deve richiedere informazioni al database generale in cui sono registrati tutti gli ordini effettuati in precedenza , essa è molto simile a quella dell' ordine , infatti abbiamo bisogno di un frame con gli stessi campi e la stessa impostazione per poter visualizzare tutti i dati che precedentemente sono stati inviati per essere registrati. Anche  qui mi trovo a dover fare una scelta : posso scegliere se fare un' altra classe java che costruisca un frame del tutto simile a quello fatto per gli ordini oppure estendere da quello , tramite l' ereditarietà di java , un' altra classe che ridefinisca solo alcuni metodi per realizzarne le differenze.
   Come unica differenza infatti ci sarà quella del pannello di controllo in cui sono racchiusi i pulsanti : se prima avevamo tre pulsanti (Invia  Annulla e Esci) ora ce ne  servono due : Esci e Ancora. Il pulsante "Ancora" ci serve per far scorrere sulla finestra principale i vari ordini registrati ; quando sono stati visualizzati tutti gli ordini , dal più recente al più vecchio , se si richiede di visualizzarne ancora viene esposta una finestra informativa per dire che non sono presenti altri ordini per quel cliente , lo stesso accade se l' archivio dello specifico cliente per quella ditta è ancora vuoto.
   Per il resto facciamo riferimento alle stesse modalità di connessione e identificazione della parte precedente con la differenza che questa volta non dobbiamo metterci in contatto con nessuna ditta perché gli ordini vengono tutti registrati nel database centrale.
 

Scelte progettuali ed Implementazione

   Utilizzando Java come linguaggio di programmazione e la sua versione 1.2.2 , ho notato che Internet Explorer 5.0 non è in grado di supportarlo , infatti lanciando applet in pagine HTML con questo browser , ottenevo un errore proprio nella parte dell' applet che non riusciva a partire.
   Con Netscape Navigator 4.51 invece l' applet veniva avviata correttamente , ma ho notato che utilizzando dei componenti swing nel codice delle varie classi , questi non venivano visualizzati nel corso del programma. Quindi non sottovalutando la comodità dei componenti Swing come ad esempio le finestre di dialogo standard della classe JOptionPane ho deciso di abbandonare questa strada e di realizzare l' intero progetto con delle applicazioni fatte partire da file batch che lanciano le varie parti di programma  utilizzando l' interprete di bytecode java.
   Per quanto riguarda la scelta sulla durata delle connessioni ho pensato che mantenere attive tutte le connessioni per l' intera durata della presenza del client era troppo oneroso in quanto se fossero presenti allo stesso tempo molti clienti i server dovrebbero mantenere attive contemporaneamente molte socket con un conseguente sovraccarico e perdita di efficienza. Molto più logico invece stabilire una connessione solo per la durata del trasferimento dei dati tra due macchine distinte , applicando così la politica one-shot connection come quella adottata per trasferire pagine HTML in rete. Questa scelta è stata determinata anche dal fatto che è posto un limite per il numero di socket aperte contemporaneamente , nel caso si scegliesse di mantenerle attive per l' intera durata dei processi client si rischia di superare questo limite e di non poter esaudire altre richieste pendenti. Infatti i client potrebbero rimanere attivi anche per molto tempo nell' attesa di effettuare magari un solo ordine tenendo così occupata una risorsa che non viene in realtà sfruttata.
   Per il tipo di connessione da stabilire ogni caso merita un discorso a sé. Riguardo al trasferimento dei dati per effettuare il riconoscimento di un client è opportuno scegliere il protocollo UDP di trasmissione dati non affidabile , infatti i dati da spedire sono pochi e non sono fondamentali per la riuscita dell' applicazione : il cliente può sempre riprovare a riconnettersi se non ottiene risposta; inoltre considerando il possibile tentativo di connessione di persone non registrate nel database , con l' invio di dati non riconosciuti l' altra scelta avrebbe reso l' utilizzo di connessioni affidabili , troppo costoso in termini di risorse. Il client così crea una connessione DatagramSocket e prova a spedire un  DatagramPacket con i dati di identificazione ad un server , subito dopo si mette in attesa di una risposta , dopo aver settato un timeout che genera un' interruzione se non viene ricevuta alcuna risposta entro 5 secondi. Se scatta il timeout si genera un' eccezione , e catturata questa interruzione si prova a spedire lo stesso pacchetto all' altro server e così via alternativamente finché non viene ricevuto l' esito                          dell' identificazione.
   Lo stesso discorso vale per per le connessioni con le ditte che gestiscono i listini e i cataloghi di tutti i pezzi che è possibile ordinare. Anche in questo caso infatti i dati da trasmettere e ricevere sono pochi e non di fondamentale importanza : se un codice non è ricevuto si può sempre tentare di richiederlo con lo stesso metodo , cioè  quello di interrogare alternativamente i due server preposti a ricevere richieste dai clienti.
   Come ho detto prima però , in questo caso non viene data l' opportunità al client di scegliere quale server contattare per primo , sta di fatto però che le richieste inoltrate a questi server possono essere molte : se il server scelto a default è guasto allora si dovrà attendere ogni volta 5 secondi per far scattare il timeout e poi interrogare l' altro server (che per ipotesi deve essere attivo , ma se anche non lo fosse si passa da un timeout all' altro finché uno dei due non è in grado di rispondere).
   Comunque , nella situazione descritta sarebbe sconveniente attendere il timeout ad ogni richiesta di codice , per questo motivo viene attivata quando si avvia un processo cliente anche un frame predisposto per configurare le modalità di connessione del client con i vari server. In questo frame c'è una casella di opzione la quale definisce quale server ditta contattare per primo per ricevere risposte , se un client si accorge del problema descritto sopra può cambiare la preferenza selezionata all' avvio dell' applicazione ottenendo così una connessione immediata con il server che è attivo trascurando provvisoriamente e interrogando solo su necessità l' altro.
   Per quanto riguarda il tipo di messaggio inviato è possibile fare una duplice scelta : scegliere di mandare messaggi tutti della stessa lunghezza oppure secondo la dimensione necessaria. In questo caso ho scelto di spedire messaggi sempre della stessa dimensione , seguendo cioè una programmazione vincolata ma che ha il vantaggio di necessitare di un supporto facile da realizzare a differenza di una non vincolata.
   L' ultima connessione che il client deve effettuare è quella che gli permette di spedire gli ordini per farli registrare nel database, questo tipo di informazione è ritenuta ben più importante delle altre e visto che la mole di dati da trasferire è notevolmente superiore ho pensato così di utilizzare una connessione affidabile secondo il protocollo TCP/IP che garantisce il corretto ricevimento dei dati all' altro nodo della socket.
   Quindi ogni volta che viene premuto il tasto "Invia" si tenta di stabilire una socket stream con il server numero 1 , se questo non c'è perché non attivo viene catturata l' eccezione da parte del client e si prova a stabilirne una uguale con il secondo server che per ipotesi dovrebbe essere attivo , ma se anche questa fallisce viene visualizzato per il cliente un messaggio di errore nel quale si spiega che i due server non sono attivi e pertanto è conveniente effettuare l' ordine in un altro momento.
   Per  creare le socket si possono utilizzare anche dei costruttori che non specificano quali porte riservare per le connessioni , infatti con questi costruttori vengono  scelti dei numeri di porta casuali fra quelli al momento non utilizzati. Utilizzando questo metodo ci può chiedere se così facendo si può incorrere in interferenze , ma non è così : infatti  dai messaggi ricevuti dai server o dalle connessioni stabilite con questi si può risalire all' Host del client che ha effettuato la richiesta ed individuare la porta sulla quale questo è in attesa della risposta.
   Per quanto riguarda le informazioni necessarie alle connessioni ho cercato di rendere l'applicazione client il più possibile trasparente            all' allocazione delle risorse dei server. Infatti il client può eseguire le sue operazioni senza essere minimamente a conoscenza di dove sono situati i vari server , in questo caso vengono utilizzate le informazioni di default definite alla creazione del programma. Per ottenere però anche una indipendenza dell' allocazione  le risorse (ad esempio su quali macchine avviare i server agenti del database generale) è stata creata      un' altra finestra , detta di configurazione , la stessa in cui è possibile fare la scelta per il server prioritario delle ditte , in cui è possibile definire con precisione l' allocazione effettiva delle risorse a cui ci si riferisce e cioè l' indirizzo e le porte che utilizzano i server per ricevere richieste da parte dei client ; precisamente con questa finestra è possibile definire gli host su cui girano le applicazioni dei server agenti e le porte che questi hanno riservato per le connessioni stream e quelle datagram.


    Per  le informazioni   riguardanti  i server  delle  ditte  , queste  vengono  fornite  insieme  al  DatagramPacket  che  viene  ricevuto dopo            l' identificazione , questo pacchetto deve essere decodificato e da esso se ne ricava un elenco di ditte che viene messo in un Vector di dimensione non stabilita a priori che può variare secondo necessità ; per ogni ditta , oltre al nome , vengono forniti gli indirizzi e i numeri di porta dei server che possono ricevere richieste per la decodifica dei codici dei pezzi. Una volta che ci si è identificati e riempito tale vettore dinamico , l' elenco delle ditte rimane disponibile per tutta la durata dell' applicazione client , infatti effettuato un ordine se ne può effettuare subito un altro anche cambiando la ditta presso cui eseguirlo.
   Il fatto di dover configurare il client per permettergli di stabilire le connessioni con i server e la scelta di ricevere informazioni registrate nel database generale per sapersi collegare ai server delle ditte può sembrare una scelta poco efficiente ; sembrerebbe più logico creare una entità unica che sia in grado di fornire i dati necessari alle connessioni su specifiche richieste , informazioni che questo gestore riceverebbe ogni volta che un nuovo componente facente parte del sistema viene avviato , in modo di avere una completa trasparenza degli indirizzi conoscendo solo quello di questo manager. Tutto ciò però dipende dall' ottica con cui si guarda il progetto : la necessità di creare una finestra di configurazione per il client è stata dettata dal fatto che il progetto è in una fase prototipale , quindi durante le prove i vari server possono essere attivati su macchine di volta in volta distinte , ma ragionando in un ottica reale , in una eventuale simulazione di questo progetto i vari server avranno una localizzazione fissa e solo in casi del tutto eccezionali si sposteranno su altri nodi , quindi l' introduzione di un gestore degli indirizzi renderebbe tutta la applicazione più faticosa aumentandone l' overhead. Inoltre con un unico gestore si ripresenterebbero di nuovo i problemi di affidabilità in caso di guasto di quest' ultimo. Si è scelto quindi di seguire la strada di effettuare una configurazione del client solo su necessità , in casi cioè del tutto eccezionali.
   Per quanto riguarda la scelta di avviare l' applicazione in modalità "informazioni" sono possibili due diverse strade su come ottenere i dati riguardanti gli ordini passati : un modo è quello di ricevere tutti insieme , una volta identificati , gli ordini passati inserendoli in un vettore dinamico che poi verrà scandito secondo necessità ; l' altro è quello di richiedere al database generale , tramite i due server , le informazioni volta per volta solo quando è necessario. Questa seconda scelta anche se giusta dal punto di vista di richiedere dati solo quando necessari mi sembra però allo stesso tempo troppo costosa , infatti ricevuto il primo ordine da visualizzare (l' ultimo in termini di tempo) , ogni volta che se ne vuole vedere un' altro è necessario riconnettersi e ricevere informazioni partendo dal punto in cui si era arrivati , ciò presuppone che ,sia il server agente , sia il database dovrebbero  tenere traccia delle richieste passate fatte da ogni client attualmente attivo , inoltre si dovrebbe aprire , per quanto riguarda il database generale , il file contenente queste informazioni ad ogni richiesta , oppure tenerlo aperto fino alla conclusione della sessione in cui tale cliente è attivo. Ho quindi pensato che fosse molto più semplice trasferire in una volta sola tutti  i dati necessari per poi lasciare al solo client il compito di gestirli e visualizzarli nei modi e nei tempi che ritiene più opportuni.
   Per ottenere informazioni e per spedire ordini si usa la stessa classe java connOrdine solo che la connessione viene effettuata con un metodo che ha come parametro di ingresso un intero il quale funge da opzione , in base a questo intero si effettuano due diverse sequenze di operazioni diverse per il trasferimento dei dati a seconda del servizio che si deve svolgere.

  Durante tutta l' applicazione del client sono state introdotte delle finestre informative sulle situazioni di errore in cui ci si imbatte man mano, ad esempio quando scatta un timeout o quando non è possibile stabilire una  connessione  affidabile  etc.   Tutto  ciò  toglie  trasparenza                   all' applicazione client che si accorge dei tentativi di connessione fatti di volta in volta. Queste finestre informative però , sono state introdotte soprattutto per far rendere conto , a chi vede l' evolversi di tutto il sistema , di come questo funziona e delle cause che inducono il programma a generare le eccezioni. Per tornare ad una situazione di completa trasparenza da questo punto di vista ,  basta eliminare queste finestre così il client avrà solo le risposte ai servizi che via via richiede , risposte che però si faranno attendere di più o di meno in termini di tempo se il programma si imbatte o no , in casi anomali ma previsti.
 
 
 
 
 
 

ServerAgente


   Avviata l' applicazione server è necessario inizializzarla tramite il pulsante OK del pannello di controllo. Premendo questo tasto infatti si attiva il server che crea le connessioni necessarie per ricevere richieste da parte dei client ; i campi che definiscono le modalità di questa connessione sono riempiti con valori di default , tali campi però possono essere modificati prima di configurare il processo.
 


 
 

   Questi campi identificano l' allocazione del database generale , con indirizzo dell' host e le porte (una per la socketdatagram e una per la stream) che questo ha riservato per ricevere le richieste che gli invia il server , inoltre si possono anche modificare i numeri di porta che il server stesso riserva per creare la socket stream e la socket datagram.
   Una volta configurato ed avviato , il server crea una ServerSocket che si metterà in attesa , tramite il metodo accept , delle richieste inviate dai client ; siccome ogni server deve provvedere a rispondere ad un numero non conosciuto di client è necessario , oltre che esaudire ogni richiesta ricevuta , tenersi sempre attivi per raccogliere altre eventuali domande anche quando si è impegnati nell' esecuzione della procedura di risposta ad un altro client. Ciò è possibile implementarlo tramite l' uso della classe java dei Threads , cioè dei processi leggeri che vengono eseguiti parallelamente al processo che li ha generati. Quindi il server con un ciclo infinito si pone in una continua attesa di richieste e ogni volta che se ne verifica una , attiva un Thread che ha il compito di inoltrare la stessa richiesta al server database.
   Bisogna dire che è stato scelto di implementare il servizio con dei  Threads perché in java costa poco , in termini di risorse , un loro utilizzo ; inoltre la registrazione di un ordine si può considerare un' operazione lunga in termini di tempo , quindi per avere un servizio real time è necessario attuare una politica di concorrenza del servizio in modo tale da poter soddisfare più richieste contemporaneamente.
   Per ipotesi abbiamo detto che il server database è sempre attivo , quindi il gestore di una richiesta di registrazione di ordine riesce sempre a creare una socket stream con successo per passare i dati al database , lo stesso discorso vale per una richiesta di lettura degli ordini : il gestore del server che fa queste operazioni è sempre lo stesso ma come primo dato che riceve dai client questo riceve un intero che sta ad identificare il tipo di richiesta voluta , a seconda dell' opzione selezionata si svolgono due sequenze di operazioni differenti.
  Se il database fosse spento o guasto si visualizza nella text area un messaggio di errore e al client è inviata una risposta di cattivo esito per la registrazione.
   Per quanto riguarda la connessione datagram il discorso da fare è un pò diverso : quando il server riceve un messaggio deve poi inoltrarlo al server che gestisce il database , ma essendo la connessione non affidabile può accadere che il messaggio non giunga a destinazione anche se il server è effettivamente attivo , quindi nell' implementazione di questo servizio dobbiamo preoccuparci di inviare il messaggio finché non riceviamo dal server del database un segnale di richiesta ricevuta. Anche in questo caso , come per il client , il server spedisce il messaggio , setta un timeout e si mette in attesa di una risposta , se questa non arriva entro il timeout viene generata un' eccezione catturata dallo stesso processo server che tenta  nuovamente di rispedire. In questo caso però non generiamo un Thread per ogni richiesta ricevuta , in quanto il servizio da svolgere è relativamente breve , così ritengo più conveniente sequenzializzare le richieste tramite il modificatore per i thread synchronized , questo modificatore svolge una funzione di semaforo per la risorsa di tipo DatagramPacket  per tutta la durata della sezione critica in cui la utilizziamo per spedire e ricevere messaggi.
   Per quanto riguarda il caso di guasto , dobbiamo rendere il cliente inconsapevole di quello che succede , infatti , se un client aveva inoltrato una richiesta e questa non viene soddisfatta perché il server non era attivo o è caduto mentre la stava esaudendo , il client non riceverà risposta e automaticamente proverà a collegarsi con l' altro server replicato che per ipotesi dovrà essere attivo , se anche questo non lo fosse il client non può ricevere risposta e proverà quindi a richiedere la connessione in un secondo momento. Vediamo che questi server sono privi di stato , cioè non tengono traccia delle interazioni che hanno avuto con i vari client , nè dei client collegati con loro attualmente ; il protocollo di rientro da un guasto  di un server  stateless  è molto semplice perché non è necessario ripristinare la situazione che si era venuta a creare fino al momento del guasto  ; quindi al loro rientro è sufficiente fare l' inizializzazione e  metterli di nuovo in attesa di richieste da parte degli utenti del servizio.
   Lo stesso discorso fatto qui è valido anche per i server replicati delle varie ditte.
 
 

Server Ditta

  Ogni ditta mette a disposizione due server replicati che si sostengono a vicenda in caso di guasti.
  Questo tipo di server ha lo stesso funzionamento di un server agente. Andrà configurato e inizializzato con le stesse modalità ma non è necessario definire una porta per le connessioni TCP/IP , infatti questo server riceve le richieste da parte dei client solo per decodificare codici corrispondenti a pezzi forniti dalla ditta in questione. Per questo servizio si usano delle DatagramSocket : una per ricevere richieste dai client e una per inoltrare le stesse richieste al server database della ditta che dovrà poi restituire una risposta  nella quale è stato decodificato il messaggio inviato dal client. Il server riceve questo messaggio dal database e lo invia a sua volta verso il client di cui avrà identificato host e numero di porta ricavandoli dal pacchetto che quest' ultimo gli ha mandato. Il protocollo di attesa di richieste dei client , quello di invio tramite connessione non affidabile di messaggi verso il server database , nonchè il protocollo di rientro da eventuali cadute o guasti è identico a quello stabilito per i server agenti nel caso di connessione UDP.
 


 
 
 
 
 
 
 

Server DataBase Generale

   Il server detto database generale è quello che gestisce le principali informazioni dei client.
  Anche questo server deve essere inizializzato e poi avviato , ma a differenza di tutti gli altri server e dei clienti non deve definire nessun campo di host , è sufficiente stabilire quali porte del suo processo debbano essere riservate per aprire delle connessioni. In questo caso sono richieste due porte : una per le richieste di connessioni secondo il protocollo TCP/IP ed una secondo quello UDP.


   Per quanto di riguarda la modalità di gestione delle informazioni si possono scegliere diverse alternative eseguibili con politiche molto diverse fra loro : ad esempio è possibile tenere in memoria virtuale i dati dei client caricandoli da programma ogni volta che viene fatta partire l' applicazione e aggiornarli in tempo reale secondo le richieste fatte dai clienti , così facendo però , una volta terminata l' applicazione le modifiche apportate fino a quel punto dall' evoluzione dell' intero sistema , andranno perse e riavviando il server in un secondo momento ci si ritroverà nella situazione di partenza iniziale , quella che è stata decisa durante la stesura del progetto. Questa soluzione non è molto accettabile anche considerando il fatto che una momentanea caduta di questo server resetterebbe inevitabilmente ogni volta il sistema. E' quindi necessario pensare ad una soluzione in cui i dati forniti dai clienti possano essere reperiti anche in sessioni di lavoro successive. Per realizzare una sorta di database è possibile usare veri e propri sistemi che gestiscono questo tipo di informazioni come il linguaggio Oracle o SQL e le loro funzioni per reperire dati , o più semplicemente si potrebbe implementare questi archivi con dei file che verrebbero memorizzati su di un supporto stabile. In questa prima versione del progetto mi limito quindi ad implementare gli archivi con semplici file.
  Quando si deve verificare l' identità di un cliente con il suo codice viene aperto il file "codici.data"  , se viene trovato in questo file un cliente con nome e codice corrispondenti a quelli ricevuti via server si invia come risposta l' elenco delle ditte che possiede questo cliente.
  Invece quando deve essere registrato un ordine si apre  in scrittura un file specifico per il cliente e la ditta  in  questione posizionandosi alla fine , in modo da non sovrascrivere informazioni già esistenti , e vengono registrati tutti i dati relativi all' ordine. Quando sono richieste informazioni sugli ordini fatti viene aperto il file desiderato in lettura e vengono spediti al server che ha fatto la richiesta , tutti gli ordini che sono presenti.
  Si presenta però il problema di come memorizzare nuovi dati : bisogna decidere per quanto tempo tenere il file aperto , decidere cioè , se è più conveniente tenere aperto il file e registrare man mano i dati inviati per chiuderlo e aggiornarlo in memoria stabile alla fine di una sessione di interazione con un client (write-on-delay), oppure se registrare e chiudere subito il file all' arrivo di ogni singolo ordine applicando così la politica write-through. Questa seconda scelta risulta essere la più conveniente in quanto si limita il numero di file aperti e inoltre si limita il rischio di imbattersi in interferenze a causa di più scritture sullo stesso file. Il problema delle interferenze può rimanere se consideriamo il fatto che due clienti , identificatisi con lo stesso nome , provino a registrare un ordine allo stesso tempo ; considerando questo caso , per non incorrere in errori , si potrebbe pensare di accedere ai file tramite un semaforo che ne vieti l' accesso se tale file è stato già aperto da un altro utente. Considerando il fatto però che un cliente debba identificarsi anche tramite un codice che dovrebbe essere segreto ipotizziamo che due clienti distinti non possano accedere allo stesso file , il nome di questo infatti è proprio costituito in parte dal nome del cliente.
 
 
 

Server DataBase Ditta


  L' unico campo che compare nella finestra di inizializzazione di questo server e quello che determina la porta riservata a ricevere richieste di decodifica dei pezzi che inviano i clienti tramite i due server ditta replicati.
  Inizializzato questo server, ci si porrà in attesa di richieste secondo lo stesso protocollo che adotta il server database generale per il caso di connessioni non affidabili. Ricevuto un codice questo server lo confronta con tutti quelli che sono registrati in un file che rappresenta l' intero catalogo che possiede la ditta in questione. Se questo codice è presente allora viene rispedita una risposta contenente la descrizione e il prezzo corrispondente al codice altrimenti viene fornita una risposta negativa.
 
 


 

Administrator

  L' uso di file per registrare i dati ha reso necessario l' introduzione di entità chiamate administrator che inizializzino questi file contenenti informazioni e che ne possano gestire l' evoluzione.
  Per quanto riguarda i file contenenti i listini dei pezzi delle varie ditte  è necessario introdurre un' applicazione che possa inserirvi dei dati validi.
 


 
 

   E' invece necessario un altro administrator che gestisca le informazioni riguardanti i codici dei clienti e inoltre questo gestore deve anche trattare e definire quali ditte può contattare ogni singolo cliente , fornendogli , quando richieste , tutte le informazioni necessarie per poter stabilire delle connessioni con i server di queste aziende. Quando viene registrato un cliente deve essere inserito un nome e un codice i quali non devono essere uguali a quelli già presenti nell' archivio ; inseriti nome e codice corretti viene scandito per il nuovo cliente l' elenco delle ditte registrate nel database chiedendo per ciascuna di esse se il cliente in considerazione debba esservi registrato. Quando invece viene registrata una nuova ditta , oltre al nome , devono essere forniti i nomi degli host e i numeri di porta dei due server replicati che ricevono le richieste per la ditta in questione ; il nome che la identifica deve essere unico e una volta registrata correttamente , viene , questa volta , scandito l' elenco dei clienti presenti nell' archivio chiedendo , per ciascuno di essi , se si vuole registrarli per quella ditta.
 
 


 
 

Testing dell' applicazione e analisi delle prestazioni

   Per testare l' applicazione mi sono servito in un primo momento di un unico PC dove ogni componente era installato sull' host locale e le varie prove di cadute di server o di richieste concorrenti non hanno causato errori nell' esecuzione dei vari processi. Esecuzione che è risultata abbastanza veloce anche se il grado di concorrenza che ho potuto simulare è stato un pò limitato non potendo mettere in esecuzione più di quattro  o cinque clienti alla volta per questioni di memoria. Inoltre con una sola macchina non è stato possibile  fare più di una richiesta concorrente dello stesso tipo nel medesimo istante.
  Provando l' applicazione nel LAB2 c'è stata la possibilità di avviare i vari servizi su macchine distinte , dato però il numero limitato di macchine presenti al LAB2 e quello più consistente dei server da avviare non è stato possibile avere una corrispondenza uno a uno tra macchine e programmi. Avendo a disposizione solo tre macchine differenti (macchine con sistema operativo Windows NT 4.0 collegate tra loro tramite una rete LAN) e avendo la necessità di testare il reale funzionamento di tutte le connessioni ho ritenuto più opportuno installare su una macchina tutte le applicazioni server , su un altra tutte quelle database e sull' ultima alcune applicazioni client. Così facendo ho riscontrato un corretto funzionamento di tutte le connessioni create e una corretta registrazione degli ordini simulati causando anche cadute accidentali dei server.
  Infine per provare a lanciare due richieste in modo contemporaneo ho avviato un' applicazione client sulla macchina dove avevo installato i server database , così da avere la possibilità di gestire due client nello stesso momento ; a questo punto ho provato a fare lo stesso tipo di richiesta per entrambi i client , ottenendo come conseguenza un' esecuzione priva di errori.
  Sia la creazione di socket sia lo svolgimento dei servizi risultano molto veloci e completabili in pochi secondi , allo stesso tempo però a causa della velocità risulta difficile simulare più richieste contemporanee le quali non rallentano sensibilmente il sistema.