2008-10-15 5 views
7

일부 테스트 코드를 작성하면서 Selector.select()가 처리 할 키가 포함 된 Selector.selectedKeys()없이 반환 할 수 있음을 발견했습니다. 관심있는 조작으로Java NIO select()는 선택된 키없이 반환합니다 - 그 이유는 무엇입니까?

SelectionKey.OP_READ | SelectionKey.OP_CONNECT

이있는 accept() 채널을 등록 할 때 이것은 단단한 루프에서 발생합니다.

워드 프로세서에 따르면, 선택() 할 때 반환해야합니다 :

1)에 따라 행동 할 수있는 채널이있다.

2) 명시 적으로 Selector.wakeup()을 호출합니다. 키가 선택되지 않습니다.

3) select()를 수행하는 스레드를 명시 적으로 Thread.interrupt() - 키를 선택하지 않았습니다.

select() 후에 아무 키도 표시되지 않으면 케이스 (2)와 (3)에 있어야합니다. 그러나 내 코드는 wakeup() 또는 interrupt()를 호출하여 이러한 반환을 시작하지 않습니다.

이 문제의 원인은 무엇입니까?

답변

9

짧은 대답 : 허용 된 연결에 관심이있는 작업 목록에서 OP_CONNECT을 제거하십시오 - 허용 된 연결이 이미 연결되어 있습니다.

내가 정확하게 당신에게 무슨 일이 일어나고 수 있습니다 어떤 문제, 재생 관리 : 위의 서버가 차단하지 않고 연결, 연결 종료에 select() 맨 처음을받은 후

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()); 
    } 
} 

을 더 키가 없습니다 준비 작업. 나는 왜 자바가 이런 식으로 행동하는지 모르지만 많은 사람들이이 행동에 물린 것으로 보인다.

결과는 Windows XP의 Sun JVM 1.5.0_06과 Linux 2.6의 Sun JVM 1.5.0_05 및 1.4.2_04에서 동일합니다.

+0

답변 해 주셔서 감사합니다. 이것은 분명히 선택의 행동에 예외이지만, 쉽게 해결할 수 있습니다. –

+0

@FrankTaylor 왜 프로그래밍 오류가 '선택 동작에 이상'으로 간주되어야하는지 모르겠습니다. – EJP

9

이유는 둘 다 동시에 (저두 OP_ACCEPTOP_READ)에 등록해서는 안 있도록 OP_CONNECTOP_WRITE가, 후드 같은 일 것을, 그리고 채널이 이미 모든 때 OP_CONNECT에 등록해서는 안됩니다 이 경우에 연결되어 있으며 수락되었습니다.

그리고 OP_WRITE은 거의 항상 준비가되어 있습니다 (e 커널의 소켓 전송 버퍼가 꽉 찼을 경우 제외). 그래서 길이가 0 인 쓰기 후에 만 ​​등록해야합니다. 따라서 이미 연결된 채널을 OP_CONNECT,에 등록하면 실제로 OP_WRITE,에 등록 했으므로 select()이 실행되었습니다.