Nio
: Selector, SelectionKey
Dans le nouveau paquetage nio, l'API java.net
ne permettait pas vraiment la gestion non-bloquante des sockets
: désormais, java.nio le permet non seulement sur les sockets
mais aussi sur les "pipes".
entrées
non bloquantes en lecture
Source de ServerSocketChannelR.java
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
public class ServerSocketChannelR {
private int port;
private ByteBuffer buffer;
public static void main (String [] args)
throws Exception {
if (args.length != 1)
throw new IllegalArgumentException("Usage: java ServerSocketChannelR port");
int port = Integer.parseInt(args[0]);
new ServerSocketChannelR(port).go ();
}
public ServerSocketChannelR(int port) {
this.port = port;
buffer = ByteBuffer.allocateDirect (1024);
}
public void go ()
throws Exception {
System.out.println ("Listening on port " + port);
ServerSocketChannel serverChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverChannel.socket();
Selector selector = Selector.open();
serverSocket.bind (new InetSocketAddress (port));
serverChannel.configureBlocking (false);
serverChannel.register (selector, SelectionKey.OP_ACCEPT);
while (true) {
int n = selector.select();
System.out.println ("selector.select() = " + n);
if (n == 0)
continue;
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
System.out.println ("SelectionKey = "+key);
if (key.isAcceptable()) {
System.out.println ("key.isAcceptable()");
ServerSocketChannel server =
(ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
System.out.println ("-- SocketChannel = "+channel
+" register SelectionKey.OP_READ");
channel.configureBlocking (false);
channel.register (selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
System.out.println ("key.isReadable()");
readDataFromSocket (key);
}
it.remove();
}
}
}
protected void readDataFromSocket (SelectionKey key)
throws Exception {
SocketChannel socketChannel = (SocketChannel) key.channel();
System.out.println ("-- SocketChannel = "+socketChannel);
int count;
buffer.clear();
while ((count = socketChannel.read (buffer)) > 0) {
System.out.println ("read count = "+count+ " contenu :");
buffer.flip();
byte[] b1 = new byte [1];
while (buffer.hasRemaining()) {
buffer.get(b1);
System.out.print ((new String(b1)));
}
buffer.clear();
}
System.out.println ("\nread count = "+count);
if (count < 0) {
System.out.println ("socketChannel.close() = "+socketChannel);
socketChannel.close();
}
}
}
|
EXECUTION
console 1:
$ java ServerSocketChannelR 4444
Listening on port 4444
console 2:
$ java ClientSocketChannelR localhost 4444
abc
Client envoie : abc
defg
Client envoie : defg
hijkl
Client envoie : hijkl
console 1:
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@1f33675
key.isAcceptable()
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:4444 remote=/127.0.0.1:36392]
register SelectionKey.OP_READ
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isReadable()
-- SocketChannel = java.nio.channels.SocketChannel[...
read count = 3 contenu :
abc
read count = 0
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isReadable()
-- SocketChannel = java.nio.channels.SocketChannel[...
read count = 4 contenu :
defg
read count = 0
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isReadable()
-- SocketChannel = java.nio.channels.SocketChannel[...
read count = 5 contenu :
hijkl
read count = 0
console 3:
$ java ClientSocketChannelR localhost 4444
hello
Client envoie : hello
Ciao
fin des requetes du Client
console 1:
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@1f33675
key.isAcceptable()
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:4444 remote=/127.0.0.1:36396]
register SelectionKey.OP_READ
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@22c95b
key.isReadable()
-- SocketChannel = java.nio.channels.SocketChannel[...
read count = 5 contenu :
hello
read count = 0
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@22c95b
key.isReadable()
-- SocketChannel = java.nio.channels.SocketChannel[...
read count = -1
socketChannel.close() = java.nio.channels.SocketChannel[...
console 2:
mnopqr
Client envoie : mnopqr
console1 :
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isReadable()
-- SocketChannel = java.nio.channels.SocketChannel[...
read count = 6 contenu :
mnopqr
read count = 0
|
Les lignes de la classe :
- Le client ouvre une connection avec le
serveur puis
- lit des lignes de caractères au
Clavier
- et les envoie en bytes au serveur
- jusqu'à ce que l'utilisateur de
la machine cliente saisisse "ciao" (à la casse
près).
- Le client peut "sentir", après
une ou deux écritures vers le serveur, que celui-ci a
coupé la connection.
- Le serveur
- affiche, sur sa console :
- les demandes de connection de client,
- le nombre de bytes envoyés par
chaque client,
- la fermeture de connection d'un client.
- Il est capable de gérer plusieurs
clients à la fois grâce :
- au mode de réception non-bloquant
- à un objet Sélector qui
écoute sur les ports et délivre un état
des activités possibles sur ces ports.
Le client est tout à fait classique :
Source de ClientSocketChannelR.java
import java.io.*;
import java.net.*;
public class ClientSocketChannelR {
public static void main(String[] args) throws Exception {
if (args.length != 2)
throw new IllegalArgumentException("Usage: java ClientSocketChannelR hostname port");
String hostName = args[0];
int port = Integer.parseInt(args[1]);
Socket socket = null;
OutputStream sockOut = null;
BufferedReader sockIn = null;
try {
socket = new Socket(hostName, port);
sockOut = socket.getOutputStream();
sockIn = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
} catch (UnknownHostException e) {
System.out.println("host inconnu ou non atteignable : "+ hostName);
System.exit(3);
} catch (IOException e) {
System.out.println("connection impossible au serveur");
System.exit(4);
}
byte[] buffer = new byte[1024];
boolean encore = true;
while (encore) {
String ligne = Clavier.lireLigne().trim();
if (ligne.equalsIgnoreCase("Ciao")) {
encore = false;
System.out.println("fin des requetes du Client ");
} else {
System.out.println("Client envoie : " + ligne);
try {
sockOut.write(ligne.getBytes());
sockOut.flush();
} catch (IOException e) {
encore = false;
System.out.println("fermeture connection par le serveur ");
}
}
}
sockOut.close();
sockIn.close();
socket.close();
}
}
|
Les lignes revisitées de la classe
du serveur :
- Le programme crée un canal ServerSocketChannel
et le lie au port d'écoute
- On ouvre le canal
- puis récupère le socket
associé : un ServerSocket
- Comme d'habitude, on lie de socket au
port d'écoute
- Le canal est mis en mode non bloquant
et on crée un objet "sélecteur" Selector
qui permettra de gérer ce mode
- le canal est ServerSocketChannel est
configuré en mode non bloquant
- un Selector est crée
- et le canal est enregistré auprès
du sélecteur pour l'activité "ACCEPT connection"
- une boucle d'attente écoute les
connections/transmissions
- C'est l'objet Sélector qui se met
en attente par la méthode select()
- il est bloqué jusqu'à ce
qu'ne activité soit possible
- les seules activités possibles
sont celles des canaux enregistrés avec le type d'activité
enregistré
- Quand l'attente est finie, il faut traiter
les différentes activités : demandes de connections,
arrivées de données, écritures possibles
- la méthode selectedKeys récupère
l'ensemble de toutes les enregistrements d'activités
- Un iterateur et une boucle permet de les
traiter un par un
- il faut absolument l'enlever (remove)
sinon on le récupèrera au prochain select()
- on regarde le type d'enregistrement d'activité
(clé / SelectionKey) : isAcceptable(), isReadable() ...pour
le traiter de façon ad-hoc
- traitement d'un SelectionKey qui est isAcceptable()
donc de bit OP_ACCEPT
- c'est une demande de connection d'un client
sur le canal ServerSocketChannel
- le ServerSocket accepte la connection
avec le client
- puis la configure en mode non bloquant
- et enregistre cette connection auprès
du sélecteur pour des opérations de lecture (OP_READ)
- traitement d'un SelectionKey qui est isReadable()
donc de bit OP_READ
- c'est un envoi de données d'un
client
- on récupère dans l'enregistrement
SelectionKey l'information de quel SocketChannel il s'agit
- et ainsi, on lit toutes les données
disponibles
écritures
non bloquantes
Source de ServerSocketChannelW.java
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
public class ServerSocketChannelW {
private int port;
private ByteBuffer buffer;
private int count = 0;
public static void main (String [] args)
throws Exception {
if (args.length != 1)
throw new IllegalArgumentException("Usage: java ServerSocketChannelW port");
int port = Integer.parseInt(args[0]);
new ServerSocketChannelW(port).go ();
}
public ServerSocketChannelW(int port) {
this.port = port;
this.buffer = ByteBuffer.allocateDirect (8192);
this.count = 0;
}
public void go ()
throws Exception {
System.out.println ("Listening on port " + port);
ServerSocketChannel serverChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverChannel.socket();
Selector selector = Selector.open();
serverSocket.bind (new InetSocketAddress (port));
serverChannel.configureBlocking (false);
serverChannel.register (selector, SelectionKey.OP_ACCEPT);
while (true) {
int n = selector.select();
System.out.println ("selector.select() = " + n);
if (n == 0)
continue;
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
System.out.println ("SelectionKey = "+key);
if (key.isAcceptable()) {
System.out.println ("key.isAcceptable()");
ServerSocketChannel server =
(ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
System.out.println ("-- SocketChannel = "+channel
+" register SelectionKey.OP_WRITE");
channel.configureBlocking (false);
channel.register (selector, SelectionKey.OP_WRITE);
}
if (key.isWritable()) {
System.out.println ("key.isWritable()");
WriteDataToSocket (key);
}
it.remove();
}
}
}
protected void WriteDataToSocket (SelectionKey key)
throws Exception {
SocketChannel socketChannel = (SocketChannel) key.channel();
System.out.println ("-- SocketChannel write :"+socketChannel);
buffer.clear();
for (int i = 0; i < 1000; i++)
buffer.put("pong".getBytes());
buffer.flip();
long byteEcrit = 0;
int aEcrire = 1000*("pong".length());
try {
byteEcrit = socketChannel.write( buffer );
System.out.println ("\nwrite count = "+count);
count += (int)byteEcrit;
} catch (IOException ioe) {
count += (int)byteEcrit;
System.out.println (aEcrire+ " a ecrire mais "
+byteEcrit +" ecrit !");
try {
socketChannel.close();
System.out.println ("socketChannel close ");
} catch (IOException e) { }
}
}
}
|
EXECUTION
console serveur :
$ java ServerSocketChannelW 6666
Listening on port 6666
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@1f33675
key.isAcceptable()
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50815]
register SelectionKey.OP_WRITE
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50815]
write count = 0
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
write count = 8000
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
write count = 12000
.....
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
write count = 100000
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
console client :
$java ClientSocketChannelW localhost 6666
100
lu : 100
1000
lu : 1100
5000
lu : 6100
console serveur :
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
.....
write count = 152000
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@c2ea3f
key.isWritable()
-- SocketChannel write :java.nio.channels.SocketChannel[...
4000 a ecrire mais 0 ecrit ! << fermeture du client
socketChannel close
|
Les lignes de la classe :
- Le client ouvre une connection avec le
serveur puis
- lit au Clavier le nombre de bytes qu'il
doit lire depuis le serveur
- et les envoie en bytes au serveur
- jusqu'à ce que l'utilisateur de
la machine cliente tape un nombre négatif.
- Le client "sent", à la
lecture du flot du serveur, que celui-ci a coupé la connection.
- Le serveur
- traite les demandes de connection de client,
- envoie sous forme de bytes des paquets
de 1000 "pong"
- la fermeture de connection d'un client.
- Il est capable de gérer plusieurs
clients à la fois grâce :
- au mode de réception non-bloquant
- à un objet Sélector qui
écoute sur les ports et délivre un état
des activités possibles sur ces ports.
Le client est tout à fait classique :
Source de ClientSocketChannelW.java
import java.io.*;
import java.net.*;
public class ClientSocketChannelW {
public static void main(String[] args) throws Exception {
if (args.length != 2)
throw new IllegalArgumentException("Usage: java ClientSocketChannelW hostname port");
String hostName = args[0];
int port = Integer.parseInt(args[1]);
int count = 0;
Socket socket = null;
BufferedReader sockIn = null;
try {
socket = new Socket(hostName, port);
sockIn = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
} catch (UnknownHostException e) {
System.out.println("host inconnu ou non atteignable : "+ hostName);
System.exit(3);
} catch (IOException e) {
System.out.println("connection impossible au serveur");
System.exit(4);
}
String messageDuServer, messageDuClient, ligne;
int combien = 0;
try {
do {
combien = Clavier.lireInt();
if (combien > 0)
for (int i = 0; i < combien; i++)
if (sockIn.read() == -1) {
System.out.println("sockIn.getChar() == -1");
break;
} else
++count;
System.out.println("lu : "+count);
} while (combien >=0);
} catch (IOException ioe) {
System.out.println ("erreur lecture socket : "+ioe.getMessage());
}
sockIn.close();
socket.close();
}
}
|
les classes : Selector,
SelectionKey et SelectableChannel
- la classe Selector :
- un objet Selector surveille plusieurs
canaux SelectableChannel qui sont configurées en mode
non bloquant
- pour obtenir une instance, il faut exécuter
la méthode statique open()
- les canaux doivent s'enregistrer auprès
du Selector
- la méthode select() se met
en attente (bloquante) d'opérations possibles à
effectuer sur les divers canaux enregistrés :
- dès qu'une ou plusieurs activités
sont possibles, elle en constitue une liste et retourne le nombre
le nombre de canaux prêts à une opération.
- la méthode selectedKeys()
retourne cette liste sous forme d'ensemble Set
- un ensemble de SelectionKey
- a parcourir avec un Iterator
pensez
à retirer les éléments de cet ensemble au
fer et à mesure de leur traitement !
- la classe SelectionKey
- une "clé" SelectionKey
comprend :
- un canal
- un masque de bit d'opérations
- représente selon les cas :
- obtenue via selectedKeys ci-dessus,
c'est une ou des activités possible d'un canal
- une lecture, une écriture, un établissement
de connection, une acceptation de connection selon les bits du
masque
- obtenue par enregistrement d'un canal
auprès d'un Selector,
c'est l'enregistrement du canal pour diverses opérations
parmi
- lecture, écriture, établissement
de connection, acceptation de connection inscrit bit à
bit dans le masque
- plusieurs constantes :
servant de masque à bit pour les opérations
- OP_READ
- OP_WRITE
- OP_CONNECT
- OP_ACCEPT
- plusieurs méthodes obtiennent l'état
du SelectionKey pour les opérations :
- isReadable()
- isWritable()
- isConnectable()
- isAcceptable()
- la méthode interestOps()
retourne un int avec les bits ci-dessus à 0 ou 1
- la méthode interestOps(int masque)
redéfinit pour le canal enregistré une liste d'opérations
souhaitées notées dans le masque
- elle retourne un nouveau SelectionKey
- la méthode cancel() supprime
l'enregistrement du canal
- la méthode channel() retourne
le canal SelectableChannel enregistré.
- la classe abstraite SelectableChannel
- définit les canaux Channel sélectionnables
par un Selector
- en particulier, les ServerSocketChannel
et les SocketChannel
- la méthode configureBlocking(booléen)
configure en mode bloquant ou non le canal
- la méthode register(selector,
masque) enregistre le canal auprès du Selector pour les
opérations précisées bit à bit dans
le masque.
- retourne un enregistrement SelectionKey
La technique des
"callback"
Source de ServerPlusHandler.java
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.net.*;
import java.util.*;
public class ServerPlusHandler {
private int port;
public static void main (String [] args)
throws Exception {
if (args.length != 1)
throw new IllegalArgumentException("Usage: java ServerPlusHandler port");
int port = Integer.parseInt(args[0]);
new ServerPlusHandler(port).go ();
}
public ServerPlusHandler(int port) {
this.port = port;
}
public void go ()
throws Exception {
System.out.println ("Listening on port " + port);
ServerSocketChannel servSockChann = ServerSocketChannel.open();
ServerSocket serverSocket = servSockChann.socket();
Selector selector = Selector.open();
serverSocket.bind (new InetSocketAddress (port));
servSockChann.configureBlocking (false);
SelectionKey keyServer =
servSockChann.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int n = selector.select();
System.out.println ("selector.select() = " + n);
if (n == 0)
continue;
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
System.out.println ("SelectionKey = "+key);
it.remove();
if (key.isAcceptable() && (key == keyServer)) {
System.out.println ("key.isAcceptable()");
ServerSocketChannel server =
(ServerSocketChannel) key.channel();
SocketChannel clientSockChann = server.accept();
System.out.println ("-- SocketChannel = "+clientSockChann
+" register SelectionKey.OP_READ");
clientSockChann.configureBlocking (false);
SelectionKey keyClient =
clientSockChann.register(selector,SelectionKey.OP_READ);
keyClient.attach(new SuiviConnectionHandler());
} else if (key.isReadable() && (key != keyServer)) {
System.out.println ("key.isReadable()");
try {
SuiviConnectionHandler handler =
(SuiviConnectionHandler)key.attachment();
System.out.println ("handler = "+handler);
handler.readDataFromSocket(key);
} catch (Exception e) {
System.out.println ("erreur de lecture "+e);
e.printStackTrace();
}
} else {
System.out.println ("SelectionKey non traite ! ");
}
}
}
}
class SuiviConnectionHandler {
private int nombreRequete = 0;
protected void readDataFromSocket (SelectionKey key)
throws Exception {
nombreRequete++;
ByteBuffer buffer = ByteBuffer.allocateDirect (1024);
Charset charset = Charset.forName("ISO-8859-1");
CharsetEncoder encoder = charset.newEncoder();
CharsetDecoder decoder = charset.newDecoder();
SocketChannel clientSockChann = (SocketChannel) key.channel();
System.out.println ("-- SocketChannel = "+clientSockChann
+ " nombre requete = " +nombreRequete);
int nbreLu = clientSockChann.read (buffer);// suppose lecture en 1 coup !!
if (nbreLu == -1) {
System.out.println ("socketChannel.close() = "+clientSockChann);
key.cancel();
clientSockChann.close();
} else {
buffer.flip();
String requete = decoder.decode(buffer).toString();
buffer.clear();
if (requete.trim().equalsIgnoreCase("ciao")) {
clientSockChann.write(encoder.encode(CharBuffer.wrap("bye bye")));
System.out.println ("socketChannel.close() = "+clientSockChann);
key.cancel();
clientSockChann.close();
} else {
String reponse = "bien recu "+nbreLu
+" bytes lors de la requete : "+nombreRequete+"\n" ;
clientSockChann.write(encoder.encode(CharBuffer.wrap(reponse)));
}
}
}
}
}
|
EXECUTION
console 1:
$ java ServerPlusHandler 6666
Listening on port 6666
console 2:
$ telnet localhost 6666
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
a
bien recu 3 bytes lors de la requete : 1
bien recu 2 bytes lors de la requete : 2b
bien recu 3 bytes lors de la requete : 3cc
bien recu 4 bytes lors de la requete : 4
console 1:
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@1888759
key.isAcceptable()
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50762]
register SelectionKey.OP_READ
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@13c5982
key.isReadable()
handler = ServerPlusHandler$SuiviConnectionHandler@14b7453
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50762]
nombre requete = 1
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@13c5982
key.isReadable()
handler = ServerPlusHandler$SuiviConnectionHandler@14b7453
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50762]
nombre requete = 2
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@13c5982
key.isReadable()
handler = ServerPlusHandler$SuiviConnectionHandler@14b7453
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50762]
nombre requete = 3
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@13c5982
key.isReadable()
handler = ServerPlusHandler$SuiviConnectionHandler@14b7453
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50762]
nombre requete = 4
console 3:
$ telnet localhost 6666
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
bonjour
bien recu 9 bytes lors de la requete : 1
console 1:
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@1888759
key.isAcceptable()
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50766]
register SelectionKey.OP_READ
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@1503a3
key.isReadable()
handler = ServerPlusHandler$SuiviConnectionHandler@1a1c887
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50766]
nombre requete = 1
console 2:
d
bien recu 3 bytes lors de la requete : 5
console 1:
selector.select() = 1
SelectionKey = sun.nio.ch.SelectionKeyImpl@13c5982
key.isReadable()
handler = ServerPlusHandler$SuiviConnectionHandler@14b7453
-- SocketChannel = java.nio.channels.SocketChannel
[connected local=/127.0.0.1:6666 remote=/127.0.0.1:50762]
nombre requete = 5
|
Les lignes de la classe :
- les clients envoient des caractères
au serveur
- le serveur renvoie un message au client
indiquant :
- combien de bytes il avait envoyé
- ainsi que le nombre de requetes qu'il
a envoyé
- le serveur doit donc mémoriser
le nombre de requetes de chaque client
- pour ce problème de mémorisation,
on utilise l'attachement d'un objet à l'enregistrement
d'un canal.
- cela permet de garder le contexte de traitement
d'une connection
- la méthode attach(objet)
de SelectionKey attache un objet à l'enregistrement du
canal.
- la méthode attachement()
de SelectionKey retourne l'objet attaché à l'enregistrement
du canal.
le
thread de boucle du Selector doit être le plus disponible
possible.
il faut éviter les traitements longs de communication
(dont la lecture et l'écriture de données) au sein
de ce thread
- La technique des attachements combinés
à une gestion multithreads permet de libérer le
thread de boucle du Selector
exercices