gpt4 book ai didi

java - 如何等到 TCP 端口在 Java 中真正( native )关闭?

转载 作者:IT王子 更新时间:2023-10-29 00:02:00 24 4
gpt4 key购买 nike

我为什么要问这个?

如果你愿意,你可以跳过这个故事。不过,有些人可能会感兴趣。

我有一个用 Java 编写的嵌入式 ZooKeeper 服务器。在单元测试中,我动态地将端口分配给测试服务器。在分配端口之前,我通过打开 ServerSocket 检查它是否未被使用。 , 然后关闭它。

它时常发生,在单元测试中我得到 BindException当我启动我的服务器时 (不可能是我将同一个端口分配给两个服务器,因为我也使用文件锁来实现互斥)。事实证明,原因是,为了进行端口检查,我​​打开了端口,然后关闭了它,它在操作系统级别等待了一段时间,直到端口可以重新打开。

然而,有一个选项 ( StandardSocketOptions.SO_REUSEADDR ) 可以告诉 Java 套接字,可以重用处于 TIMED_WAIT 状态的旧套接字。检查 ZooKeeper 代码后,它实际上设置为 true(参见 org.apache.zookeeper.server.NIOServerCnxnFactory.configure(InetSocketAddress, int)):

@Override
public void configure(InetSocketAddress addr, int maxcc) throws IOException {
configureSaslLogin();

thread = new Thread(this, "NIOServerCxn.Factory:" + addr);
thread.setDaemon(true);
maxClientCnxns = maxcc;
this.ss = ServerSocketChannel.open();
ss.socket().setReuseAddress(true);
LOG.info("binding to port " + addr);
ss.socket().bind(addr);
ss.configureBlocking(false);
ss.register(selector, SelectionKey.OP_ACCEPT);
}

我的 test但是证明它不起作用。我得到 BindException(在 Linux JDK 1.7.0_60 下)。

在检查了 ServerSocketChannel 实现(JDK 1.7.0_60)之后,我意识到,这在 linux 下是行不通的。参见 sun.nio.ch.ServerSocketChannelImpl.setOption(SocketOption<T>, T) :

public <T> ServerSocketChannel setOption(SocketOption<T> paramSocketOption, T paramT) throws IOException
{
if (paramSocketOption == null)
throw new NullPointerException();
if (!(supportedOptions().contains(paramSocketOption)))
throw new UnsupportedOperationException("'" + paramSocketOption + "' not supported");
synchronized (this.stateLock) {
if (!(isOpen()))
throw new ClosedChannelException();
if ((paramSocketOption == StandardSocketOptions.SO_REUSEADDR) && (Net.useExclusiveBind()))
{
this.isReuseAddress = ((Boolean)paramT).booleanValue();
}
else {
Net.setSocketOption(this.fd, Net.UNSPEC, paramSocketOption, paramT);
}
return this;
}
}

不幸的是Net.useExclusiveBind()在 linux 下永远不会返回 true,如果你检查它的来源 in the hopefully similar OpenJDK这取决于 Net.isExclusiveBindAvailable() , 在 Linux 下为 -1。

您有解决方法吗?

除了打开 ServerSocket 之外,Java 中有没有一种方法可以等到端口真正 native 关闭?没有 SO_REUSEADDR 并检查我是否得到 BindException ?当然,这不是解决方案,因为我必须再次关闭该 ServerSocket。为什么在 Java 中没有像在阻塞模式下关闭套接字那样的东西,只有当套接字真正在操作系统级别关闭时才会返回?

最佳答案

恐怕你分析了sun.nio.ch.ServerSocketChannelImpl.setOption(SocketOption<T>, T)实现不正确。

“独占绑定(bind)”仅在 Windows 上使用,与其他平台无关。您可以在 sources of the sun.nio.ch.Net class 中查看.具体见isExclusiveBindAvailable()的评论.

当独占绑定(bind)不可用时,ServerSocketChannelImpl.setOption只需调用 Net.setSocketOption最后会调用 native setsockopt功能。

顺便说一句,服务器端套接字可能不处于 TIME_WAIT 状态,但也处于 FIN_WAIT_1 或 FIN_WAIT_2(等待客户端确认关闭)。这in-depth description of the TCP state machine可能会有帮助。

关于java - 如何等到 TCP 端口在 Java 中真正( native )关闭?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31163513/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com