2008-10-15 11 views
7

Al escribir un código de prueba, he encontrado que Selector.select() puede retornar sin Selector.selectedKeys() que contenga las claves para procesar. Esto está sucediendo en un bucle estrecho cuando registro un canal ed aceptar() conJava NIO select() regresa sin las claves seleccionadas, ¿por qué?

SelectionKey.OP_READ | SelectionKey.OP_CONNECT

como las operaciones de interés.

Según los documentos, seleccione() debe devolver cuando:

1) Hay canales que se puede actuar.

2) Llama explícitamente a Selector.wakeup() - no se han seleccionado teclas.

3) Usted explícitamente Thread.interrupt() el hilo haciendo la selección() - no se seleccionan teclas.

Si no obtengo ninguna clave después de seleccionar() debo estar en los casos (2) y (3). Sin embargo, mi código no está llamando a wakeup() o interrupt() para iniciar estas devoluciones.

¿Alguna idea de qué está causando este comportamiento?

Respuesta

9

Respuesta corta: eliminar OP_CONNECT de la lista de operaciones que le interesan para la conexión aceptada: una conexión aceptada ya está conectada.

he conseguido reproducir el problema, lo que podría ser exactamente lo que está sucediendo:

import java.net.*; 
import java.nio.channels.*; 


public class MyNioServer { 
    public static void main(String[] params) throws Exception { 
    final ServerSocketChannel serverChannel = ServerSocketChannel.open(); 
    serverChannel.configureBlocking(true); 
    serverChannel.socket().bind(new InetSocketAddress("localhost", 12345)); 
    System.out.println("Listening for incoming connections"); 
    final SocketChannel clientChannel = serverChannel.accept(); 
    System.out.println("Accepted connection: " + clientChannel); 


    final Selector selector = Selector.open(); 
    clientChannel.configureBlocking(false); 
    final SelectionKey clientKey = clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_CONNECT); 
    System.out.println("Selecting..."); 
    System.out.println(selector.select()); 
    System.out.println(selector.selectedKeys().size()); 
    System.out.println(clientKey.readyOps()); 
    } 
} 

vez que el servidor anterior recibe una conexión, el primer select() en las salidas de conexión sin bloqueo y no hay llaves con operaciones listas No sé por qué Java se comporta de esta manera, pero parece que muchas personas son mordidas por este comportamiento.

El resultado es el mismo en JVM 1.5.0_06 de Sun en Windows XP y en JVM 1.5.0_05 y 1.4.2_04 de Sun en Linux 2.6.

+0

Gracias por la respuesta. Esto es claramente una anomalía en el comportamiento de select, pero es fácilmente solucionable. –

+0

@FrankTaylor No sé por qué un error de programación debe considerarse 'una anomalía en el comportamiento de select '. – EJP

9

La razón es que OP_CONNECT y OP_WRITE son la misma cosa bajo el capó, lo que nunca debe estar registrado para ambos simultáneamente (lo mismo OP_ACCEPT y OP_READ), y nunca debe ser registrado para OP_CONNECT en absoluto cuando el canal ya está conectado, como es en este caso, haber sido aceptado.

Y OP_WRITE casi siempre está listo, excepto cuando el socket send buffer en e kernel está lleno, por lo que solo debe registrarse después de obtener una escritura de longitud cero. Entonces al registrar el canal ya conectado para OP_CONNECT,, realmente se estaba registrando para OP_WRITE, que estaba listo, entonces select() se activó.

Cuestiones relacionadas