Skip to content

9. Programmquellcode

9.1. Der generische TCP-Client

Viele Dienste, die in den Anfängen des Internets entstanden sind, funktionieren nach dem zuvor behandelten Echo-Server-Modell: Der Austausch zwischen Client und Server besteht aus dem Austausch von Textzeilen. Wir werden einen generischen TCP-Client schreiben, der wie folgt gestartet wird: java cltTCPgenerique server port

Dieser TCP-Client stellt eine Verbindung zum Port des Servers her. Sobald die Verbindung hergestellt ist, erstellt er zwei Threads:

  1. einen Thread, der für das Einlesen der über die Tastatur eingegebenen Befehle und deren Übermittlung an den Server zuständig ist
  2. ein Thread, der für das Lesen der Antworten des Servers und deren Anzeige auf dem Bildschirm zuständig ist

Warum zwei Threads? Jeder TCP-IP-Dienst hat sein eigenes spezifisches Protokoll, und es treten manchmal folgende Situationen auf:

  • Der Client muss mehrere Zeilen Text senden, bevor er eine Antwort erhält
  • Die Antwort eines Servers kann mehrere Zeilen Text enthalten

Daher ist die Schleife, die eine einzelne Zeile an den Server sendet und eine einzelne Zeile vom Server empfängt, nicht immer geeignet. Wir werden daher zwei separate Schleifen erstellen:

  • eine Schleife zum Lesen der über die Tastatur eingegebenen Befehle, die an den Server gesendet werden sollen. Der Benutzer signalisiert das Ende der Befehle mit dem Schlüsselwort „fin“.
  • eine Schleife zum Empfangen und Anzeigen der Antworten des Servers. Dabei handelt es sich um eine Endlosschleife, die nur unterbrochen wird, wenn der Server die Netzwerkverbindung trennt oder der Benutzer den Befehl „end“ über die Tastatur eingibt.

Um diese beiden separaten Schleifen zu haben, benötigen wir zwei unabhängige Threads. Betrachten wir ein Ausführungsbeispiel, bei dem unser generischer TCP-Client eine Verbindung zu einem SMTP-Dienst (Simple Mail Transfer Protocol) herstellt. Dieser Dienst ist für die Weiterleitung von E-Mails an ihre Empfänger zuständig. Er läuft auf Port 25 und verwendet ein textbasiertes Austauschprotokoll.


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]

Lassen Sie uns diese Client-Server-Kommunikation kommentieren:

  • Der SMTP-Dienst sendet eine Willkommensnachricht, wenn ein Client eine Verbindung zu ihm herstellt:
<-- 220 istia.univ-angers.fr ESMTP Sendmail 8.11.6/8.9.3; Mon, 13 May 2002 08:37:26 +0200
  • Einige Dienste verfügen über einen „help“-Befehl, der Informationen zu den für diesen Dienst verfügbaren Befehlen liefert. Das ist hier nicht der Fall. Die im Beispiel verwendeten SMTP-Befehle lauten wie folgt:
    • mail from: Absender, um die E-Mail-Adresse des Absenders anzugeben
    • rcpt to: Empfänger, um die E-Mail-Adresse des Empfängers der Nachricht anzugeben. Bei mehreren Empfängern wird der Befehl rcpt to: so oft wie nötig für jeden Empfänger wiederholt.
    • data, das dem SMTP-Server signalisiert, dass die Nachricht versendet werden soll. Wie in der Antwort des Servers angegeben, handelt es sich hierbei um eine Folge von Zeilen, die mit einer Zeile endet, die nur einen Punkt enthält. Eine Nachricht kann Kopfzeilen enthalten, die durch eine Leerzeile vom Nachrichtentext getrennt sind. In unserem Beispiel haben wir einen Betreff mit dem Schlüsselwort Subject: eingefügt
  • Sobald die Nachricht gesendet wurde, können wir dem Server mit dem Befehl „quit“ mitteilen, dass wir fertig sind. Der Server schließt daraufhin die Netzwerkverbindung. Der Lese-Thread kann dieses Ereignis erkennen und anhalten.
  • Der Benutzer gibt dann „end“ über die Tastatur ein, um auch den Thread zu stoppen, der die über die Tastatur eingegebenen Befehle liest.

