trabajando socket servidor protocolo peticiones con cliente bidireccional java linux sockets tcp apache-zookeeper

socket - ¿Cómo esperar hasta que un puerto TCP esté realmente(nativamente) cerrado en Java?



tcp java server (1)

Hiciste un error lógico, si cierras el socket en java, el descriptor de archivo en la capa del sistema operativo está cerrado. Pero su problema es que ACEPTA en el socket que el puerto puede bloquearse esperando conexiones. Entonces quisiste cerrarlos primero. Es mejor que cree un socket de cliente y verifique si puede vincularlo al puerto seleccionado.

¿Por qué lo estoy preguntando?

Puede omitir la historia si lo desea. Todavía algunos podrían estar interesados.

Tengo un servidor ZooKeeper incrustado en Java. En las pruebas unitarias, asigno puertos a los servidores de prueba dinámicamente. Antes de asignar el puerto, verifico si no se usa abriendo un ServerSocket y luego cerrándolo.

Sucede BindException , que en las pruebas de la unidad obtengo BindException cuando inicio mi servidor (no puede ser que asigne el mismo puerto a dos servidores, ya que también uso bloqueos de archivos para la exclusión mutua) . Resultó que la razón es que para la verificación del puerto abro el puerto, luego lo cierro y espera un tiempo en el nivel del sistema operativo hasta que se pueda volver a abrir el puerto.

Sin embargo, hay una opción ( StandardSocketOptions.SO_REUSEADDR ) que puede indicar el socket Java, que un socket antiguo en estado TIMED_WAIT puede reutilizarse. Después de verificar el código de ZooKeeper, se establece en verdadero (ver 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); }

Mi prueba, sin embargo, demuestra que no funciona. Obtengo BindException (bajo Linux JDK 1.7.0_60).

Después de verificar la implementación de ServerSocketChannel (JDK 1.7.0_60) me di cuenta de que esto NUNCA funciona bajo Linux. Consulte 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; } }

Lamentablemente, Net.useExclusiveBind() nunca devolverá true bajo Linux, si comprueba su fuente en el OpenJDK , que es algo similar , depende de Net.isExclusiveBindAvailable() , que es -1 en Linux.

¿Tienes una solución?

¿Hay alguna manera en Java de esperar hasta que un puerto esté cerrado de forma nativa aparte de abrir un ServerSocket sin SO_REUSEADDR y verificar si obtengo BindException ? Por supuesto que no es una solución, desde entonces tengo que cerrar ese ServerSocket nuevamente. ¿Por qué no hay nada en Java como cerrar un socket en modo de bloqueo, que volvería solo cuando el socket esté realmente en el nivel del sistema operativo cerrado?