[英]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.