在提取出http报文(Message)里的Message-body数据以后,接下来就是要对这个部分数据进行处理了。我的方法是先将这些数据保存成文件,然后再来好好处理它们。http传输的对象数据大部分都是经过压缩传输的,所以我们得到的数据并不是直接的html文本文件,而是经过压缩/编码后的,所以这里我们还得有个解码过程。http里面涉及的编码主要有两类:一类压缩编码,主要关于对象数据的压缩,它的目的是压缩对象数据量;另一类编码是关于传输的编码,主要是基于数据传输的安全性和可靠性来考虑。对应上述两类的编码http协议在报文中都有说明,在http响应报文的报头中,当有Transfer-Encoding:chunked时表示数据经过分块处理,当Content-Encoding:gzip时表示对象数据按照gzip规格对其进行了压缩。具体意思可参考http协议说明http://www.w3.org/Protocols/rfc2616/rfc2616.html 另外,需要说明一点的是gzip和chunked哪个先实行呢(亦或说它们是怎么协调工作的呢)?答案是1.先用gzip压缩原始的对象数据(这里就是html文本文件了),2.然后用chunked对压缩后得到的数据再进行分块。 下面这些是将如何实现chunk和gzip的解码的,引用自http://nblive99.spaces.live.com/blog/cns!74A0072781B23DFB!130.entry
做tcpip协议栈数据包重组时遇到一些编码问题,主要是chunk和gzip编码。
先看chunk:
RFC2616中对Chunked的定义:
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) 以下是解码过程的伪代码: length := 0//用来记录解码后的数据体长度 read chunk-size, chunk-extension (if any) and CRLF//第一次读取块大小 while (chunk-size > 0) {//一直循环,直到读取的块大小为0 read chunk-data and CRLF//读取块数据体,以回车结束 append chunk-data to entity-body//添加块数据体到解码后实体数据 length := length + chunk-size//更新解码后的实体长度 read chunk-size and CRLF//读取新的块大小 } read entity-header//以下代码读取全部的头标记 while (entity-header not empty) { append entity-header to existing header fields read entity-header } Content-Length := length//头标记中添加内容长度 Remove "chunked" from Transfer-Encoding//头标记中移除Transfer-Encoding
伪码的逻辑有点混乱,研究了下,自己写了C语言的解码代码:
//
char * unchunk (char *filename)
{ char cmdbuf[1024]; /*if (strstr (filename, ".trunk") == 0) { strcat (filename, ".trunk"); memset (cmdbuf, 0x0, sizeof (tmpfile)); sprintf (cmdbuf, "mv %s %s", chunkfile, filename); system (cmdbuf); }*/
FILE *fp = fopen (filename, "ab+");
char newfile[128]; memset (newfile, 0x0, sizeof (tmpfile)); strcpy (newfile, filename); char *ptr = strstr(newfile, ".trunk"); *ptr = 0; printf ("%s\n", newfile);
FILE *fp_unchunk = fopen (newfile, "wb+");
char chunk_head[8];
memset (chunk_head, 0x0, sizeof (chunk_head)); fgets (chunk_head, sizeof (chunk_head), fp); char *p = strstr (chunk_head, "\r\n");
if (p)
{ int chunk_size = strtol (chunk_head, NULL, 16); char *chunk_data; while (chunk_size > 0) { chunk_data = (char *)malloc (chunk_size); memset (chunk_data, 0x0, chunk_size); fread (chunk_data, chunk_size, 1, fp); fwrite (chunk_data, chunk_size, 1, fp_unchunk);
fseek (fp, 2, SEEK_CUR);
//reread chunk head
memset (chunk_head, 0x0, sizeof (chunk_head)); fgets (chunk_head, sizeof (chunk_head), fp); char *p = strstr (chunk_head, "\r\n"); if (p) { chunk_size = strtol (chunk_head, NULL, 16); free (chunk_data); } else break; }
//remove old file
memset (cmdbuf, 0x0, sizeof (cmdbuf)); sprintf (cmdbuf, "rm %s", filename); system (cmdbuf);
fclose (fp_unchunk);
fclose (fp);
return newfile;
} else { fclose (fp_unchunk); fclose (fp);
return filename;
} }
//
接下来看gzip的解码
gzip的解码相对更加简单一些,两种方法实现:
一种是直接调用系统gzip命令解压,没有技术含量;
另一种是使用zlib库,通用性更高,但是要使用zlib库,开发过程稍复杂,下面给出gzip文件的解压C代码:
/
//调用系统gzip命令的代码(无难度)
void ungzip (char *filename)
{ char cmdbuf[1024];
if (strstr (filename, ".gz") == 0)
{ memset (cmdbuf, 0x0, sizeof (cmdbuf)); sprintf (cmdbuf, "mv %s %s.gz", filename, filename); system (cmdbuf); }
memset (cmdbuf, 0x0, sizeof (cmdbuf));
sprintf (cmdbuf, "gzip -d %s", filename); system (cmdbuf); }
// 使用zlib库的代码
#include "zlib/zlib.h"
void uncompresstorrent(char *src, char *dst)
{ gzFile *gzfp=gzopen(src,"rb"); FILE *fp=fopen(dst,"wb"); char in[CHUNK]; int retlen = -1;
while(0 != (retlen=gzread(gzfp,in,CHUNK)))
{ fwrite(in,1,retlen,fp); } gzclose(gzfp); fclose(fp); }
(编译是需要带 -Lzlib -lz 参数)
/
|
网络爬虫中的chunked和gzip的处理
最新推荐文章于 2022-05-29 19:10:28 发布