【问题标题】:Setting a timeout for socket operations设置套接字操作的超时
【发布时间】:2011-06-25 13:56:24
【问题描述】:

当我创建一个套接字时:

Socket socket = new Socket(ipAddress, port);

它抛出一个异常,这没关系,因为IP地址不可用。 (String ipAddress = "192.168.0.3"int port = 300 所在的测试变量。)

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

当我创建套接字时,如何减少获得UnknownHostException 并使套接字超时的时间?

【问题讨论】:

  • @adrianboimvaser:在这种情况下,您应该标记为重复,并留下指向重复的评论,以便版主可以关闭它(您在一年前留下了该评论 - 也许您现在知道了)。我现在会投票赞成这样做,但我不知道副本在哪里!
  • 为了延续多年回复的趋势,@EJP 这个问题的标题比你建议的重复要少得多
  • @Isaac 实际上它根本不是重复的,评论已撤回。
  • (注意:我已经调整了标题以减少歧义。)

标签: java sockets


【解决方案1】:

您没有为套接字设置超时,而是为在该套接字上执行的操作设置了超时。

例如socket.connect(otherAddress, timeout)

socket.setSoTimeout(timeout) 用于设置read() 操作的超时时间。

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

【讨论】:

  • 嗯,在这种情况下它也是一个操作,连接。我找不到任何地方表明这个 ctor 使用了无限超时,尽管情况似乎如此。
  • @sandos Javadoc 中明确说明了 connect()。然而这也是不正确的。默认超时时间是平台超时时间,大约一分钟,不是无穷大,只能通过这个connect()方法减少,不能增加。
  • 注意,socket.setSoTimeout 必须在connect 之后调用,否则它将不起作用并且读取永远不会超时。这是一个记录不充分的问题。
【解决方案2】:

default constructor 用于Socket,然后使用connect() method

【讨论】:

    【解决方案3】:

    请改用Socket() constructorconnect(SocketAddress endpoint, int timeout) method

    在你的情况下,它看起来像:

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

    引用文档

    connect

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

    将此套接字连接到具有指定超时值的服务器。超时为零被解释为无限超时。然后连接将阻塞,直到建立或发生错误。

    参数:

    endpoint - SocketAddress
    timeout - 以毫秒为单位的超时值。

    投掷:

    IOException - 如果连接期间发生错误
    SocketTimeoutException - 如果在连接之前超时到期
    IllegalBlockingModeException - 如果此套接字有关联的通道,并且通道处于非阻塞模式
    IllegalArgumentException - 如果端点为 null 或者是此套接字不支持的 SocketAddress 子类

    因为: 1.4

    【讨论】:

    • 这可以减少 ConnectException 的超时(但不会增加它),并且它对 UnknownHostException 的超时没有影响,OP 声称他正在得到它(尽管我不相信)。另请注意,Javadoc 声明默认值为无穷大是不正确的。它不是。该平台的默认超时时间约为一分钟,并且无法增加。
    • 即使 4 年后这很有帮助,这应该是公认的答案。
    • 现在已经 5 年了,这是我在互联网上找到的最佳答案.. 谢谢
    • 在阻塞 IO 操作期间调用 socket.setSoTimeout(timeoutMillis) 来处理超时也很重要。
    【解决方案4】:

    您可以使用以下解决方案:

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

    希望对你有帮助!

    【讨论】:

    • 如果如他所说,他得到了一个 UnknownHostException,它将发生在第一行,没有修改超时。
    • 但是,第一行没有抛出任何异常。从文档中:“将尝试将主机名解析为 InetAddress。如果尝试失败,地址将被标记为未解析”
    • 这种情况下会通过connect()方法解决,但不受超时控制。
    【解决方案5】:

    由于UnknownHostException,您无法控制超时。这些是 DNS 计时。您只能控制给定有效主机的连接超时。前面的答案都没有正确解决这一点。

    但是,当您指定 IP 地址而不是主机名时,我很难相信您真的得到了 UnknownHostException

    EDIT 控制 Java 的 DNS 超时see this answer

    【讨论】:

      【解决方案6】:

      一种解决方案是在不同的线程上执行 DNS 解析,该线程只给定一定的时间来完成。

      这里有一个简单的实用程序可以帮助您做到这一点:

      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);
          }
        }
      }
      

      然后按照以下思路构建套接字:

      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);
        }
      }
      

      参考:https://stackoverflow.com/a/70610305/225217

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-20
        • 2012-11-12
        • 1970-01-01
        • 2019-07-22
        • 1970-01-01
        • 2016-06-14
        • 1970-01-01
        相关资源
        最近更新 更多