/*** 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;
@Nativestatic 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 耗时:5007Exception in thread "main" java.net.SocketTimeoutException: Read timed outat 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 耗时:5496Exception in thread "main" java.net.SocketException: Connection resetat 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
