1. 程式人生 > >http協議中content-length 以及chunked編碼分析

http協議中content-length 以及chunked編碼分析

轉載請註明出處 http://blog.csdn.net/yankai0219/article/details/8269922 
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編碼的相關欄位
: entity-body只有在message-body出現時才會出現。通過對message-body的解碼獲得entity-body。transfer-encoding用於確保安全和資訊的恰當傳輸。      Entity-length:在應用任何transfer-encoding之前的message-body的長度。即沒有編碼之前message-body的長度。 :用於描述HTTP訊息實體的傳輸長度。(the transfer-length of the message-body
訊息實體長度:即Entity-length,壓縮之前的message-body的長度 訊息實體的傳輸長度:Content-length,壓縮後的message-body的長度。
在具體的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