http的响应数据由下面2部分构成:
响应头 + 数据部分
数据部分的格式由响应头说明
一般情况下,HTTP的响应头包含Content-Length域来指明数据的长度,例如:
有时候,服务器生成HTTP响应时,不好确定响应数据的大小,可能是大文件的下载或后台需要复杂的逻辑处理生成页面,这样一般就使用chunked编码进行传输。使用chunked编码进行传输不用事先说明要传输的页面内容的大小,通过一定的规则指明传输结束。
通常使用chunked编码进行传输的时候,会先将数据进行压缩。HTTP响应头中的Content-Encoding域指明了压缩格式。
当数据很大的时候,还会分多个块(chunk)传输。chunked编码的格式如下:
注意chunk数据长度的单位是字节,不包括后面的\r\n。以一个长度为0的块作为结尾。
把所有的chunk数据部分组合起来存入文件,就是一个标准的gzip压缩文件。
怎么样把所有chunk数据部分提取出来呢?明白了上面的格式,其实就很简单了。
如果我们是通过TCP接收的数据,首先要解决一个问题:找到chunk数据开始的地方,上面提到chunk前面是HTTP响应头。其它先不管,响应头是以连续的\r\n\r\n结尾的,这之后就是响应的数据部分。
假设我们有一个buffer
char *m_contentData;
里面存储着按序收到的所有响应数据。
可以通过如下方式移动到数据部分,即chunked数据开始的地方(下图所示:从A移动到B)
int n=0;
while(*m_contentData)
{
if (*m_contentData=='\r' || *m_contentData=='\n')
n++;
else
n=0;
if (n==4)
break;
m_contentData++;
}
定义char *m_gzipContentData保存gzip数据,可以通过如下方式获取到gzip数据,然后写入文件。
int len=0;
char ss[10];
char *p = m_contentData;
char *q = m_gzipData;
int gzip_len = 0;
while(1)
{
sscanf(p, "%s", ss);
//read chunk-size
len = HexToDecimal(ss); //convert hex to decimal
if (len == 0) //to chunk end
break;
p+=2; //read \r\n
memcpy(q, p, len);
q += len;
p += len;
gzip_len += len;
p += 2; //read \r\n
}
fwrite(m_gzipData, 1, gzip_len, fp);