【问题标题】:"java.net.BindException: Address already in use" when trying to create same socket with new thread as previous one used“java.net.BindException:地址已在使用中”尝试使用新线程创建与前一个线程相同的套接字时
【发布时间】:2012-08-25 18:12:28
【问题描述】:

我正在使用 Java 进行客户端-服务器模拟,其中客户端(线程)连接到服务器以获取一些数据。几秒钟后,需要杀死随机选择的客户端(线程)之一。我关闭了它用于与服务器通信的套接字并让他死了(通过退出 run() 方法)。问题是当新创建的线程试图创建与以前使用的相同的套接字(相同的地址和相同的端口)连接到服务器时,我得到:

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)

创建socket的代码:

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--;

}

【问题讨论】:

  • 连接时不必指定客户端 ip 和客户端端口。如果省略它们,它将选择一个可用端口
  • 你能发布你的run()方法的代码吗?
  • 我需要使用它们,因为客户端在 Ubuntu 中的 VM 和 Windows 7 中的服务器上运行,我需要指定客户端端口和 IP 用于其他目的。
  • “其他用途”例如?
  • 可能你的程序异常关闭,Socket 仍然绑定在进程上。操作系统将在几分钟后启动该进程。你可以通过 ServerSocket.setReuseAddress(true) 避免这种情况

标签: java multithreading sockets networking tcp-ip


【解决方案1】:

客户端套接字可能仍然在CLOSE_WAIT or TIME_WAIT status 中。操作系统确保在另一个应用程序可以重新使用之前,所有数据都已通过套接字传递。否则,新客户端可能会收到上一个连接留下的垃圾重复数据包。

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

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

引用自wikipedia

关闭等待: 服务器从本地应用程序接收到它已完成的通知。服务器将其 fin 发送给客户端。

时间等待: 表示等待足够的时间以确保远程对等方收到其连接终止请求的确认。根据 RFC 793,连接可以在 TIME-WAIT 中停留最多四分钟,称为 MSL(最大分段生命周期)。

【讨论】:

  • 他完全正确。为什么他越来越不喜欢了?
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2016-06-13
    • 1970-01-01
    • 2011-06-10
    • 2018-04-17
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 2015-10-30
    • 2014-08-11
    相关资源
    最近更新 更多