HTTP協議頭部與Keep-Alive模式詳解

BruceZhang發表於2015-11-30

  無論是接觸網際網路開發的哪個方面,都需要好好的瞭解下HTTP協議的過程,這篇文章主要講解HTTP首部欄位,文章來自HTTP協議頭部與Keep-Alive模式詳解

1、什麼是Keep-Alive模式?

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

  http 1.0中預設是關閉的,需要在http頭加入”Connection: Keep-Alive”,才能啟用Keep-Alive;http 1.1中預設啟用Keep-Alive,如果加入”Connection: close “,才關閉。目前大部分瀏覽器都是用http1.1協議,也就是說預設都會發起Keep-Alive的連線請求了,所以是否能完成一個完整的Keep- Alive連線就看伺服器設定情況。

2、啟用Keep-Alive的優點

  從上面的分析來看,啟用Keep-Alive模式肯定更高效,效能更高。因為避免了建立/釋放連線的開銷。下面是RFC 2616 上的總結:
1. By opening and closing fewer TCP connections, CPU time is saved in routers and hosts (clients, servers, proxies,
gateways, tunnels, or caches), and memory used for TCP protocol control blocks can be saved in hosts.
2. HTTP requests and responses can be pipelined on a connection. Pipelining allows a client to make multiple requests without waiting for each response, allowing a single TCP connection to be used much more efficiently, with much lower elapsed time.
3. Network congestion is reduced by reducing the number of packets caused by TCP opens, and by allowing TCP sufficient time to determine the congestion state of the network.
4. Latency on subsequent requests is reduced since there is no time spent in TCP’s connection opening handshake.
5. HTTP can evolve more gracefully, since errors can be reported without the penalty of closing the TCP connection. Clients using future versions of HTTP might optimistically try a new feature, but if communicating with an older server, retry with old semantics after an error is reported.
RFC 2616 (P47)還指出:單使用者客戶端與任何伺服器或代理之間的連線數不應該超過2個。一個代理與其它伺服器或程式碼之間應該使用不超過2 * N的活躍併發連線。這是為了提高HTTP響應時間,避免擁塞(冗餘的連線並不能程式碼執行效能的提升)。

3、回到我們的問題(即如何判斷訊息內容/長度的大小?)

  Keep-Alive模式,客戶端如何判斷請求所得到的響應資料已經接收完成(或者說如何知道伺服器已經發生完了資料)?我們已經知道 了,Keep-Alive模式傳送玩資料HTTP伺服器不會自動斷開連線,所有不能再使用返回EOF(-1)來判斷(當然你一定要這樣使用也沒有辦法,可 以想象那效率是何等的低)!下面我介紹兩種來判斷方法。
  

3.1、使用訊息首部欄位Conent-Length

  故名思意,Conent-Length表示實體內容長度,客戶端(伺服器)可以根據這個值來判斷資料是否接收完成。但是如果訊息中沒有Conent-Length,那該如何來判斷呢?又在什麼情況下會沒有Conent-Length呢?請繼續往下看……

3.2、使用訊息首部欄位Transfer-Encoding

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

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

Chunked-Body = *<strong>chunk </strong>
"0" CRLF
footer
CRLF
chunk = chunk-size [ chunk-ext ] CRLF
chunk-data CRLF

hex-no-zero = &lt;HEX excluding "0"&gt;

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、<strong>0至多個chunk塊</strong> ,2、<strong>"0" CRLF </strong>,3、<strong>footer </strong>,4、<strong>CRLF</strong> <strong>.</strong> 而每個chunk塊由:chunk-size、chunk-ext(可選)、CRLF、chunk-data、CRLF組成。

4、訊息長度的總結

  其實,上面2中方法都可以歸納為是如何判斷http訊息的大小、訊息的數量。RFC 2616 對 訊息的長度總結如下:一個訊息的transfer-length(傳輸長度)是指訊息中的message-body(訊息體)的長度。當應用了 transfer-coding(傳輸編碼),每個訊息中的message-body(訊息體)的長度(transfer-length)由以下幾種情況 決定(優先順序由高到低):
