2012-03-05 4 views
1

gzip으로 압축 된 http 응답을 압축하는 데 문제가 거의 없습니다. 헤더에서 데이터 부분을 분리했지만 gzip 헤더와 메시지에 char *가 null 터미네이터로 사용되는 \ 0 문자가 포함되어 있으므로 첫 번째 질문은 gzipped 덩어리?C - 압축 해제 된 HTTP 응답

청크 내의 여러 위치에서 \ 0 문자가 포함 된 압축 된 gzipped 데이터이기 때문에 strcat, strlen과 같은 문자열 함수를 사용할 수 없습니다.

저는 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; 
} 
+0

여기에 몇 가지 큰 문제가 있습니다. 수신 한 모든 패킷에 대해 recvData를 할당하고 지 웁니다. 1) 메모리 누수가 발생하고 2) 최종 recvData에 0 묶음과 마지막 패킷이 들어 있음을 의미합니다. 또한 오프셋의 사용은 펑키 한 것 같아요, 그것은 첫 번째 패킷에 대해 업데이 트되지 않습니다. – harald

+0

죄송합니다 ... 1) 프로그램은 recvData에 메모리를 한 번만 할당합니다. 기본적으로 코드를 복사하는 동안 해당 조건을 복사하지 않아서 문제가 없으므로 업데이트 된 코드를 참조하십시오. 2) 오프셋은 일시적으로 3으로 설정되어 사용됩니다 두 번째 memcpy()에서 memcpy()를 쓰지 않는 이유는 memcpy()가 처음부터 memcpy()의 IF 조건에서 데이터에서 헤더를 나눠서 오프셋이 헤더를 제외한 데이터 바이트의 수로 설정되어야한다는 것입니다. bytes 즉, offset = recvBytes-strtidx; 하지만 문제는 첫 번째 memcpy() 복사하는 것입니다 \ 0 chars recvBuff의 gzipped 데이터에서 3 문자를 복사합니다. – Coder

+0

이제 프로그램이 http 응답의 압축을 풉니 다! memcpy()는 \ 0 문자를 복사하고 있었지만 Eclipse 디버거는 전체 gzip 청크를 표시하지 않고 \ 0에서 중단되었습니다. 위 코드를 업데이트했습니다. – Coder

답변

4

아니 유형 char * 그것이 가리키는 내용에 대해서는 아무 것도 말하지 않으며, 어떤 값도 종결 자로 해석하지 않습니다. 반면에 str * 함수는 문자열이 어떻게 표현되는지에 대한 가정을 가지고 있으며 이진 데이터 또는 다른 표현을 갖는 텍스트 데이터에도 사용할 수 없습니다.

압축 풀림은 다소 복잡 할 수 있지만, 당신을 도울 수있는 zlib을 볼 수 있습니다.

+0

감사합니다. 이미 zlib을 압축 해제하려고 시도했습니다. 올바른 gzipped 입력을 얻지 못해서 즉 -3 데이터 오류가 반환됩니다. – Coder

+0

어떻게 데이터를 zlib에 전달합니까? – harald

+0

나는 청크에 의해 청크를 압축 해제 할 수 있지만 전체 데이터 부분을 하나씩 압축하는 inflate 기능을 가지고 있지만 문제는 그 하나의 데이터 청크를 선택하는 방법입니다. strtok()을 사용하여 \ r \ n에서 분할하면 나머지 청크를 반환하는 대신 첫 번째 recv() 반복에서 3 gzipped 문자를 반환하며 recv()의 나머지 호출은 \ 0에서 중단되고 ' 완전한 청크를 반환하지 않습니다. 디버깅 할 때 일식은 gzip 헤더의 일부이고 다른 0이 아닌 문자의 일부인 것으로 추측되는 3 개의 문자 다음에 7 \ 0 문자가 있음을 보여줍니다. – Coder

2

콘텐츠 길이 : 12605는 gzipped 파일의 크기가 12605 바이트임을 의미합니다. 따라서 메시지 헤더 뒤의 12605 바이트를 로컬 버퍼에 복사하고 해당 버퍼를 압축 해제 함수에 제공하십시오. 또한 소켓 읽기 기능이 하나의 흐름에서 전체 12605를 읽는 지 확실하지 않습니다. 그렇지 않은 경우 다음 로컬 버퍼에 12605 바이트를 읽은 후 나머지 데이터를 추가 한 다음 압축 해제 함수를 호출해야합니다. char *를 버퍼로 사용하는 데 문제가 없습니다. UR 문제는 문자열이 gzip 데이터를 인쇄하려고하기 때문에 발생합니다.

+0

gzip 데이터를 인쇄하지 않습니다. 단지 사람들에게 보여줄뿐입니다. 현재 프로그램은 한 번에 2048 바이트의 반복에서 12605 바이트를 읽습니다. 데이터 중간에 \ 0을 만나면 gzipped 데이터를 추가 할 수 없기 때문에 일시적으로 버퍼 크기를 12605로 늘려야합니까? – Coder

+0

'char buf [12605]; int offset = 0; do { recv_len = recv (소켓, & recv_data, 2048, 0); 경우 (recv_len> 0) { \t/* 버퍼에 데이터를 복사하고, 다음 수신 */ \t memcpy가 데이터 블록 (BUF + 오프셋 recv_data, recv_len)를 복사하기 위해 오프셋을 증가; \t 오프셋 = 오프셋 + recv_len; } } (recv_len> 0); 희망은 – snibu

+0

@ 코더가 도움이 되었기를 바랍니다. 우리는 코드를 보여 주면 도움이 될 것입니다. 그래도 여전히 바이너리 데이터에 문자열 함수를 사용하려고하는 것 같습니다. – harald

0

HTTP 페이로드의 시작 부분은 "\ r \ n \ r \ n"(HTTP 헤더 뒤) 뒤에 나옵니다.

HTTP 필드 "Content-Length"를 사용하여 HTTP 페이로드 크기를 가져옵니다.

이 정보를 사용하여 데이터의 압축을 해제하는 함수를 만들어야합니다. Zlib을 사용하면 그렇게 할 수 있습니다.

추신. 헤더 나 트레일러가있는 원시 형식이나 zlib을 사용하는 경우주의를 기울여야합니다. 일반적으로 HTTP는 헤더와 트레일러를 사용하며 IMAP4는 원시 형식을 사용합니다.