Java中非常奇怪的套接字行为; 不总是关闭端口?

gdu*_*002 5 java sockets clojure

在一个大型项目的开发过程中,我们已经积累了大量的单元测试.许多这些测试通常在同一个进程中启动服务器,连接到这些服务器并关闭服务器和客户端.

但是,这些测试随机失败,并显示"无法绑定地址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
    ))
  )
Run Code Online (Sandbox Code Playgroud)

在我们启动第二台服务器的行上随机出现该异常.似乎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
      )))
  )
Run Code Online (Sandbox Code Playgroud)

我还注意到10毫秒甚至100毫秒的睡眠并不能解决这个问题.然而,1000毫秒(到目前为止)已设法阻止它.

编辑2:有人把我放到了SO_LINGER上 - 但我找不到在ServerSockets上设置它的方法.有人有任何想法吗?

编辑3:默认情况下,SO_LINGER被禁用.我们还能看到什么?

更新:问题已经解决了大部分,使用10,000个左右端口的动态端口分配.但是,我仍然希望看到人们可以想出什么.

and*_*dri 3

我不太熟悉 Clojure 语法,但您应该调用socket.setReuseAddr(true). 这允许程序重用端口,即使可能存在处于 TIME_WAIT 状态的套接字。