On souhaite écrire un serveur qui traite de multiples clients avec un seul thread en non-bloquant et qui permet d'effectuer des additions de deux entiers pour chaque client.
int
(4 octets) en Big Endian, puis ferme la
connexion en écriture. Le serveur renvoie un int
en Big Endian, valant
la somme des deux entiers reçus, puis il ferme la connexion. En partant de la trame ServerSumOneShot.java,
écrire le code du serveur ServerSumOneShot
en non bloquant, pour le protocole SumOneShot. Vous aurez également besoin de la classe Helpers.java
Il n'est pas nécessaire de vérifier
si le client a bien fermé la connexion après l'envoi des deux int
: après envoi de la réponse, le serveur ferme la connexion.
attachement
de la SelectionKey
un
ByteBuffer
de taille 2 * Integer.BYTES
(alloué lors de l'acceptation du client). socketChannel.write
(comme la méthode socketChannel.read
) est non-bloquante
et qu'il n'y a donc aucune garantie que toute la zone de travail du ByteBuffer
soit écrite.
Regardez dans votre code ce qu'il se produirait si, au moment d'écrire la somme vers un client,
le serveur ne pouvait écrire que 2 des 4 octets lors d'une tentative
d'écriture non bloquante. Même s'il est improbable dans notre exemple,
prévoyez ce cas dans votre code.
processSelectedKeys
, comment faut-il traiter les exceptions levées dans les méthodes doRead
, doWrite
et doAccept
?
Pour tester votre code, utilisez le jar ClientSumOneShot.jar qui demande 2 entiers au clavier, les envoie au serveur et affiche la réponse du serveur.
% java fr.upem.net.tcp.nonblocking.ServerSumOneShot 7777 % java -jar ClientSumOneShot.jar localhost 7777 Connected to server. Enter two integers: 1 2 Waiting for server's reply ... Server replied : 3
En passant l'option -bug à ClientSumOneShot.jar
, on simule un client qui ferme la connexion en écriture sans
avoir envoyé les deux entiers. Dans ce cas, votre serveur doit fermer la connexion car le client ne respecte pas le protocole.
% java fr.upem.net.tcp.nonblocking.ServerSumOneShot 7777 % java -jar ClientSumOneShot.jar localhost 7777 -bug Connected to server. Enter two integers: 1 2 Closing the write-connection without sending the second INT. Waiting for server's reply ... Server closed the connection before sending the answer (This is the normal behavior).
On change maintenant légèrement le protocole pour autoriser un client à effectuer plusieurs sommes sur la même connexion.
int
(4 octets) en Big Endian. Le serveur répond un int
en Big Endian, valant
la somme des deux entiers reçus. Ces échanges continuent jusqu'à ce que le client ferme la connexion en écriture.
Adaptez votre code en un serveur ServerSum
pour qu'il puisse traiter une succession de sommes.
% java fr.upem.net.tcp.nonblocking.ServerSum 7777 % java -jar ClientSum.jar localhost 7777
Client 0 connected to server. Client 1 connected to server. Client 2 connected to server. Client 3 connected to server. Client 4 connected to server. Client 1 finished sending its requests. Client 2 finished sending its requests. Client 3 finished sending its requests. Client 0 finished sending its requests. Client 4 finished sending its requests. Client 1 finished receiving the answers. Client 0 finished receiving the answers. Client 4 finished receiving the answers. Client 3 finished receiving the answers. Client 2 finished receiving the answers.
SocketChannel
8 octets par 8 octets. Nous verrons dans le TP suivant comment coder un serveur plus performant qui effectue
des lectures et des écritures en plus grande quantité si cela est possible. Le but de cet exercice est d'écrire un serveur pour le protocole Echo. Ce sera
l'occasion de commencer à mieux structurer nos serveurs non-bloquants en utilisant des Context
.
On veut que notre serveur puisse être à la fois en OP_READ
et en OP_WRITE
.
Pour cela, on va attacher à chaque SelectionKey
correspondant à une SocketChannel
d'un client un objet de la classe Context
que vous allez coder.
static private class Context { private final SelectionKey key; private final SocketChannel sc; private final ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); private boolean closed = false; private Context(SelectionKey key){ this.key = key; this.sc = (SocketChannel) key.channel(); } private void updateInterestOps() { // TODO } private void doRead() throws IOException { // TODO } private void doWrite() throws IOException { // TODO } }
Le principe de la classe Context
est de contenir toutes les informations relatives à la SocketChannel
, i.e., au client. Elle contiendra toujours la SocketChannel
et la SelectionKey
. Dans le cas d'un serveur pour le protocole Echo, elle contiendra un ByteBuffer
et
un boolean
qui servira
à mémoriser si le client a fermé la connexion. De plus, la classe Context
contient les méthodes doRead
,
doWrite
et une méthode updateInterestOps
. La méthode updateInterestOps
sera appelée
dans doRead
et dans doWrite
et est en charge
de mettre à jour les interestOps
de la SelectionKey
. La mise à jour se fera uniquement à partir
de l'état du ByteBuffer
et du boolean
.
Convention : Pour les méthodes doRead
, doWrite
et updateInterestOps
on supposera que le ByteBuffer
est en mode écriture avant et après l'appel de ces méthodes.
En partant de la trame ServerEcho.java, écrivez un serveur pour le protocole Echo en mode en non-bloquant.
Une fois que votre serveur semble fonctionner, testez le avec le jar ClientEchoSlow.jar, qui envoie en boucle l'octet 1 et, dans un autre thread, affiche le numéro des octets reçus. Le client ne s'arrête jamais.
L'intérêt de ce client est qu'il lit "lentement" et donc, à terme, ralentit
le rythme d'écriture du serveur (à cause du contrôle de flot effectué par TCP).
Ainsi, en consultant (par la commande top
sous linux) les ressources CPU
utilisées par votre serveur, vous devrez constater qu'elles sont très limitées.
Si le serveur consomme beaucoup de CPU, c'est par exemple parce qu'il tente d'écrire
alors que ce n'est pas possible (il fait alors une forme d'attente active) : le principe
des entrées sorties non bloquantes est précisément d'éviter ces comportements.
% java fr.upem.net.tcp.nonblocking.ServerEcho 7777 % java -jar ClientEchoSlow.jar localhost 7777