Home > database >  How can I catch the SocketTimeoutException "raised" in setSoTimeout()?
How can I catch the SocketTimeoutException "raised" in setSoTimeout()?

Time:10-20

I am doing socket programming in Java and I am confused by setSoTimeout() and its exceptions.

I have a simple Callable<String> on the server-side for accepting connections:

private Callable<String> waitForConnectionCallable = new Callable<String>() {

    @Override
    public String call() throws IOException {
        if (socket == null) {
            socket = new ServerSocket(PORT);
        }

        socket.setSoTimeout(CONNECTION_TIMEOUT);
        Socket inSocket = socket.accept();
        
        return "CONNECTED";
    }
};

And I call and handle it in this way:

    try {
        Future<String> connectionFuture = executorService.submit(waitForConnectionCallable);
        String connectionResult = connectionFuture.get();
    } catch (ExecutionException | InterruptedException e) {
        Logs.error(TAG, "No connection received");
        e.printStackTrace();
    }

Now, the java doc for setSoTimeout() mentions raising a SocketTimeoutException, though strangely it is not included in the Throws list:

Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. With this option set to a positive timeout value, a call to accept() for this ServerSocket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the ServerSocket is still valid. A timeout of zero is interpreted as an infinite timeout. The option must be enabled prior to entering the blocking operation to have effect.

Params: timeout – the specified timeout, in milliseconds

Throws: SocketException – if there is an error in the underlying protocol, such as a TCP error IllegalArgumentException – if timeout is negative

When I run the program and the connection times out, I get the SocketTimeoutException in the catch block, but in this way:

java.util.concurrent.ExecutionException: java.net.SocketTimeoutException: Accept timed out

My question is, how can I catch that specific exception? I can't add it to my caller's catch statement, because I get the compile error of it not being thrown in the method. Also, I can't add to the call() method (in the Callable), since IOException, a more general exception is already there.

Additionally: why SocketTimeoutException is not part of the setSoTimeout() signature? Considering it is not a subclass of SocketException.

CodePudding user response:

You are mixing a few things: the SocketTimeoutException is thrown by socket.accept(). socket.setSoTimeout() can throw an IOException (though it's very seldom).

When the SocketTimeoutException is thrown inside your call() method (by socket.accept()), it will be wrapped inside an ExecutionException, which is a wrapper-exception. You can then access the original exception by calling e.getCause() and handle it accordingly.

CodePudding user response:

There are few things to correct in your observations

Now, the java doc for setSoTimeout() mentions raising a SocketTimeoutException, though strangely it is not included in the Throws list

No, it says

Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. With this option set to a non-zero timeout, a call to accept() for this ServerSocket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the ServerSocket is still valid. The option must be enabled prior to entering the blocking operation to have effect. The timeout must be > 0. A timeout of zero is interpreted as an infinite timeout.

Which means that, if you set a timeout using the setSoTimeout(), a SocketTimeoutException will be thrown, if the timeout happens while accepting the connection. So SocketTimeoutException is not thrown by setSoTimeout(), hence its not in its throws list.

You can catch the timeout exception in the accept() method. It throws an IOException if something goes wrong in accepting the connection. Timeout can be one of the reasons.

Like below:

private Callable<String> waitForConnectionCallable = new Callable<String>() {

        @Override
        public String call() throws IOException {
            if (socket == null) {
                socket = new ServerSocket(PORT);
            }

            socket.setSoTimeout(CONNECTION_TIMEOUT);

            try {
                Socket inSocket = socket.accept();
            } catch (SocketTimeoutException e) {
                // do timeout handling...
                return "TIMEOUT";
            }

            return "CONNECTED";
        }
    };
  • Related