套接字连接超时上限

时间:2015-10-22 05:33:46

标签: java sockets

我正在使用Java套接字。我有这行代码:

Socket webSocket = new Socket();
webSocket.connect(new InetSocketAddress(domain, 80), 120000);

指定的超时为120000毫秒(2分钟)。我很好奇这个超时是否真正得到尊重,或者它是否受到平台默认连接超时值的限制。另外,如何检查平台的默认连接超时值?换句话说,当我调用此代码时超时是多少:

Socket webSocket = new Socket(domain, 80);

首先,这个超时平台是否依赖?我知道有SO_TIMEOUT,但我认为这只会影响read()超时,而不会影响connect()

我认为存在默认超时,因为未指定超时值仍会导致Connection timed out: connect

2 个答案:

答案 0 :(得分:4)

进一步调查,您会发现java连接超时与系统级连接超时之间存在细微差别。

通过更改行:

webSocket.connect(new InetSocketAddress(domain, 80), 120000);

类似于:

webSocket.connect(new InetSocketAddress(domain, 80), 10);

您会看到一个完全不同的例外:java.net.SocketTimeoutException而不是通常的java.net.ConnectException

Socket.connect()的实际调用实际上被推迟到SocketImpl抽象类,以便精确的机制是实现定义的。正如connect()Socket方法的摘录中所示:

...
if (!oldImpl)
    impl.connect(epoint, timeout);
else if (timeout == 0) {
    if (epoint.isUnresolved())
        impl.connect(addr.getHostName(), port);
    else
        impl.connect(addr, port);
...

从根本上说,第二个异常(ConnectException)是一个依赖于实现的异常,这意味着操作系统本身具有在设置java之前达到的最大超时。进一步探索,在检查ConnectException的堆栈跟踪后,我们看到类似的内容(在Windows机器上):

Exception in thread "main" java.net.ConnectException: Connection timed out: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at java.net.Socket.connect(Socket.java:538)

注意实际从本机方法传播的异常,即从本节(由OpenJDK提供):

rv = connect(fd, (struct sockaddr *)&sa, sa_len);
if (rv == SOCKET_ERROR) {
    int err = WSAGetLastError();
    if (err == WSAEWOULDBLOCK) {
        return java_net_DualStackPlainSocketImpl_WOULDBLOCK;
    } else if (err == WSAEADDRNOTAVAIL) {
        JNU_ThrowByName(env, JNU_JAVANETPKG "ConnectException",
            "connect: Address is invalid on local machine, or port is not valid on remote machine");
    } else {
        NET_ThrowNew(env, err, "connect");
    }
    return -1;  // return value not important.
}

该行:

rv = connect(fd, (struct sockaddr *)&sa, sa_len);

实际上是对Winsock2连接功能的调用,其MSDN页面可以被查看here。 Winsock2套接字本身已设置为默认套接字(optval中的setsockopt为零)。这意味着它继承了发送和接收的任何系统默认超时。如果连接尝试超过默认超时值,SOCKET_ERROR会被放入rv,这会导致该行:

NET_ThrowNew(env, err, "connect");

运行,将ConnectException传播回堆栈。 (如果您不相信我在OpenJDK来源中查看DualStackPlainSocketImpl.c)。

嗯... java-set超时在哪里?在方法DualStackPlainSocketImpl.java下转换到文件socketConnect()中的本机层之前,答案结果是一个堆栈级别,特别是此片段:

configureBlocking(nativefd, false);
try {
    connectResult = connect0(nativefd, address, port);
    if (connectResult == WOULDBLOCK) {
        waitForConnect(nativefd, timeout);
    }
} finally {
    configureBlocking(nativefd, true);
}

其中connect0是对本机函数的实际调用。

本质上,当java呈现有限的非零超时时,它以异步模式启动本机套接字连接,并等待超时到期或本机方法完成,以先发生者为准。设置较大超时值的问题是本机连接函数本身可能会超时并抛出ConnectException而不是通常的SocketTimeoutException。 (在Java 8下的特定Windows 10系统上)。

可以在各种系统上进行相同的练习,以确定套接字超时的确切机制。

<强> TL; DR:

共识是基本上所有套接字操作都是实现定义的。即使从第一个堆栈层,您发现Socket将其大部分工作推迟到操作系统实现的一些抽象SocketImpl。因此,操作系统完全有可能(如上所述)具有更长,更短或完全不存在的超时。从某种意义上说,java超时只有在短于操作系统默认超时(如果有超时)的情况下才能正常工作,因此您应该准备好处理这两种情况(操作系统因为它原生而引发异常)超时与特定于Java的超时到期时间。)

答案 1 :(得分:-1)

默认套接字超时为0,表示永不超时。 如果它在x分钟后实际超时,这意味着它已在您的代码中设置。在你的情况下,它是2分钟。