如果你愿意,你可以跳过这个故事。 还有些人可能会感兴趣。
我有一个embedded式的JavaKeeper服务器。 在unit testing中,我将端口分配给testing服务器。 在分配端口之前,我打开ServerSocket
检查是否未使用,然后closures它。
BindException
,在unit testing中,当我启动服务器的时候,我得到了BindException
(不能把我的端口分配给两台服务器,因为我使用了文件锁来互斥) 。 原来是这样的,对于端口检查我打开端口,然后closures它,在操作系统级别等待一会儿,直到端口可以重新打开。
然而,有一个选项( 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); }
但是我的testingcertificate它不起作用。 我得到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,如果你在类似的OpenJDK中检查它的源代码,它依赖于Net.isExclusiveBindAvailable()
,在Linux下它是-1。
有没有办法在Java中等待,直到一个端口是真正的本地closures开放一个ServerSocket
没有SO_REUSEADDR,并检查如果我得到BindException
? 当然,这不是一个解决scheme,因为那么我必须再次closuresServerSocket。 为什么在Java中没有像封闭模式closures套接字那样,只有当套接字真的在操作系统级closures时才会返回?
你做了一个逻辑错误,如果关闭在java中的套接字操作系统层上的文件描述符是关闭的。 但是你的问题是因为你接受了套接字的端口可以通过等待连接被阻塞。 所以你一定要先关闭它们。 最好创建一个客户端套接字,并检查是否可以将其绑定到选定的端口。