HTTP協議中Content-Length 以及chunked編碼分析


0.序
1.http/1.1協議中與chunked編碼的相關字段
1)Entity Body
2)Content-length
3)Message Length
4)content-length字段的作用 
2.chunked編碼
1)定義
2)說明:
3)格式:
4)chunked編碼的程序化表示


0.序
在研究百度雲盤的響應過程中,發現其響應採用chunked編碼形式,並且沒有Content-length字段,因爲項目需要,就需要研究一下http/1.1協議中的chunked編碼。
首先介紹與chunked編碼相關的幾個概念,從而引出chunked編碼
1.http/1.1協議中與chunked編碼的相關字段
1)Entity Body: entity-body只有在message-body出現時纔會出現。通過對message-body的解碼獲得entity-body。transfer-encoding用於確保安全和信息的恰當傳輸。
     Entity-length:在應用任何transfer-encoding之前的message-body的長度。即沒有編碼之前message-body的長度。
2)Content-length:用於描述HTTP消息實體的傳輸長度。(the transfer-length of the message-body
消息實體長度:即Entity-length,壓縮之前的message-body的長度
消息實體的傳輸長度:Content-length,壓縮後的message-body的長度。
3)Message Length:這部分的解釋必須得看看大牛的解釋 http://blog.xiuwz.com/tag/content-length/


在具體的HTTP交互中,客戶端是如何獲取消息長度的呢,主要基於以下幾個規則:

  • 響應爲1xx,204,304相應或者head請求,則直接忽視掉消息實體內容。
  • 如果有Transfer-Encoding,則優先採用Transfer-Encoding裏面的方法來找到對應的長度。比如說Chunked模式
  • “如果head中有Content-Length,那麼這個Content-Length既表示實體長度,又表示傳輸長度。如果實體長度和傳輸長度不相等(比如說設置了Transfer-Encoding),那麼則不能設置Content-Length。如果設置了Transfer-Encoding,那麼Content-Length將被忽視”。這句話翻譯的優點饒,其實關鍵就一點:有了Transfer-Encoding,則不能有Content-Length。
  • Range傳輸。不關注,沒詳細看了:)
  • 通過服務器關閉連接能確定消息的傳輸長度。(請求端不能通過關閉連接來指明請求消息體的結束,因爲這樣可以讓服務器沒有機會繼續給予響應)。這種情況主要對應爲短連接,即非keep-alive模式。
  • HTTP1.1必須支持chunk模式。因爲當不確定消息長度的時候,可以通過chunk機制來處理這種情況。
  • 在包含消息內容的header中,如果有content-length字段,那麼該字段對應的值必須完全和消息主題裏面的長度匹配。
    “The entity-length of a message is the length of the message-body before any transfer-codings have been applied”
    也就是有chunk就不能有content-length 。
  • 其實後面幾條几乎可以忽視,簡單總結後如下:
  • 1、Content-Length如果存在並且有效的話,則必須和消息內容的傳輸長度完全一致。(經過測試,如果過短則會截斷,過長則會導致超時。)

    2、如果存在Transfer-Encoding(重點是chunked),則在header中不能有Content-Length,有也會被忽視

    3、如果採用短連接,則直接可以通過服務器關閉連接來確定消息的傳輸長度。(這個很容易懂)

    結合HTTP協議其他的特點,比如說Http1.1之前的不支持keep alive。那麼可以得出以下結論:

    1、在Http 1.0及之前版本中,content-length字段可有可無。

    2、在http1.1及之後版本。如果是keep alive,則content-length和chunk必然是二選一。若是非keep alive,則和http1.0一樣。content-length可有可無。


其中比較重要的一點如下所述:
          如果存在Transfer-encoding存在,則在header中不能由content-length,有也會被忽略。
          如果Content-length存在並且有效的話,則必須和消息內容的傳輸長度完全一致。
4)content-length字段的作用   
     Conent-Length表示實體內容長度,客戶端(服務器)可以根據這個值來判斷數據是否接收完成。但是如果消息中沒有Conent-Length,那該如何來判斷呢?又在什麼情況下會沒有Conent-Length呢?

