原文出处:囚兔

摘要: Java的网络编程Socket常常用于各种网络工具,比如数据库的jdbc客户端,redis客户端jedis,各种RPC工具java客户端,这其中存在一些参数来配置timeout,但是之前一直对timeout的理解还不清晰,所以会导致使用这些网络工具的时候有点迷茫。在此做个总结。

1. Socket timeout

Java socket有如下两种timeout:

  1. 建立连接timeout,暂时就叫 connect timeout;
  2. 读取数据timeout,暂时就叫so 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 小结

我们可以通过设置connect timeout来控制连接建立的超时时间(不是绝对的,当设置的主机名不合法,比如我设置主机名为abc,会抛异常java.net.UnknownHostException: abc,但是此时connect timeout设置是不起作用的,测试得出的结论,仅供参考)。

通过设置so timeout可以控制流读取数据的超时时间。

2. 使用案例

2.1 MySQL jdbc timeout

查阅MySQL Connector/J 5.1 Developer Guide 中的jdbc配置参数,有

connectTimeout

Timeout for socket connect (in milliseconds), with 0 being no timeout. Only works on JDK-1.4 or newer. Defaults to '0'.

Default: 0

Since version: 3.0.1

socketTimeout

Timeout on network socket operations (0, the default means no timeout).

Default: 0

Since version: 3.0.1

这两个参数分别就是对应上面我们分析的connect timeout和so timeout。

参数的设置方法有两种,一种是通过url设置,

1

jdbc:mysql://[host1][:port1][,[host2][:port2]]...[/[database]] [?propertyName1=propertyValue1[&propertyName2=propertyValue2]...]

即在url后面通过?加参数,比如jdbc:mysql://192.168.1.1:3306/test?connectTimeout=2000&socketTime=2000

还有一种方式是:

Properties info = new Properties();

info.put("user", this.username);

info.put("password", this.password);

info.put("connectTimeout", "2000");

info.put("socketTime", "2000");

return DriverManager.getConnection(this.url, info);

2.2 Jedis timeout

Jedis是最流行的redis java客户端工具,redis.clients.jedis.Jedis对象的构造器中就有参数设置,

public Jedis(final String host, final int port, final int connectionTimeout, final int soTimeout) {

super(host, port, connectionTimeout, soTimeout);

}

// 用一个参数timeout同时设置connect timeout 和 so timeout

public Jedis(final String host, final int port, final int timeout) {

super(host, port, timeout);

}

Jedis中so timeout个人觉得是有比较重要意义的,首先jedis so timeout默认值为2000毫秒,jedis的操作流程是客户端发送命令给客户端执行,然后客户端就开始执行InputStream.read()读取响应,当某个命令比较耗时(比如数据非常多的情况下执行“keys *”),而导致客户端迟迟没有收到响应,就可能导致java.net.SocketTimeoutException: Read timed out异常抛出。一般是不建议客户端执行非常耗时的命令,但是也不排除有这种特殊逻辑,那这时候就有可能需要修改Jeids中这个so timeout的值。

3. 总结

了解了这两个timeout之后,可以更好的处理一些网络服务的客户端和服务端,同时对排查一些问题也很有帮助。一般的成熟的网络服务和客户端都应该有这两个参数的配置方法,当使用遇到类似问题可以从这个方向去考虑下。