Wenn wir die empfangene E-Mail überprüfen, sehen wir Folgendes (Outlook):

Image

Beachten Sie, dass der SMTP-Dienst nicht erkennen kann, ob ein Absender gültig ist oder nicht. Daher können Sie dem Feld „Von“ einer Nachricht niemals vertrauen. In diesem Fall existierte die Absender-E-Mail-Adresse machin@univ-angers.fr nicht.

Dieser generische TCP-Client ermöglicht es uns, das Kommunikationsprotokoll von Internetdiensten zu ermitteln und darauf aufbauend spezialisierte Klassen für Clients dieser Dienste zu erstellen. Sehen wir uns das Kommunikationsprotokoll des POP-Dienstes (Post Office Protocol) an, mit dem wir auf einem Server gespeicherte E-Mails abrufen können. Er läuft auf Port 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]

Die wichtigsten Befehle lauten wie folgt:

  • user login, wobei Sie Ihren Benutzernamen auf dem Rechner eingeben, der Ihre E-Mails hostet
  • pass password, wo Sie das Passwort eingeben, das mit dem vorherigen Login verknüpft ist
  • list, um eine Liste der Nachrichten im Format Nummer, Größe in Byte abzurufen
  • retr i, um die Nachricht Nummer i zu lesen
  • quit, um die Sitzung zu beenden.

Betrachten wir nun das Kommunikationsprotokoll zwischen einem Client und einem Webserver, der in der Regel auf Port 80 läuft:


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]

Ein Web-Client sendet seine Befehle nach folgendem Muster an den Server:

commande1
commande2
...
commanden
[ligne vide]

Der Webserver antwortet erst, nachdem er die Leerzeile empfangen hat. In diesem Beispiel haben wir nur einen Befehl verwendet:

GET /index.html HTTP/1.0

der die URL /index.html vom Server anfordert und angibt, dass HTTP Version 1.0 verwendet wird. Die aktuellste Version dieses Protokolls ist 1.1. Das Beispiel zeigt, dass der Server mit dem Inhalt der Datei index.html geantwortet und anschließend die Verbindung geschlossen hat, wie wir an der Meldung „thread terminating“ in der Antwort erkennen können. Vor dem Senden des Inhalts der Datei index.html hat der Webserver eine Reihe von Headern gesendet, gefolgt von einer Leerzeile:

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

Die Zeile <html> ist die erste Zeile der Datei /index.html. Der vorangehende Text wird als HTTP-Header (HyperText Transfer Protocol) bezeichnet. Wir werden hier nicht näher auf diese Header eingehen, aber denken Sie daran, dass unser generischer Client Zugriff darauf bietet, was für das Verständnis hilfreich sein kann. Zum Beispiel die erste Zeile:

<-- HTTP/1.1 200 OK

bedeutet, dass der kontaktierte Webserver das HTTP/1.1-Protokoll unterstützt und die angeforderte Datei erfolgreich gefunden hat (200 OK), wobei 200 ein HTTP-Antwortcode ist. Die Zeilen

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

Teilen Sie dem Client mit, dass er 11.251 Byte HTML-Text (HyperText Markup Language) erhalten wird und dass die Verbindung geschlossen wird, sobald die Daten gesendet wurden.

Hier haben wir also einen sehr praktischen TCP-Client. Er kann zwar weniger als das zuvor verwendete Telnet-Programm, aber es war interessant, ihn selbst zu schreiben. Das generische TCP-Client-Programm sieht wie folgt aus:

// 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. Der generische TCP-Server

Nun sehen wir uns einen Server an

  • , der die von seinen Clients gesendeten Befehle auf dem Bildschirm anzeigt
  • und ihnen als Antwort die Textzeilen sendet, die ein Benutzer über die Tastatur eingegeben hat. Es ist also der Benutzer, der als Server fungiert.

