【问题标题】:ServerSocketChannel in non-blocking mode is not closing properly非阻塞模式下的 ServerSocketChannel 未正确关闭
【发布时间】:2016-09-23 08:44:44
【问题描述】:

当在非阻塞模式下使用 ServerSocketChannel 并在选择器中注册时,随后的 channel.close() 调用不会立即关闭套接字,它仍然在 LISTENING 状态下的 netstat 输出中可见。

一个简单的测试用例。

// 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 上。在下面的评论中找到并发布了解决方案。

【问题讨论】:

  • 一直处于 LISTENING 状态的端口将永远不会转换到 TIME_WAIT 状态。
  • 我相信如果此端口上有传入连接已注册,则套接字将进入 TIME_WAIT:microhowto.info/howto/…
  • 正确,但这是一个之前处于 ESTABLISHED 状态的端口。由于两个端口都有相同的端口号,这有点令人困惑,但它就是这样工作的。

标签: java io nio


【解决方案1】:

在尝试了很多选项后,我发现这段代码有帮助:

channel.close();

selector.selectNow();

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

channel.close();

但我不知道它为什么会起作用。可能有人会解释这一点。

【讨论】:

  • 正确。在 Javadoc 中一个非常晦涩的地方有一些关于此的措辞,当我查找它时,我永远找不到它,在 SelectableChannelAbstractSelectableChannelSelectorAbstractSelector 的某个地方。它也适用于SocketChannelsDatagramChannels
猜你喜欢
  • 2015-05-02
  • 2011-05-19
  • 2014-02-17
  • 2012-01-01
  • 1970-01-01
  • 2011-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多