Skip to content

9. Codice sorgente del programma

9.1. Il client TCP generico

Molti servizi creati agli albori di Internet funzionano secondo il modello del server echo studiato in precedenza: gli scambi client-server consistono nello scambio di righe di testo. Scriveremo un client TCP generico che verrà avviato come segue: java cltTCPgenerique server port

Questo client TCP si connetterà alla porta del server. Una volta connesso, creerà due thread:

  1. un thread responsabile della lettura dei comandi digitati sulla tastiera e del loro invio al server
  2. un thread responsabile della lettura delle risposte del server e della loro visualizzazione sullo schermo

Perché due thread? Ogni servizio TCP-IP ha un proprio protocollo specifico e talvolta si verificano le seguenti situazioni:

  • il client deve inviare diverse righe di testo prima di ricevere una risposta
  • la risposta di un server può contenere più righe di testo

Pertanto, il ciclo che invia una singola riga al server e ne riceve una dal server non è sempre adatto. Creeremo quindi due cicli separati:

  • un ciclo per leggere i comandi digitati sulla tastiera da inviare al server. L'utente segnalerà la fine dei comandi con la parola chiave "fin".
  • un ciclo per ricevere e visualizzare le risposte del server. Si tratterà di un ciclo infinito che verrà interrotto solo dalla chiusura della connessione di rete da parte del server o dall'immissione del comando «end» da parte dell'utente tramite la tastiera.

Per avere questi due cicli separati, abbiamo bisogno di due thread indipendenti. Vediamo un esempio di esecuzione in cui il nostro client TCP generico si connette a un servizio SMTP (Simple Mail Transfer Protocol). Questo servizio è responsabile dell'inoltro delle e-mail ai destinatari. Opera sulla porta 25 e utilizza un protocollo di scambio basato su testo.


Dos>java clientTCPgenerique istia.univ-angers.fr 25
Commandes :
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
help
<-- 502 5.3.0 Sendmail 8.11.6 -- HELP not implemented
mail from: machin@univ-angers.fr
<-- 250 2.1.0 machin@univ-angers.fr... Sender ok
rcpt to: serge.tahe@istia.univ-angers.fr
<-- 250 2.1.5 serge.tahe@istia.univ-angers.fr... Recipient ok
data
<-- 354 Enter mail, end with "." on a line by itself
Subject: test
 
ligne1
ligne2
ligne3
.
<-- 250 2.0.0 g4D6bks25951 Message accepted for delivery
quit
<-- 221 2.0.0 istia.univ-angers.fr closing connection
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]

Commentiamo questi scambi client-server:

  • Il servizio SMTP invia un messaggio di benvenuto quando un client si connette ad esso:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
  • Alcuni servizi dispongono di un comando "help" che fornisce informazioni sui comandi disponibili per quel servizio. Non è questo il caso. I comandi SMTP utilizzati nell'esempio sono i seguenti:
    • mail from: mittente, per specificare l'indirizzo e-mail del mittente
    • rcpt to: destinatario, per specificare l'indirizzo e-mail del destinatario del messaggio. Se ci sono più destinatari, il comando rcpt to: viene ripetuto tante volte quante necessarie per ciascun destinatario.
    • data, che segnala al server SMTP che il messaggio sta per essere inviato. Come indicato nella risposta del server, si tratta di una sequenza di righe che termina con una riga contenente solo un punto. Un messaggio può avere intestazioni separate dal corpo del messaggio da una riga vuota. Nel nostro esempio, abbiamo incluso un oggetto utilizzando la parola chiave Subject:
  • Una volta inviato il messaggio, possiamo comunicare al server che abbiamo terminato utilizzando il comando quit. Il server chiude quindi la connessione di rete. Il thread di lettura può rilevare questo evento e arrestarsi.
  • L'utente digita quindi "end" sulla tastiera per arrestare anche il thread che legge i comandi digitati sulla tastiera.

Se controlliamo l'e-mail ricevuta, vediamo quanto segue (Outlook):

Image

