MokaByte Numero 10 - Luglio 1997 |
|||
|
Programmazione |
||
Impariamo a realizzare una semplice applicazione distribuita secondo la logica C/S | |||
Come promesso la scorsa volta, e dopo aver visto in teoria come si possono usare i Socket in una programmazione Client/Server, vediamo ora un esempio pratico di come risolvere un problema (quello di una semplice calcolatrice) tramite questo sistema. Si era detto che il server doveva potersi "duplicare" per ogni client che si presentava. Si era anche fatto vedere come questo potesse essere fatto usando i Thread, cioè quelle "parti di programma" che possono essere eseguite senza bloccare il resto del programma. A questo punto prendiamo l'esempio del mese scorso (il server attendeva una stringa e la rispediva) e lo modifichiamo in modo da fargli attendere due stringhe, convertirle in Integer, sommare i due valori e rispedire al mittente il risultato. Questo è il membro main del programma che addiziona:
public static void main(String args[]){
ServerAdd p;
Socket channel;
ServerSocket server;
try{
/* Apre il socket */
server = new ServerSocket(12000, 15);
while(true){
/* Si pone in attesa di un Client.
Appena qualcuno fa richiesta crea una nuova istanza di
ServerAdd e la esegue */
channel = server.accept();
p = new ServerAdd();
p.Channel=channel;
new Thread(p).start();
}
}
catch (IOException e){
}
}
Come
si può notare è estremamente simile (per non dire identico)
al main del programma della volta scorsa. Inutile dire che ci sarà
un programma identico anche per la funzione "Sottrai". Anche la classe
che effettivamente esegue materialmente la procedura è molto simile
a quella vista precedentemente. Ecco qui il sorgente:
/* Questo membro viene richiamato ogni volta che un client richiede la funzione. Possono
essere richiamate anche più istanze della funzione, in modo da servire più client
contemporaneamente */
public void run()
{
String c="";
int A1,A2,B;
try
{
/* Apre i canali di comunicazione */
out = new PrintStream(Channel.getOutputStream());
in = new DataInputStream(Channel.getInputStream());
A1=A2=0;
/* Prende i dati, li somma e spedisce il risultato */
try
{
c=in.readLine();
if(c!=null)
A1=atoi(c);
c=in.readLine();
if(c!=null)
A2=atoi(c);
B=A1+A2;
out.println(B);
}
catch(IOException e)
{
}
in.close();
out.close();
}
catch(IOException e)
{
}
}
Si
vede chiaramente cosa fa il programma. Attende due dati, li converte in
interi e li somma. La procedura che si è usata per convertire la
Stringa in Integer ha un nome che ricorderà qualcosa a tutti quelli
che arrivano dal C, infatti ho chiamato la funzione con lo stesso nome
usato con questo linguaggio. Vediamo un attimo in dettaglio la funzione
atoi.
/* Funzione che converte una stringa in un intero */
public static int atoi(String s)
{
Integer tempInt;
int k;
if((s.charAt(0)>='0') && (s.charAt(0)<='9'))
{
tempInt = Integer.valueOf(s);
k=tempInt.intValue();
}
else
k=0;
return(k);
}
Anche
in questo caso il codice è semplicissimo. Per prima cosa controllo
che il primo carattere sia un numero (se fosse un altro carattere e continuassi
la procedura, questa genererebbe un errore).
Poi
uso il membro valueOf della classe Integer. Questa funzione provvede a
convertire la Stringa in Intero. Il controllo precedente serviva solo per
evitare spiacevoli errori. A questo punto ritorno il valore (convertito
in int dalla funzione intValue) ed esco dalla procedura.
In fondo all'articolo proporrò i listati di tutti i programmi (Add,
Sub, Calc). Vediamo quindi il programma Calc.
Questo
programma sfrutta i due precedenti per eseguire la somma e la sottrazione.
Si pone quindi in attesa di un'istruzione da parte dell'operatore, accetta
i due dati e li spediscea chi di dovere. Quindi attende il risultato. Vediamo
in dettaglio il listato:
/* Questa procedura esegue l'addizione dei due operandi passati.
Per fare ciò manda i due dati alla funzione che esegue materialmente l'addizione
(classe Add) attraverso il canale Socket A_client e i suoi stream.
Infine legge il risultato che la classe Add pone sullo stesso Socket
*/
public static void Add(String op1,String op2) throws Exception
{
String ris;
ris="";
A_out.println(op1);
A_out.println(op2);
ris=A_in.readLine();
System.out.println(op1+"+"+op2+"="+ris);
}
/* Analogamente alla funzione Add, questa procedura esegue la funzione Sub.
Passa i dati alla classe Sub tramite il canale Socket S_client e i suoi stream,
infine, come la Add, legge il risultato mandato dalla classe Sub.
*/
public static void Sub(String op1,String op2) throws Exception
{
String ris;
ris="";
S_out.println(op1);
S_out.println(op2);
ris=S_in.readLine();
System.out.println(op1+"-"+op2+"="+ris);
}
/* Funzione che gestisce la calcolatrice.
La procedura è estremamente semplice: si limita a chiedere l'operazione (add, sub o quit)
e i parametri.
In base al tipo di operazione richiesto chiama la procedura corretta, oppure termina
il ciclo.
*/
public static void Calc() throws Exception
{
String Op,Op1,Op2;
DataInputStream In;
Op=Op1=Op2="";
In=new DataInputStream(System.in);
System.out.println("Calcolatrice MultiThread\n");
System.out.println("Inserire l'operazione da fare [Add/Sub/Quit]");
Op=In.readLine();
if(Op.compareTo("Quit")!=0)
{
System.out.println("Inserire il primo operando");
Op1=In.readLine();
System.out.println("Inserire il secondo operando");
Op2=In.readLine();
if(Op.compareTo("Add")==0)
Add(Op1,Op2);
else
if(Op.compareTo("Sub")==0)
Sub(Op1,Op2);
else
System.out.println("Operazione non valida");
}
else
Loop=false;
}
Chi
ha la memoria buona (o si è scaricato i listati) noterà che
questa procedura è identica a quella vista un po' di tempo fa quando
i server servivano un solo utente per volta. Si può notare che non
è cambiato proprio niente.
Il
cambiamento è solo sul server. Ma vediamo un attimo (per chi non
si ricorda o non l'ha mai vista) come funziona questo programma.
La
funzione Calc chiede all'utente cosa vuole fare. Chiede poi i due operandi
e, a seconda dell'operazione prescelta, lancia la procedura di Addizione
o Sottrazione. Queste due procedure non fanno altro che aprire un Socket
con il server che ospita il programma che esegue l'operazione e spedire
i dati.
Infine
si mettono in attesa del risultato.
Potete
provare questo programma aprendo tre "Prompt di DOS" e lanciando un programma
per ognuna (in uno il programma di Somma, in un altro quello di sottrazione
e nell'ultimo la calcolatrice).
I
parametri da passare alla calcolatrice sono solo due: i due indirizzi degli
Host sui quali si trovano gli altri programmi. Per chi lancia tutto sulla
stessa macchina può (a meno che non abbia un DNS registrato) usare
l'indirizzo 127.0.0.1, che corrisponde alla macchina locale.
Conclusioni
Bene, a questo punto è stata data (in questo ciclo di "lezioni")
un'idea di come si costruiscono programmi con l'architettura Client/Server.
Naturalmente questa è solo la base, da cui si può costruire
il tutto. Tenete però presente che ogni server che esiste si basa
su questa struttura, cioè crea una procedura "server" per ogni client
che si presenta. E questo naturalmente è fondamentale. Immaginate
che fine farebbe Internet se ogni sito servisse solo un utente alla volta?
|
||
|
||
MokaByte
rivista web su Java |
||
|