Dans cet exercice, on réalise un client pour le protocole Chaton en mode non-bloquant.
Les trames circulant entre les clients et le serveur représentent des messages. Un message contient un login et un texte au format suivant :
+------------------+------------+------------------+------------+ | Login size (INT) | Login UTF8 | Texte size (INT) | Texte UTF8 | +------------------+------------+------------------+------------+
Les entiers sont en Big Endian. Les chaînes ne font pas plus de 1024 octets.
Quand un message est reçu par le serveur, il le transmet à tous les clients.
Nous vous proposons de partir du squelette suivant ClientChat.java. Dans ce squelette, la lecture au clavier se fait dans le thread console
et la boucle de sélection
est exécutée par le thread main
. Attention, cela signifie qu'il faut utiliser une mécanisme thread-safe pour les faire communiquer.
sendCommand(String msg)
et processCommands()
que vous devrez implémenter.
BlockingQueue
, mais attention, on n'est pas dans le cas d'un producteur-consommateur au sens strict.Écrivez un client non-bloquant pour le protocole Chaton.
On souhaite maintenant rajouter une console au serveur non-bloquant : c'est à dire la possibilité de lire des instructions au clavier.
Votre console devra reconnaître trois instructions :
Rajouter une console à votre ServerEcho
dans une une classe
ServerEchoWithConsole
.
Vous pouvez tester votre serveur en utilisant ClientEchoSlow.jar et/ou la commande netcat
. Attention, lorsque la communication est coupée avec le jar, le message "Sender thread killed by IOException ..."
apparaît mais son exécution continue, c'est normal.
Rajouter à votre ServerEchoWithConsole
la suppression des clients inactifs dans une classe ServerEchoWithConsoleAndTimeout
Il n'est pas nécessaire de rajouter de nouveaux threads ! Une solution peu coûteuse consiste à rajouter un champ booléen activeSinceLastTimeoutCheck
dans chaque Context
. Ce booléen est à vrai initialement et il sera remis à vrai à chaque appel à doRead
et doWrite
. Dans la boucle de sélection, on va, toutes les TIMEOUT
millisecondes,
parcourir les contextes de tous les clients connectés et fermer ceux dont le booléen est faux. Pour tous les clients encore connectés on positionnera le booléen à faux.
De cette manière, si pendant les TIMEOUT
millisecondes qui séparent deux parcours, un client n'a pas fait d'appel à doRead
ou doWrite
, il sera déconnecté.
Encore une fois, il faut prendre garde au fait que la méthode select
est bloquante. Pour garantir que l'on ne passe pas plus de TIMEOUT
millisecondes à attendre dans le select
, on utilisera la version avec timeout de la méthode select
.
selector.select(this::treatKey,TIMEOUT);