【问题标题】:Why my HTTP message ends before the body size reached the size stated in Content-Length header?为什么我的 HTTP 消息在正文大小达到 Content-Length 标头中规定的大小之前就结束了?
【发布时间】:2012-01-19 00:25:08
【问题描述】:

我用 Java 为 HTTP 消息编写了一个标记器。它有一个方法nextToken(),它应该返回一个包含接收到的整个HTTP 消息的字符串。问题是消息在预期的正文大小被读取之前就结束了。

我将输入流一直读取到正文的开头。然后我尝试从流中读取 n 个字节,其中 n 是 Content-Length 标头中规定的主体的字节大小。问题是在while 循环内,charsRead = in.read(buffer) 行阻塞,因为输入流中没有更多的输入。但它发生在 n 个字节被读取之前。

示例:在大小为 12,493 的主体中,当预期读取更多 675 个字节时,它会阻塞。

输入流使用 UTF-8,因此每个字节都被编码为一个 char

/* Somewhere else in the code: 
InputStreamReader _isr =
     new InputStreamReader(clientSocket.getInputStream(), "UTF-8")
*/
BufferedReader in = new BufferedReader(_isr);
StringBuilder tmp = new StringBuilder();
String line = "";
boolean body = false;
int bodylen = -1;

for (;;) {
   line = in.readLine();

   if (line == null)
       break;
   if (line.equals("")) { /* We've reached the body */
       body = true;
       break;
   }

   tmp.append(line + "\r\n");

   if ((bodylen == -1) && (line.contains("Content-Length:"))) {
       /* Make `bodylen` hold the length of the body */
       String[] splitted = line.split("Content-Length:");
       bodylen = Integer.parseInt(splitted[1].trim());
   }
}

if (body == true) { 
    int charsRead;
    char[] buffer = new char[1024];

    while (bodylen > 0) {
        charsRead = in.read(buffer);
        if (charsRead == -1)
            break;
        bodylen -= charsRead;
        tmp.append(buffer);
    }
}

为什么会发生以及如何解决?

【问题讨论】:

    标签: java http


    【解决方案1】:

    您似乎将字符与字节混淆了。 Content-Length 以字节为单位,但您正在计算字符。

    【讨论】:

    • 因为是 UTF-8 编码的,所以每个字节都编码为一个char。我还用调试器检查了它。
    • 另外,如果不是这种情况,那么缓冲区读取将在中途停止...
    • Julian 正确地指出 UTF-8 字符可能需要最多 6 个字节。
    • UTF-8 != 每个字符 1 个字节! UTF-8 == 1 到 6 个字节,具体取决于字符。
    • 那么...每次迭代我应该从bodylen 中减去什么?
    【解决方案2】:

    您使用了错误的read() 方法。您应该使用read(byte[], int start, int len) 方法。

    以下是您应该如何阅读的示例助手:

    private void readAll(InputStream is, byte[] buffer){
        int read = 0;
        while (read != buffer.length){
           int ret = in.read(buffer, read, buffer.length - read);
           if (ret == -1) return;
           read += ret;
        }
    }
    

    您在代码中所做的是每次调用 read 时都要求 API 读取 1024 个字节。发生的事情是底层 InputStream 只能读取 675 个字节(这是一个网络调用,所以这是可以预料的),在您通过循环的下一次迭代中,您要求 API 再次读取 1024 个字节。 API 读取剩余的(1024 - 675 字节)并阻塞,直到它填满整个缓冲区,这是永远无法做到的,因为您已经将读取拆分为 2 个调用(您的代码也会覆盖先前的读取,因为它们都从 0 开始)。

    在处理网络内容时这是非常正常的行为,人们习惯于处理文件,当他们无法完全准备好缓冲区长度时,他们会觉得很奇怪。

    【讨论】:

    • 你错了。你写道:“......并阻塞,直到它填满整个缓冲区” - 这是不正确的。根据 Java,read 方法会阻塞,直到输入流中出现某些输入,直到缓冲区已满。
    • Leif,重新阅读 API。读取(缓冲区)== 读取(缓冲区,0,缓冲区长度)== 而(int char = 读取()!= -1)。如果可以或在流结束时停止,api 将尝试读取整个缓冲区。 “输入出现之前的阻塞”是 read() API 的继承。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 2017-03-25
    相关资源
    最近更新 更多