t
t
t
t
t t 15) Informatique distribuée
tttt
15) Informatique distribuée
Texte original t Traducteur : Jean-Pierre VIDAL, Alban PEIGNIER
t
t
///
Ce chapitre contient 12 pages
1 2 3 4 5 6 7 8 9 10 11 12
\\\
t t t
t t t
t
t t t
The responsibility for cleaning up the sockets is crafted carefully here. If the ServerSocket constructor fails, the program just quits (notice we must assume that the constructor for ServerSocket doesn’t leave any open network sockets lying around if it fails). For this case, main( ) throws IOException so a try block is not necessary. If the ServerSocket constructor is successful then all other method calls must be guarded in a try-finally block to ensure that, no matter how the block is left, the ServerSocket is properly closed. t Nettoyer les sockets est une responsabilité, elle est soigneusement traitée ici. Si le constructeur de ServerSocket échoue, le programme se termine simplement (il faut remarquer que nous supposons que le constructeur de ServerSocket ne laisse traîner aucune socket de réseau s'il échoue). Dans ce cas, la méthode main( ) renverrait une IOException et par conséquent un bloc try n'est pas nécessaire. Si le constructeur du ServerSocket se termine avec succès, alors tous les autres appels de méthode doivent être inclus dans un bloc try-finallyafin d'assurer que le ServerSocket sera fermé correctement quelle que soit la manière dont le bloc se termine.
t t t
The same logic is used for the Socket returned by accept( ). If accept( ) fails, then we must assume that the Socket doesn’t exist or hold any resources, so it doesn’t need to be cleaned up. If it’s successful, however, the following statements must be in a try-finally block so that if they fail the Socket will still be cleaned up. Care is required here because sockets use important nonmemory resources, so you must be diligent in order to clean them up (since there is no destructor in Java to do it for you). t La même logique est utilisée pour le Socket renvoyé par accept( ). Si accept( ) échoue, nous devons supposer que le Socket n'existe pas et qu'il ne monopolise aucune ressource, et donc qu'il n'est pas besoin de le nettoyer. Au contraire, si accept( ) se termine avec succès, les instructions qui suivent doivent se trouver dans un bloc try-finally pour que Socketsoit toujours nettoyé si l'une d'entre elles échoue. Il faut porter beaucoup d'attention à cela car les sockets sont des ressources importantes qui ne résident pas en mémoire, et on ne doit pas oublier de les fermer (car il n'existe pas en Java de destructeur qui le ferait pour nous).
t t t
Both the ServerSocket and the Socket produced by accept( ) are printed to System.out. This means that their toString( ) methods are automatically called. These produce: t Le ServerSocket et le Socket fournis par accept( ) sont imprimés sur System.out. Leur méthode toString( ) est donc appelée automatiquement. Voici le résultat :
t t t
ServerSocket[addr=0.0.0.0,PORT=0,localport=8080]
Socket[addr=127.0.0.1,PORT=1077,localport=8080]
t
ServerSocket[addr=0.0.0.0,PORT=0,localport=8080]
Socket[addr=127.0.0.1,PORT=1077,localport=8080]
t t t
Shortly, you’ll see how these fit together with what the client is doing. t En raccourci, on peut voir comment cela « colle » avec ce que fait le client.
t t t
The next part of the program looks just like opening files for reading and writing except that the InputStream and OutputStream are created from the Socket object. Both the InputStream and OutputStream objects are converted to Reader and Writer objects using the “converter” classes InputStreamReader and OutputStreamWriter, respectively. You could also have used the Java 1.0 InputStream and OutputStream classes directly, but with output there’s a distinct advantage to using the Writer approach. This appears with PrintWriter, which has an overloaded constructor that takes a second argument, a boolean flag that indicates whether to automatically flush the output at the end of each println( ) (but not print( )) statement. Every time you write to out, its buffer must be flushed so the information goes out over the network. Flushing is important for this particular example because the client and server each wait for a line from the other party before proceeding. If flushing doesn’t occur, the information will not be put onto the network until the buffer is full, which causes lots of problems in this example. t Le reste du programme consiste seulement à ouvrir des fichiers pour lire et écrire, sauf que InputStream et OutputStream sont créés à partir de l'objet Socket. Les deux objets InputStream et OutputStream sont convertis en objets Reader et Writer au moyen des « classes de conversion InputStreamReader et OutputStreamWriter, respectivement. On aurait pu travailler directement avec les classes Java 1.0 InputStream et OutputStream, mais pour la sortie il y a un avantage certain à utiliser l'approche Writer. C'est évident avec PrintWriter, qui possède un constructeur surchargé prenant en compte un deuxième argument, un flag boolean indiquant que le tampon de sortie doit être automatiquement vidé après chaque println( ) (mais nonaprès les instructions print( )). Chaque fois qu'on écrit sur out, son buffer doit être vidé afin que l'information parte sur le réseau. Le vidage du tampon est important dans cet exemple particulier car aussi bien le serveur que le client attendent de l'autre une ligne complète avant de la traiter. Si le tampon n'est pas vidé à chaque ligne, l'information ne circule pas sur le réseau tans que le buffer n'est pas plein, ce qui occasionnerait de nombreux problèmes dans cet exemple.
t t t
When writing network programs you need to be careful about using automatic flushing. Every time you flush the buffer a packet must be created and sent. In this case, that’s exactly what we want, since if the packet containing the line isn’t sent then the handshaking back and forth between server and client will stop. Put another way, the end of a line is the end of a message. But in many cases, messages aren’t delimited by lines so it’s much more efficient to not use auto flushing and instead let the built-in buffering decide when to build and send a packet. This way, larger packets can be sent and the process will be faster. t Lorsqu'on écrit des programmes réseau, il faut être très attentif à l'utilisation du vidage automatique de buffer. Chaque fois que l'on vide un buffer, un paquet est créé et envoyé. Dans notre exemple, c'est exactement ce que nous recherchons, puisque si le paquet contenant la ligne n'est pas envoyé, l'échange entre serveur et client sera stoppé. Dit d'une autre manière, la fin de ligne est la fin du message. Mais dans de nombreux cas, les messages ne sont pas découpés en lignes et il sera plus efficace de ne pas utiliser le vidage automatique de buffer et à la place de laisser le gestionnaire du buffer décider du moment pour construire et envoyer un paquet. De cette manière, les paquets seront plus importants et le processus plus rapide.
t t t
Note that, like virtually all streams you open, these are buffered. There’s an exercise at the end of this chapter to show you what happens if you don’t buffer the streams (things get slow). t Remarquons que, comme tous les flux qu'on peut ouvrir, ceux-ci sont tamponnés. À la fin de ce chapitre, vous trouverez un exercice montrant ce qui se passe lorsqu'on ne tamponne pas les flux (tout est ralenti).
t t t
The infinite while loop reads lines from the BufferedReader in and writes information to System.out and to the PrintWriter out. Note that in and out could be any streams, they just happen to be connected to the network. t La boucle while infinie lit les lignes depuis le BufferedReader inet écrit sur System.out et PrintWriter out. Remarquons que in et out peuvent être n'importe quel flux, ils sont simplement connectés au réseau.
t t t
When the client sends the line consisting of “END,” the program breaks out of the loop and closes the Socket. t Lorsque le client envoie une ligne contenant juste le mot END, « la boucle est interrompue et le programme ferme le Socket.
t t t
Here’s the client: t Et voici le client :
t t t
//: c15:JabberClient.java
// Very simple client that just sends
// lines to the server and reads lines
// that the server sends.
import java.net.*;
import java.io.*;