沒有Content-length時,客戶端如何來判斷數據是否接收完成呢?
1)靜態頁面或者圖片:當客戶端向服務器請求一個靜態頁面或者一張圖片時,服務器可以很清楚的知道內容大小,然後通過Content-length消息首部字段告訴客戶端 需要接收多少數據。
2)動態頁面: 如果是動態頁面等時,服務器是不可能預先知道內容大小,這時就可以使用Transfer-Encoding:chunk模式來傳輸 數據了。即如果要一邊產生數據,一邊發給客戶端,服務器就需要使用”Transfer-Encoding: chunked”這樣的方式來代替Content-Length。
採用Transfer-encoding的目的
     一邊產生數據,一邊發給客戶端。


1)定義
     分塊傳輸編碼(Chunked transfer encoding)是超文本傳輸協議(HTTP)中的一種數據傳輸機制,允許HTTP由網頁服務器發送給客戶端應用( 通常是網頁瀏覽器)的數據可以分成多個部分。分塊傳輸編碼只在HTTP協議1.1版本(HTTP/1.1)中提供。
2)說明:
     通常,HTTP應答消息中發送的數據是整個發送的,Content-Length消息頭字段表示數據的長度。數據的長度很重要,因爲客戶端需要知道哪裏是應答消息的結束,以及後續應答消息的開始。然而,使用分塊傳輸編碼,數據分解成一系列數據塊,並以一個或多個塊發送,這樣服務器可以發送數據而不需要預先知道發送內容的總大小。
3)格式:
     如果一個HTTP消息(請求消息或應答消息)的Transfer-Encoding消息頭的值爲chunked,那麼,消息體由數量未定的塊組成,並以最後一個大小爲0的塊爲結束。
每一個非空的塊都以該塊包含數據的字節數(字節數以十六進制表示)開始,跟隨一個CRLF (回車及換行),然後是數據本身,最後塊CRLF結束。在一些實現中,塊大小和CRLF之間填充有白空格(0x20)。
最後一塊是單行,由塊大小(0),一些可選的填充白空格,以及CRLF。最後一塊不再包含任何數據,但是可以發送可選的尾部,包括消息頭字段。
消息最後以CRLF結尾。

     chunk編碼將數據分成一塊一塊的發生。Chunked編碼將使用若干個Chunk串連而成,由一個標明長度爲0 的chunk標示結束。每個Chunk分爲頭部和正文兩部分,頭部內容指定正文的字符總數(十六進制的數字 )和數量單位(一般不寫),正文部分就是指定長度的實際內容,兩部分之間用回車換行(CRLF) 隔開。在最後一個長度爲0的Chunk中的內容是稱爲footer的內容,是一些附加的Header信息(通常可以直接忽略)。

Chunk編碼的格式如下:

Chunked-Body = *chunk 
“0″ CRLF
footer
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF

hex-no-zero = <HEX excluding “0″>

chunk-size = hex-no-zero *HEX
chunk-ext = *( “;” chunk-ext-name [ "=" chunk-ext-value ] )
chunk-ext-name = token
chunk-ext-val = token | quoted-string
chunk-data = chunk-size(OCTET)

footer = *entity-header

即Chunk編碼由四部分組成: 1、0至多個chunk塊 ,2、“0″ CRLF ,3、footer ,4、CRLF . 而每個chunk塊由:chunk-size、chunk-ext(可選)、CRLF、chunk-data、CRLF組成。

4)chunked編碼的程序化表示
     (1)c++ 詳見http://wuhua.iteye.com/blog/673841
     (2)c語言
char * chunkpart1 = "42\r\n" ;
char * chunkpart2 = tmpuchar_body_data ;
char * chunkpart3 = "\r\n0\r\n\r\n" ;
int chunklen = 0;
 chunklen = strlen(chunkpart1) + strlen(chunkpart2) + strlen(chunkpart3);
char * chunk = (char*)ngx_pcalloc(r->pool,chunklen+1);
strncpy(chunk,chunkpart1,strlen(chunkpart1));
strncpy((chunk+strlen(chunkpart1)),chunkpart2, strlen(chunkpart2) );
strncpy((chunk+strlen(chunkpart1) + strlen(chunkpart2) ),chunkpart3, strlen(chunkpart3));


參考文章
1)http://zh.wikipedia.org/wiki/%E5%88%86%E5%9D%97%E4%BC%A0%E8%BE%93%E7%BC%96%E7%A0%81
2)http://blog.xiuwz.com/tag/content-length/
3)http://wuhua.iteye.com/blog/673841



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章