Java Socket设置timeout的几种常用方式说明

目录
  • 1. Socket timeout
    • 1.1 建立连接connect timeout
    • 1.2 读取数据so timeout
    • 1.3 小结
  • 2. 使用案例
    • 2.1 MySQL jdbc timeout
    • 2.2 Jedis timeout
  • 3. 总结

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

1. Socket timeout

Java socket有如下两种timeout:

  • 建立连接timeout,暂时就叫 connect timeout
  • 读取数据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设置,

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之后,可以更好的处理一些网络服务的客户端和服务端,同时对排查一些问题也很有帮助。

一般的成熟的网络服务和客户端都应该有这两个参数的配置方法,当使用遇到类似问题可以从这个方向去考虑下。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java如何实现socket连接方法封装

    目录 Java实现socket连接技巧 Java Socket的封装 1 客户端Socket API要点 2 服务端Socket API要点 常见问题 Java使用socket实现一个多线程web服务器的方法 除了服务器类,还包括请求类和响应类 服务器处理类 请求类 响应类 Java实现socket连接技巧 Socket通信几乎无时不在,当然能够搜集到的信息也大量存在, 为了避免重复的劳作, 抽取了关于客户端和服务端的Socket, 并将其应用到适合JVM(LInux/Windows)或者DVM

  • Java中Socket设置连接超时的代码分享

    前言 大家都知道Java的Socket如果连接不到指定IP和Port的主机,就会抛出IOException异常,而通常情况下,从连接到出现IOException会有一段不定的时长,就是所谓的超时时长,如果超时时长过长,在开发时会影响测试,如果代码成了产品,又会非常的影响用户体验,那么要怎么设置这个时长呢?下面来一起看看详细的介绍: Socket的类提供了一个方法: public void connect(SocketAddress endpoint, int timeout) throws IO

  • Java面试Socket编程常用参数设置源码问题分析

    目录 引导语 1.Socket整体结构 2.初始化 3.connect连接服务端 4.Socket常用设置参数 4.1.setTcpNoDelay 4.2.setSoLinger 4.3.setOOBInline 4.4.setSoTimeout 4.5.setSendBufferSize 4.6.setReceiveBufferSize 4.7.setKeepAlive 4.8.setReuseAddress 5.总结 引导语 Socket 中文翻译叫套接字,可能很多工作四五年的同学都没有用过

  • Java Socket设置timeout的几种常用方式说明

    目录 1. Socket timeout 1.1 建立连接connect timeout 1.2 读取数据so timeout 1.3 小结 2. 使用案例 2.1 MySQL jdbc timeout 2.2 Jedis timeout 3. 总结 Java的网络编程Socket常常用于各种网络工具,比如数据库的jdbc客户端,redis客户端jedis,各种RPC工具java客户端,这其中存在一些参数来配置timeout,但是之前一直对timeout的理解还不清晰,所以会导致使用这些网络工具

  • Android 中倒计时验证两种常用方式实例详解

    Android 中倒计时验证两种常用方式实例详解 短信验证码功能,这里总结了两种常用的方式,可以直接拿来使用.看图: 说明:这里的及时从10开始,是为了演示的时间不要等太长而修改的. 1.第一种方式:Timer /** * Description:自定义Timer * <p> * Created by Mjj on 2016/12/4. */ public class TimeCount extends CountDownTimer { private Button button; //参数依

  • python里读写excel等数据文件的6种常用方式(小结)

    下面整理下python有哪些方式可以读取数据文件. 1. python内置方法(read.readline.readlines) read() : 一次性读取整个文件内容.推荐使用read(size)方法,size越大运行时间越长 readline() :每次读取一行内容.内存不够时使用,一般不太用 readlines() :一次性读取整个文件内容,并按行返回到list,方便我们遍历 2. 内置模块(csv) python内置了csv模块用于读写csv文件,csv是一种逗号分隔符文件,是数据科学

  • Java基础之多线程的三种实现方式

    一.前言 Java多线程实现的三种方式有继承Thread类,实现Runnable接口,使用ExectorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的. 二.继承Thread类实现多线程 1.Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法. 2.start()方法是一个native方法,它将启动一个新线程

  • SpringBoot实现前后端、json数据交互以及Controller接收参数的几种常用方式

    目录 前言 获取参数的几种常用注解 一.请求路径参数get请求 二.Body参数POST请求 四.HttpServletRequest 五.参数校检 最终选择交互方式 参考文献 总结 前言 现在大多数互联网项目都是采用前后端分离的方式开发,前端人员负责页面展示和数据获取,后端负责业务逻辑处理和接口封装.当与前端交互的过程当中,常用json数据与前端进行交互,这样想取出前端传送过来的json数据的时候,就需要用到@RequestBody这个注解.@RequestBody注解用于读取http请求的内

  • 分享Vue组件传值的几种常用方式(一)

    目录 前言 第一种:父向子传值 新建文件导入结构 引入 注册 使用子组件 子组件内部代码完善 父组件内部代码完善 操作main.js文件 思路总结 前言 大家好,这个系列我们来讲解一下vue组件传值的几种常见方法和逻辑链路.最常见的vue组件传值分为三种,第一种是父向子传值,第二种是子向父传值,第三种是兄弟组件之间的传值,下面我们先就第一种情况来进行分析和编写. 第一种:父向子传值 父向子传值意思就是要把父组件里的值传递给子组件,方法是在子组件内部自定义一个props属性,通过props属性来完

  • JavaScript实现页面跳转的几种常用方式

    本文实例讲述了JavaScript实现页面跳转的几种常用方式.分享给大家供大家参考,具体如下: 第一种: <script language="javascript" type="text/javascript"> window.location.href="login.jsp?backurl="+window.location.href; </script> 第二种: <script language="j

  • Java多线程中线程的两种创建方式及比较代码示例

    1.线程的概念:线程(thread)是指一个任务从头至尾的执行流,线程提供一个运行任务的机制,对于java而言,一个程序中可以并发的执行多个线程,这些线程可以在多处理器系统上同时运行.当程序作为一个应用程序运行时,java解释器为main()方法启动一个线程. 2.并行与并发: (1)并发:在单处理器系统中,多个线程共享CPU时间,而操作系统负责调度及分配资源给它们. (2)并行:在多处理器系统中,多个处理器可以同时运行多个线程,这些线程在同一时间可以同时运行,而不同于并发,只能多个线程共享CP

  • vue动态绑定class的几种常用方式小结

    本文实例讲述了vue动态绑定class的几种常用方式.分享给大家供大家参考,具体如下: 对象方法 最简单的绑定(这里的active加不加单引号都可以,以下也一样都能渲染) :class="{ 'active': isActive }" 判断是否绑定一个active :class="{'active':isActive==-1}" 或者 :class="{'active':isActive==index}" 绑定并判断多个 第一种(用逗号隔开) :

  • JS实现继承的几种常用方式示例

    本文实例讲述了JS实现继承的几种常用方式.分享给大家供大家参考,具体如下: 1,原型链继承 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>原型链继承</title> </head> <body> <script> /** * 优点: * 实例是父类的实例也是子类的实例

随机推荐