使用zlib库函数实现http报文的解压
2015-10-25 21:14
1001 查看
最近做项目的时候遇到了一个问题,那就是需要获得http响应报文主体内容,一般响应主体会通过gzip格式进行压缩,本文就是针对这种情况所写的,可以进行内存解压,而不需要保存至本地文件再解压。至于chunked分块传输方式要先进行报文重组再进行解压,本文暂不考虑。
下图是我抓取的一个http数据包,由Content-Encoding:gzip\r\n知该报文经过了gzip压缩,而压缩后的长度可以根据Content_Length=:785\r\n知为785字节,即输入参数len。压缩后的报文保存在哪里呢??我们可以查找“\r\n\r\n"这个字符串,压缩后的报文部分就保存在其接下来的内存地址之中,即输入参数source指针。
下面直接上代码:
但上面代码中有一个不对的地方,那就是len的取值,这里说的是取Content_Length=:785\r\n后的785,但是当此数字比较大时会导致解压报错ret=-3,即数据格式出错,这是因为数据包再网络中传输时有最大传输长度,我们知道MTU=1560字节(我记得是这个),而tcp协议再三次握手时会协商最大接受长度MSS,该值默认为560多,具体数字我忘了,最大为1460。这意味着什么呢?我抓得另一个数据包Content_Length=:38188\r\n,但是当我调试时发现source指针在1049字节(这个数字没有意义,只不过是1460-响应报文首部长度)后就没有任何内容了,这时用上面的函数解压时当这1049个字节解压完之后必然会报ret=-3的错,因为这之后都是空,显然不符合gzip压缩格式。所以要想不报错len值应该取Content_Length和MSS(握手协商出来的)的较小者!!!!这样解压正确,但只解压了网页的一部分,对吧。要想全部解压就要把其他的包也找到并解压。这和chunked并不一样,chunked是先重组在解压,而这里是先解压在组合。因为我做的项目里面我需要的内容就在第一个包里,所以这部分没有做,希望感兴趣的同学可以补充上~~
好了,我的第一篇博客就先写到这里吧。
下图是我抓取的一个http数据包,由Content-Encoding:gzip\r\n知该报文经过了gzip压缩,而压缩后的长度可以根据Content_Length=:785\r\n知为785字节,即输入参数len。压缩后的报文保存在哪里呢??我们可以查找“\r\n\r\n"这个字符串,压缩后的报文部分就保存在其接下来的内存地址之中,即输入参数source指针。
下面直接上代码:
#define MY_BUFF_SIZE = 1024 int ungzip(char* source,int len,char*des)//des为解压之后的内容要保存的地方 { int ret,have; int offset=0; void * buff = source; z_stream d_stream; char uncompr[MY_BUFF_SIZE]={0};// d_stream.zalloc = Z_NULL; d_stream.zfree = Z_NULL; d_stream.opaque = Z_NULL; d_stream.next_in = Z_NULL;//inflateInit和inflateInit2都必须初始化next_in和avail_in d_stream.avail_in = 0;//deflateInit和deflateInit2则不用 ret = inflateInit2(&d_stream,47);//这里一定要用inflateInit2()函数进行初始化,其他的不行,我试过貌似第二个参数为31也可以,可以按照自己情况选择 d_stream.next_in= buff;//下一个要解压的字节 d_stream.avail_in= len;//还有多少字节需要解压 do//本处含义为:开始解压,每循环一次代表解压出了1024个字节(可能只解压了几百个字节)。并将解压出的内容缓存到uncompr中,然后赋值给des; { bzero(uncompr, MY_BUFF_SIZE); d_stream.next_out=(Bytef *)uncompr; d_stream.avail_out=MY_BUFF_SIZE; ret = inflate(&d_stream,Z_NO_FLUSH);//这里就是解压函数 assert(ret != Z_STREAM_ERROR); if (ret != Z_OK && ret != Z_STREAM_END)//解压正常返回Z_OK,解压结束返回Z_STREAM_END, { printf("\ninflate ret = %d\n", ret);//可能出现ret=-3,即Z_DATA_ERROR,这可能是因为数据格式不对;还有其他错误,暂时没碰到 break; } have=MY_BUFF_SIZE-d_stream.avail_out;//这里是将uncompr赋值给des memcpy(des+offset,uncompr,have); offset+=have; }while(d_stream.avail_out==0);//结束循环条件,当最后一次循环时,解压出的字节一般不到1024字节,所以d_stream.avail_out!=0,即还有可用字节。 inflateEnd(&d_stream); memcpy(des+offset,"\0",1);//\0表示结束 return ret; }对上面的数据包用上面的程序可以解压正常不报错!!
但上面代码中有一个不对的地方,那就是len的取值,这里说的是取Content_Length=:785\r\n后的785,但是当此数字比较大时会导致解压报错ret=-3,即数据格式出错,这是因为数据包再网络中传输时有最大传输长度,我们知道MTU=1560字节(我记得是这个),而tcp协议再三次握手时会协商最大接受长度MSS,该值默认为560多,具体数字我忘了,最大为1460。这意味着什么呢?我抓得另一个数据包Content_Length=:38188\r\n,但是当我调试时发现source指针在1049字节(这个数字没有意义,只不过是1460-响应报文首部长度)后就没有任何内容了,这时用上面的函数解压时当这1049个字节解压完之后必然会报ret=-3的错,因为这之后都是空,显然不符合gzip压缩格式。所以要想不报错len值应该取Content_Length和MSS(握手协商出来的)的较小者!!!!这样解压正确,但只解压了网页的一部分,对吧。要想全部解压就要把其他的包也找到并解压。这和chunked并不一样,chunked是先重组在解压,而这里是先解压在组合。因为我做的项目里面我需要的内容就在第一个包里,所以这部分没有做,希望感兴趣的同学可以补充上~~
好了,我的第一篇博客就先写到这里吧。
相关文章推荐
- Android 使用OkHttp扩展Volley
- 网络必须首先了解的基本知识
- 《TCP/IP详解 卷一》读书笔记-----TCP数据流
- 实验五 Java网络编程及安全
- 洛谷 1948 笨笨的电话网络
- 浏览器HTTP缓存原理分析(转)
- HTTP和FTP的区别
- 在android中使用HTTPClient以post方法发送二进制文件
- 【codevs4093】EZ的间谍网络 tarjan
- Android批量图片加载经典系列——使用二级缓存、异步网络负载形象
- 图解TCP-IP协议
- 计算机网络概述
- python学习之【16】网络编程
- 浏览器HTTP缓存原理分析
- Linux-网络配置
- TCP粘包处理 参考spserver
- 【PHPsocket编程专题(实战篇②)】兼容 Curl/Socket/Stream 的 HTTP 操作类[转]
- 编译问题:App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecur
- 网络爬虫基本原理(二)
- 网络爬虫基本原理(一)