Reputation: 1451
I'm working with Java Sockets. I have this line of code:
Socket webSocket = new Socket();
webSocket.connect(new InetSocketAddress(domain, 80), 120000);
The specified timeout is 120000ms (2 minutes). I'm curious whether this timeout is actually honored, or whether it is capped at the platform default connection timeout value. Also, how do I check the platform's default connection timeout value? In other words, what is the timeout when I call this code:
Socket webSocket = new Socket(domain, 80);
In the first place, is this timeout platform dependent? I know there's SO_TIMEOUT
, but I think it only affects the read()
timeout and not connect()
.
I think there's a default timeout, because not specifying the timeout value still causes Connection timed out: connect
.
Upvotes: 0
Views: 2984
Reputation: 8415
Investigating further, you'll find that there is a subtle difference between a java connection time out and a system-level connection time out.
By changing the line:
webSocket.connect(new InetSocketAddress(domain, 80), 120000);
to something like:
webSocket.connect(new InetSocketAddress(domain, 80), 10);
You'll see you get a whole different exception: java.net.SocketTimeoutException
rather than the usual java.net.ConnectException
.
The actual call to Socket.connect()
is actually being deferred to a SocketImpl
abstract class such that the exact mechanics are implementation-defined. As seen in this extract from the connect()
method in Socket
:
...
if (!oldImpl)
impl.connect(epoint, timeout);
else if (timeout == 0) {
if (epoint.isUnresolved())
impl.connect(addr.getHostName(), port);
else
impl.connect(addr, port);
...
Fundamentally, the second exception (ConnectException
) is a implementation-dependent exception meaning that the OS itself has a maximum timeout that is being reached before the java one is set. Exploring further, upon inspection of the stack trace of the ConnectException
we see something like this (on a windows machine):
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)
Note the exception actually propagated from a native method, namely from this section (as provided by 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.
}
The line:
rv = connect(fd, (struct sockaddr *)&sa, sa_len);
Is actually a call to the Winsock2 connect function whose MSDN page can be viewed here. The Winsock2 socket itself has been setup as a default socket (optval
in setsockopt
was zero). Meaning it inherits whatever system default timeouts are for both sending and receiving. If a connection attempt exceeds the default timeout value, a SOCKET_ERROR
is placed into rv
which causes the line:
NET_ThrowNew(env, err, "connect");
to run, propagating the ConnectException
back up the stack. (If you don't believe me check out DualStackPlainSocketImpl.c
in the OpenJDK sources).
Well... Where did the java-set timeout go? The answer turns out to be one stack-level up before the transition into the native layer in the file DualStackPlainSocketImpl.java
under the method socketConnect()
, in particular this fragment:
configureBlocking(nativefd, false);
try {
connectResult = connect0(nativefd, address, port);
if (connectResult == WOULDBLOCK) {
waitForConnect(nativefd, timeout);
}
} finally {
configureBlocking(nativefd, true);
}
where connect0
is the actual call to the native function.
In essence, when java is presented with a finite, non-zero timeout, it starts the native socket connection in asynchronous mode and waits for the timeout to expire or the native method to complete, whichever occurs first. The problem with setting a large timeout value is that the native connect function may itself timeout and throw a ConnectException
instead of the usual SocketTimeoutException
. (On my particular Windows 10 system under Java 8).
The same exercise can be done on various systems to determine the exact mechanism in which socket timeouts work.
tl;dr:
The consensus is that basically all socket operations are implementation defined. Even from the first stack layer you'll find Socket
defers most of it's work to some abstract SocketImpl
that gets implemented by the OS. As such, it is entirely possible (as proven above) for a OS to have a timeout that is longer, shorter or completely non-existent. In a sense, a java timeout would only work properly if it were shorter than the OS default timeout (if it has one) so you should be prepared to handle both cases (where the OS throws an exception due to it's native timeout vs a java-specific timeout expiry).
Upvotes: 6
Reputation: 1165
The default socket timeout is 0, which means never timeout. If it actually time out after x number of minutes, This means it has been set within your code. In your case it's 2 minutes.
Upvotes: -1