Java,MalformedChunkCodingException

Vai*_*hav 3 java apache android http

我有一个 Android 应用程序给了我这个例外:

org.apache.http.MalformedChunkCodingException: CRLF expected at end of chunk
Run Code Online (Sandbox Code Playgroud)

该方法抛出异常:(目的是将服务器收到的响应写出到文件中。)

public static void getResponseBodyForServerData(
    final HttpEntity entity) throws IOException, ParseException {

    if (entity == null) {
        throw new IllegalArgumentException("HTTP entity may not be null");
    }
    InputStream instream = entity.getContent();

    if (instream == null) {
        return;
    }
    File file = new File(Environment.getExternalStorageDirectory() + 
        "/foo/Response.txt");

    if (!file.exists()) {
        file.createNewFile();
    }
    OutputStream out = new FileOutputStream(file);
    byte buf[] = new byte[1024];
    int len;
    while ((len = instream.read(buf)) > 0)
        out.write(buf, 0, len);
    out.flush();
    out.close();
}
Run Code Online (Sandbox Code Playgroud)

于是我把上面的代码修改为:

public static void getResponseBodyForServerData(
    final HttpEntity entity) throws IOException, ParseException {

    if (entity == null) {
        throw new IllegalArgumentException("HTTP entity may not be null");
    }
    InputStream instream = entity.getContent();
    InputStreamReader inputStreamReader = new InputStreamReader(
        instream, "UNICODE") ;

    if (instream == null) {
        return;
    }
    File file = new File(Environment.getExternalStorageDirectory() + 
        "/foo/Response.txt");
    if (!file.exists()) {
        file.createNewFile();
    }

    BufferedReader bufferedReader = new BufferedReader(inputStreamReader) ;
    BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(
        new FileOutputStream(file),"UNICODE")) ;

    String singleLine = null ;

    while((singleLine = bufferedReader.readLine()) != null) {
        bufferedWriter.write(singleLine) ;
    }

    bufferedWriter.flush() ;
    bufferedWriter.close() ;
}
Run Code Online (Sandbox Code Playgroud)

然后代码就可以工作了,是什么导致了这个错误,原始代码有什么问题?

Ari*_*ion 5

我发现这个问题相当老了,尽管如此,当我搜索 MalformedChunkCodingException 时,它还是出现了。所以这里有一些提示正在发生的事情。

我只能猜测你的问题是什么,因为你没有告诉我们异常发生在哪一行以及你使用的 org.apache.http.HttpEntity 版本。

首先MalformedChunkCodingException: CRLF expected at end of chunk确实是问题所在。当您进行分块 http 传输并且该传输中至少\r\n缺少一个时,就会发生这种情况。

有两种可能的错误。首先,服务器不发送它们,或者客户端丢失它们。

要检查服务器是否正常,请检查请求响应是否正常。例如,您可以使用curl 查看十六进制响应:

curl -ivs --raw http://host:port/somePath/toResource --trace /dev/stdout
Run Code Online (Sandbox Code Playgroud)

这是一些示例输出:

00a0: 30 31 22 7d 5d 0d 0a 30 0d 0a 0d 0a             01"}]..0....
Run Code Online (Sandbox Code Playgroud)

看到输出中的\r\nas 了吗?0d 0a这意味着服务器实际上发送了正确的消息。

因此,您在读取响应时可能会丢失一些字节。

我使用了调试器并为 MalformedChunkCodingException(Eclipse 中的 Java 异常断点)设置了断点。我查看了第一次处理传入响应的堆栈,实际上缓冲区中只有一半的响应。

分块消息是流消息。因此,您可能在 InputStream 读取完整消息之前关闭了套接字。例如,这发生在泽西岛 apache 连接器中。

他们有这样的代码:

return new FilterInputStream(inputStream) {
    @Override
    public void close() throws IOException {
        response.close();
        super.close();
    }
Run Code Online (Sandbox Code Playgroud)

失败了:

JulianasJezov 2016 年 10 月 18 日

使用分块传输编码时,此更改不适用于 org.apache.httpcomponents: httpclient: 4.5.1+。这是因为他们改变了关闭连接的方式。

在这种情况下,当调用response.close()时,连接被关闭并清除输入流的缓冲区,然后super.close()函数无法关闭ChunkedInputStream并导致org.apache.http.ConnectionClosedException:过早结束块编码消息正文:预期关闭块。

发生异常的原因是,在关闭 ChunkedInputStream 时,close() 函数读取分块消息的其余部分,但失败,因为在调用 response.close() 中关闭连接时流的缓冲区已被清除。参考: https://hc.apache.org/httpclient-3.x/apidocs/org/apache/commons/httpclient/ChunkedInputStream.html#close()

解决此问题的方法是将 super.close() 行代码放在 response.close() 之前。这允许在清除缓冲区之前正确关闭流。

所以当我发现在我们的项目中我们在 maven pom 中使用了不兼容版本的jersey-apache-connector(旧)和http-client(新)时,我的问题就解决了。(依赖层次结构说httpclient:4.5.3 (managed from 4.5) (ommitted for conflict with 4.5.3):)