HTTP Keep-Alive模式客戶端與伺服器如何判定傳輸完成

李帆1998發表於2021-01-25

長連線是什麼

我們知道HTTP協議採用“請求-應答”模式,當使用普通模式,即非KeepAlive模式時,每個請求/應答客戶和伺服器都要新建一個連線,完成 之後立即斷開連線(HTTP協議為無連線的協議);當使用Keep-Alive模式(又稱持久連線、連線重用)時,Keep-Alive功能使客戶端到服 務器端的連線持續有效,當出現對伺服器的後繼請求時,Keep-Alive功能避免了建立或者重新建立連線。

伺服器如何知道已經完全接受客戶端傳送的資料

Content-Length 是一個實體訊息首部,用來指明傳送給接收方的訊息主體的大小,即用十進位制數字表示的八位元組的數目。

客戶端如何知道已經完全接受服務端傳送的資料

  • 在HTTP 1.0 短連線中客戶端傳送一個小請求,伺服器響應以所期望的資訊(例如一個html檔案或一副gif影像)。伺服器通常在傳送回所請求的資料之後就關閉連線。這樣客戶端讀資料時會返回EOF(-1),就知道資料已經接收完全了。
  • 訊息首部欄位Conent-Length
  • 訊息首部欄位Transfer-Encoding

Transfer-Encoding

當客戶端向伺服器請求一個靜態頁面或者一張圖片時,伺服器可以很清楚的知道內容大小,然後通過Content-length訊息首部欄位告訴客戶端需要接收多少資料。但是如果是動態頁面等時,伺服器是不可能預先知道內容大小,這時就可以使用Transfer-Encoding:chunk模式來傳輸資料了。即如果要一邊產生資料,一邊發給客戶端,伺服器就需要使用"Transfer-Encoding: chunked"這樣的方式來代替Content-Length。

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

Transfer-Encoding 是一個用來標示 HTTP 報文傳輸格式的頭部值。儘管這個取值理論上可以有很多,但是當前的 HTTP 規範裡實際上只定義了一種傳輸取值——chunked。

如果一個HTTP訊息(請求訊息或應答訊息)的Transfer-Encoding訊息頭的值為chunked,那麼,訊息體由數量未定的塊組成,並以最後一個大小為0的塊為結束。

每一個非空的塊都以該塊包含資料的位元組數(位元組數以十六進位制表示)開始,跟隨一個CRLF (回車及換行),然後是資料本身,最後塊CRLF結束。在一些實現中,塊大小和CRLF之間填充有白空格(0x20)。

最後一塊是單行,由塊大小(0),一些可選的填充白空格,以及CRLF。最後一塊不再包含任何資料,但是可以傳送可選的尾部,包括訊息頭欄位。訊息最後以CRLF結尾。

一個示例響應如下:

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked

25
This is the data in the first chunk

1A
and this is the second one
0

注意:

  • chunked 和 multipart 兩個名詞在意義上有類似的地方,不過在 HTTP 協議當中這兩個概念則不是一個類別的。multipart 是一種 Content-Type,標示 HTTP 報文內容的型別,而 chunked 是一種傳輸格式,標示報頭將以何種方式進行傳輸。
  • chunked 傳輸不能事先知道內容的長度,只能靠最後的空 chunk 塊來判斷,因此對於下載請求來說,是沒有辦法實現進度的。在瀏覽器和下載工具中,偶爾我們也會看到有些檔案是看不到下載進度的,即採用 chunked 方式進行下載。
  • chunked 的優勢在於,伺服器端可以邊生成內容邊傳送,無需事先生成全部的內容。HTTP/2 不支援 Transfer-Encoding: chunked,因為 HTTP/2 有自己的 streaming 傳輸方式(Source:MDN - Transfer-Encoding)。

transfer-coding與Content-Length

其實,上面2中方法都可以歸納為是如何判斷http訊息的大小、訊息的數量。RFC 2616對訊息的長度總結如下:一個訊息的transfer-length(傳輸長度)是指訊息中的message-body(訊息體)的長度。當應用了transfer-coding(傳輸編碼),每個訊息中的message-body(訊息體)的長度(transfer-length)由以下幾種情況決定(優先順序由高到低):

  • 任何不含有訊息體的訊息(如1XXX、204、304等響應訊息和任何頭(HEAD,首部)請求的響應訊息),總是由一個空行(CLRF)結束。
  • 如果出現了Transfer-Encoding頭欄位 並且值為非“identity”,那麼transfer-length由“chunked” 傳輸編碼定義,除非訊息由於關閉連線而終止。
  • 如果出現了Content-Length頭欄位,它的值表示entity-length(實體長度)和transfer-length(傳輸長度)。如果這兩個長度的大小不一樣(i.e.設定了Transfer-Encoding頭欄位),那麼將不能傳送Content-Length頭欄位。並且如果同時收到了Transfer-Encoding欄位和Content-Length頭欄位,那麼必須忽略Content-Length欄位。
  • 如果訊息使用媒體型別“multipart/byteranges”,並且transfer-length 沒有另外指定,那麼這種自定界(self-delimiting)媒體型別定義transfer-length 。除非傳送者知道接收者能夠解析該型別,否則不能使用該型別。
  • 由伺服器關閉連線確定訊息長度。(注意:關閉連線不能用於確定請求訊息的結束,因為伺服器不能再發響應訊息給客戶端了。)

為了相容HTTP/1.0應用程式,HTTP/1.1的請求訊息體中必須包含一個合法的Content-Length頭欄位,除非知道伺服器相容HTTP/1.1。一個請求包含訊息體,並且Content-Length欄位沒有給定,如果不能判斷訊息的長度,伺服器應該用用400 (bad request) 來響應;或者伺服器堅持希望收到一個合法的Content-Length欄位,用 411 (length required)來響應。

所有HTTP/1.1的接收者應用程式必須接受“chunked” transfer-coding (傳輸編碼),因此當不能事先知道訊息的長度,允許使用這種機制來傳輸訊息。訊息不應該夠同時包含 Content-Length頭欄位和non-identity transfer-coding。如果一個訊息同時包含non-identity transfer-coding和Content-Length ,必須忽略Content-Length 。

相關文章