【问题标题】:How to create a Java non-blocking InputStream from a HttpsURLConnection?如何从 HttpsURLConnection 创建 Java 非阻塞 InputStream?
【发布时间】:2011-02-19 06:41:21
【问题描述】:

基本上,我有一个 URL,可以在发布新消息时从聊天室流式传输 xml 更新。我想将该 URL 转换为 InputStream 并继续从中读取,只要保持连接并且只要我没有发送 Thread.interrupt()。我遇到的问题是当有内容要从流中读取时,BufferedReader.ready() 似乎不会成为真的。

我正在使用以下代码:

BufferedReader buf = new BufferedReader(new InputStreamReader(ins));


String str = "";
while(Thread.interrupted() != true)
{
    connected = true;
    debug("Listening...");

    if(buf.ready())
    {
        debug("Something to be read.");
        if ((str = buf.readLine()) != null) {
            // str is one line of text; readLine() strips the newline character(s)
            urlContents += String.format("%s%n", str);
            urlContents = filter(urlContents);
        }
    }

    // Give the system a chance to buffer or interrupt.
    try{Thread.sleep(1000);} catch(Exception ee) {debug("Caught thread exception.");}
}

当我运行代码并将某些内容发布到聊天室时,buf.ready() 永远不会变为 true,导致这些行永远不会被读取。但是,如果我跳过“buf.ready()”部分并直接读取行,它会阻止进一步的操作,直到读取行。

我该如何 a) 让 buf.ready() 返回 true,或 b) 以防止阻塞的方式执行此操作?

提前致谢, 詹姆斯

【问题讨论】:

  • 每个连接都应该分成一个单独的线程。

标签: java inputstream nonblocking


【解决方案1】:

对于非阻塞 IO,不要使用 InputStream 和 Reader(或 OutputStream/Writer),而是使用 java.nio.* 类,在本例中为 SocketChannel(以及额外的 CharsetDecoder)。


编辑:作为对您评论的回答:

专门寻找如何创建到 https url 的套接字通道。

套接字(以及 SocketChannel)在传输层 (TCP) 上工作,比 HTTP 等应用层协议低一层(或两层)。所以你不能创建一个到 https url 的套接字通道

您将不得不打开一个 Socket-Channel 到正确的服务器和正确的端口(如果 URI 中没有给出其他内容,则为 443),在客户端模式下创建一个 SSLEngine(在 javax.net.ssl 中),然后读取数据从通道,将其馈送到 SSL 引擎,反之亦然,向/从您的 SSLEngine 发送/获取正确的 HTTP 协议行,始终检查返回值以了解实际处理了多少字节以及将是什么下一步要采取的措施。

这个is quite complicated(我做过一次),如果你没有实现一个同时连接很多客户端的服务器(你不能有一个线程),你真的不想这样做每个连接)。相反,保留从 URLConnection 读取的阻塞 InputStream,并将其简单地放在不会妨碍应用程序其余部分的备用线程中。

【讨论】:

  • 专门寻找如何创建一个到 https url 的套接字通道。
  • @Warkior:看我上次的编辑——你真的不想这样做。
  • 您好 Paŭlo,感谢您的建议。这很有意义,并回答了我对该特定方法的主要关注。我真的无法控制服务器......只是从流中读取的客户端。在这种情况下是否有适当的方法来终止阻塞的连接?如果用户更改为新的聊天室,则需要执行此操作。 (意味着系统需要开始监听不同的流,终止旧的监听器)
【解决方案2】:

如何创建Java非阻塞InputStream

你不能。您的问题在术语上体现了矛盾。 Java 中的流是阻塞的。因此不存在“非阻塞InputStream”之类的东西。

Reader.ready() 在可以无阻塞地读取数据时返回 true。时期。 InputStreamsReaders 正在阻塞。时期。这里的一切都按设计工作。如果您希望这些 API 具有更高的并发性,您将不得不使用多个线程。或Socket.setSoTimeout() 及其在HttpURLConnection 中的近亲。

