【问题标题】:Why is the end of the input stream never reached using Java Sockets?为什么使用 Java Sockets 永远不会到达输入流的末尾?
【发布时间】:2010-10-06 19:18:44
【问题描述】:

我正在用 Java 编写一个简单的代理。我无法将给定请求的全部内容读入字节数组。具体来说,在下面的循环中,即使客户端已经发送了它将发送的所有数据(也就是说,永远不会到达流的末尾),对“读取”的调用也会阻塞。因为在我读完整个输入之前我不能确定是时候开始写输出了,这会造成一些麻烦。如果我终止与服务器的连接,则最终到达流的末尾,并且一切都顺利进行(来自客户端的所有数据,在这种情况下,Firefox 请求 www.google.com,已被服务器读取,并且它能够根据需要对其进行处理,但显然它无法将任何内容发送回客户端)。

public static void copyStream(InputStream is, OutputStream os) throws IOException
{
    int read = 0;
    byte[] buffer = new byte[BUFFER_SIZE];
    while((read = is.read(buffer, 0, BUFFER_SIZE)) != -1)
    {
      os.write(buffer, 0, read);
    }
    return;
}

InputStream 直接来自客户端套接字(getInputStream(),然后缓冲); OutputStream 是一个 ByteArrayOutputStream。

我做错了什么?

【问题讨论】:

    标签: java sockets stream


    【解决方案1】:

    请记住,并非所有连接都有Content-Length 标头;有些人可能正在使用Transfer-Encoding: chunked,其中内容长度被编码并包含在正文中。

    【讨论】:

      【解决方案2】:

      所有现代浏览器都支持的 HTTP 1.1 有一个称为“keep-alive”或“持久连接”的功能,默认情况下允许客户端为多个请求重用与服务器的 HTTP 1.1 连接(请参阅@ 987654321@)。 因此,如果您将 FF 指向 http://www.google.com,则与 www.google.com:80 的连接将保持打开一段时间,即使第一个请求已完成。因此,如果您的应用程序对 HTTP 协议没有基本了解,您将无法知道是否所有数据都已发送。 您可以通过在连接上使用超时以某种方式规避这种情况,希望客户端不会卡在某个地方,并且静音实际上意味着数据块的结束。 另一种方法是重写服务器响应标头,将您的代理宣传为符合 HTTP 1.0 而不是 1.1,从而禁止客户端使用持久连接。

      【讨论】:

        【解决方案3】:

        通常在 HTTP 中,Content-Length 标头指示您应该从流中读取多少数据。基本上,它会告诉您指示 HTTP 标头结束的双换行符(实际上是双-\r\n)后面有多少字节。请参阅W3C 了解更多信息...

        如果没有发送Content-Length 标头,您可以尝试在经过一定时间后中断读取,并且没有通过连接发送数据,尽管这绝对不是可取的。

        (我假设您将以某种方式处理正在读取的数据,否则您可以在读取时写出每个字节)

        【讨论】:

        • 完全正确。在套接字级别,除非一方明确关闭连接,否则流将永远不会结束,因为它可以重复使用(参见:连接:保持活动)。
        • 唉,这就是我所怀疑的……确实让我做的事情变得更丑了;如果请求中的第一件事是大小,那就太好了。
        • 哇,不错的收获。我怀疑你的方法应该适用于几乎所有的东西,除了 http 连接。有大量的文件/流复制代码与您所拥有的完全一样。
        猜你喜欢
        • 2021-08-01
        • 2016-10-29
        • 1970-01-01
        • 2021-02-17
        • 2020-06-12
        • 2022-11-20
        • 2020-02-29
        • 1970-01-01
        • 2019-12-21
        相关资源
        最近更新 更多