1. Socket connectTimeOut 在JDK中

  1. public Socket(InetAddress address, int port) throws IOException {
  2. this(address != null ? new InetSocketAddress(address, port) : null,
  3. (SocketAddress) null, true);
  4. }
  5. private Socket(SocketAddress address, SocketAddress localAddr,
  6. boolean stream) throws IOException {
  7. setImpl();
  8. // backward compatibility
  9. if (address == null)
  10. throw new NullPointerException();
  11. try {
  12. createImpl(stream);
  13. if (localAddr != null)
  14. bind(localAddr);
  15. connect(address);
  16. } catch (IOException | IllegalArgumentException | SecurityException e) {
  17. try {
  18. close();
  19. } catch (IOException ce) {
  20. e.addSuppressed(ce);
  21. }
  22. throw e;
  23. }
  24. }
  25. public void connect(SocketAddress endpoint) throws IOException {
  26. //默认超时时间为0
  27. connect(endpoint, 0);
  28. }
  29. /**
  30. * Connects this socket to the server with a specified timeout value.
  31. * A timeout of zero is interpreted as an infinite timeout. The connection
  32. * will then block until established or an error occurs.
  33. *
  34. * @param endpoint the {@code SocketAddress}
  35. * @param timeout the timeout value to be used in milliseconds.
  36. * @throws IOException if an error occurs during the connection
  37. * @throws SocketTimeoutException if timeout expires before connecting
  38. * @throws java.nio.channels.IllegalBlockingModeException
  39. * if this socket has an associated channel,
  40. * and the channel is in non-blocking mode
  41. * @throws IllegalArgumentException if endpoint is null or is a
  42. * SocketAddress subclass not supported by this socket
  43. * @since 1.4
  44. * @spec JSR-51
  45. */
  46. public void connect(SocketAddress endpoint, int timeout) throws IOException {
  47. //省略
  48. }
  49. //java.net.DualStackPlainSocketImpl#connect0
  50. static native int connect0(int fd, InetAddress remote, int remotePort) throws IOException;

image.png
默认超时时间为0,意味着没有设置超时时间,连接是不是过期的。

2. Socket 连接建立超时表现在TCP协议

socket连接建立是基于TCP的连接建立过程。TCP的连接需要通过3次握手报文来完成,开始建立TCP连接时需要发送同步SYN报文,然后等待确认报文SYN+ACK,最后再发送确认报文ACK。TCP连接的关闭通过4次挥手来完成,主动关闭TCP连接的一方发送FIN报文,等待对方的确认报文;被动关闭的一方也发送FIN报文,然等待确认报文。
image.png
正在等待TCP连接请求的一端有一个固定长度的连接队列,该队列中的连接已经被TCP接受(即三次握手已经完成),但还没有被应用层所接受。TCP接受一个连接是将其放入这个连接队列,而应用层接受连接是将其从该队列中移出。应用层可以通过设置backlog变量来指明该连接队列的最大长度,即已被TCP接受而等待应用层接受的最大连接数。

当一个连接请求SYN到达时,TCP确定是否接受这个连接。如果队列中还有空间,TCP模块将对SYN进行确认并完成连接的建立。但应用层只有在三次握手中的第三个报文收到后才会知道这个新连接。如果队列没有空间,TCP将不理会收到的SYN。

如果应用层不能及时接受已被TCP接受的连接,这些连接可能占满整个连接队列,新的连接请求可能不被响应而会超时。如果一个连接请求SYN发送后,一段时间后没有收到确认SYN+ACK,TCP会重传这个连接请求SYN两次,每次重传的时间间隔加倍,在规定的时间内仍没有收到SYN+ACK,TCP将放弃这个连接请求,连接建立就超时了。

JAVA Socket连接建立超时和TCP是相同的,如果TCP建立连接时三次握手超时,那么导致Socket连接建立也就超时了。可以设置Socket连接建立的超时时间 connect(SocketAddress endpoint, int timeout)

如果在timeout内,连接没有建立成功,在TimeoutException异常被抛出。如果timeout的值小于三次握手的时间,那么Socket连接永远也不会建立。

不同的应用层有不同的连接建立过程,Socket的连接建立和TCP一样-仅仅需要三次握手就完成连接,但有些应用程序需要交互很多信息后才能成功建立连接,比如Telnet协议,在TCP三次握手完成后,需要进行选项协商之后,Telnet连接才建立完成。

3. 代码测试

  1. import java.net.InetSocketAddress;
  2. import java.net.Socket;
  3. public class ClientMain {
  4. public static void main(String[] args) throws Exception {
  5. Socket socket = new Socket();
  6. long t1 = System.currentTimeMillis();
  7. // 设置 connection timeout (单位毫秒)
  8. try {
  9. //ip地址是随意写的
  10. //socket.connect(new InetSocketAddress("192.168.2.145",8888));
  11. //设置超时时间
  12. socket.connect(new InetSocketAddress("192.168.2.145",8888), 5000);
  13. } finally {
  14. System.out.println("客户端connection Timeout 耗时:"
  15. + (System.currentTimeMillis() - t1) + "毫秒");
  16. }
  17. }
  18. }

设置了 timout

客户端connection Timeout 耗时:5011毫秒 Exception in thread “main” java.net.SocketTimeoutException: connect timed out at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:476) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:218) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:200) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:394) at java.net.Socket.connect(Socket.java:606) at ClientMain.main(ClientMain.java:11)

未设置timout, 为什么是20秒左右,不知道原因。将来慢慢研究一下。

客户端connection Timeout 耗时:21010毫秒 Exception in thread “main” java.net.ConnectException: 拒绝连接 (Connection refused) at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:476) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:218) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:200) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:394) at java.net.Socket.connect(Socket.java:606) at java.net.Socket.connect(Socket.java:555) at ClientMain.main(ClientMain.java:10)

netsh int tcp show global
image.png