2

我正在使用 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)

创建套接字的代码:

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

}
4

2 回答 2

0

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

于 2012-08-30T22:55:36.717 回答
0

客户端套接字可能仍然处于 inCLOSE_WAITTIME_WAITstatus。操作系统确保在另一个应用程序可以重新使用之前,所有数据都已通过套接字传递。否则,新客户端可能会从先前的连接中获得垃圾重复数据包。

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

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

引用自维基百科

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

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

于 2012-08-30T23:29:31.147 回答