Das Programm wird gestartet mit: java genericTCPserver listeningPort, wobei listeningPort der Port ist, mit dem sich die Clients verbinden müssen. Der Client-Dienst wird von zwei Threads abgewickelt:

  • ein Thread, der ausschließlich für das Lesen der vom Client gesendeten Textzeilen zuständig ist
  • ein Thread, der ausschließlich dem Lesen der vom Benutzer über die Tastatur eingegebenen Antworten gewidmet ist. Dieser Thread signalisiert mithilfe des Befehls `fin`, dass er die Verbindung zum Client schließt.

Der Server erstellt zwei Threads pro Client. Bei n Clients sind somit 2n Threads gleichzeitig aktiv. Der Server selbst wird nicht beendet, es sei denn, der Benutzer drückt die Tastenkombination Strg-C auf der Tastatur. Sehen wir uns einige Beispiele an.

Der Server läuft auf Port 100, und wir verwenden den generischen Client, um mit ihm zu kommunizieren. Das Client-Fenster sieht wie folgt aus:


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]

Zeilen, die mit <-- beginnen, stammen vom Server und sind an den Client gesendet; die anderen stammen vom Client und sind an den Server gesendet. Das Serverfenster sieht wie folgt aus:


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]

Zeilen, die mit <-- beginnen, sind vom Client an den Server gesendet. Zeilen N: sind vom Server an Client N gesendet. Der oben genannte Server ist weiterhin aktiv, obwohl Client 1 beendet ist. Wir starten einen zweiten Client für denselben 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]

Das Serverfenster sieht dann wie folgt aus:


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

Simulieren wir nun einen Webserver, indem wir unseren generischen Server auf Port 88 ausführen:


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

Öffnen wir nun einen Browser und rufen die URL http://localhost:88/exemple.html auf. Der Browser stellt dann eine Verbindung zu Port 88 auf dem Localhost-Rechner her und fordert die Seite /example.html an:

Image

Schauen wir uns nun unser Serverfenster an:

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

Hier werden die vom Browser gesendeten HTTP-Header angezeigt. So können wir nach und nach mehr über das HTTP-Protokoll lernen. In einem früheren Beispiel haben wir einen Web-Client erstellt, der lediglich den GET-Befehl gesendet hat. Das war ausreichend. Hier sehen wir, dass der Browser weitere Informationen an den Server sendet. Diese Informationen sollen dem Server mitteilen, um welche Art von Client es sich handelt. Wir sehen außerdem, dass die HTTP-Header mit einer Leerzeile enden.

Erstellen wir eine Antwort für unseren Client. Der Benutzer am Bildschirm ist hier der eigentliche Server und kann manuell eine Antwort erstellen. Erinnern Sie sich an die Antwort, die ein Webserver in einem früheren Beispiel gesendet hat:

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

Versuchen wir, eine ähnliche Antwort zu geben:

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

Zeilen, die mit 2: beginnen, werden vom Server an Client Nr. 2 gesendet. Der Befehl „end“ schließt die Verbindung vom Server zum Client. In unserer Antwort haben wir uns auf die folgenden HTTP-Header beschränkt:

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

Wir geben die Größe der zu sendenden Datei (Content-Length) nicht an, sondern weisen lediglich darauf hin, dass wir die Verbindung (Connection: close) nach dem Senden schließen werden. Dies reicht für den Browser aus. Sobald er sieht, dass die Verbindung geschlossen wurde, weiß er, dass die Antwort des Servers vollständig ist, und zeigt die ihm gesendete HTML-Seite an. Die Seite sieht wie folgt aus:

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>

Der Benutzer schließt dann die Verbindung zum Client, indem er den Befehl „fin“ eingibt. Der Browser erkennt daraufhin, dass die Antwort des Servers vollständig ist, und kann sie anzeigen:

Image

Wenn Sie im obigen Beispiel „Ansicht/Quelltext“ auswählen, um zu sehen, was der Browser empfangen hat, erhalten Sie:

Image

das heißt, genau das, was vom generischen Server gesendet wurde.

Der Code für den generischen TCP-Server lautet wie folgt:

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