Si noti che il servizio SMTP non è in grado di rilevare se un mittente sia valido o meno. Pertanto, non ci si può mai fidare del campo "Da" di un messaggio. In questo caso, il mittente machin@univ-angers.fr non esisteva.

Questo client TCP generico ci permette di scoprire il protocollo di comunicazione dei servizi Internet e, da lì, creare classi specializzate per i client di questi servizi. Esploriamo il protocollo di comunicazione del servizio POP (Post Office Protocol), che ci permette di recuperare le email memorizzate su un server. Opera sulla porta 110.


Dos> java clientTCPgenerique istia.univ-angers.fr 110
Commandes :
<-- +OK Qpopper (version 4.0.3) at istia.univ-angers.fr starting.
help
<-- -ERR Unknown command: "help".
user st
<-- +OK Password required for st.
pass monpassword
<-- +OK st has 157 visible messages (0 hidden) in 11755927 octets.
list
<-- +OK 157 visible messages (11755927 octets)
<-- 1 892847
<-- 2 171661
...
<-- 156 2843
<-- 157 2796
<-- .
retr 157
<-- +OK 2796 octets
<-- Received: from lagaffe.univ-angers.fr (lagaffe.univ-angers.fr [193.49.144.1])
<--     by istia.univ-angers.fr (8.11.6/8.9.3) with ESMTP id g4D6wZs26600;
<--     Mon, 13 May 2002 08:58:35 +0200
<-- Received: from jaume ([193.49.146.242])
<--     by lagaffe.univ-angers.fr (8.11.1/8.11.2/GeO20000215) with SMTP id g4D6wSd37691;
<--     Mon, 13 May 2002 08:58:28 +0200 (CEST)
...
<-- ------------------------------------------------------------------------
<-- NOC-RENATER2                  Tl.  : 0800 77 47 95
<-- Fax : (+33) 01 40 78 64 00 ,  Email : noc-r2@cssi.renater.fr
<-- ------------------------------------------------------------------------
<--
<-- .
quit
<-- +OK Pop server at istia.univ-angers.fr signing off.
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]

I comandi principali sono i seguenti:

  • user login, dove si inserisce il proprio login sulla macchina che ospita le proprie e-mail
  • pass password, dove si inserisce la password associata al login precedente
  • list, per ottenere un elenco dei messaggi nel formato numero, dimensione in byte
  • retr i, per leggere il messaggio numero i
  • quit, per terminare la sessione.

Esploriamo ora il protocollo di comunicazione tra un client e un server web, che in genere funziona sulla porta 80:


Dos> java clientTCPgenerique istia.univ-angers.fr 80
Commandes :
GET /index.html HTTP/1.0
 
