2016-09-23 18 views
0

ServerSocketChannelがノンブロッキングモードで使用され、セレクタに登録されている場合、次のchannel.close()コールはソケットをただちに閉じることはなく、netstat出力LISTENING状態です。非ブロックモードのServerSocketChannelが正しく閉じない

単純なテストケースです。

// windows 7/jdk1.8.0_71 x64 

@Test 
public void test() throws Exception { 
    Selector selector = Selector.open(); 

    for (int i = 0; i < 3; i++) { 
     System.out.printf("Trial %d\n", i); 
     reopen(selector); 
    } 
} 

private void reopen(Selector selector) throws Exception { 
    ServerSocketChannel channel = ServerSocketChannel.open(); 
    channel.configureBlocking(false); 
    channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); 
    channel.bind(new InetSocketAddress("127.0.0.1", 17777)); 

    // --- if channel is not registered with selector the following close() method works fine 
    SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_ACCEPT); 

    // --- trying to cancel the registration in selector - doesn't help 
    // selectionKey.cancel(); 
    // selector.wakeup(); 

    // --- trying to configure the socket as blocking - doesn't help 
    // selectionKey.cancel(); 
    // channel.configureBlocking(true); 

    // --- trying to register the channel in other selector - doesn't help 
    // selectionKey.cancel(); 
    // Selector nullSelector = Selector.open(); 
    // channel.register(nullSelector, 0); 
    // nullSelector.close(); 

    channel.close(); 

    // PROBLEM: after close() has returned I still see this port is listening 
    // 
    //  C:\Dev>netstat -nao | grep 17777 
    //  TCP 127.0.0.1:17777  0.0.0.0:0    LISTENING  xxxx 
    // 
    // so on the next bind I get an exception: java.net.BindException: Address already in use: bind 

    // --- it helps!!! but I don't want to because there could multiple server sockets on the same selector 
    // selector.close(); 

    // --- trying to shake-up the selector - doesn't help 
    // selector.wakeup(); 

    // --- trying to wait some time - doesn't help 
    // Thread.sleep(10000); 
} 

ServerSocketChannelを正常に閉じることができるのは、セレクタ自体を閉じることです。しかし、セレクタは他のソケットに使用されており、私はそれを閉じたくありません。

セレクタを閉じずにServerSocketChannelを正しく閉じるにはどうすればいいですか?またはそれが閉じられるまで待つ方法?

UPD:Windowsでのみ問題が発生します。解決策が見つけられ、下のコメントに掲載されます。

+0

LISTENING状態にあったポートは、TIME_WAIT状態を決して通過しません。 – EJP

+0

私は、このポートに着信接続がある場合、ソケットがTIME_WAITに登録されていると信じています:http://www.microhowto.info/howto/listen_on_a_tcp_port_with_connections_in_the_time_wait_state.html –

+0

これは以前はESTABLISHED状態だったポートです。両方のポートが同じポート番号を持つので、少し混乱しますが、それが動作する方法です。 – EJP

答えて

1

多くのオプションを試した後、私はこのコードが役立つことがわかりました:

channel.close(); 

selector.selectNow(); 

または

selectionKey.cancel(); 
selector.selectNow(); 

channel.close(); 

が、私はそれが動作する理由見当もつかない。誰かがこれを説明するかもしれない。

+0

正しい。これについては、Javadocの非常に不明瞭な場所で、 'SelectableChannel'や' AbstractSelectableChannel'や 'Selector'や' AbstractSelector'のどこかで私が見つけられないことがあります。これは 'SocketChannels'と' DatagramChannels'にも適用されます。 – EJP

関連する問題