“java.net.BindException:地址已经在使用”当试图创建与上一个使用新线程相同的套接字

“java.net.BindException:地址已经在使用”当试图创建与上一个使用新线程相同的套接字

问题描述:

我正在Java客户端(线程)连接到服务器以获取Java一些数据。几秒钟后,随机选择的一个客户端(线程)需要被杀死。我关闭了用于与服务器通信的套接字,并让他死亡(通过从run()方法退出)。问题是,当新创建的线程试图创建相同的插座上一个使用(同一地址和同一端口)连接到服务器,我得到:“java.net.BindException:地址已经在使用”当试图创建与上一个使用新线程相同的套接字

java.net.BindException: Address already in use 
    at java.net.PlainSocketImpl.socketBind(Native Method) 
    at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:374) 
    at java.net.Socket.bind(Socket.java:627) 
    at java.net.Socket.<init>(Socket.java:423) 
    at java.net.Socket.<init>(Socket.java:319) 

代码创建套接字:

private void createNewSocket(InetAddress sIP, int sPort, 
     InetAddress cIP, int cPort) { 
    try { 
     socket = new Socket(sIP, sPort, cIP, cPort); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     System.err.println("Socket unsuccessfully created"); 
    } 
    try { 
     in = new BufferedReader(new InputStreamReader(
       socket.getInputStream())); 

     out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
       socket.getOutputStream())), true); 

    } catch (IOException e) { 
     e.printStackTrace();  
     try { 
      socket.close(); 
     } catch (IOException e2) { 
      System.err.println("Socket unsuccessfully closed"); 
     } 
    } 
} 

public void run() { 

    createNewSocket(gprsServerIP, Util.PORT_SERVER_PORT, 
      clientIP, sendPort); 

    out.println(REQUEST); 
    try { 
     serverPort = Integer.parseInt(in.readLine()); 
     TCPClient.serverPort[clientID] = serverPort; 
     System.out.println("Server port: " + serverPort + '\n' + 
          "Send port: " + sendPort + '\n'); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } finally { 
     try { 
      socket.close(); 
     } catch (IOException e) { 
      System.err.println("Socket unsuccessfully closed"); 
     } 
    } 

    while (true) {  

     if (clientID == TCPClient.selectedID) { 

      TCPClient.selectedID = -1; 

      createNewSocket(gprsServerIP, Util.PORT_SERVER_PORT, 
        clientIP, sendPort); 

      out.println(FREE_PORT + serverPort); 
      try { 
       socket.close(); 
      } catch (IOException e2) { 
       System.err.println("Socket unsuccessfully closed"); 
      } 
      //System.out.println(socket.isClosed()); 
      System.out.println("Port:" + serverPort + " is free"); 
      TCPClient.id[clientID] = -1; 
      break;    
     }       
    } 
    clientCount--; 

} 
+1

连接时,您不必指定客户端IP和客户端端口。如果您忽略它们,它会选择一个可用的端口 – Patrick

+0

您可以发布代码以用于run()方法吗? –

+0

我需要使用它们,因为客户端从Ubuntu上的VM和Windows 7上的服务器上运行,并且我需要指定客户端端口和IP用于其他用途。 – Renton

有可能是客户端套接字仍在CLOSE_WAIT or TIME_WAIT status之内。在另一个应用程序可以重新使用它之前,操作系统正在确保所有数据已经​​传递到套接字中。否则,新客户端可能会从上一次连接中得到垃圾重复数据包。

我建议你的客户使用一系列端口而不是常数。然后他们可以使用范围中的下一个端口,并在到达范围的末尾时循环。

但是,如果您不需要设置客户端端口,则不应通过将端口号0传递给Socket来在您的代码中设置客户端端口。在这种情况下,JDK和操作系统会做正确的事情并为您选择合适的自由端口。

wikipedia引:

CLOSE-WAIT: 服务器从它完成本地应用程序接收通知。服务器将它的fin发送给客户端。

TIME-WAIT: 表示等待足够的时间以确保远程节点收到其连接终止请求的确认。根据RFC 793,连接可以保持在TIME-WAIT中最多四分钟,即MSL(最大段寿命)。

+0

感谢您的回答!我会考虑有关端口范围的选项.. – Renton

+0

嗯,在维基百科文章中的几个错误,修复... – EJP

+0

他是完全正确的。他为什么不喜欢? –

当进程不自然地死亡时,有时端口不会立即解除绑定,您必须等待几分钟才能使端口可用。如果可能的话,你的程序应该优雅地失败(即使它必须被终止,关闭钩子应该关闭套接字)。

+0

来避免这种情况,谢谢你的回复!我感谢您的帮助! – Renton