Sie können die Geschichte überspringen, wenn Sie möchten. Trotzdem könnte es einige interessieren.
Ich habe einen eingebetteten ZooKeeper Server in Java. In den Unit Tests weise ich den Testservern dynamisch Ports zu. Bevor ich den Port zulasse, überprüfe ich, ob er ungenutzt ist, indem ich ein ServerSocket
öffne und dann schließe.
Es kommt von Zeit zu Zeit vor, dass ich in den Komponententests BindException
bekomme, wenn ich meinen Server starte (es kann nicht sein, dass ich den gleichen Port zwei Servern zuweiße, da ich auch Dateisperren für gegenseitiger Ausschluss) . Es stellte sich heraus, dass der Grund dafür ist, dass ich für den Port-Check den Port öffne, dann schließe ich ihn und es wartet eine Weile auf Betriebssystemebene, bis der Port wieder geöffnet werden kann.
Es gibt jedoch eine Option ( StandardSocketOptions.SO_REUSEADDR ), die für den Java-Socket feststellen können, dass ein alter Socket im TIMED_WAIT-Zustand wiederverwendet werden kann. Nach der Überprüfung des ZooKeeper-Codes ist dieser Wert tatsächlich auf "true" gesetzt (siehe org.apache.zookeeper.server.NIOServerCnxnFactory.configure (InetSocketAddress, int) ):
%Vor%Mein Test zeigt jedoch, dass es nicht funktioniert. Ich bekomme BindException (unter Linux JDK 1.7.0_60).
Nach dem Überprüfen der ServerSocketChannel-Implementierung (JDK 1.7.0_60) habe ich festgestellt, dass dies NIEMALS unter Linux funktioniert. Siehe sun.nio.ch.ServerSocketChannelImpl.setOption(SocketOption<T>, T)
:
Leider wird Net.useExclusiveBind()
niemals unter Linux zurückgeben, wenn Sie die Quelle in der hoffentlich ähnlichen OpenJDK hängt es von Net.isExclusiveBindAvailable()
ab, das ist -1 unter Linux.
Gibt es einen Weg in Java zu warten, bis ein Port wirklich nativ geschlossen ist, abgesehen davon, dass ich eine ServerSocket
ohne SO_REUSEADDR öffne und überprüfe, ob ich BindException
bekomme? Das ist natürlich keine Lösung, seitdem muss ich den ServerSocket wieder schließen.
Warum gibt es in Java nichts, als würde man einen Socket im Sperrmodus schließen, der nur zurückkehrt, wenn der Socket wirklich auf Betriebssystemebene geschlossen ist?
Sie haben einen logischen Fehler gemacht. Wenn Sie den Socket in Java schließen, wird der Dateideskriptor auf dem Betriebssystemlayer geschlossen. Aber Ihr Problem ist, seit Sie ACCEPT Auf dem Socket kann der Port durch wartende Verbindungen blockiert werden. Also wolltest du sie zuerst schließen. Besser, Sie erstellen einen Client-Socket und prüfen, ob Sie ihn an den ausgewählten Port binden können.
Tags und Links java sockets linux tcp apache-zookeeper