A-A+

C++爬虫原理(四):组包Chunked网页数据

2015年07月14日 大数据 暂无评论 阅读 408 次

One、about  HTTP chunked+gzip

  1. Chunked transfer encoding allows a server to maintain an HTTP persistent connection for dynamically generated content. In this case the HTTP Content-Length header cannot be used to delimit the content and the next HTTP request/response, as the content size is as yet unknown. Chunked encoding has the benefit that it is not necessary to generate the full content before writing the header, as it allows streaming of content as chunks and explicitly signaling the end of the content, making the connection available for the next HTTP request/response.
  2. Chunked encoding allows the sender to send additional header fields after the message body. This is important in cases where values of a field cannot be known until the content has been produced such as when the content of the message must be digitally signed. Without chunked encoding, the sender would have to buffer the content until it was complete in order to calculate a field value and send it before the content.
  3. HTTP servers sometimes use compression (gzip) or deflate methods to optimize transmission. How both chunked and gzip encoding interact is dictated by the two-staged encoding of HTTP: first the content stream is encoded as (Content-Encoding: gzip), after which the resulting byte stream is encoded for transfer using another encoder (Transfer-Encoding: chunked). This means that in case both compression and chunked encoding are enabled, the chunk encoding itself is not compressed, and the data in each chunk should not be compressed individually. The remote endpoint can decode the incoming stream by first decoding it with the Transfer-Encoding, followed by the specified Content-Encoding.

above from: http://en.wikipedia.org/wiki/Chunked_transfer_encoding

译:

  1. HTTP分块传输编码允许服务器为动态生成的内容维持HTTP持久链接。通常,持久链接需要服务器在开始发送消息体前发送Content-Length消息头字段,但是对于动态生成的内容来说,在内容创建完之前是不可知的。 分块编码的好处,没有必要在获取完整的内容之前写头字段,它允许内容流分块(chunk),并显式标记内容的末尾,使连接可用于下一个HTTP请求/响应。
  2. 分块传输编码允许服务器在最后发送消息头字段。对于那些头字段值在内容被生成之前无法知道的情形非常重要,例如消息的内容要使用散列进行签名,散列的结果通过HTTP消息头字段进行传输。没有分块传输编码时,服务器必须缓冲内容直到完成后计算头字段的值并在发送内容前发送这些头字段的值。
  3. HTTP服务器有时使用压缩 (gzip或deflate)以缩短传输花费的时间。分块传输编码可以用来分隔压缩对象的多个部分。在这种情况下,块不是分别压缩的,而是整个负载进行压缩,压缩的输出使用本文描述的方案进行分块传输。在压缩的情形中,分块编码有利于一边进行压缩一边发送数据,而不是先完成压缩过程以得知压缩后数据的大小。

注:如果采用chunked+gzip方式,并且每个chunk都是一个gzip压缩包的方式,浏览器不支持。

One from :http://blog.sciencenet.cn/blog-297739-702590.html

Two、Transfer-Encoding简介

transfer-eccoding所描述的是消息请求(request)和响应(response)所附带的实体对象(entity)的传输形式,格式如下:Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding

举个例子:Transfer-Encoding: chunked

transfer-encoding的可选值有:chunked,identity ;
transfer-encoding的可选值有:chunked,identity,从字面意义可以理解,前者指把要发送传输的数据切割成一系列的块数据传输,后者指传输时不做任何处理,自身的本质数据形式传输。举个例子,如果我们要传输一本“红楼梦”小说到服务器,chunked方式就会先把这本小说分成一章一章的,然后逐个章节上传,而identity方式则是从小说的第一个字按顺序传输到最后一个字结束。

相关的头定义
Content-Encoding : content-encoding和transfer-encoding所作用的对象不同,行为目标也不同,前者是对数据内容采用什么样的编码方式,后者是对数据传输采用什么样的编码。前者通常是对数据内容进行一些压缩编码操作,后者通常是对传传输采用分块策略之类的。

Content-length : content-length头的作用是指定待传输的内容的字节长度。比如上面举的例子中,我们要上传一本红楼梦小说,则可以指定其长度大小,如:content-length:731017。细心的读者可能会有疑惑,它和transfer-encoding又有什么关系呢?如果想知道它们的关系,只要反过来问下自己,为什么transfer-encoding会有identity和chunked两种,各在什么上下文情景中要用到。比如chunked方式,把数据分块传输在很多地方就非常有用,如服务端在处理一个复杂的问题时,其返回结果是阶段性的产出,不能一次性知道最终的返回的总长度(content-lenght值),所以这时候返回头中就不能有content-lenght头信息,有也要忽略处理。所以你可以这样理解,transfer-encoding在不能一次性确定消息实体(entity)内容时自定义一些传输协议,如果能确定的话,则可以在消息头中加入content-length头信息指示其长度,可以把transfer-encoding和content-length看成互斥性的两种头。

Three、RFC文档关于Chunked定义(rfc2616 3.6.1)

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-data = chunk-size(OCTET)
trailer = *(entity-header CRLF)

举例说明:还是以上传“红楼梦”这本书

chunked0

chunked0

解释:24E5是指第一个块数据长度为24E5(16进制格式字符串表示),CRLF为换行控制符。紧接着是第一个块数据内容,其长度就是上面定义的24E5,以CRLF标志结束。3485是指第二块数据长度为3485,CRLF结束,然后后面是第二块的数据内容......,以这样的格式直到所有的块数据结束。最后以“0”CRLF结束,表示数据传输完成(这里对比rfc规范内容,省略了chunk-extension和trailer的东西,因为这并不重要)。

two、three from:http://www.cnblogs.com/jcli/archive/2012/10/19/2730440.html

Four、抓包分析QQ网站(im.qq.com)的数据

请求头:Accept-Encoding: gzip, deflate; http 1.1 ;

如图所示,绿色部分为http响应头,黑体乱码部分就为chunked数据包了。可以清楚的看到数据包的格式。

chunked1

chunked1

Five、C++重组解析Chunked数据包。

void HttpHandle::reGroupChunked(Byte *pChunkData, int nChunkDataSize, Byte *pNewData)		//组包
{
	char *pStart = (char *)pChunkData;		//chunked data
	char *pTemp;			
	int chunkedIntLength;				//chunked 数据包的大小 - 十进制
	char chunkedStrLength[10];			//chunked 数据包的大小 - 十六进制字符型
	while (true)
	{
		chunkedIntLength = 0;
		memset(chunkedStrLength, 0, sizeof(chunkedStrLength));
		pTemp = strstr(pStart, "\r\n");
		int chunkedByteLength = pTemp - pStart;	//chunked 大小 存放的字节长度
		for (int i = 0; i < chunkedByteLength; i++)
			chunkedStrLength[i] = pStart[i];
		pStart = pTemp + 2;									//指针移位,跳过\r\n
		sscanf_s(chunkedStrLength, "%x", &chunkedIntLength);
		if (0 == chunkedIntLength)
			return;
		else
		{
			memcpy(pNewData, pStart, chunkedIntLength);
			pStart = pStart + chunkedIntLength + 2;
			pNewData = pNewData + chunkedIntLength;
		}
	}
}

 

Copyright:www.cplusplus.me Share、Open- C/C++程序员之家

标签:

给我留言