C - 解压缩Gzipped http响应

时间:2012-03-05 12:05:36

标签: c http sockets gzip gnu

我在解压缩gzip http响应方面遇到的问题很少,我将数据部分与头文件分开,但是它的gzip头和消息包含\ 0字符,其中char *作为空终止符,所以第一个问题是如何提取gzip压缩块?

我不能使用像strcat,strlen这样的字符串函数,因为它是压缩的gzip压缩数据,在块内的不同位置包含\ 0字符。

我使用了libcurl,但它比C套接字慢。

以下是样本回复的一部分:

HTTP/1.1 200 OK
Cache-Control: private, max-age=0
Content-Type: text/html; charset=utf-8
P3P: CP="NON UNI COM NAV STA LOC CURa DEVa PSAa PSDa OUR IND"
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 12605
Date: Mon, 05 Mar 2012 11:46:30 GMT
Connection: keep-alive
Set-Cookie: _FP=EM=1; expires=Wed, 05-Mar-2014 11:46:29 GMT; domain=.bing.com; path=/

����ՠ����AU��o�

示例代码:

#define MAXDATASIZE 1024

char *recvData; // Holds entire gzip data
char recvBuff[MAXDATASIZE]; // Holds gzip chunk
int offset=0;
while(1){
    recvBytes = recv(sockfd, &recvBuff, MAXDATASIZE-1, 0);
    totalRecvBytes += recvBytes;

    // get content length, this runs first time only as required
    if(!clfnd){
        regi = regexec(&clregex, &recvBuff, 3, clmatch, 0);
        if(!regi){
            strncpy(clarr, recvBuff + clmatch[2].rm_so, clmatch[2].rm_eo-clmatch[2].rm_so);
            clarr[clmatch[2].rm_eo-clmatch[2].rm_so] = '\0';
            cl = atoi(clarr);
            clfnd=1;
            regfree(&clregex);
            recvData = malloc(cl * sizeof(char));
            memset(recvData, 0, sizeof recvData);
        }
    }

    // get data part from 1st iteration, furthur iterations contain only data
    if(!datasplit){
        int strtidx;
        char *datastrt = strstr(&recvBuff, "\r\n\r\n");
        if(datastrt != NULL){
            strtidx = datastrt - recvBuff + 4;
            memcpy(recvData, recvBuff + strtidx, recvBytes-strtidx);
            datasplit=1;
            offset = recvBytes-strtidx;
        }
    }
    else{
        memcpy(recvData + offset, recvBuff, recvBytes);
        offset += recvBytes;
    }
    if (offset >= cl)
        break;
}

char *outData = malloc(offset*4 * sizeof(char));
memset(outData, 0, sizeof outData);
int ret = inf(recvData, offset, outData, offset*4);

充气功能:

int inf(const char *src, int srcLen, const char *dst, int dstLen){
z_stream strm;
strm.zalloc=NULL;
strm.zfree=NULL;
strm.opaque=NULL;

strm.avail_in = srcLen;
strm.avail_out = dstLen;
strm.next_in = (Bytef *)src;
strm.next_out = (Bytef *)dst;

int err=-1, ret=-1;
err = inflateInit2(&strm, MAX_WBITS+16);
if (err == Z_OK){
    err = inflate(&strm, Z_FINISH);
    if (err == Z_STREAM_END){
        ret = strm.total_out;
    }
    else{
        inflateEnd(&strm);
        return err;
    }
}
else{
    inflateEnd(&strm);
    return err;
}
inflateEnd(&strm);
printf("%s\n", dst);
return err;
}

3 个答案:

答案 0 :(得分:4)

不,类型char *没有说明它指向的内容,也没有将任何值解释为终结符。另一方面,str *函数假设有关字符串的表示方式,不能用于二进制数据,甚至不能用于具有不同表示的文本数据。

减压可能相当复杂,但您可以查看zlib,这应该可以帮助您。

答案 1 :(得分:2)

Content-Length:12605表示gzip压缩文件的大小为12605字节。因此,只需将消息头之后的12605个字节复制到本地缓冲区,并将该缓冲区提供给解压缩功能。此外,我不确定您的套接字读取功能是否在一个流程中读取整个12605。如果没有,则需要将下一次读取的其余数据附加到此本地缓冲区,并在读取12605个字节时再调用解压缩函数。 使用char *作为缓冲区没有问题。您面临的问题是因为您尝试将gzip数据打印为字符串。

答案 2 :(得分:0)

HTTP有效负载的开头在“\ r \ n \ r \ n”之后(在HTTP标头之后)开始。

使用HTTP字段“Content-Length”获取HTTP有效负载大小。

使用此信息,您必须创建解压缩数据的功能。使用Zlib,你可以做到这一点。

PS。如果它使用原始格式或带有标题和预告片的zlib,请注意。 Usualy HTTP使用标题和预告片,IMAP4使用原始格式。