10

I have a GUI with a list of servers to connect to. If a user clicks a server it connects to it. If a user clicks a second server, it will disconnect the first and connect to the second. Each new connection runs in a new thread so that the program can perform other tasks.

However, if a user clicks a second server while the first is still connecting, there are two simultaneous connections.

I'm connecting using this, and connect() is the line that blocks:

Socket socket = new Socket();
socket.connect(socketAddress, connectTimeout);

I thought maybe Thread.currentThread().interrupt(); would work, but didn't.

Do I have to restructure my code a bit so that it continues making the first connection, but closes it straight after? Or is there actually a way to interrupt the connect method.

Matt
  • 11,157
  • 26
  • 81
  • 110

4 Answers4

4

If you are using a blocking socket implementation, interrupting the thread won't 'cancel' or interrupt your socket connection. The only way of breaking out of the 'blocking call' is to 'close' the socket. You can expose a method on your Runnable tasks (e.g. cancel) which close the socket and clean up the resources when the user tries connecting to a second server.

If you want you can have a look at my throwaway attempt at interrupting threads which make blocking calls.

Sanjay T. Sharma
  • 22,857
  • 4
  • 59
  • 71
  • When I try socket.close(), it says that socket is null.. but it should be a valid (but unconnected) Socket object. I'm calling close() in the same Runnable class inside a method called disconnect(), which is called by another thread, is this why? Does close() have to be called on the socket directly from a different thread? – Matt Mar 22 '11 at 17:43
  • @Matt: It would be difficult to tell without having a look at the code but just wanted to confirm; is the socket variable and instance variable or a method scoped one? Are you sure the `socket` attribute of the Runnable task is non-null? If yes, then can you try making the socket variable `volatile` in the task object and try again? Does it still fail? – Sanjay T. Sharma Mar 22 '11 at 17:58
2

I tried the suggested answers but nothing worked for me. So what I did was, instead of setting my connection timeout to 10 seconds I try to connect 5 times in a row with a connection timeout of 2 seconds. I also have a global variable boolean cancelConnection declared.

Every time a timeout exception is thrown, I can eather break out of or continue the loop based on the value of cancelConnection.

Here's a code snippet from an android app I'm writing:

try {
    SocketAddress socketaddres = new InetSocketAddress(server.ip,server.port);
    int max=5;
    for (int i = 1; i<=max; i++) {
        try {
            socket = new Socket();
            socket.connect(socketaddres, 2000);
            break;
        } catch (Exception e) {
            Log.d(TAG, "attempt "+i+ " failed");
            if (cancelConnection) {
                Log.d(TAG, "cancelling connection");
                throw new Exception();
            } else if (i==max) {
                throw new Exception();
            }
        }
    }
} catch (Exception e) {
    if (cancelConnection) {
            // Do whatever you would do after connection was canceled.
    } else {
            // Do whatever you would do after connection error or timeout
    }
}
Jules Colle
  • 11,227
  • 8
  • 60
  • 67
2

Can you instead use a non-blocking socket? I'm not much of a Java expert, but it looks like SocketChannel is their non-blocking socket class.

Here is an example:

// Create a non-blocking socket and check for connections
try {
    // Create a non-blocking socket channel on port 80
    SocketChannel sChannel = createSocketChannel("hostname.com", 80);

    // Before the socket is usable, the connection must be completed
    // by calling finishConnect(), which is non-blocking
    while (!sChannel.finishConnect()) {
        // Do something else
    }
    // Socket channel is now ready to use
} catch (IOException e) {
}

Taken from here: http://www.exampledepot.com/egs/java.nio/NbClientSocket.html

Inside the while loop you can check for some shared notification that you need to be cancelled and bail out, closing the SocketChannel as you go.

Luke
  • 3,742
  • 4
  • 31
  • 50
0

You can use something like this construction:

ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {
  public Thread newThread(Runnable r) {
    Thread t = Executors.defaultThreadFactory().newThread(r);
    t.setDaemon(true);
    return t;
  }
});

Future<Socket> res = es.submit(() -> {
  try {
    return new Socket(addr, port);
  } catch (Exception ex) {
    logger.error("Error while connecting. " + ex.getMessage());
    return null;
  }
});

es.shutdown();

try {
  while (!res.isDone()) {
    Thread.sleep(5);
  }
} catch (InterruptedException iex) {
  logger.error("Connection interrupted.");
  return; 
}

Socket client = res.get();
                
Rusty Sage
  • 21
  • 1
  • 3