1. 任何不含有訊息體的訊息(如1XXX、204、304等響應訊息和任何頭(HEAD,首部)請求的響應訊息),總是由一個空行(CLRF)結束。
2. 如果出現了Transfer-Encoding頭欄位 並且值為非“identity”,那麼transfer-length由“chunked” 傳輸編碼定義,除非訊息由於關閉連線而終止。
3. 如果出現了Content-Length頭欄位,它的值表示entity-length(實體長度)和transfer-length(傳輸長 度)。如果這兩個長度的大小不一樣(i.e.設定了Transfer-Encoding頭欄位),那麼將不能傳送Content-Length頭欄位。並 且如果同時收到了Transfer-Encoding欄位和Content-Length頭欄位,那麼必須忽略Content-Length欄位。
4. 如果訊息使用媒體型別“multipart/byteranges”,並且transfer-length 沒有另外指定,那麼這種自定界(self-delimiting)媒體型別定義transfer-length 。除非傳送者知道接收者能夠解析該型別,否則不能使用該型別。
5. 由伺服器關閉連線確定訊息長度。(注意:關閉連線不能用於確定請求訊息的結束,因為伺服器不能再發響應訊息給客戶端了。)

為了相容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 。

5、HTTP頭欄位總結

最後我總結下HTTP協議的頭部欄位。

1、 Accept:告訴WEB伺服器自己接受什麼介質型別,/ 表示任何型別,type/* 表示該型別下的所有子型別,type/sub-type。

2、 Accept-Charset: 瀏覽器申明自己接收的字符集 Accept-Encoding: 瀏覽器申明自己接收的編碼方法,通常指定壓縮方法,是否支援壓縮,支援什麼壓縮方法(gzip,deflate) Accept-Language:瀏覽器申明自己接收的語言 語言跟字符集的區別:中文是語言,中文有多種字符集,比如big5,gb2312,gbk等等。

3、 Accept-Ranges:WEB伺服器表明自己是否接受獲取其某個實體的一部分(比如檔案的一部分)的請求。bytes:表示接受,none:表示不接受。

4、 Age:當代理伺服器用自己快取的實體去響應請求時,用該頭部表明該實體從產生到現在經過多長時間了。

5、 Authorization:當客戶端接收到來自WEB伺服器的 WWW-Authenticate 響應時,用該頭部來回應自己的身份驗證資訊給WEB伺服器。

6、 Cache-Control:請求:no-cache(不要快取的實體,要求現在從WEB伺服器去取) max-age:(只接受 Age 值小於 max-age 值,並且沒有過期的物件) max-stale:(可以接受過去的物件,但是過期時間必須小於 max-stale 值) min-fresh:(接受其新鮮生命期大於其當前 Age 跟 min-fresh 值之和的快取物件) 響應:public(可以用 Cached 內容迴應任何使用者) private(只能用快取內容迴應先前請求該內容的那個使用者) no-cache(可以快取,但是隻有在跟WEB伺服器驗證了其有效後,才能返回給客戶端) max-age:(本響應包含的物件的過期時間) ALL: no-store(不允許快取)

7、 Connection:請求:close(告訴WEB伺服器或者代理伺服器,在完成本次請求的響應後,斷開連線,不要等待本次連線的後續請求了)。 keepalive(告訴WEB伺服器或者代理伺服器,在完成本次請求的響應後,保持連線,等待本次連線的後續請求)。 響應:close(連線已經關閉)。 keepalive(連線保持著,在等待本次連線的後續請求)。 Keep-Alive:如果瀏覽器請求保持連線,則該頭部表明希望 WEB 伺服器保持連線多長時間(秒)。例如:Keep-Alive:300

8、 Content-Encoding:WEB伺服器表明自己使用了什麼壓縮方法(gzip,deflate)壓縮響應中的物件。例如:Content-Encoding:gzip

9、Content-Language:WEB 伺服器告訴瀏覽器自己響應的物件的語言。

10、Content-Length: WEB 伺服器告訴瀏覽器自己響應的物件的長度。例如:Content-Length: 26012

11、Content-Range: WEB 伺服器表明該響應包含的部分物件為整個物件的哪個部分。例如:Content-Range: bytes 21010-47021/47022

12、Content-Type: WEB 伺服器告訴瀏覽器自己響應的物件的型別。例如:Content-Type:application/xml

13、ETag:就是一個物件(比如URL)的標誌值,就一個物件而言,比如一個 html 檔案,如果被修改了,其 Etag 也會別修改,所以ETag 的作用跟 Last-Modified 的作用差不多,主要供 WEB 伺服器判斷一個物件是否改變了。比如前一次請求某個 html 檔案時,獲得了其 ETag,當這次又請求這個檔案時,瀏覽器就會把先前獲得的 ETag 值傳送給WEB 伺服器,然後 WEB 伺服器會把這個 ETag 跟該檔案的當前 ETag 進行對比,然後就知道這個檔案有沒有改變了。

14、 Expired:WEB伺服器表明該實體將在什麼時候過期,對於過期了的物件,只有在跟WEB伺服器驗證了其有效性後,才能用來響應客戶請求。是 HTTP/1.0 的頭部。例如:Expires:Sat, 23 May 2009 10:02:12 GMT

15、 Host:客戶端指定自己想訪問的WEB伺服器的域名/IP 地址和埠號。例如:Host:rss.sina.com.cn

16、 If-Match:如果物件的 ETag 沒有改變,其實也就意味著物件沒有改變,才執行請求的動作。

17、 If-None-Match:如果物件的 ETag 改變了,其實也就意味著物件也改變了,才執行請求的動作。

18、 If-Modified-Since:如果請求的物件在該頭部指定的時間之後修改了,才執行請求的動作(比如返回物件),否則返回程式碼304,告訴瀏覽器 該物件沒有修改。例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT

19、 If-Unmodified-Since:如果請求的物件在該頭部指定的時間之後沒修改過,才執行請求的動作(比如返回物件)。

20、 If-Range:瀏覽器告訴 WEB 伺服器,如果我請求的物件沒有改變,就把我缺少的部分給我,如果物件改變了,就把整個物件給我。瀏覽器通過傳送請求物件的 ETag 或者 自己所知道的最後修改時間給 WEB 伺服器,讓其判斷物件是否改變了。總是跟 Range 頭部一起使用。

21、 Last-Modified:WEB 伺服器認為物件的最後修改時間,比如檔案的最後修改時間,動態頁面的最後產生時間等等。例如:Last-Modified:Tue, 06 May 2008 02:42:43 GMT

22、 Location:WEB 伺服器告訴瀏覽器,試圖訪問的物件已經被移到別的位置了,到該頭部指定的位置去取。例如:Location:http://i0.sinaimg.cn/dy/deco/2008/0528/sinahome_0803_ws_005_text_0.gif

23、 Pramga:主要使用 Pramga: no-cache,相當於 Cache-Control: no-cache。例如:Pragma:no-cache

24、 Proxy-Authenticate: 代理伺服器響應瀏覽器,要求其提供代理身份驗證資訊。Proxy-Authorization:瀏覽器響應代理伺服器的身份驗證請求,提供自己的身份資訊。

25、 Range:瀏覽器(比如 Flashget 多執行緒下載時)告訴 WEB 伺服器自己想取物件的哪部分。例如:Range: bytes=1173546-

26、 Referer:瀏覽器向 WEB 伺服器表明自己是從哪個 網頁/URL 獲得/點選 當前請求中的網址/URL。例如:Referer:http://www.sina.com/

27、 Server: WEB 伺服器表明自己是什麼軟體及版本等資訊。例如:Server:Apache/2.0.61 (Unix)

28、 User-Agent: 瀏覽器表明自己的身份(是哪種瀏覽器)。例如:User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.8.1.14) Gecko/20080404 Firefox/2、0、0、14

29、 Transfer-Encoding: WEB 伺服器表明自己對本響應訊息體(不是訊息體裡面的物件)作了怎樣的編碼,比如是否分塊(chunked)。例如:Transfer-Encoding: chunked

30、 Vary: WEB伺服器用該頭部的內容告訴 Cache 伺服器,在什麼條件下才能用本響應所返回的物件響應後續的請求。假如源WEB伺服器在接到第一個請求訊息時,其響應訊息的頭部為:Content- Encoding: gzip; Vary: Content-Encoding那麼 Cache 伺服器會分析後續請求訊息的頭部,檢查其 Accept-Encoding,是否跟先前響應的 Vary 頭部值一致,即是否使用相同的內容編碼方法,這樣就可以防止 Cache 伺服器用自己 Cache 裡面壓縮後的實體響應給不具備解壓能力的瀏覽器。例如:Vary:Accept-Encoding

31、 Via: 列出從客戶端到 OCS 或者相反方向的響應經過了哪些代理伺服器,他們用什麼協議(和版本)傳送的請求。當客戶端請求到達第一個代理伺服器時,該伺服器會在自己發出的請求裡面添 加 Via 頭部,並填上自己的相關資訊,當下一個代理伺服器收到第一個代理伺服器的請求時,會在自己發出的請求裡面複製前一個代理伺服器的請求的Via 頭部,並把自己的相關資訊加到後面,以此類推,當 OCS 收到最後一個代理伺服器的請求時,檢查 Via 頭部,就知道該請求所經過的路由。例如:Via:1.0 236.D0707195.sina.com.cn:80 (squid/2.6.STABLE13)

相關文章