数据压缩
转载别人的,备用。原文地址:
再对比下: snappy
http://www.oschina.net/p/snappy
http://bbs.itcast.cn/thread-85176-1-1.html
http://fengmm521.blog.163.com/blog/static/25091358201401325758620/
http://www.cnblogs.com/zisou/p/cocos2dxJQ-8.html
原文地址:
因为服务器发送过的字符串比较长,加上手机用户的流量是要花钱的。所以要把服务器发送给客端的数据进行压缩。服务器端使用java自代的gzip压缩方法。而我要作的就是把这个用cocos2d-x的zlib库进行解压。在网上找了几个方法,自已都试过了。可以用。在这里记一下,方便自已以后再一次使用。
1.用http从服务器获取压缩字符串:
这里要在.h头文件中加入cocos2d-x的zlib库所在的头文件
void VersionManager::testVersion()
{
CCHttpClient* httpClient = CCHttpClient::getInstance();
CCHttpRequest* httpReq =new CCHttpRequest();
httpReq->setRequestType(CCHttpRequest::kHttpGet);
httpReq->setUrl("http://192.168.1.16:80/51256e283d370370b653ae19c12524?{'account':1234567,'stageId':1}");
httpReq->setResponseCallback(this,httpresponse_selector(VersionManager::httpReqFinished));
httpReq->setTag("FirstNet");
httpClient->setTimeoutForConnect(30);
httpClient->send(httpReq);
httpReq->release();
}
void VersionManager::httpReqFinished( CCHttpClient* client, CCHttpResponse* response )
{
//CCHttpResponse* response = (CCHttpResponse*)obj;
if (!response->isSucceed())
{
CCLog("Receive Error! %s\n",response->getErrorBuffer());
return ;
}
const char* tag = response->getHttpRequest()->getTag();
if ( 0 == strcmp("FirstNet",tag))
{
std::vector<char> *data = response->getResponseData();
int data_length = data->size();
std::string res;
for (int i = 0;i<data_length;++i)
{
res+=(*data)[i];
}
res+='\0';
//CCLog("%s",res.c_str());
this->testVersionCallBack(res);
}
}
//文件接收完回调
void VersionManager::testVersionCallBack(std::string netData)
{
//CCLog("%s",netData.c_str());
unsigned char xxx[segment_size];
for (int i = 0;i< (int)netData.size()-1;i++)
{
xxx[i] = netData[i];
CCLog("%x ",xxx[i]);
}
char out[4*segment_size];
char* xstrout = this->ungzip(xxx,(int)netData.size()-1);
//int eroNumber = this->ungzip(xxx,(int)(netData.size()-1),out);
std::string outstrtmp = std::string(xstrout);
free(xstrout);
CCLog("out:%s",outstrtmp.c_str());
std::string getstrxxx = this->ungZip(netData);
CCLog("get out:%s",getstrxxx.c_str());
}
下边的是zlib在cocos2d-x中所在的位置:
#include "support/zip_support/unzip.h"
#include "support/zip_support/ZipUtils.h"
下边是几个解压的方法:
#define segment_size 2048 //接收的缓存,单位是字节
char* VersionManager::ungZip(std::string source)
{
unsigned char xxx[segment_size];
int len = source.size()-1;
for (int i = 0;i< (int)source.size()-1;i++)
{
xxx[i] = source[i];
//CCLog("%x ",xxx[i]);
}
int err;
z_stream d_stream;
Byte compr[segment_size]={0}, uncompr[segment_size*4]={0};
memcpy(compr,(Byte*)xxx,len);
//free(xxx);
uLong comprLen, uncomprLen;
comprLen = sizeof(compr) / sizeof(compr[0]);
uncomprLen = 4*comprLen;
strcpy((char*)uncompr, "garbage");
d_stream.zalloc = (alloc_func)0;
d_stream.zfree = (free_func)0;
d_stream.opaque = (voidpf)0;
d_stream.next_in = compr;
d_stream.avail_in = 0;
d_stream.next_out = uncompr;
err = inflateInit2(&d_stream,47);
if(err!=Z_OK)
{
printf("inflateInit2 error:%d",err);
return NULL;
}
while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
d_stream.avail_in = d_stream.avail_out = 1;
err = inflate(&d_stream,Z_NO_FLUSH);
if(err == Z_STREAM_END) break;
if(err!=Z_OK)
{
printf("inflate error:%d",err);
return NULL;
}
}
err = inflateEnd(&d_stream);
if(err!=Z_OK)
{
printf("inflateEnd error:%d",err);
return NULL;
}
char* b = new char[d_stream.total_out+1];
memset(b,0,d_stream.total_out+1);
memcpy(b,(char*)uncompr,d_stream.total_out);
return b;
}
下边是另外的几个参考方法:
char* VersionManager::ungzip(unsigned char* source,int len)
{
int err;
z_stream d_stream;
Byte compr[segment_size]={0}, uncompr[segment_size*4]={0};
memcpy(compr,(Byte*)source,len);
uLong comprLen, uncomprLen;
comprLen = sizeof(compr) / sizeof(compr[0]);
uncomprLen = 4*comprLen;
strcpy((char*)uncompr, "garbage");
d_stream.zalloc = (alloc_func)0;
d_stream.zfree = (free_func)0;
d_stream.opaque = (voidpf)0;
d_stream.next_in = compr;
d_stream.avail_in = 0;
d_stream.next_out = uncompr;
err = inflateInit2(&d_stream,47);
if(err!=Z_OK)
{
printf("inflateInit2 error:%d",err);
return NULL;
}
while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) {
d_stream.avail_in = d_stream.avail_out = 1;
err = inflate(&d_stream,Z_NO_FLUSH);
if(err == Z_STREAM_END) break;
if(err!=Z_OK)
{
printf("inflate error:%d",err);
return NULL;
}
}
err = inflateEnd(&d_stream);
if(err!=Z_OK)
{
printf("inflateEnd error:%d",err);
return NULL;
}
char* b = new char[d_stream.total_out+1];
memset(b,0,d_stream.total_out+1);
memcpy(b,(char*)uncompr,d_stream.total_out);
return b;
}
int VersionManager::ungzip(unsigned char* source,int len,char*des)
{
int ret,have;
int offset=0;
z_stream d_stream;
Byte compr[segment_size]={0}, uncompr[segment_size*4]={0};
memcpy(compr,(Byte*)source,len);
uLong comprLen, uncomprLen;
comprLen =len;//一开始写成了comprlen=sizeof(compr)以及comprlen=strlen(compr),后来发现都不对。
//sizeof(compr)永远都是segment_size,显然不对,strlen(compr)也是不对的,因为strlen只算到\0之前,
//但是gzip或者zlib数据里\0很多。
uncomprLen = segment_size*4;
strcpy((char*)uncompr, "garbage");
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);
if(ret!=Z_OK)
{
printf("inflateInit2 error:%d",ret);
return ret;
}
d_stream.next_in=compr;
d_stream.avail_in=comprLen;
do
{
d_stream.next_out=uncompr;
d_stream.avail_out=uncomprLen;
ret = inflate(&d_stream,Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR);
switch (ret)
{
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&d_stream);
return ret;
}
have=uncomprLen-d_stream.avail_out;
memcpy(des+offset,uncompr,have);//这里一开始我写成了memcpy(des+offset,d_stream.next_out,have);
//后来发现这是不对的,因为next_out指向的下次的输出,现在指向的是无有意义数据的内存。见下图
offset+=have;
}while(d_stream.avail_out==0);
inflateEnd(&d_stream);
memcpy(des+offset,"\0",1);
return ret;
}
另外的两个,一个是客户端用来压缩的,和一个比较基础的解压缩方法,我都试过,这几个方法都可以正常解压和压缩。
压缩数据:
/* Compress gzip data */
/* data 原数据 ndata 原数据长度 zdata 压缩后数据 nzdata 压缩后长度 */
int VersionManager::gzcompress(Bytef *data, uLong ndata,Bytef *zdata, uLong *nzdata)
{
z_stream c_stream;
int err = 0;
if(data && ndata > 0) {
c_stream.zalloc = NULL;
c_stream.zfree = NULL;
c_stream.opaque = NULL;
//只有设置为MAX_WBITS + 16才能在在压缩文本中带header和trailer
if(deflateInit2(&c_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
MAX_WBITS + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) return -1;
c_stream.next_in = data;
c_stream.avail_in = ndata;
c_stream.next_out = zdata;
c_stream.avail_out = *nzdata;
while(c_stream.avail_in != 0 && c_stream.total_out < *nzdata) {
if(deflate(&c_stream, Z_NO_FLUSH) != Z_OK) return -1;
}
if(c_stream.avail_in != 0) return c_stream.avail_in;
for(;;) {
if((err = deflate(&c_stream, Z_FINISH)) == Z_STREAM_END) break;
if(err != Z_OK) return -1;
}
if(deflateEnd(&c_stream) != Z_OK) return -1; *nzdata = c_stream.total_out; return 0; } return -1;}
解决数据:
/* Uncompress gzip data */
/* zdata 数据 nzdata 原数据长度 data 解压后数据 ndata 解压后长度 */
int VersionManager::gzdecompress(Byte *zdata, uLong nzdata,Byte *data, uLong *ndata)
{
int err = 0;
z_stream d_stream = {0}; /* decompression stream */
static char dummy_head[2] = {
0x8 + 0x7 * 0x10,
(((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
};
d_stream.zalloc = NULL;
d_stream.zfree = NULL;
d_stream.opaque = NULL;
d_stream.next_in = zdata;
d_stream.avail_in = 0;
d_stream.next_out = data;
//只有设置为MAX_WBITS + 16才能在解压带header和trailer的文本
if(inflateInit2(&d_stream, MAX_WBITS + 16) != Z_OK) return -1;
//if(inflateInit2(&d_stream, 47) != Z_OK) return -1;
while(d_stream.total_out < *ndata && d_stream.total_in < nzdata) {
d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ if((err = inflate(&d_stream, Z_NO_FLUSH)) == Z_STREAM_END) break; if(err != Z_OK) { if(err == Z_DATA_ERROR) { d_stream.next_in = (Bytef*) dummy_head; d_stream.avail_in = sizeof(dummy_head); if((err = inflate(&d_stream, Z_NO_FLUSH)) != Z_OK) { return -1; } } else return -1; } } if(inflateEnd(&d_stream) != Z_OK) return -1; *ndata = d_stream.total_out; return 0;}
如果在调试时出现了“test dword ptr [eax],eax ; probe page. 分页错误”,那就有可能是因为接收网络缓存的空间过大了。vs默认的堆栈最大值为1MB.不过可以用下边的方法进行修改:
项目->属性->链接器->系统->堆栈保留大小
注:这里填的是字节数
如果你想把他扩大为2M的话,
1024*1024*2 = 2097152