/**
* Enable/disable {@link SocketOptions#SO_TIMEOUT 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 <B>java.net.SocketTimeoutException</B> is raised, though the
* Socket is still valid. The option <B>must</B> be enabled
* prior to entering the blocking operation to have effect. The
* timeout must be {@code > 0}.
* A timeout of zero is interpreted as an infinite timeout.
*
* @param timeout the specified timeout, in milliseconds.
* @exception SocketException if there is an error
* in the underlying protocol, such as a TCP error.
* @since JDK 1.1
* @see #getSoTimeout()
*/
public synchronized int getSoTimeout() throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
Object o = getImpl().getOption(SocketOptions.SO_TIMEOUT);
/* extra type safety */
if (o instanceof Integer) {
return ((Integer) o).intValue();
} else {
return 0;
}
}
public synchronized void setSoTimeout(int timeout) throws SocketException {
if (isClosed())
throw new SocketException("Socket is closed");
if (timeout < 0)
throw new IllegalArgumentException("timeout can't be negative");
getImpl().setOption(SocketOptions.SO_TIMEOUT, new Integer(timeout));
}
public void setSoTimeout(int timeout) throws SocketException
使用指定的超时时间启用/禁用SO_TIMEOUT(以毫秒为单位)。 使用此选项设置为非零超时时,
与此Socket相关联的InputStream上的read()调用将仅阻止此时间。
如果超时超时,则引发java.net.SocketTimeoutException ,尽管Socket仍然有效。
必须先启用该选项才能进入阻止操作才能生效。 超时时间必须为> 0 。 超时为零被解释为无限超时。
参数: timeout - 指定的超时时间,以毫秒为单位。
异常: SocketException - 如果底层协议有错误,如TCP错误。
/** Set a timeout on blocking Socket operations:
* <PRE>
* ServerSocket.accept();
* SocketInputStream.read();
* DatagramSocket.receive();
* </PRE>
*
* <P> The option must be set prior to entering a blocking
* operation to take effect. If the timeout expires and the
* operation would continue to block,
* <B>java.io.InterruptedIOException</B> is raised. The Socket is
* not closed in this case.
*
* <P> Valid for all sockets: SocketImpl, DatagramSocketImpl
*
* @see Socket#setSoTimeout
* @see ServerSocket#setSoTimeout
* @see DatagramSocket#setSoTimeout
*/
@Native public final static int SO_TIMEOUT = 0x1006;
@Native
static final int SO_TIMEOUT
在阻塞套接字操作时设置超时:
ServerSocket.accept();
SocketInputStream.read();
DatagramSocket.receive();
必须先设置该选项才能进入阻止操作才能生效。 如果超时过期,并且操作将继续阻止,
则引发java.io.InterruptedIOException 。 在这种情况下,Socket不关闭。
适用于所有套接字:SocketImpl,DatagramSocketImpl
另请参见:
Socket.setSoTimeout(int) , ServerSocket.setSoTimeout(int) ,
DatagramSocket.setSoTimeout(int) , Constant Field Values
如果输入缓冲队列RecvQ中没有数据,read操作会一直阻塞而挂起线程,直到有新的数据到来或者有异常产生。调用setSoTimeout(int timeout)可以设置超时时间,如果到了超时时间仍没有数据,read会抛出一个SocketTimeoutException,程序需要捕获这个异常,但是当前的socket连接仍然是有效的。
如果对方进程崩溃、对方机器突然重启、网络断开,本端的read会一直阻塞下去,这时设置超时时间是非常重要的,否则调用read的线程会一直挂起。
TCP模块把接收到的数据放入RecvQ中,直到应用层调用输入流的read方法来读取。如果RecvQ队列被填满了,这时TCP会根据滑动窗口机制通知对方不要继续发送数据,本端停止接收从对端发送来的数据,直到接收者应用程序调用输入流的read方法后腾出了空间。
代码说明SoTimeout
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerMain {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8888);
long t1 = 0;
try {
Socket socket = serverSocket.accept();
System.out.println("服务端接收到一个连接");
t1 = System.currentTimeMillis();
//设置该通道的read()方法超时
socket.setSoTimeout(5000);
InputStream inputStream = accept.getInputStream();
//read阻塞
inputStream.read();
} finally {
System.out.println("服务端setSoTimeout 耗时:"
+ (System.currentTimeMillis() - t1));
}
}
}
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class ClientMain {
public static void main(String[] args) throws Exception {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(8888));
//设置超时时间
socket.setSoTimeout(10000);
InputStream inputStream = socket.getInputStream();
long t1 = System.currentTimeMillis();
try {
inputStream.read();
} finally {
System.out.println("客户端setSoTimeout 耗时:"
+ (System.currentTimeMillis() - t1));
}
}
}
启动以后
服务端日志
服务端接收到一个连接
服务端setSoTimeout 耗时:5007
Exception in thread "main" java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.net.SocketInputStream.read(SocketInputStream.java:224)
at cn.java.money.bio.demo9.ServerMain.main(ServerMain.java:21)
客户端日志
客户端setSoTimeout 耗时:5496
Exception in thread "main" java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:210)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.net.SocketInputStream.read(SocketInputStream.java:224)
at cn.java.money.bio.demo9.ClientMain.main(ClientMain.java:17)
说明:
soTimeout默认值是0,也就是没有超时时间,会无限的等待。
服务端抛出异常 java.net.SocketTimeoutException: Read timed out 是因为我们设置了soTimeout socket.setSoTimeout(5000); 但是客户端一致没有写数据,服务端读数据等待5000毫秒,超时就抛出异常SocketTimeoutException,抛出异常以后,该连接就会被关闭,向客户端发送了RST包。因此客户端收到的是java.net.SocketException: Connection reset (RST包)。https://www.yuque.com/protocal/tcp/nq74g5