Hooli
Hooli

Reputation: 1195

new ObjectInputStream causing hang/timeout

I know that this question has been asked several times already. However, after following the advise in all the other questions I am still stuck as to what the problem might be.

I have a Server and a Client. A simple ping/pong program. After running the server and then the client and giving it some time to run its course, timeout exceptions start being thrown every now and then...

The timeout is there to prevent a block, however, if removed, it would cause the program to stall.

Is there a way to prevent this from occurring?

Server.java

public static void main(String args[]) {
        try {
            ServerSocket serverSocket = new ServerSocket(3000);
            while (true) {
                Socket socket = serverSocket.accept();
                String message = null;
                try {
                    socket.setSoTimeout(3000);
                    try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
                        message = (String) objectInputStream.readObject();
                        System.out.println("Server received " + message);
                    }
                    socket.close();
                } catch (IOException | ClassNotFoundException ex) {
                    //This exception is thrown because it hangs, but why does it hang?
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
                if ((message != null) && (message.equals("Ping"))) {
                    try {
                        Socket pongSocket = new Socket("localhost", 3000);
                        pongSocket.setSoTimeout(3000);
                        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(pongSocket.getOutputStream())) {
                            objectOutputStream.writeObject("Pong");
                            objectOutputStream.flush();
                            System.out.println("Server sent Pong");
                        }
                        pongSocket.close();
                        continue;
                    } catch (IOException ex) {
                        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

Client.java

public static void main(String args[]) {
        while (true) {
            try {
                Socket pingSocket = new Socket("localhost", 3000);
                pingSocket.setSoTimeout(3000);
                try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(pingSocket.getOutputStream())) {
                    objectOutputStream.writeObject("Ping");
                    System.out.println("Client sent Ping");
                    objectOutputStream.flush();
                }
                pingSocket.close();
            } catch (IOException ex) {
                Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

Upvotes: 0

Views: 970

Answers (2)

blm
blm

Reputation: 2446

You're confused about how servers and clients work with sockets. You can probably find a bunch of examples on stackoverflow and even more via Google, but the general idiom is:

server:
    create server socket
    call accept on server socket
    with accepted socket
        read request from socket
        write response to socket
        close accepted socket
        loop back to accept

client:
    create socket
    call connect on socket
    write request to socket
    read response from socket
    close socket

(Java automatically does some of those for you, for example, when creating a socket and specifying a host and port, the Socket class calls connect for you.)

In your server, you're closing the accepted socket after reading the request, then creating and connecting to a new socket to send the response, which is going to send the response to whatever's listening on localhost:3000, which is your server. Also, in your client you're writing the request but not reading the response, and doing so in a tight loop, so you're creating a lot of connections to your server, which will quickly fill the accept backlog.

Real, production applications would use threading in the server, or even use higher level libraries or even entire servers like Tomcat, but at the bottom, they're all basically doing the above, so it's good to understand that.

To demonstrate, this is what your code should look like:

Server.java

public static void main(String args[]) {
    try {
        ServerSocket serverSocket = new ServerSocket(3000);
        while (true) {
            Socket socket = serverSocket.accept();
            socket.setSoTimeout(250);
            String message = null;
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            try {
                message = (String) objectInputStream.readObject();
                System.out.println("server read: " + message);
            } catch (IOException | ClassNotFoundException ex) {
                Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            }
            if ((message != null) && (message.equals("Ping"))) {
                try {
                    objectOutputStream.writeObject("Pong");
                    System.out.println("server sent pong");
                } catch (IOException ex) {
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            objectInputStream.close();
            objectOutputStream.close();
            socket.close();
        }
    } catch (IOException ex) {
        Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
    }
}

Client.java

public static void main(String args[]) {
    while (true) {
        try {
            Socket socket = new Socket("localhost", 3000);
            String message;
            socket.setSoTimeout(250);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject("Ping");
            System.out.println("client sent ping");
            try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
                message = (String) objectInputStream.readObject();
                System.out.println("client read: " + message);
            }
            objectOutputStream.close();
            socket.close();
        } catch (IOException | ClassNotFoundException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException ex) {
            Logger.getLogger(Client.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Upvotes: 2

jgitter
jgitter

Reputation: 3414

You have nothing throttling the speed of your senders. Judging at a glance, I would say you are simply overwhelming the server by sending requests faster than they can be processed. Your "server" listening on the socket can't compete with the non-throttled "client".

Upvotes: 0

Related Questions