簡體   English   中英

套接字連接超時上限

[英]Socket connect timeout ceiling

我正在使用Java套接字。 我有這行代碼:

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

指定的超時為120000ms(2分鍾)。 我很好奇這個超時是否真正兌現了,或者它是否被設置為平台默認的連接超時值。 另外,如何檢查平台的默認連接超時值? 換句話說,調用此代碼的超時時間是:

Socket webSocket = new Socket(domain, 80);

首先,此超時平台是否依賴? 我知道有SO_TIMEOUT ,但我認為它僅影響read()超時,而不影響connect()

我認為存在默認超時,因為未指定超時值仍會導致Connection timed out: connect

進一步研究,您會發現Java連接超時和系統級連接超時之間有細微的差別。

通過更改行:

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

像這樣:

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

您會看到完全不同的異常: java.net.SocketTimeoutException而不是通常的java.net.ConnectException

實際Socket.connect()的實際調用延遲到SocketImpl抽象類,以便確切的機制由實現定義。 Socket connect()方法的摘錄中可以看出:

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

從根本上講,第二個異常( ConnectException )是一個與實現有關的異常,這意味着OS本身具有設置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 connect函數的調用,可以在此處查看其MSDN頁面。 Winsock2套接字本身已設置為默認套接字( setsockopt中的optval為零)。 這意味着它將繼承發送和接收的所有系統默認超時。 如果連接嘗試超過默認超時值,則將SOCKET_ERROR放入rv ,從而導致該行:

NET_ThrowNew(env, err, "connect");

運行時,將ConnectException傳播到堆棧中。 (如果您不相信我,請查看OpenJDK源DualStackPlainSocketImpl.c中的DualStackPlainSocketImpl.c )。

好吧……Java設置的超時值在哪里? 在轉換為文件DualStackPlainSocketImpl.java的本地層之前, 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超時才能正常工作,因此您應該准備處理兩種情況(由於OS的本機超時與Java相比,操作系統拋出異常)特定的超時時間)。

默認套接字超時為0,這表示永不超時。 如果它實際上在x分鍾后超時,則表明它已在代碼中設置。 您的情況是2分鍾。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM