Skip to content

9. Código-fonte do programa

9.1. O cliente TCP genérico

Muitos serviços criados nos primórdios da Internet funcionam de acordo com o modelo de servidor de eco estudado anteriormente: as trocas cliente-servidor consistem na troca de linhas de texto. Vamos escrever um cliente TCP genérico que será iniciado da seguinte forma: java cltTCPgenerique server port

Este cliente TCP irá ligar-se à porta do servidor. Uma vez ligado, irá criar duas threads:

  1. uma thread responsável por ler os comandos digitados no teclado e enviá-los ao servidor
  2. uma thread responsável por ler as respostas do servidor e exibi-las no ecrã

Porquê duas threads? Cada serviço TCP-IP tem o seu próprio protocolo específico, e por vezes surgem as seguintes situações:

  • o cliente deve enviar várias linhas de texto antes de receber uma resposta
  • a resposta de um servidor pode conter várias linhas de texto

Por isso, o ciclo que envia uma única linha para o servidor e recebe uma única linha do servidor nem sempre é adequado. Vamos, portanto, criar dois ciclos separados:

  • um ciclo para ler os comandos digitados no teclado a enviar para o servidor. O utilizador indicará o fim dos comandos com a palavra-chave «fin».
  • um ciclo para receber e apresentar as respostas do servidor. Trata-se de um ciclo infinito que só será interrompido se o servidor encerrar a ligação de rede ou se o utilizador digitar o comando «end» no teclado.

Para ter estes dois loops separados, precisamos de duas threads independentes. Vejamos um exemplo de execução em que o nosso cliente TCP genérico se liga a um serviço SMTP (Simple Mail Transfer Protocol). Este serviço é responsável por encaminhar e-mails para os seus destinatários. Opera na porta 25 e utiliza um protocolo de troca baseado em texto.


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]

Vamos comentar estas trocas cliente-servidor:

  • O serviço SMTP envia uma mensagem de boas-vindas quando um cliente se liga a ele:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
  • Alguns serviços têm um comando «help» que fornece informações sobre os comandos disponíveis para esse serviço. Não é esse o caso aqui. Os comandos SMTP utilizados no exemplo são os seguintes:
    • mail from: remetente, para especificar o endereço de e-mail do remetente
    • rcpt to: destinatário, para especificar o endereço de e-mail do destinatário da mensagem. Se houver vários destinatários, o comando rcpt to: é repetido tantas vezes quantas forem necessárias para cada destinatário.
    • data, que sinaliza ao servidor SMTP que a mensagem está prestes a ser enviada. Conforme indicado na resposta do servidor, trata-se de uma sequência de linhas que termina com uma linha contendo apenas um ponto. Uma mensagem pode ter cabeçalhos separados do corpo da mensagem por uma linha em branco. No nosso exemplo, incluímos um assunto utilizando a palavra-chave Subject:
  • Assim que a mensagem for enviada, podemos indicar ao servidor que terminámos utilizando o comando quit. O servidor encerra então a ligação de rede. O segmento de leitura pode detetar este evento e parar.
  • O utilizador digita então «end» no teclado para também parar a thread que lê os comandos digitados no teclado.

Se verificarmos o e-mail recebido, vemos o seguinte (Outlook):

Image

Note que o serviço SMTP não consegue detetar se um remetente é válido ou não. Por isso, nunca se pode confiar no campo «De» de uma mensagem. Neste caso, o remetente machin@univ-angers.fr não existia.

Este cliente TCP genérico permite-nos descobrir o protocolo de comunicação dos serviços da Internet e, a partir daí, criar classes especializadas para clientes desses serviços. Vamos explorar o protocolo de comunicação do serviço POP (Post Office Protocol), que nos permite recuperar e-mails armazenados num servidor. Funciona na 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]

Os principais comandos são os seguintes:

  • login do utilizador, onde introduz o seu nome de utilizador na máquina que hospeda os seus e-mails
  • pass password, onde introduz a palavra-passe associada ao login anterior
  • list, para obter uma lista de mensagens no formato número, tamanho em bytes
  • retr i, para ler a mensagem número i
  • sair, para encerrar a sessão.

Vamos agora explorar o protocolo de comunicação entre um cliente e um servidor web, que normalmente funciona na 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]

Um cliente web envia os seus comandos para o servidor de acordo com o seguinte padrão:

commande1
commande2
...
commanden
[ligne vide]

O servidor web só responde após receber a linha vazia. Neste exemplo, utilizámos apenas um comando:

GET /index.html HTTP/1.0

que solicita a URL /index.html ao servidor e indica que está a utilizar a versão 1.0 do HTTP. A versão mais recente deste protocolo é a 1.1. O exemplo mostra que o servidor respondeu enviando o conteúdo do ficheiro index.html e, em seguida, encerrou a ligação, como podemos ver na mensagem «thread terminating» da resposta. Antes de enviar o conteúdo do ficheiro index.html, o servidor web enviou uma série de cabeçalhos seguidos de uma linha vazia:

