在一个大型项目的开发过程中,我们已经积累了大量的单元测试。许多这些测试通常在同一个过程中启动服务器,连接到这些服务器并关闭服务器和客户端。
但是,这些测试随机失败,并显示“无法绑定地址127.0.0.1:(port)”。重新运行测试时,错误通常会消失。
现在,我们认为这是我们测试的问题,但我们决定在Clojure中编写一个小测试,我将在下面发布(并评论非Clojure人员)。
(ns test
(:import [java.net Socket ServerSocket]))
(dotimes [n 10000] ; Run the test ten thousand times
(let [server (ServerSocket. 10000) ; Start a server on port 10000
client (Socket. "localhost" 10000) ; Start a client on port 10000
p (.getLocalPort client)] ; Get the local port of the client
(.close client) ; Close the client
(.close server) ; Close the server
(println "n = " n) ; Debug
(println "p = " p) ; Debug
(println "client = " client) ; Debug
(println "server = " server) ; Debug
(let [server (ServerSocket. p)] ; Start a server on the local port of the client we just closed
(.close server) ; Close the server
(println "client = " client) ; Debug
(println "server = " server) ; Debug
))
)
异常出现在我们启动第二台服务器的行上。似乎Java正在保留本地端口 - 即使该端口上的客户端已经关闭。
所以,我的问题:为什么Java会这样做,为什么它看似随机?
编辑:有人建议我将套接字的reuseAddr设置为true。我已经这样做了,没有任何改变,所以这是下面的代码。
(ns test
(:import [java.net Socket ServerSocket InetSocketAddress]))
(dotimes [n 10000] ; Run the test ten thousand times
(let [server (ServerSocket. )] ; Create a server socket
(. server (setReuseAddress true)) ; Set the socket to reuse address
(. server (bind (InetSocketAddress. 10000))) ; Bind the socket
(let [client (Socket. "localhost" 10000) ; Start a client on port 10000
p (.getLocalPort client)] ; Get the client's local port
(.close client) ; Close the client
(.close server) ; Close the server
; (. Thread (sleep 1000)) ; A sleep for testing
(println "n = " n) ; Debug
(println "p = " p) ; Debug
(println "client = " client) ; Debug
(println "server = " server) ; Debug
(let [server (ServerSocket. )] ; Create a server socket
(. server (setReuseAddress true)) ; Set the socket to reuse address
(. server (bind (InetSocketAddress. p))) ; Bind the socket to the local port of the client we just had
(.close server) ; Close the server
(println "client = " client) ; Debug
(println "server = " server) ; Debug
)))
)
我也注意到10毫秒甚至100毫秒的睡眠并不能解决问题。然而,1000毫秒(到目前为止)设法阻止了它。
编辑2 :有人让我加入SO_LINGER - 但我找不到在ServerSockets上设置它的方法。有人对此有任何想法吗?
编辑3 :原来默认情况下禁用SO_LINGER。我们还能看到什么?
更新:问题已在大多数情况下解决,使用10,000个左右端口的动态端口分配。但是,我仍然希望看到人们能想出什么。
答案 0 :(得分:3)
我不是(也)使用Clojure语法,但你应该调用socket.setReuseAddr(true)
。这允许程序重用端口,即使可能存在TIME_WAIT状态的套接字。
答案 1 :(得分:2)
测试本身无效。测试这种行为毫无意义,并且与任何所需的应用程序行为无关:它只是在TCP堆栈中运行一个角落条件,当然没有应用程序应该尝试依赖它。我希望在刚出现连接端口的端口上打开一个侦听套接字会因为TIME_WAIT而永远不会成功,或者由于关于哪一端发布的不确定性,最多只能成功一半时间关闭第一。
我会删除测试。其余部分也没有做任何有用的事,
答案 2 :(得分:1)
您可以在服务器套接字上尝试setReuseAddress(true)
。
如果同一端口上的另一个套接字在关闭后处于TIME_WAIT状态,则此标志将允许套接字绑定到该端口。