public class JabberClient {
  public static void main(String[] args)
      throws IOException {
    // Passing null to getByName() produces the
    // special "Local Loopback" IP address, for
    // testing on one machine w/o a network:
    InetAddress addr =
      InetAddress.getByName(null);
    // Alternatively, you can use
    // the address or name:
    // InetAddress addr =
    //    InetAddress.getByName("127.0.0.1");
    // InetAddress addr =
    //    InetAddress.getByName("localhost");
    System.out.println("addr = " + addr);
    Socket socket =
      new Socket(addr, JabberServer.PORT);
    // Guard everything in a try-finally to make
    // sure that the socket is closed:
    try {
      System.out.println("socket = " + socket);
      BufferedReader in =
        new BufferedReader(
          new InputStreamReader(
            socket.getInputStream()));
      // Output is automatically flushed
      // by PrintWriter:
      PrintWriter out =
        new PrintWriter(
          new BufferedWriter(
            new OutputStreamWriter(
              socket.getOutputStream())),true);
      for(int i = 0; i < 10; i ++) {
        out.println("howdy " + i);
        String str = in.readLine();
        System.out.println(str);
      }
      out.println("END");
    } finally {
      System.out.println("closing...");
      socket.close();
    }
  }
} ///:~
t
//: c15:JabberClient.java
// Client simplifié se contentant d'envoyer
// des lignes au serveur et de lire les lignes
// que le serveur lui envoie.
import java.net.*;
import java.io.*;

