使用zlib库函数实现http报文的解压

最近做项目的时候遇到了一个问题,那就是需要获得http响应报文主体内容,一般响应主体会通过gzip格式进行压缩,本文就是针对这种情况所写的,可以进行内存解压,而不需要保存至本地文件再解压。至于chunked分块传输方式要先进行报文重组再进行解压,本文暂不考虑。

下图是我抓取的一个http数据包,由Content-Encoding:gzip\r\n知该报文经过了gzip压缩,而压缩后的长度可以根据Content_Length=:785\r\n知为785字节,即输入参数len。压缩后的报文保存在哪里呢??我们可以查找“\r\n\r\n"这个字符串,压缩后的报文部分就保存在其接下来的内存地址之中,即输入参数source指针。

使用zlib库函数实现http报文的解压_第1张图片

下面直接上代码:

#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是先重组在解压,而这里是先解压在组合。因为我做的项目里面我需要的内容就在第一个包里,所以这部分没有做,希望感兴趣的同学可以补充上~~


好了,我的第一篇博客就先写到这里吧。

你可能感兴趣的:(http,GZip,zlib,内存解压)