Java Socket Timeout 总结的更多相关文章

  1. 格式化namenode时 报错 No Route to Host from node1/192.168.1.111 to node3:8485 failed on socket timeout exception: java.net.NoRouteToHostException: No route to host

    // :: FATAL namenode.NameNode: Failed to start namenode. org.apache.hadoop.hdfs.qjournal.client.Quor ...

  2. java socket相关的timeout

    1 java socket的两个timeout 一个是connect timeout,即建立连接的timeout,另外一个是so timeout,是读取数据的timeout.这两个timeout都是因 ...

  3. JAVA Socket超时浅析

    JAVA Socket超时浅析 套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的"终端".针对一个特定的连接,每台机器上都有一个"套接字&q ...

  4. Java Socket(2): 异常处理

    1 超时 套接字底层是基于TCP的,所以socket的超时和TCP超时是相同的.下面先讨论套接字读写缓冲区,接着讨论连接建立超时.读写超时以及JAVA套接字编程的嵌套异常捕获和一个超时例子程序的抓包示 ...

  5. [ 转载]JAVA Socket超时浅析

    JAVA Socket超时浅析 转载自 http://blog.csdn.net/sureyonder/article/details/5633647 套接字或插座(socket)是一种软件形 式的抽 ...

  6. 如何为可扩展系统进行Java Socket编程

    从简单I/O到异步非阻塞channel的Java Socket模型演变之旅 上世纪九十年代后期,我在一家在线视频游戏工资工作,在哪里我主要的工作就是编写Unix Unix Berkley Socket ...

  7. java socket 的参数选项解读(转)

    java socket中有很多参数可以选择,这篇博客的目的是沉淀出这些参数的语义和用法,供自己以后查阅. 1.java socket参数选项总览 在JDK1.6中有如下参数选项: 1 public f ...

  8. JAVA Socket超时浅析(转)

    套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的“终端”.针对一个特定的连接,每台机器上都有一个“套接字”,可以想象它们之间有一条虚拟的“线缆”.JAVA有两个基于数据流 ...

  9. Java Socket:Java-NIO-Selector

    Selector 的出现,大大改善了多个 Java Socket的效率.在没有NIO的时候,轮询多个socket是通过read阻塞来完成,即使是非阻塞模式,我们在轮询socket是否就绪的时候依然需要 ...

随机推荐

  1. Javascript实现大整数加法

    记得之前面试还被问到过用两个字符串实现两个大整数相加,当时还特别好奇好好的整数相加,为什么要用字符串去执行.哈哈,感觉当时自己还是很无知的,面试官肯定特别的无奈.今天在刷算法的时候,无意中看到了为什么 ...

  2. 利用JAVA制作简单登录窗口

    import java.awt.Container; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; ...

  3. mysql非安装包安装教程

    设置mysql的环境变量 本人设置安装的路径是:E:\WebApplication\webMySQL\mysql-5.7.13-winx64 我的电脑 ---> 高级系统配置 ---> 环 ...

  4. 11th 单元测试工具JUnit的学习

    1.写好一个简易的四则运算的程序 UnitTest类文件: public class UnitTest { int a; int b; int answer;//正确答案 public int plu ...

  5. sql server数据库中raiserror函数的用法

    server数据库中raiserror的作用就和asp.NET中的throw new Exception一样,用于抛出一个异常或错误.这个错误可以被程序捕捉到. raiserror的常用格式如下:ra ...

  6. oracle 简单的sysTimeStamp类型转date 类型

    oracle  简单的SYSTIMESTAMP 类型转date 类型 SELECT SYSTIMESTAMP , SYSTIMESTAMP+0 FROM dual; SAMPLE_TIME ----- ...

  7. [转帖]Nginx 的配置文件详解.

    nginx配置文件nginx.conf超详细讲解  https://www.cnblogs.com/liang-wei/p/5849771.html   #nginx进程,一般设置为和cpu核数一样w ...

  8. vmware 已将该虚拟机配置为使用 64 位客户机操作系统。但是,无法执行 64 位操作。

    错误提示:已将该虚拟机配置为使用 64 位客户机操作系统.但是,无法执行 64 位操作. 此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态. 如果已在 BIOS/固件设置中禁用 ...

  9. logback错误日志发送邮件

    logback 一.介绍 logback是由log4j的作者开发的一个开源日志组件,用以替代log4j. logback由3个部分组成: ♦ logback-core (基础模块) ♦ logback ...

  10. tomcat 启动异常 EOFException: Unexpected end of ZLIB input stream

    EVERE: Exception fixing docBase for context [/agdis] java.io .EOFException: Unexpected end of ZLIB i ...