 |
 |
15) Informatique distribuée |
|
 |
|
Texte original |
 |
Traducteur :
Jean-Pierre Vidal, Alban Peignier |
|
 |
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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).
|
 |
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).
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
ServerSocket[addr=0.0.0.0,PORT=0,localport=8080]
Socket[addr=127.0.0.1,PORT=1077,localport=8080]
|
 |
ServerSocket[addr=0.0.0.0,PORT=0,localport=8080] Socket[addr=127.0.0.1,PORT=1077,localport=8080]
|
 |
 |
 |
Shortly, you’ll see how these fit
together with what the client is doing.
|
 |
En raccourci, on peut voir comment cela « colle » avec ce que
fait le client.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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).
|
 |
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).
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
When the client sends the line consisting
of “END,” the program breaks out of the loop and closes the
Socket.
|
 |
Lorsque le client envoie une ligne contenant juste le mot END, « la boucle
est interrompue et le programme ferme le Socket.
|
 |
 |
 |
Here’s the client:
|
 |
Et voici le client :
|
 |
 |
 |
//: 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();
}
}
} ///:~
|
 |
//: 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(); } } } ///:~
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
localhost/127.0.0.1
|
 |
localhost/127.0.0.1
|
 |
 |
 |
By handing getByName( ) a
null, it defaulted to finding the localhost, and that produced the
special address 127.0.0.1.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
Socket[addr=127.0.0.1,port=1077,localport=8080]
|
 |
Socket[addr=127.0.0.1,port=1077,localport=8080]
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
Socket[addr=localhost/127.0.0.1,PORT=8080,localport=1077]
|
 |
Socket[addr=localhost/127.0.0.1,PORT=8080,localport=1077]
|
 |
 |
 |
which means that the client made a
connection to 127.0.0.1 on port 8080 using the local port 1077.
|
 |
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.
|
 |
 |
 |
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.)
|
 |
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).
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
Serving multiple clients
|
 |
Servir des clients multiples
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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:
|
 |
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 :
|
 |
 |
 |
//: 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();
}
}
} ///:~
|
 |
//: 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(); } } } ///:~
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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( ).
|
 |
À 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( ).
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
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.
|
 |
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.
|
 |
 |
 |
//: 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);
}
}
} ///:~
|
 |
//: 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); } } } ///:~
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |