92

When I create a socket:

Socket socket = new Socket(ipAddress, port);

It throws an exception, which is OK, because the IP address is not available. (The test variables where String ipAddress = "192.168.0.3" and int port = 300.)

The problem is: how do I set it to timeout for that socket?

When I create the socket, how do I reduce the time before I get a UnknownHostException and get the socket to timeout?

Sachin Bankar
  • 372
  • 6
  • 13
Jennifer
  • 921
  • 1
  • 7
  • 3
  • 10
    @adrianboimvaser: In that case, you should flag as a duplicate, and leave a comment pointing to the duplicate, so a moderator can close it as such (you left that comment a year ago - perhaps you know that by now). I would cast my vote for doing that now, but i have no idea where the duplicate is! – Tom Anderson Mar 02 '12 at 16:18
  • 1
    To continue the trend of replying years late, @EJP the title of this question is much less ambiguous than the duplicate you suggested – Isaac Jan 10 '15 at 04:17
  • @Isaac And in fact it isn't a duplicate at all, comment retracted. – user207421 Sep 12 '15 at 00:12
  • (Note: I have tweaked the tittle to be less ambiguous.) – Stephen C Jan 15 '17 at 05:19

6 Answers6

176

Use the Socket() constructor, and connect(SocketAddress endpoint, int timeout) method instead.

In your case it would look something like:

Socket socket = new Socket();
socket.connect(new InetSocketAddress(ipAddress, port), 1000);

Quoting from the documentation

connect

public void connect(SocketAddress endpoint, int timeout) throws IOException

Connects this socket to the server with a specified timeout value. A timeout of zero is interpreted as an infinite timeout. The connection will then block until established or an error occurs.

Parameters:

endpoint - the SocketAddress
timeout - the timeout value to be used in milliseconds.

Throws:

IOException - if an error occurs during the connection
SocketTimeoutException - if timeout expires before connecting
IllegalBlockingModeException - if this socket has an associated channel, and the channel is in non-blocking mode
IllegalArgumentException - if endpoint is null or is a SocketAddress subclass not supported by this socket

Since: 1.4

aioobe
  • 413,195
  • 112
  • 811
  • 826
  • 6
    This can reduce the timeout for ConnectException (but not increase it), and it has no effect on the timeout for UnknownHostException, which the OP claims he is getting (although I don't believe it). Note also that the Javadoc is incorrect in stating that the default is infinity. It isn't. The platform has a default timeout of around a minute, and this cannot be increased. – user207421 May 19 '13 at 00:18
  • 3
    Even after 4 years this was helpful, this should be the accepted answer. – Sharp Edge Jan 15 '15 at 08:01
  • 4
    It is now after 5 years and it is the best answer i found on Internet .. Thank you – MBH Aug 02 '16 at 13:08
  • 1
    It's also important to call `socket.setSoTimeout(timeoutMillis)` for timeouts during blocking IO operations. – Gray Jun 05 '17 at 23:32
46

You don't set a timeout for the socket, you set a timeout for the operations you perform on that socket.

For example socket.connect(otherAddress, timeout)

Or socket.setSoTimeout(timeout) for setting a timeout on read() operations.

See: http://docs.oracle.com/javase/7/docs/api/java/net/Socket.html

payne
  • 13,833
  • 5
  • 42
  • 49
  • Well, in this case it is also an operation, connecting. I can not find any place that states that this ctor uses an infinite timeout although that seems to be the case. – sandos Aug 11 '11 at 13:15
  • 4
    @sandos It is clearly stated in the Javadoc for connect(). However it is also incorrect. The default timeout is the platform timeout, around a minute, not infinity, and it can only be reduced by this connect() method, not increased. – user207421 May 19 '13 at 00:23
  • 3
    note, `socket.setSoTimeout` must be called AFTER `connect`, or it will have no effect and reads will never time out. this is one poorly documented gotcha. – pstanton Sep 01 '16 at 05:18
20

You could use the following solution:

SocketAddress sockaddr = new InetSocketAddress(ip, port);
// Create your socket
Socket socket = new Socket();
// Connect with 10 s timeout
socket.connect(sockaddr, 10000);

Hope it helps!

A--C
  • 36,351
  • 10
  • 106
  • 92
reef
  • 1,813
  • 2
  • 23
  • 36
  • 1
    If, as he claims, he is getting an UnknownHostException, it will happen on the first line, where there is no modified timeout in effect. – user207421 May 18 '13 at 23:49
  • However, the first line doesn't throw any exception. From the documentation: "An attempt will be made to resolve the hostname into an InetAddress. If that attempt fails, the address will be flagged as unresolved" – ady Jul 20 '16 at 13:15
  • In which case it will be resolved by the `connect()` method, but not under control of a timeout. – user207421 Feb 03 '19 at 20:34
8

You can't control the timeout due to UnknownHostException. These are DNS timings. You can only control the connect timeout given a valid host. None of the preceding answers addresses this point correctly.

But I find it hard to believe that you are really getting an UnknownHostException when you specify an IP address rather than a hostname.

EDIT To control Java's DNS timeouts see this answer.

Community
  • 1
  • 1
user207421
  • 305,947
  • 44
  • 307
  • 483
8

Use the default constructor for Socket and then use the connect() method.

Sjoerd
  • 74,049
  • 16
  • 131
  • 175
1

One solution is to execute the DNS resolution on a different thread which is given only a certain amount of time to complete.

Here's a simple utility that can help you do this:

public class TimeSliceExecutor {

  public static class TimeSliceExecutorException extends RuntimeException {
    public TimeSliceExecutorException(String message, Throwable cause) {
      super(message, cause);
    }
  }

  public static void execute(Runnable runnable, long timeoutInMillis) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    try {
      Future<?> future = executor.submit(runnable);
      getFuture(future, timeoutInMillis);
    }
    finally {
      if (executor != null) {
        executor.shutdown();
      }
    }
  }

  public static <T> T execute(Callable<T> callable, long timeoutInMillis) {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    try {
      Future<T> future = executor.submit(callable);
      return getFuture(future, timeoutInMillis);
    }
    finally {
      if (executor != null) {
        executor.shutdown();
      }
    }
  }

  public static <T> T getFuture(Future<T> future, long timeoutInMillis) {
    try {
      return future.get(timeoutInMillis, TimeUnit.MILLISECONDS);
    }
    catch (InterruptedException ex) {
      Thread.currentThread().interrupt();
      throw new TimeSliceExecutorException("Interrupton exception", ex);
    }
    catch (ExecutionException ex) {
      throw new TimeSliceExecutorException("Execution exception", ex);
    }
    catch (TimeoutException ex) {
      throw new TimeSliceExecutorException(String.format("%dms timeout reached", timeoutInMillis), ex);
    }
  }
}

Then build the socket along these lines:

private Socket buildSocket() throws IOException {
  final Socket socket = new Socket();
  socket.setSoTimeout(socketTimeout);
  socket.connect(new InetSocketAddress(resolveHost(host, dnsTimeout), port), connectionTimeout);
  return socket;
}

private static InetAddress resolveHost(String host, long dnsTimeout) throws IOException {
  try {
    return TimeSliceExecutor.execute(() -> InetAddress.getByName(host), dnsTimeout);
  }
  catch (TimeSliceExecutor.TimeSliceExecutorException ex) {
    throw new UnknownHostException(host);
  }
}

Ref: https://stackoverflow.com/a/70610305/225217

Brice Roncace
  • 10,110
  • 9
  • 60
  • 69