public class JabberClient {
  public static void main(String[ « args)
      throws IOException {
// Appeler getByName() avec un argument null revient
    // à utiliser une adresse IP spéciale "Boucle Locale"
// pour faire des tests réseau sur une seule machine.

     InetAddress addr =
      InetAddress.getByName(null);
    // Il est également possible d'utiliser
    // l'adresse ou le nom:
    // InetAddress addr =
    //    InetAddress.getByName("127.0.0.1");
    // InetAddress addr =
    //    InetAddress.getByName("localhost");
    System.out.println("addr = " + addr);
    Socket socket =
      new Socket(addr, JabberServer.PORT);
    // Le code doit être inclus dans un bloc
    // try-finally afin de garantir
    // que socket sera fermé:
    try {
      System.out.println("socket = " + socket);
      BufferedReader in =        new BufferedReader(
          new InputStreamReader(
            socket.getInputStream()));
      // Le tampon de sortie est automatiquement
      // vidé par PrintWriter:
      PrintWriter out =        new PrintWriter(
          new BufferedWriter(
            new OutputStreamWriter(
              socket.getOutputStream())),true);
      for(int i = 0; i < 10; i ++) {
        out.println("howdy " + i);
        String str = in.readLine();
        System.out.println(str);
      }
      out.println("END");
    } finally {
      System.out.println("closing...");
      socket.close();
    }
  }
} ///:~
t t t
In main( ) you can see all three ways to produce the InetAddress of the local loopback IP address: using null, localhost, or the explicit reserved address 127.0.0.1. Of course, if you want to connect to a machine across a network you substitute that machine’s IP address. When the InetAddress addr is printed (via the automatic call to its toString( ) method) the result is: t La méthode main( ) montre qu'il existe trois manières de produire l'InetAddress de l'adresse IP de la boucle locale  : avec null, localhost, ou bien l'adresse réservée et explicite 127.0.0.1. Bien entendu, si l'on désire se connecter à une machine du réseau, il suffit d'y substituer son adresse. Lorsque InetAddress addr est imprimée (via l'appel automatique de sa méthode toString( )), voici le résultat :
t t t
localhost/127.0.0.1
t
localhost/127.0.0.1
t t t
By handing getByName( ) a
null, it defaulted to finding the localhost, and that produced the
special address 127.0.0.1.
t Lorsque getByName( ) a été appelée avec un argument null, elle a cherché par défaut localhost, ce qui a fourni l'adresse spéciale 127.0.0.1.
t t t
Note that the
Socket called
socket is created with both the InetAddress and the port number.
To understand what it means when you print one of these Socket objects,
remember that an Internet connection is determined uniquely by these four pieces
of data: clientHost, clientPortNumber, serverHost, and
serverPortNumber. When the server comes up, it takes up its assigned port
(8080) on the localhost (127.0.0.1). When the client comes up, it is allocated
to the next available port on its machine, 1077 in this case, which also happens
to be on the same machine (127.0.0.1) as the server. Now, in order for data to
move between the client and server, each side has to know where to send it.
Therefore, during the process of connecting to the “known” server,
the client sends a “return address” so the server knows where to
send its data. This is what you see in the example output for the server
side:

t Remarquons que le Socket nommé socket est créé avec InetAddress ainsi que le numéro de port. Pour comprendre ce qui se passe lorsqu'on imprime un de ces objets Socket, il faut se souvenir qu'une connexion Internet est déterminée de manière unique à partir de quatre données : clientHost, clientPortNumber, serverHost, et serverPortNumber. Lorsque le serveur démarre, il prend en compte le port qui lui est assigné (8080) sur la machine locale (127.0.0.1). Lorsque le client démarre, le premier port suivant disponible sur sa machine lui est alloué, 1077 dans ce cas, qui se trouve sur la même machine (127.0.0.1) que le serveur. Maintenant, pour que les données circulent entre le client et le serveur, chacun doit savoir où les envoyer. En conséquence, pendant la connexion au serveur connu, le client envoie une adresse de retour afin que le serveur sache où envoyer ses données. Ce qu'on peut voir dans la sortie de l'exemple côté serveur :
t t t
Socket[addr=127.0.0.1,port=1077,localport=8080]
t
Socket[addr=127.0.0.1,port=1077,localport=8080]
t t t
This means that the server just accepted
a connection from 127.0.0.1 on port 1077 while listening on its local port
(8080). On the client side:

t Cela signifie que le serveur vient d'accepter une demande de connexion provenant de 127.0.0.1 sur le port 1077 alors qu'il est à l'écoute sur le port local (8080). Du côté client :
t t t
Socket[addr=localhost/127.0.0.1,PORT=8080,localport=1077]
t
Socket[addr=localhost/127.0.0.1,PORT=8080,localport=1077]
t t t
which means that the client made a
connection to 127.0.0.1 on port 8080 using the local port 1077.
t ce qui signifie que le client vient d'établir une connexion à 127.0.0.1 sur le port 8080 en utilisant le port local 1077.
t t t
You’ll notice that every time you
start up the client anew, the local port number is incremented. It starts at
1025 (one past the reserved block of ports) and keeps going up until you reboot
the machine, at which point it starts at 1025 again. (On UNIX machines, once the
upper limit of the socket range is reached, the numbers will wrap around to the
lowest available number again.)
t Il faut remarquer que chaque fois qu'on relance le client, le numéro du port local est incrémenté. Il commence à 1025 (un après le bloc de ports réservé) et ne cesse d'augmenter jusqu'à ce qu'on reboote la machine, auquel cas il recommence à 1025 (sur les machines UNIX, lorsque la limite supérieure de la plage accordée aux sockets est atteinte, on recommence avec la plus petite valeur possible).
t t t
Once the Socket object has been
created, the process of turning it into a BufferedReader and
PrintWriter is the same as in the server (again, in both cases you start
with a Socket). Here, the client initiates the conversation by sending
the string “howdy” followed by a number. Note that the buffer must
again be flushed (which happens automatically via the second argument to the
PrintWriter constructor). If the buffer isn’t flushed, the whole
conversation will hang because the initial “howdy” will never get
sent (the buffer isn’t full enough to cause the send to happen
automatically). Each line that is sent back from the server is written to
System.out to verify that everything is working correctly. To terminate
the conversation, the agreed-upon “END” is sent. If the client
simply hangs up, then the server throws an exception.
t L'objet Socket créé, le processus consistant à en faire un BufferedReader puis un PrintWriter est le même que pour le serveur (encore une fois, dans les deux cas on commence par un Socket). Ici, le client entame la conversation en envoyant la chaîne « [howdy] » suivie d'un nombre. Remarquons que le buffer doit être vidé à nouveau (ce qui est automatique via le deuxième argument du constructeur de PrintWriter). Si le buffer n'est pas vidé, la conversation est complètement suspendue parce que le « howdy » initial ne sera jamais envoyé (le buffer n'est pas assez rempli pour que l'envoi se fasse automatiquement). Chaque ligne renvoyée par le serveur est écrite sur System.out pour vérifier que tout fonctionne correctement. Pour arrêter l'échange, le client envoie le mot connu » END. Si le client ne se manifeste plus, le serveur lance une exception.
t t t
You can see that the same care is taken
here to ensure that the network resources represented by the Socket are
properly cleaned up, using a try-finally block.
t Remarquons qu'ici aussi le même soin est apporté pour assurer que la ressource réseau que représente le Socket est relâchée correctement, au moyen d'un bloc try-finally.
t t t
Sockets produce a
“dedicated” connection that persists until
it is explicitly disconnected. (The dedicated connection can still be
disconnected unexplicitly if one side, or an intermediary link, of the
connection crashes.) This means the two parties are locked in communication and
the connection is constantly open. This seems like a logical approach to
networking, but it puts an extra load on the network. Later in this chapter
you’ll see a different approach to networking, in which the connections
are only
temporary.
t Les sockets fournissent une connexion » dédiée « qui persiste jusqu'à ce qu'elle soit explicitement déconnectée (la connexion dédiée peut encore être rompue de manière non explicite si l'un des deux côtés ou un lien intermédiaire de la connexion se plante). La conséquence est que les deux parties sont verrouillées en communication et que la connexion est constamment ouverte. Cela peut paraître une approche logique du travail en réseau, en fait on surcharge le réseau. Plus loin dans ce chapitre on verra une approche différente du travail en réseau, dans laquelle les connexions seront temporaires.
t t t


Serving multiple clients


t

Servir des clients multiples

t t t
The JabberServer works, but it can
handle only one client at a time. In a typical server, you’ll want to be
able to deal with many clients at once. The answer is
multithreading, and in languages
that don’t directly support multithreading this means all sorts of
complications. In Chapter 14 you saw that multithreading in Java is about as
simple as possible, considering that multithreading is a rather complex topic.
Because threading in Java is reasonably straightforward, making a server that
handles multiple clients is relatively easy.
t Le programme JabberServer fonctionne, mais ne peut traiter qu'un client à la fois. Dans un serveur typique, on désire traiter plusieurs clients en même temps. La réponse est le multithreading, et dans les langages ne supportant pas cette fonctionnalité cela entraîne toutes sortes de complications. Dans le Chapitre 14 nous avons vu que le multithreading de Java est aussi simple que possible, si l'on considère le multithreading comme un sujet quelque peu complexe. Parce que gérer des threads est relativement simple en Java, écrire un serveur prenant en compte de multiples clients est relativement facile.
t t t
The basic scheme is to make a single
ServerSocket in the server and call accept( ) to wait for a
new connection. When accept( ) returns, you take the resulting
Socket and use it to create a new thread whose job is to serve that
particular client. Then you call accept( ) again to wait for a new
client.
t L'idée de base est de construire dans le serveur un seul ServerSocket et d'appeler accept( ) pour attendre une nouvelle connexion. Au retour d'accept( ), on crée un nouveau thread utilisant le Socket résultant, thread dont le travail est de servir ce client particulier. Puis on appelle à nouveau accept( ) pour attendre un nouveau client.
t t t
In the following server code, you can see
that it looks similar to the JabberServer.java example except that all of
the operations to serve a particular client have been moved inside a separate
thread class:

t Dans le code serveur suivant, on remarquera qu'il ressemble à l'exemple JabberServer.java sauf que toutes les opérations destinées à servir un client particulier ont été déplacées dans une classe thread séparée :
t t t
//: c15:MultiJabberServer.java
// A server that uses multithreading
// to handle any number of clients.
import java.io.*;
import java.net.*;

class ServeOneJabber extends Thread {
  private Socket socket;
  private BufferedReader in;
  private PrintWriter out;
  public ServeOneJabber(Socket s)
      throws IOException {
    socket = s;
    in =
      new BufferedReader(
        new InputStreamReader(
          socket.getInputStream()));
    // Enable auto-flush:
    out =
      new PrintWriter(
        new BufferedWriter(
          new OutputStreamWriter(
            socket.getOutputStream())), true);
    // If any of the above calls throw an
    // exception, the caller is responsible for
    // closing the socket. Otherwise the thread
    // will close it.
    start(); // Calls run()
  }
  public void run() {
    try {
      while (true) {  
        String str = in.readLine();
        if (str.equals("END")) break;
        System.out.println("Echoing: " + str);
        out.println(str);
      }
      System.out.println("closing...");
    } catch(IOException e) {
      System.err.println("IO Exception");
    } finally {
      try {
        socket.close();
      } catch(IOException e) {
        System.err.println("Socket not closed");
      }
    }
  }
}

public class MultiJabberServer {  
  static final int PORT = 8080;
  public static void main(String[] args)
      throws IOException {
    ServerSocket s = new ServerSocket(PORT);
    System.out.println("Server Started");
    try {
      while(true) {
        // Blocks until a connection occurs:
        Socket socket = s.accept();
        try {
          new ServeOneJabber(socket);
        } catch(IOException e) {
          // If it fails, close the socket,
          // otherwise the thread will close it:
          socket.close();
        }
      }
    } finally {
      s.close();
    }
  }
} ///:~
t
//: c15:MultiJabberServer.java
// Un serveur utilisant le multithreading
// pour traiter un nombre quelconque de clients.
import java.io.*;
import java.net.*;

class ServeOneJabber extends Thread {
  private Socket socket;
  private BufferedReader in;
  private PrintWriter out;
  public ServeOneJabber(Socket s)
      throws IOException {
    socket = s;
    in =
      new BufferedReader(
        new InputStreamReader(
          socket.getInputStream()));
    // Autoriser l'auto-vidage:
    out =
      new PrintWriter(
        new BufferedWriter(
          new OutputStreamWriter(
            socket.getOutputStream())), true);
    // Si l'un des appels ci-dessus résulte en une
    // exception, l'appelant a la responsabilité
    // de fermer socket. Sinon le thread
    // s'en chargera.
    start(); // Appelle run()
  }
  public void run() {
    try {
      while (true) {  
        String str = in.readLine();
        if (str.equals("END")) break;
        System.out.println("Echoing: " + str);
        out.println(str);
      }
      System.out.println("closing...");
    } catch(IOException e) {
      System.err.println("IO Exception");
    } finally {
      try {
        socket.close();
      } catch(IOException e) {
        System.err.println("Socket not closed");
      }
    }
  }
}

public class MultiJabberServer {  
  static final int PORT = 8080;
  public static void main(String[ « args)
      throws IOException {
    ServerSocket s = new ServerSocket(PORT);
    System.out.println("Server Started");
    try {
      while(true) {
// On attend ici jusqu'à avoir
// une demande de connexion:
        Socket socket = s.accept();
        try {
          new ServeOneJabber(socket);
        } catch(IOException e) {
          // En cas d'échec, fermer l'objet socket,
          // sinon le thread le fermera:
          socket.close();
        }
      }
    } finally {
      s.close();
    }
  }
} ///:~
t t t
The ServeOneJabber thread takes the Socket object that’s produced by accept( ) in main( ) every time a new client makes a connection. Then, as before, it creates a BufferedReader and auto-flushed PrintWriter object using the Socket. Finally, it calls the special Thread method start( ), which performs thread initialization and then calls run( ). This performs the same kind of action as in the previous example: reading something from the socket and then echoing it back until it reads the special “END” signal. t Chaque fois qu'un nouveau client se connecte, le thread ServeOneJabber prend l'objet Socket produit par accept( ) dans main( ). Puis, comme auparavant, il crée un BufferedReader et un objet PrintWriter(avec auto-vidage du buffer) à partir du Socket. Finalement, il appelle la méthode spéciale start( ) de la classe Thread qui initialise le thread puis appelle run( ). On réalise ainsi le même genre de traitement que dans l'exemple précédent : lire quelque chose sur la socket et le renvoyer en écho jusqu'à l'arrivée du signal spécial » END.
t t t
The responsibility for cleaning up the socket must again be carefully designed. In this case, the socket is created outside of the ServeOneJabber so the responsibility can be shared. If the ServeOneJabber constructor fails, it will just throw the exception to the caller, who will then clean up the thread. But if the constructor succeeds, then the ServeOneJabber object takes over responsibility for cleaning up the thread, in its run( ). t À nouveau, il nous faut penser à notre responsabilité en ce qui concerne la ressource socket. Dans ce cas, la socket est créée hors de ServeOneJabber et donc la responsabilité doit être partagée. Si le constructeur de ServeOneJabber échoue, il suffit qu'il lance une exception vers l'appelant, qui nettoiera alors le thread. Mais dans le cas contraire, l'objet ServeOneJabber a la responsabilité de nettoyer le thread, dans sa méthode run( ).
t t t
Notice the simplicity of the MultiJabberServer. As before, a ServerSocket is created and accept( ) is called to allow a new connection. But this time, the return value of accept( ) (a Socket) is passed to the constructor for ServeOneJabber, which creates a new thread to handle that connection. When the connection is terminated, the thread simply goes away. t Remarquons la simplicité de MultiJabberServer. Comme auparavant, on crée un ServerSocket, et on appelle accept( ) pour autoriser une nouvelle connexion. Mais cette fois, la valeur de retour de accept( ) (un Socket) est passée au constructeur de ServeOneJabber, qui crée un nouveau thread afin de prendre en compte cette connexion. La connexion terminée, le thread se termine tout simplement.
t t t
If the creation of the ServerSocket fails, the exception is again thrown through main( ). But if the creation succeeds, the outer try-finally guarantees its cleanup. The inner try-catch guards only against the failure of the ServeOneJabber constructor; if the constructor succeeds, then the ServeOneJabber thread will close the associated socket. t Si la création du ServerSocket échoue, à nouveau une exception est lancée par main( ). En cas de succès, le bloc try-finally extérieur garantit le nettoyage. Le bloc try-catch intérieur protège d'une défaillance du constructeur ServeOneJabber  ; en cas de succès, le thread ServeOneJabber fermera la socket associée.
t t t
To test that the server really does handle multiple clients, the following program creates many clients (using threads) that connect to the same server. The maximum number of threads allowed is determined by the final int MAX_THREADS. t Afin de tester que le serveur prend réellement en compte plusieurs clients, le programme suivant crée beaucoup de clients (sous la forme de threads) et se connecte au même serveur. Le nombre maximum de threads autorisés est fixé par la constante final int MAX_THREADS.
t t t
//: c15:MultiJabberClient.java
// Client that tests the MultiJabberServer
// by starting up multiple clients.
import java.net.*;
import java.io.*;

class JabberClientThread extends Thread {
  private Socket socket;
  private BufferedReader in;
  private PrintWriter out;
  private static int counter = 0;
  private int id = counter++;
  private static int threadcount = 0;
  public static int threadCount() {
    return threadcount;
  }
  public JabberClientThread(InetAddress addr) {
    System.out.println("Making client " + id);
    threadcount++;
    try {
      socket =
        new Socket(addr, MultiJabberServer.PORT);
    } catch(IOException e) {
      System.err.println("Socket failed");
      // If the creation of the socket fails,
      // nothing needs to be cleaned up.
    }
    try {    
      in =
        new BufferedReader(
          new InputStreamReader(
            socket.getInputStream()));
      // Enable auto-flush:
      out =
        new PrintWriter(
          new BufferedWriter(
            new OutputStreamWriter(
              socket.getOutputStream())), true);
      start();
    } catch(IOException e) {
      // The socket should be closed on any
      // failures other than the socket
      // constructor:
      try {
        socket.close();
      } catch(IOException e2) {
        System.err.println("Socket not closed");
      }
    }
    // Otherwise the socket will be closed by
    // the run() method of the thread.
  }
  public void run() {
    try {
      for(int i = 0; i < 25; i++) {
        out.println("Client " + id + ": " + i);
        String str = in.readLine();
        System.out.println(str);
      }
      out.println("END");
    } catch(IOException e) {
      System.err.println("IO Exception");
    } finally {
      // Always close it:
      try {
        socket.close();
      } catch(IOException e) {
        System.err.println("Socket not closed");
      }
      threadcount--; // Ending this thread
    }
  }
}

public class MultiJabberClient {
  static final int MAX_THREADS = 40;
  public static void main(String[] args)
      throws IOException, InterruptedException {
    InetAddress addr =
      InetAddress.getByName(null);
    while(true) {
      if(JabberClientThread.threadCount()
         < MAX_THREADS)
        new JabberClientThread(addr);
      Thread.currentThread().sleep(100);
    }
  }
} ///:~
t
//: c15:MultiJabberClient.java
// Client destiné à tester MultiJabberServer
// en lançant des clients multiple.
import java.net.*;
import java.io.*;

class JabberClientThread extends Thread {
  private Socket socket;
  private BufferedReader in;
  private PrintWriter out;
  private static int counter = 0;
  private int id = counter++;
  private static int threadcount = 0;
  public static int threadCount() {
    return threadcount;
  }
  public JabberClientThread(InetAddress addr) {
    System.out.println("Making client " + id);
    threadcount++;
    try {
      socket =
        new Socket(addr, MultiJabberServer.PORT);
    } catch(IOException e) {
      System.err.println("Socket failed");
      // Si la création de socket échoue,
      // il n'y a rien à nettoyer.
    }
    try {    
      in =
        new BufferedReader(
          new InputStreamReader(
            socket.getInputStream()));
      // Autoriser l'auto-vidage du tampon:
      out =
        new PrintWriter(
          new BufferedWriter(
            new OutputStreamWriter(
              socket.getOutputStream())), true);
      start();
    } catch(IOException e) {
      // socket doit être fermé sur n'importe quelle
      // erreur autre que celle de son constructeur:
      try {
        socket.close();
      } catch(IOException e2) {
        System.err.println("Socket not closed");
      }
    }
    // Sinon socket doit être fermé par
    // la méthode run() du thread.
  }
  public void run() {
    try {
      for(int i = 0; i < 25; i++) {
        out.println("Client " + id + ": " + i);
        String str = in.readLine();
        System.out.println(str);
      }
      out.println("END");
    } catch(IOException e) {
      System.err.println("IO Exception");
    } finally {
      // Toujours fermer:
      try {
        socket.close();
      } catch(IOException e) {
        System.err.println("Socket not closed");
      }
      threadcount--; // Fin de ce thread
    }
  }
}

public class MultiJabberClient {
  static final int MAX_THREADS = 40;
  public static void main(String[ « args)
      throws IOException, InterruptedException {
    InetAddress addr =
      InetAddress.getByName(null);
    while(true) {
      if(JabberClientThread.threadCount()
         < MAX_THREADS)
        new JabberClientThread(addr);
      Thread.currentThread().sleep(100);
    }
  }
} ///:~
t t t
t t t
t t
\\\
///
t t t
t
     
Sommaire Le site de Bruce Eckel