tags:

views:

1029

answers:

4

I'm reading from a Java Socket in a loop.

I want to re-open the socket if a read throws an exception.

In an attempt to test that, I simply close the socket and catch the exception.

The catch is, when I attempt to re-open the socket:

serverSocket = new ServerSocket(port)
socket = serverSocket.accept()

it throws an exception:

java.net.BindException: The socket name is already in use

That's an immediate problem for testing. But it might also occur in production.

How can I reliably recover, by opening a new socket connection, after an exception has been thrown?

A: 

you should close the socket when an exception was thrown during a conenction session.

Markus Lausberg
+3  A: 

I'm sorry if I'm misunderstanding what you're saying.

If a read() method on a socket fails, this will not close the ServerSocket.

You should still be able to call serverSocket.accept() without creating a new one. I think that's why you're getting the BindException.

Phill Sacre
+2  A: 

What you need is to create the ServerSocket once, and pass any accepted socket to its handler - usually done in a different thread.

The error you get is because you are trying to listen on the same port twice, which is impossible.

This article may help you. Here is one of the code samples:

public void await() {
    ServerSocket serverSocket = null;
    int port = 8080;
    try {
        serverSocket =  new ServerSocket(port, 1,
        InetAddress.getByName("127.0.0.1"));
    }
    catch (IOException e) {
        e.printStackTrace();
        System.exit(1);
    }

    // Loop waiting for a request
    while (!shutdown) {
        Socket socket = null;
        InputStream input = null;
        OutputStream output = null;
        try {
            socket = serverSocket.accept();
            input = socket.getInputStream();
            output = socket.getOutputStream();

            // create Request object and parse
            Request request = new Request(input);
            request.parse();

            // create Response object
            Response response = new Response(output);
            response.setRequest(request);
            response.sendStaticResource();

            // Close the socket
            socket.close();

            //check if the previous URI is a shutdown command
            shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
        }
        catch (Exception e) {
            e.printStackTrace();
            continue;
        }
    }
}
David Rabinowitz
A: 

What I found to work was this:

  1. Close the ServerSocket
  2. Create a new ServerSocket

No delay was required after closing the ServerSocket. That's on an IBM AIX machine -- perhaps the behaviour is different on other platforms.

There was no need to close the Socket.

I also added a timeout to the Socket to recover from the other end of the connection failing. Here's the code that runs on startup and on exception:

if (serverSocket != null) serverSocket.close();
serverSocket = new ServerSocket(port);
socket = serverSocket.accept();
socket.setSoTimeout(TIMEOUT);
is = socket.getInputStream();

Thanks also for the tip to close the ServerSocket. That got me on the right track. And also for the useful network programming references.

I'm confused...why would you ever need to close the ServerSocket instead of the client Socket? The client side should be the one to initiate the reconnect, and you shouldn't ever have to close/reopen the ServerSocket. If you're uploading a large amount of data and need to resume, I'd just have the server transmit a session ID to the client upon accepting a "new" connection. After the connection is broken, the server should store the current state of the session, then when the client tries to reconnect, it transmits the previous session ID upon opening a "resume" connection.
rob