Java NIO select () retorna sem chaves selecionados - por quê?
Pergunta
Ao escrever um código de teste eu descobri que Selector.select () pode retornar sem Selector.selectedKeys () contendo todas as chaves para processo. Isto acontece num ciclo apertado quando registar um aceitar () ed canal com
SelectionKey.OP_READ | SelectionKey.OP_CONNECT
como as operações de interesse.
De acordo com os documentos, seleccione () deve retornar quando:
1) Existem canais que podem ser postas em prática.
2) Você chama explicitamente Selector.wakeup (.) - sem chaves são selecionados
3) Você explicitamente Thread.interrupt () o fio fazer o select () -. Nenhuma tecla for selecionado
Se eu ficar sem chaves após o select () Eu devo ser em casos (2) e (3). No entanto, o meu código não está chamando de despertar () ou interrupção () para iniciar esses retornos.
Todas as ideias sobre o que está causando esse comportamento?
Solução
Resposta curta:. OP_CONNECT
remover da lista de operações que você está interessado para a conexão aceita - uma conexão aceita já está conectado
Eu consegui reproduzir o problema, que pode ser exatamente o que está acontecendo com você:
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());
}
}
Depois que o servidor acima recebe uma ligação, o primeiro select()
nas saídas de conexão sem bloqueio e não há chaves com operações prontas. Eu não sei por que se comporta em Java desta maneira, mas parece que muitas pessoas mordido por este comportamento.
O resultado é o mesmo em Sun JVM 1.5.0_06 no Windows XP, bem como da Sun JVM 1.5.0_05 e 1.4.2_04 no Linux 2.6.
Outras dicas
A razão é que OP_CONNECT
e OP_WRITE
são a mesma coisa sob o capô, então você nunca deve ser registrado para ambos simultaneamente (OP_ACCEPT
ditto e OP_READ
), e você nunca deve ser registrado para OP_CONNECT
em tudo quando o canal já está conectado , como é neste caso, depois de ter sido aceite.
E OP_WRITE
é quase sempre pronto, exceto quando o buffer de envio tomada em e kernel é completo, assim você só deve se registrar para que depois que você começa uma gravação comprimento zero. Então por registrar o canal já conectado para OP_CONNECT,
você estava realmente registrar para OP_WRITE,
que estava pronto, por isso select()
foi acionado.