为套接字操作设置超时

当我创建一个套接字时:

Socket socket = new Socket(ipAddress, port);

它抛出一个异常,这是可以的,因为 IP 地址不可用。(其中 String ipAddress = "192.168.0.3"int port = 300的测试变量。)

问题是: 如何将该套接字设置为超时?

创建套接字时,如何缩短获得 UnknownHostException并使套接字超时的时间?

201817 次浏览

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

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

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

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!

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.

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