使用zlib庫函式實現http報文的解壓
最近做專案的時候遇到了一個問題,那就是需要獲得http響應報文主體內容,一般響應主體會通過gzip格式進行壓縮,本文就是針對這種情況所寫的,可以進行記憶體解壓,而不需要儲存至本地檔案再解壓。至於chunked分塊傳輸方式要先進行報文重組再進行解壓,本文暫不考慮。
下圖是我抓取的一個http資料包,由Content-Encoding:gzip\r\n知該報文經過了gzip壓縮,而壓縮後的長度可以根據Content_Length=:785\r\n知為785位元組,即輸入引數len。壓縮後的報文儲存在哪裡呢??我們可以查詢“\r\n\r\n"這個字串,壓縮後的報文部分就儲存在其接下來的記憶體地址之中,即輸入引數source指標。
下面直接上程式碼:
對上面的資料包用上面的程式可以解壓正常不報錯!!#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是先重組在解壓,而這裡是先解壓在組合。因為我做的專案裡面我需要的內容就在第一個包裡,所以這部分沒有做,希望感興趣的同學可以補充上~~
好了,我的第一篇部落格就先寫到這裡吧。