<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix)  (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>
<--
<-- <head>
<-- <meta http-equiv="Content-Type"
<-- content="text/html; charset=iso-8859-1">
<-- <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
<-- <title>Bienvenue a l'ISTIA - Universite d'Angers</title>
<-- </head>
....
<-- face="Verdana"> - Dernire mise  jour le <b>10 janvier 2002</b></font></p>
<-- </body>
<-- </html>
<--
[fin du thread de lecture des réponses du serveur]
fin
[fin du thread d'envoi des commandes au serveur]

Un client web invia i propri comandi al server secondo il seguente schema:

commande1
commande2
...
commanden
[ligne vide]

Il server web risponde solo dopo aver ricevuto la riga vuota. In questo esempio, abbiamo utilizzato un solo comando:

GET /index.html HTTP/1.0

che richiede l'URL /index.html dal server e indica che sta utilizzando la versione 1.0 di HTTP. La versione più recente di questo protocollo è la 1.1. L'esempio mostra che il server ha risposto inviando il contenuto del file index.html e poi ha chiuso la connessione, come possiamo vedere dalla lettura della risposta "thread terminating". Prima di inviare il contenuto del file index.html, il server web ha inviato una serie di intestazioni seguite da una riga vuota:

<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix)  (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>

La riga <html> è la prima riga del file /index.html. Il testo che la precede è chiamato intestazioni HTTP (HyperText Transfer Protocol). Non entreremo nei dettagli su queste intestazioni in questa sede, ma tieni presente che il nostro client generico fornisce l'accesso ad esse, il che può essere utile per comprenderle. Ad esempio, la prima riga:

<-- HTTP/1.1 200 OK

indica che il server web contattato supporta il protocollo HTTP/1.1 e che ha trovato con successo il file richiesto (200 OK), dove 200 è un codice di risposta HTTP. Le righe

<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html

comunicano al client che riceverà 11.251 byte di testo HTML (HyperText Markup Language) e che la connessione verrà chiusa una volta inviati i dati.

Ecco quindi un client TCP molto pratico. Certamente fa meno del programma telnet che abbiamo usato in precedenza, ma è stato interessante scriverlo da soli. Il programma client TCP generico è il seguente:

// imported packages
import java.io.*;
import java.net.*;

public class clientTCPgenerique{

    // receives the characteristics of a service as a parameter in the form
     // server port
     // connects to the service
     // creates a thread to read keyboard commands
     // these will be sent to the
     // creates a thread to read server responses
     // these will be displayed on the screen
     // the whole thing ends with the command end typed on the keyboard

   // instance variable
  private static Socket client;

    public static void main(String[] args){

         // syntax
        final String syntaxe="pg serveur port";

         // number of arguments
        if(args.length != 2)
            erreur(syntaxe,1);

         // note the server name
        String serveur=args[0];

         // port must be integer >0
        int port=0;
        boolean erreurPort=false;
        Exception E=null;
        try{
            port=Integer.parseInt(args[1]);
        }catch(Exception e){
            E=e;
            erreurPort=true;
        }
        erreurPort=erreurPort || port <=0;
        if(erreurPort)
            erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);

        client=null;
         // there may be problems
        try{
             // connect to the service
            client=new Socket(serveur,port);
        }catch(Exception ex){
             // error
            erreur("Impossible de se connecter au service ("+ serveur
                +","+port+"), erreur : "+ex.getMessage(),3);
             // end
            return;
        }//catch

         // create read/write threads
    new ClientSend(client).start();
    new ClientReceive(client).start();

        // end thread main
        return;
    }// hand

     // error display
    public static void erreur(String msg, int exitCode){
         // error display
        System.err.println(msg);
         // stop with error
        System.exit(exitCode);
    }//error
}//class  

class ClientSend extends Thread {
    // class for reading keyboard commands
     // and send them to a server via a tcp client passed in parameter

    private Socket client;    // tcp client

     // manufacturer
    public ClientSend(Socket client){
         // we note the tcp client
        this.client=client;
    }//manufacturer

     // thread Run method
    public void run(){

        // local data
        PrintWriter OUT=null;            // network write streams
    BufferedReader IN=null;        // keyboard flow
        String commande=null;            // command read from keyboard

         // error management
        try{
             // network write stream creation
            OUT=new PrintWriter(client.getOutputStream(),true);
      // keyboard input stream creation
      IN=new BufferedReader(new InputStreamReader(System.in));
            // order entry-send loop
            System.out.println("Commandes : ");
            while(true){
                 // read command typed on keyboard
                commande=IN.readLine().trim();
                // finished?
                if (commande.toLowerCase().equals("fin")) break;
                // send order to server
                OUT.println(commande);
                 // next order
            }//while
        }catch(Exception ex){
             // error
            System.err.println("Envoi : L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         // end - close flows
        try{
            OUT.close();client.close();
        }catch(Exception ex){}
         // signals the end of the thread
        System.out.println("[Envoi : fin du thread d'envoi des commandes au serveur]");
    }//run
}//class

class ClientReceive extends Thread{
    // class responsible for reading lines of text intended for a 
     // tcp client passed as parameter

    private Socket client;    // tcp client

     // manufacturer
    public ClientReceive(Socket client){
         // we note the tcp client
        this.client=client;
    }//manufacturer

     // thread Run method
    public void run(){

        // local data
        BufferedReader IN=null;        // network read stream
        String réponse=null;        // server response

         // error management
        try{
             // create network read stream
            IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
            // loop read text lines from IN stream
            while(true){
                 // network streaming
                réponse=IN.readLine();
                 // closed flow?
                if(réponse==null) break;
                // display
                System.out.println("<-- "+réponse);
            }//while
        }catch(Exception ex){
            // error
            System.err.println("Réception : L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         // end - we close the feeds
        try{
            IN.close();client.close();
        }catch(Exception ex){}
         // signals the end of the thread
        System.out.println("[Réception : fin du thread de lecture des réponses du serveur]");
    }//run
}//class

9.2. Il server TCP generico

Ora vedremo un server

  • che visualizza sullo schermo i comandi inviati dai propri client
  • e invia loro, in risposta, le righe di testo digitate sulla tastiera da un utente. È quindi l’utente a fungere da server.

Il programma viene avviato con: java genericTCPserver listeningPort, dove listeningPort è la porta a cui i client devono connettersi. Il servizio client sarà gestito da due thread:

  • un thread dedicato esclusivamente alla lettura delle righe di testo inviate dal client
  • un thread dedicato esclusivamente alla lettura delle risposte digitate sulla tastiera dall'utente. Questo thread segnalerà, utilizzando il comando `fin`, che sta chiudendo la connessione con il client.

Il server crea due thread per ogni client. Se ci sono n client, ci saranno 2n thread attivi contemporaneamente. Il server stesso non si ferma mai a meno che l'utente non prema Ctrl-C sulla tastiera. Vediamo alcuni esempi.

Il server è in esecuzione sulla porta 100 e utilizziamo il client generico per comunicare con esso. La finestra del client ha questo aspetto:


E:\data\serge\MSNET\c#\réseau\client tcp générique> java clientTCPgenerique localhost 100
Commandes :
commande 1 du client 1
<-- réponse 1 au client 1
commande 2 du client 1
<-- réponse 2 au client 1
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]

Le righe che iniziano con <-- sono quelle inviate dal server al client; le altre provengono dal client al server. La finestra del server è la seguente:


Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]

Le righe che iniziano con <-- sono quelle inviate dal cliente al server. Le righe N: sono quelle inviate dal server al cliente N. Il server sopra indicato è ancora attivo anche se il cliente 1 ha terminato. Avviamo un secondo cliente per lo stesso server:


Dos> java clientTCPgenerique localhost 100
Commandes :
commande 3 du client 2
<-- réponse 3 au client 2
fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du thread de lecture des réponses du serveur]
[fin du thread d'envoi des commandes au serveur]

La finestra del server appare quindi così:


Dos> java serveurTCPgenerique 100
Serveur générique lancé sur le port 100
Thread de lecture des réponses du serveur au client 1 lancé
1 : Thread de lecture des demandes du client 1 lancé
<-- commande 1 du client 1
réponse 1 au client 1
1 : <-- commande 2 du client 1
réponse 2 au client 1
1 : [fin du Thread de lecture des demandes du client 1]
fin
[fin du Thread de lecture des réponses du serveur au client 1]
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- commande 3 du client 2
réponse 3 au client 2
2 : [fin du Thread de lecture des demandes du client 2]
fin
[fin du Thread de lecture des réponses du serveur au client 2]
^C

Ora simuliamo un server web eseguendo il nostro server generico sulla porta 88:


Dos> java serveurTCPgenerique 88
Serveur générique lancé sur le port 88

Ora apriamo un browser e richiediamo l'URL http://localhost:88/exemple.html. Il browser si connetterà quindi alla porta 88 sul computer localhost e richiederà la pagina /example.html:

Image

Ora diamo un'occhiata alla finestra del nostro server:

Dos>java serveurTCPgenerique 88
Serveur générique lancé sur le port 88
Thread de lecture des réponses du serveur au client 2 lancé
2 : Thread de lecture des demandes du client 2 lancé
<-- GET /exemple.html HTTP/1.1
<-- Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/msword, */*
<-- Accept-Language: fr
<-- Accept-Encoding: gzip, deflate
<-- User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.0.3705; .NET CLR 1.0.2
914)
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--

Questo mostra le intestazioni HTTP inviate dal browser. Ciò ci permette di approfondire gradualmente la conoscenza del protocollo HTTP. In un esempio precedente, avevamo creato un client web che inviava solo il comando GET. Era sufficiente. Qui vediamo che il browser invia altre informazioni al server. Queste informazioni servono a comunicare al server con quale tipo di client ha a che fare. Notiamo inoltre che le intestazioni HTTP terminano con una riga vuota.

Creiamo una risposta per il nostro client. L'utente alla tastiera è il vero e proprio server in questo caso e può creare manualmente una risposta. Ricordiamo la risposta inviata da un server web in un esempio precedente:

<-- HTTP/1.1 200 OK
<-- Date: Mon, 13 May 2002 07:30:58 GMT
<-- Server: Apache/1.3.12 (Unix)  (Red Hat/Linux) PHP/3.0.15 mod_perl/1.21
<-- Last-Modified: Wed, 06 Feb 2002 09:00:58 GMT
<-- ETag: "23432-2bf3-3c60f0ca"
<-- Accept-Ranges: bytes
<-- Content-Length: 11251
<-- Connection: close
<-- Content-Type: text/html
<--
<-- <html>

Proviamo a fornire una risposta simile:

...
<-- Host: localhost:88
<-- Connection: Keep-Alive
<--
2 : HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :
2 : <html>
2 :   <head><title>Serveur generique</title></head>
2 :   <body>
2 :     <center>
2 :       <h2>Reponse du serveur generique</h2>
2 :     </center>
2 :    </body>
2 : </html>
2 : fin
L'erreur suivante s'est produite : Impossible de lire les données de la connexion de transport.
[fin du Thread de lecture des demandes du client 2]
[fin du Thread de lecture des réponses du serveur au client 2]

Le righe che iniziano con 2: vengono inviate dal server al client n. 2. Il comando end chiude la connessione dal server al client. Nella nostra risposta, ci siamo limitati alle seguenti intestazioni HTTP:

HTTP/1.1 200 OK
2 : Server: serveur tcp generique
2 : Connection: close
2 : Content-Type: text/html
2 :

Non specifichiamo la dimensione del file che stiamo inviando (Content-Length), ma indichiamo semplicemente che chiuderemo la connessione (Connection: close) dopo averlo inviato. Questo è sufficiente per il browser. Quando vedrà che la connessione è chiusa, capirà che la risposta del server è completa e visualizzerà la pagina HTML che gli è stata inviata. La pagina è la seguente:

2 : <html>
2 :   <head><title>Serveur generique</title></head>
2 :   <body>
2 :     <center>
2 :       <h2>Reponse du serveur generique</h2>
2 :     </center>
2 :    </body>
2 : </html>

L'utente chiude quindi la connessione al client digitando il comando "fin". Il browser capisce così che la risposta del server è completa e può visualizzarla:

Image

Se, nell'esempio sopra riportato, si seleziona Visualizza/Sorgente per vedere cosa ha ricevuto il browser, si ottiene:

Image

cioè esattamente ciò che è stato inviato dal server generico.

Il codice per il server TCP generico è il seguente:

// packages
import java.io.*;
import java.net.*;

public class serveurTCPgenerique{

    // main program
    public static void main (String[] args){

    // receives the port of listening to customer requests
     // creates a thread to read client requests
     // these will be displayed on the screen
     // creates a thread to read keyboard commands
     // these will be sent as a reply to the customer
     // the whole thing ends with the command end typed on the keyboard

    final String syntaxe="Syntaxe : pg port";
   // instance variable
         // is there an argument
     if(args.length != 1)
        erreur(syntaxe,1);

         // port must be integer >0
        int port=0;
        boolean erreurPort=false;
        Exception E=null;
        try{
            port=Integer.parseInt(args[0]);
        }catch(Exception e){
            E=e;
            erreurPort=true;
        }
        erreurPort=erreurPort || port <=0;
        if(erreurPort)
            erreur(syntaxe+"\n"+"Port incorrect ("+E+")",2);

     // we create the listening service
    ServerSocket ecoute=null;
    int nbClients=0;    // no. of customers handled
        try{
             // create the service
            ecoute=new ServerSocket(port);
             // follow-up
            System.out.println("Serveur générique lancé sur le port " + port);

             // customer service loop
            Socket client=null;
            while (true){ // infinite loop - will be stopped by Ctrl-C
                 // waiting for a customer
                client=ecoute.accept();

                 // the service is provided by separate threads
                nbClients++;

                 // create read/write threads
        new ServeurSend(client,nbClients).start();
        new ServeurReceive(client,nbClients).start();

                // back to listening to requests
            }// end while
        }catch(Exception ex){
             // we report the error
            erreur("L'erreur suivante s'est produite : " + ex.getMessage(),3);
        }//catch
    }// fine hand

     // error display
    public static void erreur(String msg, int exitCode){
         // error display
        System.err.println(msg);
         // stop with error
        System.exit(exitCode);
    }//error
}//class

class ServeurSend extends Thread{
    // class responsible for reading typed responses
     // and send them to a client via a tcp client passed to the

    Socket client;    // tcp client
    int numClient;        // customer no

     // manufacturer
    public ServeurSend(Socket client, int numClient){
         // we note the tcp client
        this.client=client;
         // and its
        this.numClient=numClient;
    }//manufacturer

     // thread Run method
    public void run(){

        // local data
        PrintWriter OUT=null;        // network write streams
        String réponse=null;        // answer read from keyboard
    BufferedReader IN=null;    // keyboard flow

        // follow-up
        System.out.println("Thread de lecture des réponses du serveur au client "+ numClient + " lancé");
         // error management
        try{
             // network write stream creation
            OUT=new PrintWriter(client.getOutputStream(),true);
      // keyboard flow creation
      IN=new BufferedReader(new InputStreamReader(System.in));
            // order entry-send loop
            while(true){
                 // customer identification
                System.out.print("--> " + numClient + " : ");
                 // read response typed on keyboard
                réponse=IN.readLine().trim();
                // finished?
                if (réponse.toLowerCase().equals("fin")) break;
                // send response to server
                OUT.println(réponse);
                 // following response
            }//while
        }catch(Exception ex){
             // error
            System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         // end - close flows
        try{
            OUT.close();client.close();
        }catch(Exception ex){}
         // signals the end of the thread
        System.out.println("[fin du Thread de lecture des réponses du serveur au client "+ numClient+ "]");
    }//run
}//class

class ServeurReceive extends Thread{
    // class responsible for reading text lines sent to the server 
     // via a tcp client passed to the builder

    Socket client;    // tcp client
    int numClient;        // customer no

     // manufacturer
    public ServeurReceive(Socket client, int numClient){
         // we note the tcp client
        this.client=client;
         // and its
        this.numClient=numClient;
    }//manufacturer

     // thread Run method
    public void run(){

        // local data
        BufferedReader IN=null;        // network read stream
        String réponse=null;        // server response

         // follow-up
        System.out.println("Thread de lecture des demandes du client "+ numClient + " lancé");
         // error management
        try{
             // create network read stream
            IN=new BufferedReader(new InputStreamReader(client.getInputStream()));
            // loop read text lines from IN stream
            while(true){
                 // network streaming
                réponse=IN.readLine();
                 // closed flow?
                if(réponse==null) break;
                // display
                System.out.println("<-- "+réponse);
            }//while
        }catch(Exception ex){
            // error
            System.err.println("L'erreur suivante s'est produite : " + ex.getMessage());
        }//catch
         // end - close flows
        try{
            IN.close();client.close();
        }catch(Exception ex){}
         // signals the end of the thread
        System.out.println("[fin du Thread de lecture des demandes du client "+ numClient+"]");
    }//run
}//class