客户端套接字的超时
(timeout)
就是指在客户端通过
Socket
和服务器进行通讯的过程中,由于网络延迟,网络阻塞等原因,造成服务器并未及时响应客户端的一种现象。
在一段时间后,客户端由于未收到服务端的响应而抛出一个超时错误
;
其中客户端所等待的时间就是超时时间。
由于生产超时错误的一端都是被动端;也就是说,这一端是在接收数据,而不是发送数据。对于客户端Socket
来说,只有两个地方是在接收数据;一个是在连接服务器时;
另一个是在连接服务器成功后,接收服务器发过来的数据时。因此,客户端超时也分为两种类型:连接超时和读取数据超时。
一、连接超时
这种超时在前面的例子
中已经使用过。在Socket
类中只有通过connect
方法的第二个参数才能指定连接超时的时间。由于使用connect
方法连接服务器必须要指定IP
和端口;因此,无效的IP
或端口将会引发连接超时错误。
二、读取数据超时
在连接服务器成功后,Socket
所做的最重要的两件事就是“
接收数据”
和“
发送数据”
;而在接收数据时可能因为网络延迟、网络阻塞等原因,客户端一直处于等待状态;而客户端在等待一段时间后,如果服务器还没有发送数据到客户端,那么客户端Socket
将会抛出一个超时错误。
我们可以通过Socket
类的setSoTimeout
方法来设置读取数据超时的时间;时间的单位是毫秒。这个方法必须在读取数据之前调用才会生效。如果将超时时间设为0
,则不使用超时时间;也就是说,客户端什么时候和服务器断开,将完全取决于服务端程序的超时设置。如下面的语句将读取数据超时时间设为5
秒。
Socket socket
=
new
Socket();
socket.setSoTimeout( 5000 );
socket.connect(… …);
socket.getInputStream().read();
socket.setSoTimeout( 5000 );
socket.connect(… …);
socket.getInputStream().read();
要注意的是不要将设置连接超时和读取数据超时设置得太小,如果值太小,如100
,可能会造成服务器的数据还没来得及发过来,客户端就抛出超时错误的现象。下面的代码
给出了一个用于测试连接超时和读取数据超时的例子。
package
mynet;
import java.net. * ;
public class SocketTimeout
{
public static void main(String[] args)
{
long time1 = 0 , time2 = 0 ;
Socket socket = new Socket();
try
{
if (args.length < 4 )
{
System.out.println( " 参数错误! " );
return ;
}
time1 = System.currentTimeMillis();
socket.connect( new InetSocketAddress(args[ 0 ], Integer
.parseInt(args[ 1 ])), Integer.parseInt(args[ 2 ]));
socket.setSoTimeout(Integer.parseInt(args[ 3 ]));
time1 = System.currentTimeMillis();
socket.getInputStream().read();
}
catch (SocketTimeoutException e)
{
if ( ! socket.isClosed() && socket.isConnected())
System.out.println( " 读取数据超时! " );
else
System.out.println( " 连接超时 " );
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
finally
{
time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
}
}
}
import java.net. * ;
public class SocketTimeout
{
public static void main(String[] args)
{
long time1 = 0 , time2 = 0 ;
Socket socket = new Socket();
try
{
if (args.length < 4 )
{
System.out.println( " 参数错误! " );
return ;
}
time1 = System.currentTimeMillis();
socket.connect( new InetSocketAddress(args[ 0 ], Integer
.parseInt(args[ 1 ])), Integer.parseInt(args[ 2 ]));
socket.setSoTimeout(Integer.parseInt(args[ 3 ]));
time1 = System.currentTimeMillis();
socket.getInputStream().read();
}
catch (SocketTimeoutException e)
{
if ( ! socket.isClosed() && socket.isConnected())
System.out.println( " 读取数据超时! " );
else
System.out.println( " 连接超时 " );
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
finally
{
time2 = System.currentTimeMillis();
System.out.println(time2 - time1);
}
}
}
SocketTimeout类的main方法
需要4
个参数:IP
(域名)、端口、连接超时、读取数据超时。下面让我们来用一组数据来测试这个例子。
测试
1
:无效
IP
引发的超时错误
执行如下命令:
java mynet.SocketTimeout
192.168
.
18.24
80
3000
5000
运行结果:
连接超时
3045
3045
注意:192.168.18.24是一个无效的IP;如果这个IP在网络环境中存在,请换其它的无效的IP。在这个测试用例中不能将无效的IP换成无效的域名;这是因为如果使用了域名来连接服务器,Java会先通过DNS将域名映射成相应的IP;如果这个域名是无效的,在映射的过程中就会出错;因此,程序也就不会执行连接服务器操作,自然也就不会抛出“连接超时”错误了。
测试
2
:无效端口引发的超时错误
执行如下命令:
java mynet.SocketTimeout www.ptpress.com.cn
8888
3000
5000
运行结果:
连接超时
3075
3075
测试
3
:读取数据超时错误
执行如下命令:
java mynet.SocketTimeout www.ptpress.com.cn
80
3000
5000
运行结果:
读取数据超时
!
5008
5008
测试
4
:将读取数据超时设为
0
的效果
执行如下命令:
java mynet.SocketTimeout www.ptpress.com.cn
80
3000
0
运行结果:
Connection reset
131519
131519
从前3
个测试的输出结果不难看出,每个测试用例都将连接超时和读取数据超时分别设为3000
和5000
毫秒;而它们的运行结果并不是3000
和5000
毫秒,而是在所设定的超时时间的左右摇摆;这主要是因为系统所输出的时间并不都是超时时间;如有一些时间是Java
处理错误、向控制台输出信息的时间。另外,由于系统计时的误差,也会影响到超时时间的准确性。但不管怎样,超时时间总会在所设定的时间周围摇摆。
对于测试4
,将读取数据超时设为0
后,SocketTimeout类
经过了2
分多钟(131519
毫秒)
才抛出Connection reset
错误。这个抛出错误的时间和服务端程序的超时设置有关;也就是这个错误是由于服务端程序主动将客户端网络连接断开而产生的。
本文转自 androidguy 51CTO博客,原文链接:http://blog.51cto.com/androidguy/214445,如需转载请自行联系原作者