数据压缩

转载别人的,备用。原文地址:

再对比下: 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

posted @ 2017-04-14 09:50  骑着驴的王子  阅读(566)  评论(0编辑  收藏  举报