MokaByte Numero 10 - Luglio 1997

Foto
 

Programmazione 
Client Server 

 

di  
Luca Bertoncello

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

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it