setSoTimeout的功能是什么,它是如何工作的?

62

我正在尝试学习 Socket,但是我对 Oracle 网站上的以下文本感到困惑:

setSoTimeout

public void setSoTimeout(int timeout) throws SocketException

以毫秒为单位启用/禁用具有指定超时时间的 SO_TIMEOUT。如果将此选项设置为非零超时时间,则与此 Socket 关联的 InputStream 上的 read() 调用仅会阻塞该时间量。如果超时时间到期,则引发 java.net.SocketTimeoutException,但 Socket 仍然有效。必须在进入阻塞操作之前启用该选项才能生效。超时时间必须 > 0。超时时间为零被解释为无限超时。

我的问题:

  1. 什么是 SO_TIMEOUT

  2. Socket 是连接的端点。如果我说

    mySocket.setSoTimeout(2000);
    

    这是否意味着我将在2000毫秒内阻止从服务器/客户端获取任何输入,而在此时间之后,套接字准备好读取数据?

  3. 什么是超时过期的意思?

  4. 在执行阻塞操作之前,必须启用哪个选项?

  5. 无限超时是否意味着套接字不再读取数据?

3个回答

55

这是不是意味着我会阻塞该套接字从服务器/客户端读取任何输入2000毫秒,等到时间结束后它就可以准备好读取数据了?

不是这样的,这意味着如果在2000毫秒内没有收到任何数据,将会抛出一个SocketTimeoutException异常。

什么是超时到期

这意味着2000毫秒(在这种情况下)已经过去,但没有收到任何数据。

在进行阻塞操作之前必须启用哪个选项?

并没有一定要启用的选项。如果你指的是“可能启用的选项”,那么这就是其中之一。

无限超时是指套接字不再读取吗?

多么奇怪的建议。这意味着如果永远没有收到任何数据,你将永远阻塞在读取状态。


2
笔误?并非如此,SO_TIMEOUT 是对操作系统特定机制的抽象。在Unix系统中,JVM使用poll或select。在Windows上,它使用仅适用于Windows的SO_RCVTIMEO套接字选项。 - Joni
3
EJP,这个问题涉及Java socket API中的SO_TIMEOUT选项。如果你研究它的实现(至少在OpenJDK中),你会发现Windows实现使用SO_RCVTIMEO,而Linux/Solaris实现则使用poll或select来达到相同的效果。请比较http://hg.openjdk.java.net/jdk7/jdk7-gate/jdk/file/9b8c96f96a0f/src/solaris/native/java/net/PlainSocketImpl.c和http://hg.openjdk.java.net/jdk7/jdk7-gate/jdk/file/9b8c96f96a0f/src/windows/native/java/net/TwoStacksPlainSocketImpl.c。 - Joni
1
@Joni,是你开始了这个名字游戏,让我们准确地结束它。Java Socket API 中没有 'SO_TIMEOUT' 选项。有一个 Socket.setSoTimeout() 方法,其 Javadoc 错误地提到了 SO_TIMEOUT,正如你在第一条评论中正确指出的那样,它不存在。我知道它是如何实现的,也知道原因。 - user207421
@Joni 无论它的抽象/存在/不存在状态如何,它都是通过SO_RCVTIMEO选项在大多数平台上实现的,因此100%等效。所有这些的重点让我感到困惑。 - user207421
1
没有理由一定要使用SO_RCVTIMEO来实现SO_TIMEOUT。如果你阅读OpenJDK 7的源代码,你会发现这样的注释:SO_TIMEOUT是用于指定ServerSocket.accept和Socket.getInputStream().read超时的套接字选项。它通常不映射到本地级别的套接字选项。对于Windows,我们特殊处理并使用SOL_SOCKET/SO_RCVTIMEO套接字选项来指定套接字上的接收超时。在Linux和Solaris中,它们使用poll代替。如果你不明白为什么要讨论SO_TIMEOUT的实现方式,那么为什么要提出SO_RCVTIMEO呢? - Joni
显示剩余10条评论

40

JavaDoc 很好地解释了它:

当此选项设置为非零超时时间时,与此套接字关联的 InputStream 上的 read() 调用仅会阻塞此超时时间。如果超时过期,将引发 java.net.SocketTimeoutException,但套接字仍然有效。必须在进入阻塞操作之前启用该选项才能生效。超时必须 >0。超时为零被解释为无限超时。

SO_TIMEOUTread() 调用将阻塞的超时时间。如果达到超时时间,将抛出 java.net.SocketTimeoutException。如果想永久阻止,请将此选项设置为零(默认值),然后 read() 调用将一直阻塞,直到至少读取 1 个字节。


8
我觉得setSoTimeout被命名为setSoTimeout而不是setSocketTimeout很奇怪,为什么? - tom_mai78101
2
有时候,方法会被缩短以避免过长的名称。 - logoff
7
setSoTimeout()方法的名称显然是根据SO_TIMEOUT选项命名的。它设置了SO_TIMEOUT - user207421
6
这个值仅适用于套接字上没有收到数据的情况吗?如果是一个长时间运行的读操作呢?顺便说一下,我有一个服务调用,我想确保它不会超过2秒钟。唯一的方法是创建一个监视请求的线程吗? - Kevin M
@KevinM 如果没有数据到达,读取才可能是“长时间运行”的。你的问题体现了一个无关紧要的区别。正确的方法仍然是 setSoTimeout() - user207421
显示剩余4条评论

13
这个例子让我明白了一切:
正如你所看到的,setSoTimeout 防止程序挂起!它等待 SO_TIMEOUT 时间!如果没有收到任何信号,它就会抛出异常!这意味着时间已过期!
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class SocketTest extends Thread {
  private ServerSocket serverSocket;

  public SocketTest() throws IOException {
    serverSocket = new ServerSocket(8008);
    serverSocket.setSoTimeout(10000);
  }

  public void run() {
    while (true) {
      try {
        System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "...");
        Socket client = serverSocket.accept();

        System.out.println("Just connected to " + client.getRemoteSocketAddress());
        client.close();
      } catch (SocketTimeoutException s) {
        System.out.println("Socket timed out!");
        break;
      } catch (IOException e) {
        e.printStackTrace();
        break;
      }
    }
  }

  public static void main(String[] args) {
    try {
      Thread t = new SocketTest();
      t.start();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

3
这正是我在回复中解释的内容......无论如何,我很高兴因为你终于理解了这个套接字选项的目的 ;.)) - logoff
这也正是Javadoc中所说的。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接