<-- 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>

A linha <html> é a primeira linha do ficheiro /index.html. O texto que a precede é designado por cabeçalhos HTTP (HyperText Transfer Protocol). Não entraremos em pormenores sobre estes cabeçalhos aqui, mas tenha em mente que o nosso cliente genérico fornece acesso aos mesmos, o que pode ser útil para os compreender. Por exemplo, a primeira linha:

<-- HTTP/1.1 200 OK

indica que o servidor web contactado suporta o protocolo HTTP/1.1 e que encontrou com sucesso o ficheiro solicitado (200 OK), sendo que 200 é um código de resposta HTTP. As linhas

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

indica ao cliente que irá receber 11 251 bytes de texto HTML (HyperText Markup Language) e que a ligação será encerrada assim que os dados forem enviados.

Portanto, aqui temos um cliente TCP muito útil. É certo que faz menos do que o programa telnet que utilizámos anteriormente, mas foi interessante escrevê-lo nós próprios. O programa cliente TCP genérico é o seguinte:

// 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. O servidor TCP genérico

Agora vamos ver um servidor

  • que exibe no ecrã os comandos enviados pelos seus clientes
  • e lhes envia, em resposta, as linhas de texto digitadas no teclado por um utilizador. É, portanto, o utilizador que atua como servidor.

O programa é iniciado por: java genericTCPserver listeningPort, onde listeningPort é a porta à qual os clientes devem ligar-se. O serviço ao cliente será tratado por duas threads:

  • uma thread dedicada exclusivamente à leitura das linhas de texto enviadas pelo cliente
  • uma thread dedicada exclusivamente à leitura das respostas digitadas no teclado pelo utilizador. Esta thread sinalizará, utilizando o comando `fin`, que está a encerrar a ligação com o cliente.

O servidor cria duas threads por cliente. Se houver n clientes, haverá 2n threads ativas ao mesmo tempo. O próprio servidor nunca pára, a menos que o utilizador pressione Ctrl-C no teclado. Vejamos alguns exemplos.

O servidor está a funcionar na porta 100 e usamos o cliente genérico para comunicar com ele. A janela do cliente tem este aspeto:


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]

As linhas que começam com <-- são as enviadas do servidor para o cliente; as outras são do cliente para o servidor. A janela do servidor é a seguinte:


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]

As linhas que começam com <-- são as enviadas do cliente para o servidor. As linhas N: são as enviadas do servidor para o cliente N. O servidor acima continua ativo, apesar de o cliente 1 ter terminado. Lançamos um segundo cliente para o mesmo servidor:


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]

A janela do servidor fica então com este aspeto:


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

Agora vamos simular um servidor web executando o nosso servidor genérico na porta 88:


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

Agora vamos abrir um navegador e solicitar a URL http://localhost:88/exemple.html. O navegador irá então ligar-se à porta 88 na máquina localhost e solicitar a página /example.html:

Image

Agora vamos dar uma olhada na janela do nosso servidor:

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
<--

Isto revela os cabeçalhos HTTP enviados pelo navegador. Isto permite-nos ir aprendendo gradualmente sobre o protocolo HTTP. Num exemplo anterior, criámos um cliente web que enviava apenas o comando GET. Isso foi suficiente. Aqui vemos que o navegador envia outras informações para o servidor. Estas informações têm como objetivo indicar ao servidor com que tipo de cliente está a lidar. Vemos também que os cabeçalhos HTTP terminam com uma linha em branco.

Vamos criar uma resposta para o nosso cliente. O utilizador ao teclado é, neste caso, o servidor e pode criar manualmente uma resposta. Lembre-se da resposta enviada por um servidor web num exemplo anterior:

<-- 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>

Vamos tentar fornecer uma resposta semelhante:

...
<-- 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]

As linhas que começam com 2: são enviadas do servidor para o cliente n.º 2. O comando «end» encerra a ligação do servidor ao cliente. Na nossa resposta, limitámo-nos aos seguintes cabeçalhos HTTP:

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

Não especificamos o tamanho do ficheiro que estamos a enviar (Content-Length), mas indicamos simplesmente que iremos encerrar a ligação (Connection: close) após o envio. Isto é suficiente para o navegador. Ao verificar que a ligação foi encerrada, saberá que a resposta do servidor está completa e exibirá a página HTML que lhe foi enviada. A página é a seguinte:

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>

O utilizador encerra então a ligação ao cliente digitando o comando «fin». O navegador fica assim a saber que a resposta do servidor está completa e pode exibi-la:

Image

Se, no exemplo acima, selecionar «Ver/Fonte» para ver o que o navegador recebeu, obtém:

Image

ou seja, exatamente o que foi enviado pelo servidor genérico.

O código do servidor TCP genérico é o seguinte:

// 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