关于c#:使用HttpWebResponse读取“分块”响应

关于c#:使用HttpWebResponse读取“分块”响应

Reading “chunked” response with HttpWebResponse

使用StreamReader读取HttpWebResponse的GetResponseStream()返回的流时,我无法读取"分块"响应:

1
2
3
// 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),但这似乎是一个me脚的解决方法。

有任何想法吗?

@查克

您的解决方案效果很好。 它仍然在最后一个Read()上抛出相同的IOExeception。 但是在检查了StringBuilder的内容之后,看起来好像已经收到了所有数据。 因此,也许我只需要在尝试捕获中包装Read()并吞下"错误"。


还没有尝试过以"块状"响应的方式进行尝试,但是这样的事情会起作用吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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


在尝试了StackOverflow和Google的很多代码段之后,最终我发现这是最好的方法(假设您知道数据是UTF8字符串,如果没有,则可以保留字节数组并进行适当处理):

1
2
3
4
5
byte[] data;
var responseStream = response.GetResponseStream();
var reader = new StreamReader(responseStream, Encoding.UTF8);
data = Encoding.UTF8.GetBytes(reader.ReadToEnd());
return Encoding.Default.GetString(data.ToArray());

我发现大多数时候其他版本都可以使用,但是偶尔会截断数据。我从以下获得此代码段:

https://social.msdn.microsoft.com/Forums/zh-CN/4f28d99d-9794-434b-8b78-7f9245c099c4/problems-with-httpwebrequest-and-transferencoding-chunked?forum=ncl


我遇到了同样的问题(这就是我到这里结束的方式:-)。最终将其追溯到分块流无效的事实-最终的零长度块丢失了。我想出了以下代码,可同时处理有效和无效的分块流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}

克雷格(Craig),虽然看不到您正在阅读的流,但调试起来有些困难,但是也许您可以将count变量的设置更改为:

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

这有点骇人听闻,但是如果最后一次读取使您丧命并且不返回任何数据,那么从理论上讲,这将避免该问题。我仍然想知道为什么流正在这样做。


推荐阅读