【讨论】:

  • 我知道我可以将事情拆分成线程......这段代码已经在它自己的线程(可运行)对象中。我想知道如何通过发送某种中断来停止线程。当输入流等待更多数据发布到流中时,它似乎会阻塞其他所有内容,包括 thread.interrupts。
  • 如果(如您所说)Reader 自然阻塞,您能否描述 Reader.ready() 能够返回 true 的情况?根据您上面所说的,Reader.ready() 似乎是一个无用的方法。
  • 如果数据已经可用,ready() 返回 true,Reader 不会阻塞。如果没有可用数据,ready() 返回 false,Reader 将阻塞。
  • 那么,如果我知道流中有数据可供读取,为什么 buf.ready() 会继续返回 false?这就是让我感到困惑的部分。我知道流中有可供读取的数据。
  • 你无法“知道”这一点。只有 ready() 知道(和 InputStream.available(),在两种情况下都支持)。没有其他测试。对于像 SSL 这样的一些流,两者都不支持,所以 ready() 返回 false 并且 available() 返回零。 Aso 可用数据和可用于 readLine() 的完整行(包括行终止符)之间存在差异。 readLine() 将阻塞直到所有到达
【解决方案3】:

您可以使用提供非阻塞 I/O 功能的 Java NIO 库。详情和示例代码请看这篇文章:http://www.drdobbs.com/java/184406242

【讨论】:

  • 这看起来像是我正在寻找的导致线程超时、中断阻塞连接的方法。打算试一试。谢谢。
  • 嗯。还没有设法找到一种方法来正确中断被阻塞的线程。也许我只是做得不对。
【解决方案4】:
import java.io.InputStream;
import java.util.Arrays;

/**
 * This code demonstrates non blocking read from standard input using separate
 * thread for reading.
 */
public class NonBlockingRead {

    // Holder for temporary store of read(InputStream is) value
    private static String threadValue = "";

    public static void main(String[] args) throws InterruptedException {

        NonBlockingRead test = new NonBlockingRead();

        while (true) {
            String tmp = test.read(System.in, 100);
            if (tmp.length() > 0)
                System.out.println(tmp);
            Thread.sleep(1000);
        }
    }

    /**
     * Non blocking read from input stream using controlled thread
     * 
     * @param is
     *            — InputStream to read
     * @param timeout
     *            — timeout, should not be less that 10
     * @return
     */
    String read(final InputStream is, int timeout) {

        // Start reading bytes from stream in separate thread
        Thread thread = new Thread() {

            public void run() {
                byte[] buffer = new byte[1024]; // read buffer
                byte[] readBytes = new byte[0]; // holder of actually read bytes
                try {
                    Thread.sleep(5);
                    // Read available bytes from stream
                    int size = is.read(buffer);
                    if (size > 0)
                        readBytes = Arrays.copyOf(buffer, size);
                    // and save read value in static variable
                    setValue(new String(readBytes, "UTF-8"));
                } catch (Exception e) {
                    System.err.println("Error reading input stream\nStack trace:\n" + e.getStackTrace());
                }
            }
        };
        thread.start(); // Start thread
        try {
            thread.join(timeout); // and join it with specified timeout
        } catch (InterruptedException e) {
            System.err.println("Data were note read in " + timeout + " ms");
        }
        return getValue();

    }

    private synchronized void setValue(String value) {
        threadValue = value;
    }

    private synchronized String getValue() {
        String tmp = new String(threadValue);
        setValue("");
        return tmp;
    }

}

【讨论】:

  • 这不是非阻塞 I/O。这是一个用超时阻塞 I/O 的徒劳和冗余示例,这已经可以通过读取超时和SocketTimeoutException 来完成。该代码显然甚至没有经过测试。 InputStream.read(byte[]) 不能返回零,除非缓冲区的长度为零,它不在这里。您没有测试流的结束,也没有将其传回给原始调用者。 String 不是潜在二进制数据的容器。答案在每个细节上都不正确。
  • 感谢指出的逻辑错误。我修好了它们。通常,此代码的目的是在面向文本的服务器套接字中运行单元测试。如果你能提供更优雅的解决方案,我会很乐意使用它。
  • 没有人可以为包含矛盾的问题提供代码。
【解决方案5】:

没有使用通道的 HTTP/HTTPS 实现。无法以非阻塞方式从 httpurlconnaction 读取输入流。您要么必须使用第三方库,要么自己通过 SocketChannel 实现 http。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    • 2010-10-11
    • 1970-01-01
    • 2012-03-21
    相关资源
    最近更新 更多