摘要: Java的网络编程Socket经常用于各类网络东西,好比数据库的jdbc客户端,redis客户端jedis,各类RPC东西java客户端,这个中存在一些参数来设置timeout,可是之前一直对timeout的领略还不清晰,所以会导致利用这些网络东西的时候有点苍茫。在此做个总结。
1. Socket timeout
Java socket有如下两种timeout:
1.1 成立毗连connect timeout
当不配置该参数时,指客户端请求和处事端成立tcp毗连时,会一直阻塞直到毗连成立乐成,或抛异常。当配置了connectTimeout, 客户端请求和处事端成立毗连时,阻塞时间高出connectTimeout时,就会抛出异常java.net.ConnectException: Connection timed out: connect。
我们看如下精简后的代码,首先是处事端:
serverSocket = new ServerSocket(8080); Socket socket = serverSocket.accept();
处事端开启ServerSocket监听8080端口,再看客户端:
socket = new Socket(); socket.connect(new InetSocketAddress("localhost", 8080)); System.out.println("Connected.");
打印“Connected.”,修改客户端代码中的主机名为一个不存在的主机:
socket = new Socket(); long t1 = 0; try { t1 = System.currentTimeMillis(); socket.connect(new InetSocketAddress("www.ss.ssss", 8080)); } catch (IOException e) { long t2 = System.currentTimeMillis(); e.printStackTrace(); System.out.println("Connect failed, take time -> " + (t2 - t1) + "ms."); }
抛出异常:java.net.ConnectException: Connection timed out: connect,并打印:Connect failed, take time -> 18532ms. 也就是当未配置connect timeout时,软件开发,connect要了解阻塞直到底层异常抛出。颠末测试socket有个默认的超时时间,或许在20秒阁下(测试的值,不必然精确,待研究JVM源码)。下面我们来配置connect timeout,再看看结果:
socket = new Socket(); long t1 = 0; try { t1 = System.currentTimeMillis(); // 配置connect timeout 为2000毫秒 socket.connect(new InetSocketAddress("www.ss.ssss", 8080), 2000); } catch (IOException e) { long t2 = System.currentTimeMillis(); e.printStackTrace(); System.out.println("Connect failed, take time -> " + (t2 - t1) + "ms."); }
抛出异常:java.net.SocketTimeoutException: connect timed out,并打印:Connect failed, take time -> 2014ms. 这里就是connect timeout发挥浸染了。
1.2 读取数据so timeout
先看下jdk源码注释:
Enable/disable SO_TIMEOUT with the specified timeout, in milliseconds. With this option set to a non-zero timeout, a read() call on the InputStream associated with this Socket will block for only this amount of time. If the timeout expires, a java.net.SocketTimeoutException is raised, though the Socket is still valid. The option must be enabled prior to entering the blocking operation to have effect. The timeout must be > 0. A timeout of zero is interpreted as an infinite timeout.
这个参数通过socket.setSoTimeout(int timeout)要领配置,可以看出它的意思是,socket关联的InputStream的read()要了解阻塞,直到高出配置的so timeout,就会抛出SocketTimeoutException。当不配置这个参数时,默认值为无穷大,即InputStream的read要了解一直阻塞下去,除非毗连断开。
下面通过代码来看下结果:
处事端代码:
serverSocket = new ServerSocket(8080); Socket socket = serverSocket.accept();
处事端只接管socket但不发送任何数据给客户端。客户端代码:
socket = new Socket(); socket.connect(new InetSocketAddress("localhost", 8080)); System.out.println("Connected."); in = socket.getInputStream(); System.out.println("reading..."); in.read(); System.out.println("read end");
客户端成立毗连就开始读取InputStream。打印:
Connected. reading...
而且一直阻塞在in.read(); 上。接下来我配置so timeout,代码如下:
long t1 = 0; try { socket = new Socket(); socket.connect(new InetSocketAddress("localhost", 8080)); // 配置so timeout 为2000毫秒 socket.setSoTimeout(2000); System.out.println("Connected."); in = socket.getInputStream(); System.out.println("reading..."); t1 = System.currentTimeMillis(); in.read(); } catch (IOException e) { long t2 = System.currentTimeMillis(); System.out.println("read end, take -> " + (t2 - t1) + "ms"); e.printStackTrace(); } finally { if (this.reader != null) { try { this.reader.close(); } catch (IOException e) { } } }
抛出异常:java.net.SocketTimeoutException: Read timed out, 打印:read end, take -> 2000ms , 说明so timeout起浸染了。
1.3 小结