Multiple sockets cause ConnectException: Connectio

2019-05-18 03:59发布

问题:

I am programming java in Eclipse on Windows XP. I have a multiprocess simulation which uses ProcessBuilder to run a server and two clients. The server starts a thread to listen to two different sockets - one for each client. I can comment out the code for each client and have the other work perfectly fine. When I try to run them both, one client will always error with ConnectException: Connection refused: connect. Which client it is seems to be whichever is running slower, though it's hard to say. I can pause after starting the server but before the clients, and netstat verifies that both sockets are active. What could be causing this? I have some simplified code below.

Update: Based on comments, I have edited the code to multithread the server on a single socket, but I am still having the same problem. The code below reflects the changes. It appears that the socket is being opened and closed by one client before the other has a chance to open it. I can throw pause statements at the end of each client which allows the other to finish, but this is a fix, not a solution. So now the real question is, how do I keep the ServerSocket listening until I instruct it to close?

server

try{
    server = new ServerSocket(sockNum); 
} catch (IOException e) {
        System.out.printf("Could not listen on port %d\n",sockNum);
        System.exit(-1);
}
while(true){
    ClientWorker w;
    try{
        Socket connection = server.accept();
        w = new ClientWorker(connection);
        Thread t = new Thread(w);
        t.start();
    } catch (IOException e) {
        System.out.printf("Accept failed: %d\n",sockNum);
        System.exit(-1);
    }
}

class ClientWorker implements Runnable {
    private Socket client;

          ClientWorker(Socket client) {
           this.client = client;
          }

          public void run(){
            Object line;
            ObjectInputStream in = null;
            PrintWriter out = null;
            try{
              in = new ObjectInputStream(client.getInputStream());
              out = new PrintWriter(client.getOutputStream(), true);
            } catch (IOException e) {
              System.out.println("in or out failed");
              System.exit(-1);
            }

            while(true){
                try{
                    line = in.readObject();
                    //Send data back to client
                    out.println("Sent from broker");
                    if(line instanceof String)
                        System.out.println(line);
                    else
                        System.out.println(line.getClass());                        
                } catch (IOException e) {
                    System.out.println("Read failed");
                    System.exit(-1);
                } catch (ClassNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

clients

 try{
    socket = new Socket("localhost", socketNum);
    out = new ObjectOutputStream(socket.getOutputStream());
    in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    out.writeObject(message);
    String line = in.readLine();
    System.out.println(line);
    } catch (UnknownHostException e) {
        System.out.println("Unknown host: localhost.eng");
        System.exit(1);
    } catch  (IOException e) {
        System.out.println("No I/O");
        System.exit(1);
    }

Controller

ProcessBuilder server = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ServerProcess");
server.redirectErrorStream(true);
Process runServer = server.start();

ProcessBuilder clientA = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientAProcess");
clientA.redirectErrorStream(true);
Process runClientA = clientA.start();

ProcessBuilder clientB = new ProcessBuilder("java.exe","-Xss64m","-cp","bin;jscheme.jar","ClientBProcess");
clientB.redirectErrorStream(true);
Process runClientB = clientB.start();

回答1:

'The server starts a thread to listen to two different sockets - one for each client.'

No, please don't do this! It's abnormal design to assign a separate listening socket for each client. Just start one listening thread to which both clients connect. When accept() returns, start a read thread for that client, passing the ServerClient Socket returned by the accept() call.



回答2:

The usual way to write a server is to listen on a single socket, in a loop.

For each incoming connection, spawn off a thread to handle the client, then immediately do a listen again on the same server socket.

In your case, if two clients attempt to connect to the same server port, the second will ALWAYS get "connection refused". The first client grabs the connection and the server doesn't reissue a listen on the socket. You haven't shown how the clients get their port number, but I'm guessing they're both trying to connect to the same port.



回答3:

Your code, as I write this, looks good to me. You have one ServerSocket which listens for new connections. Each connection, when it happens, sets up a new thread to handle it and gives it its very own Socket. Your Socket s, and their threads, should be completely independent. The fate of each should be completely independent of the other. They should not know of each other's existence.

Figure out how they are interacting and separate them.

One thing to try is set up 100 clients instead of 2. It might make the problem more obvious; a ton of TNT will leave a bigger crater than a firecracker.

One other possible problem: be sure you are resetting your ObjectOutputStream regularly. This sort of I/O is real handy, but it can do very strange things. Without resets, if you send the same object twice, the second time it will only send a reference to the first version, which is useless if the latest version has changed. Also, even with resets, if you send an object that references another, that object will be sent down the line also. (This can be very nice. You can send a single object that references a vast structure involving millions of objects, and the whole thing will be sent and retain its organization. It's ingenious and really useful, if you know what its doing.)



回答4:

From OP

So in the process of addresses what I thought to be an unrelated issue, I seem to have fixed the initial problem. There are two primary things that I added. First, I set up the clients to send an "end" command when complete, so that the socket would not block when trying to readObject(). Second, (and I think this is what fixed it), I defined a timeout value for the ServerSocket, rather than leaving it undefined (aka infinite). When I went back to clean up my code after getting things working, I tried removing the pauses in the client processes, and things still worked! My guess is that with no defined timeout, when a single socket closed before the other was opened, the ServerSocket closed as well. With a timeout defined, the ServerSocket will wait until timed out before closing. (This is just a guess though.)

Here is the current working code. Feel free to comment if you think there's a different cause for the fix.

server

try{
    server = new ServerSocket(sockNum);
    server.setSoTimeout(timeOut);
} catch (IOException e) {
    System.out.printf("Could not listen on port %d\n",sockNum);
    System.exit(-1);
}

while(isActive){
    ClientWorker w;
    try{
        Socket connection = server.accept();
        w = new ClientWorker(connection);
        Thread t = new Thread(w);
        t.start();
    } catch (IOException e) {
        if(e instanceof SocketTimeoutException)
            isActive = false;
        else{
            System.out.printf("Accept failed: %d\n",sockNum);
            System.exit(-1);
        }
    }
}

 class ClientWorker implements Runnable {
        private Socket client;
        private Source source = Source.NONE;
        private String sourceName = "?";
        private Boolean threadActive = true;

        ClientWorker(Socket client) {
            this.client = client;
        }

        public void run(){
            Object line;
            ObjectInputStream in = null;
            PrintWriter out = null;
            try{
                in = new ObjectInputStream(client.getInputStream());
                out = new PrintWriter(client.getOutputStream(), true);
            } catch (IOException e) {
                System.out.println("in or out failed");
                System.exit(-1);
            }

            while(threadActive){
                try{
                    line = in.readObject();
                    if(line instanceof String){
                        if(line.equals("end")){
                            threadActive = false;
                        }else
                            sourceName = line;
                        }
                    } else 
                        handleData;
                } catch (IOException e) {
                    System.out.println("Read failed");
                    threadActive = false;
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                    threadActive = false;
                }
            }
            try {
                this.client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }