当使用 StreamReader 读取 HttpWebResponse 的 GetResponseStream() 返回的流时,我在读取“分块”响应时遇到问题:

// response is an HttpWebResponse
StreamReader reader = new StreamReader(response.GetResponseStream());
string output = reader.ReadToEnd(); // throws exception...

当。。。的时候 reader.ReadToEnd() 调用方法我收到以下 System.IO.IOException: 无法从传输连接读取数据:连接已关闭。

当服务器返回“非分块”响应时,上面的代码可以正常工作。

我能够让它工作的唯一方法是使用 HTTP/1.0 进行初始请求(而不是默认的 HTTP/1.1),但这似乎是一个蹩脚的解决方法。

有任何想法吗?


@查克

你的解决方案效果很好。它仍然在最后一个 Read() 上抛出相同的 IOException。但在检查 StringBuilder 的内容后,看起来所有数据都已收到。所以也许我只需要将 Read() 包装在 try-catch 中并吞下“错误”。

有帮助吗?

解决方案

还没有尝试过“分块”响应,但是这样的东西会起作用吗?

StringBuilder sb = new StringBuilder();
Byte[] buf = new byte[8192];
Stream resStream = response.GetResponseStream();
string tmpString = null;
int count = 0;
do
{
     count = resStream.Read(buf, 0, buf.Length);
     if(count != 0)
     {
          tmpString = Encoding.ASCII.GetString(buf, 0, count);
          sb.Append(tmpString);
     }
}while (count > 0);

其他提示

我正在研究类似的问题。.net HttpWebRequest 和 HttpWebRequest 自动处理 cookie 和重定向,但它们不会自动处理响应正文上的分块内容。

这可能是因为分块内容可能包含的不仅仅是简单数据(即:块名称、尾随标头)。

简单地读取流并忽略 EOF 异常是行不通的,因为流包含的内容多于所需的内容。流将包含块,每个块首先声明其大小。如果只是从头到尾读取流,则最终数据将包含块元数据(如果是 gzip 内容,则在解压缩时将无法通过 CRC 检查)。

为了解决这个问题,需要手动解析流,从每个块中删除块大小(以及 CR LF 分隔符),检测最终块并仅保留块数据。可能有一个图书馆可以做到这一点,但我还没有找到。

有用的资源:

http://en.wikipedia.org/wiki/Chunked_transfer_encoding http://tools.ietf.org/html/rfc2616#section-3.6.1

Craig,如果没有看到您正在读取的流,调试起来有点困难,但也许您可以将 count 变量的设置更改为:

count = resStream.Read(buf, 0, buf.Length-1);

这有点像黑客,但如果最后一次读取让你丧命并且它没有返回任何数据,那么理论上这将避免这个问题。我仍然想知道为什么流要这样做。

我也遇到了同样的问题(这就是我最终来到这里的原因:-)。最终追踪到分块流无效的事实 - 最终的零长度块丢失。我想出了以下代码来处理有效和无效的分块流。

using (StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
    StringBuilder sb = new StringBuilder();

    try
    {
        while (!sr.EndOfStream)
        {
            sb.Append((char)sr.Read());
        }
    }
    catch (System.IO.IOException)
    { }

    string content = sb.ToString();
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top