如何在 Java 1.4 中设置 BufferedReader 和 PrintWriter 的超时时间?

Posted

技术标签:

【中文标题】如何在 Java 1.4 中设置 BufferedReader 和 PrintWriter 的超时时间?【英文标题】:How do you set a timeout on BufferedReader and PrintWriter in Java 1.4? 【发布时间】:2011-10-11 04:37:46 【问题描述】:

如何在使用套接字连接创建的 BufferedReader 和 PrintWriter 上设置超时?这是我现在为服务器提供的代码,它可以在服务器或客户端崩溃之前工作:

while(isReceiving)
    str = null;
    BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);

    while ((str = br.readLine()) != null)
        System.out.println("Processing command " + str);
        pw.println(client.message(str));
    

在此代码范围之外,我设置了 1000 毫秒的套接字超时,这在等待初始连接时按预期工作。但是程序在 (str = br.readLine()) 处阻塞。如果客户端挂起或崩溃,它永远不会停止阻塞,除非我终止进程(即使那样并不总是有效)。

有问题的客户端代码与此非常相似,并且以类似的方式阻塞。

【问题讨论】:

【参考方案1】:

    您需要在套接字上设置读取超时,使用Socket.setSoTimeout()。如果指定的读取超时到期,这将导致任何读取方法抛出SocketTimeoutException。注意 读取超时不是在流上设置的,而是在底层Socket, 通过Socket.setSoTimeout().

    TCP 中不存在写超时。

【讨论】:

在上面我发布了我的解决方法以在我的情况下获得所需的行为,但在这种情况下没有办法停止阻止?正如我的问题所述,我在我发布的内容之外调用了 socket.setSoTimeout(1000L),所以套接字不应该在此时超时并结束套接字上的所有读写操作吗? @MetalSearGolid 它应该结束读取操作,并在读取阻塞超时时间后出现 SocketTimeoutException。正如我已经说过的,TCP 中没有写超时。 @YoussefDir 这设置了读取超时,正如我在答案中所述。不是写超时,【参考方案2】:

您可以使用来自 Google 的 Guava 库的 SimpleTimeLimiter。

示例代码(在 Java 8 中):

BufferedReader br = ...;
TimeLimiter timeLimiter = new SimpleTimeLimiter();

try 
    String line = timeLimiter.callWithTimeout(br::readLine, 10, TimeUnit.SECONDS);
 catch (TimeoutException | UncheckedTimeoutException e) 
    // timed out
 catch (Exception e) 
    // something bad happened while reading the line

【讨论】:

发布的链接已失效,您现在可以在github.com/google/guava/tree/master/guava/src/com/google/common/…找到该项目 更新了我的答案。谢谢! ...Java 8。问题说Java 1.4。 所描述的原理将在 Java 1.4 中完美运行——唯一的区别是语法。 Guava 代码仅在相关的read() 方法是可中断的情况下才有效。 java.net.Socket 读取不是。【参考方案3】:

question 中的答案描述了一个有趣的方法,使用 Timer 来关闭连接。我不能 100% 确定这在阅读过程中是否有效,但值得一试。

从那个答案复制:

TimerTask ft = new TimerTask()
   public void run()
     if (!isFinished)
       socket.close();
     
   
;

(new Timer()).schedule(ft, timeout);

isFinished 应该是一个 boolean 变量,当您从流中读取完毕后,该变量应该设置为 true

【讨论】:

+1:我会使用 socket.close() 来关闭套接字和流。您可以多次关闭它。 您也可以说出isFinished 测试的用途。 ;) 我会试一试,但我怀疑 socket.close() 是否会起作用,因为我已经尝试过以这种方式关闭它......没有骰子。我要关闭的套接字是否在另一个线程中应该没关系吗? 这不起作用,即使在我关闭套接字后 br.readLine() 仍然会继续阻塞。不过,我确实找到了解决方法,我会尽快将其作为答案发布。 @BendertheGreatest 不可能。你会得到一个socket closed 异常。【参考方案4】:

由于调用 socket.close() 似乎没有中断 br.readLine() 处的块,所以我做了一些解决方法。在断开客户端与服务器的连接时,我只是通过一个字符串“bye”发送,并告诉服务器在收到此命令时关闭套接字连接。

while ((str = br.readLine()) != null)
    // If we receive a command of "bye" the RemoteControl is instructing
    // the RemoteReceiver to close the connection.
    if (str.equalsIgnoreCase("bye"))
        socket.close();
            break;
    
    System.out.println("Processing command " + str);
    pw.println(client.message(str));

【讨论】:

如果你可以发送“再见”,你可以直接关闭套接字,这将导致对端的readLine() 返回 null。很难看出这如何解决您所说的问题。 我问这个问题已经有一段时间了,但是如果内存服务于从客户端关闭套接字后监听进程继续挂起。我记得我们使用了一些伪善的 Java 自定义实现,但它的行为并不总是像人们预期的那样。 你所说的不可能。请重新测试。

以上是关于如何在 Java 1.4 中设置 BufferedReader 和 PrintWriter 的超时时间?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java 中设置 SourceDataLine 的音量

如何在java中设置最高位?

如何在 Java 应用程序中设置锁定模式

如何在 Java 中设置标签(彩色文本)的颜色?

如何在 JAVA 中设置主机名?

如何在 Ubuntu 中设置 Java 环境路径