2016-09-23 20 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