以前在制作一个项目的时候需要下载网页页面代码来处理信息,可是用一般的方式下载
网页的速度太慢,而且有可能会下载出错,我就在想有没有一种办法可以提高下载网页页面
的速度。一次我用火狐截包的时候偶然发现了“Accept-Encoding: gzip, deflate”的字样,
然后使用这样的包返回的却不是ASCII表示的页面代码并且数据长度大幅度缩短了!我就猜
这是不是一种压缩的传输方式,立刻Google了一下,这的确是一种压缩传输的方式。继续
搜索,可惜,网上没有找到如何使用它的代码,没办法,只能自力更生了。
原理
在HTTP1.1(rfc2616)中,定义了三种内容编码格式(Content Codings),即压缩算法:
gzip、compress和deflate,但大多数浏览器只支持gzip和deflate。在实际使用中,还
有其他压缩格式,如目前Google Chrome支持sdch,不再支持bzip2。
gzip是基于deflate算法的封装,deflate代表的是zlib stream,两者有些微区别。
我们这里只讨论应用gzip的压缩数据传输,因为大多数网站支持gzip数据压缩。
实现
1.在发送的请求头中包含Accept-Encoding,指定gzip压缩格式,如:Accept-Encoding:
gzip, deflate
2.服务器接收请求头,选择请求头中的一种压缩算法对内容进行压缩,并在返回给客户端的
响应头中,声明采用的压缩格式(部分网页可能不支持这种压缩格式),如: Content-Encoding:
gzip
3.对数据进行解压缩,得到网页代码。
对于gzip压缩算法封装,网上有很多相关介绍,这里就不细谈了。我们主要说说
分析服务器返回的压缩数据的格式。
首先是Transfer-Encoding的分块数据(chunked) ,如果返回的响应头中包含了
chunked,则说明服务器返回的数据是分块的,我们需要将数据全部整合到一起,看看
RFC2616中对Chunked的定义:
chunk-data = chunk-size(OCTET)
trailer = *(entity-header CRLF)
Chunked-Body = *chunk
last-chunk
trailer
CRLF
chunk = chunk-size [ chunk-extension ] CRLF
chunk-data CRLF
chunk-size = 1*HEX
last-chunk = 1*("0") [ chunk-extension ] CRLF
chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
每次服务器返回的数据先是当前分块的数据大小(chunk-size,其中数据是以数字的
16进制的ASCII码表示的) ,然后一个回车(CRLF) ,接着是数据(chunk-data) ,最后还有
个回车(CRLF)。
当当前的分块传输完成后,会读取到最后的一个块长度0,来表示当前分块的结束。
看看数据处理的代码:(这里用于网页ASCII文本数据,故没有返回实际的长度。 )
char *tstr = NULL;
int nChunkSize;
int nChunkNow;
char *nChunkData;
CBuffer cBuf;
while (true)
{
nResult = recv(sock, buffer, 1024, 0); //没进行错误处理
if (nResult <= 0) break;
if (strLine == NULL)
{
strLine = strstr(buffer, "\r\n\r\n");
if (!strLine) return false;
bool RecvDataEx(SOCKET sock, LPSTR *szScr)
{
if (!szScr) return false;
char buffer[1024];
int nResult ;
bool bIsChunked = false,bIsGzip = false;
char *strLine = NULL;
strLine[0] = 0;
if (strstr(buffer, "chunked"))
{
bIsChunked = true;
}else{
bIsChunked = false;
nResult -= (strLine + 4 - buffer);
|