Les sélecteurs de canaux (Selector) sont utilisés pour des opérations d'entrée/sortie asynchrones, dans le cas notamment d'applications client/serveur.

ClasseDescription
SelectionKeycontient un jeton (token) représentant l'enregistrement d'un objet SelectableChannel avec un objet Selector.
Selectorcontient un multiplexeur d'objets SelectableChannel.

Les sélecteurs de canaux se créent suite à l'appel de la méthode statique open() de la classe Selector.

Selector selecteur = Selector.open();

Les canaux sélectionnables (SelectableChannel) doivent être référencés dans l'objet Selector par l'intermédiaire d'une clé (SelectionKey).

ServerSocketChannel sserveur = ServerSocketChannel.open();
//...
SelectionKey cle = sserveur.register(selecteur, SelectionKey.OP_ACCEPT);

La méthode register() prend deux arguments, le premier est le sélecteur qui sera chargé de sélectionner le canal vers le socket du serveur, et le second indique au sélecteur le genre d'événement pour lequel ce canal devra être sélectionné pour un traitement ultérieur. Dans le cas d'un objet ServerSocketChannel, la connexion d'un client sur le canal précité produit le lancement de l'événement OP_ACCEPT. Les autres événements possibles sont OP_CONNECT (connexion), OP_READ (lecture) et OP_WRITE (écriture).

Le sélecteur est maintenant prêt à sélectionner le canal qui produira l'événement enregistré avec la méthode register(). La méthode select() met en oeuvre cette observation événementielle en sélectionnant un jeu de clés correspondants aux canaux sur lesquels s'est produit une opération d'entrée/sortie.

while(true){
    int nbCles = selecteur.select();
    //...
}

La boucle infinie permet à l'application, de constamment consulter le sélecteur à propos du déclenchement d'un événement sur l'un des canaux référencés.

Les clés sélectionnées sont accessibles en invoquant la méthode selectedKeys().

Set scles = selecteur.selectedKeys();

Un ensemble de clés est retourné par la méthode selectedKeys(). Le parcours de cette collection peut s'effectuer par le truchement d'un itérateur.

Iterator ite = scles.iterator();
while(ite.hasNext()){
    SelectionKey scle = (SelectionKey)ite.next();
    //...
}

Afin de déclencher un traitement approprié à la clé sélectionnée, il faut vérifier quel événement est associé à cette clé. Il existe plusieurs méthodes qui permettent de contrôler l'effectivité des différents types d'événement : isAcceptable, isConnectable(), isReadable() et isWritable().

if(scle.isAcceptable()){
//ou if((scle.readyOps() & SelectionKey.OP_ACCEPT) != 0)
    //...
}

Connaissant le type d'événement produit et possèdant la clé sélectionnée, il devient possible d'exécuter un traitement approprié sur le canal concerné. La récupération de ce canal est possible en appelant la méthode channel().

ServerSocketChannel canal = (ServerSocketChannel)cle.channel();

Enfin, le traitement adéquat peut être exécuté sur le canal.

SocketChannel serveur = canal.accept();

Maintenant, le nouveau canal doit être placé dans le sélecteur au moyen de la méthode register(). Dans ce cas, le canal est chargé de lire les données transmises par le client, ainsi l'événement adéquat pour enregistrer ce canal est une opération de lecture.

SelectionKey cleserveur = 
        serveur.register(selecteur, SelectionKey.OP_READ);

Afin d'éviter de retrouver, lors d'une prochaine itération, la clé sélectionnée que l'on vient de traiter, il est impératif de supprimer cette clé de l'objet Iterator courant.

ite.remove();

Lorsque la connexion est établie, les sockets (client et serveur) peuvent alors communiquer entre eux. Ainsi, des données sont envoyées par le socket client vers le serveur. Le canal référencé en lecture est, dès lors, sélectionné par le sélecteur. Les données transmises peuvent être traitées en conséquence et si besoin est une réponse peut être retournée au client. Auparavant, à l'instar du contrôle de l'événement d'acceptation d'une connexion, il faut vérifier s'il s'agît effectivement de gérer une opération de lecture en appelant la méthode isReadable().

if(scle.isReadable()){
    SocketChannel serveur = (SocketChannel)scle.channel();
    //Traitement des données...
}
Exemple [voir]