1. 程式人生 > >http協議各個版本

http協議各個版本

一、HTTP協議版本更替

HTTP/0.9

        HTTP協議的最初版本,功能簡陋,僅支援請求方式GET,並且僅能請求訪問HTML格式的資源

HTTP/1.0    

請求行必須在尾部新增協議版本欄位(http/1.0);必須包含頭訊息        

在0.9版本上做了進步,增加了請求方式POST和HEAD;不再侷限於0.9版本的HTML格式,根據Content-Type可以支援多種資料格式,即MIME多用途網際網路郵件擴充套件,例如text/html、image/jpeg等;同時也開始支援cache,就是客戶端在規定時間內訪問統一網站,直接訪問cache即可。

再次,HTTP請求和迴應的格式也變了。除了資料部分,每次通訊都必須包括頭資訊(HTTP header),用來描述一些元資料。

其他的新增功能還包括狀態碼(status code)、多字符集支援、多部分發送(multi-part type)、許可權(authorization)、快取(cache)、內容編碼(content encoding)等。

        但是1.0版本的工作方式是每次TCP連線只能傳送一個請求,當伺服器響應後就會關閉這次連線,下一個請求需要再次建立TCP連線,就是不支援keepalive

        TCP連線的新建成本很高,因為需要客戶端和伺服器三次握手,並且開始時傳送速率較慢(slow start)。所以,HTTP 1.0版本的效能比較差。隨著網頁載入的外部資源越來越多,這個問題就愈發突出了。

為了解決這個問題,有些瀏覽器在請求時,用了一個非標準的Connection欄位


Connection: keep-alive

這個欄位要求伺服器不要關閉TCP連線,以便其他請求複用。伺服器同樣迴應這個欄位。


Connection: keep-alive

一個可以複用的TCP連線就建立了,直到客戶端或伺服器主動關閉連線。但是,這不是標準欄位,不同實現的行為可能不一致,因此不是根本的解決辦法。

Content-Type 欄位

關於字元的編碼,1.0版規定,頭資訊必須是 ASCII 碼,後面的資料可以是任何格式。因此,伺服器迴應的時候,必須告訴客戶端,資料是什麼格式,這就是Content-Type

欄位的作用。

下面是一些常見的Content-Type欄位的值。

  • text/plain
  • text/html
  • text/css
  • image/jpeg
  • image/png
  • image/svg+xml
  • audio/mp4
  • video/mp4
  • application/javascript
  • application/pdf
  • application/zip
  • application/atom+xml

這些資料型別總稱為MIME type,每個值包括一級型別和二級型別,之間用斜槓分隔。

除了預定義的型別,廠商也可以自定義型別。


application/vnd.debian.binary-package

上面的型別表明,傳送的是Debian系統的二進位制資料包。

MIME type還可以在尾部使用分號,新增引數。


Content-Type: text/html; charset=utf-8

上面的型別表明,傳送的是網頁,而且編碼是UTF-8。

客戶端請求的時候,可以使用Accept欄位宣告自己可以接受哪些資料格式。


Accept: */*

上面程式碼中,客戶端宣告自己可以接受任何格式的資料。

MIME type不僅用在HTTP協議,還可以用在其他地方,比如HTML網頁。


<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- 等同於 -->
<meta charset="utf-8" /> 

Content-Encoding 欄位

由於傳送的資料可以是任何格式,因此可以把資料壓縮後再發送。Content-Encoding欄位說明資料的壓縮方法。


Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate

客戶端在請求時,用Accept-Encoding欄位說明自己可以接受哪些壓縮方法。


Accept-Encoding: gzip, deflate

HTTP/1.1    

1.1 版的最大變化,就是引入了持久連線(persistent connection),即TCP連線預設不關閉,可以被多個請求複用,不用宣告Connection: keep-alive解決了1.0版本的keepalive問題,1.1版本加入了持久連線一個TCP連線可以允許多個HTTP請求

客戶端和伺服器發現對方一段時間沒有活動,就可以主動關閉連線。不過,規範的做法是,客戶端在最後一個請求時,傳送Connection: close,明確要求伺服器關閉TCP連線。


Connection: close

目前,對於同一個域名,大多數瀏覽器允許同時建立6個持久連線。降低了延遲同時提高了頻寬的利用率。

加入了管道機制,在同一個TCP連線裡,允許多個請求同時傳送,增加了併發性,進一步改善了HTTP協議的效率;舉例來說,客戶端需要請求兩個資源。以前的做法是,在同一個TCP連線裡面,先發送A請求,然後等待伺服器做出迴應,收到後再發出B請求。管道機制則是允許瀏覽器同時發出A請求和B請求,但是伺服器還是按照順序,先回應A請求,完成後再回應B請求。

Content-Length 欄位

一個TCP連線現在可以傳送多個迴應,勢必就要有一種機制,區分資料包是屬於哪一個迴應的。這就是Content-length欄位的作用,宣告本次迴應的資料長度。


Content-Length: 3495

上面程式碼告訴瀏覽器,本次迴應的長度是3495個位元組,後面的位元組就屬於下一個迴應了。

在1.0版中,Content-Length欄位不是必需的,因為瀏覽器發現伺服器關閉了TCP連線,就表明收到的資料包已經全了。

分塊傳輸編碼

使用Content-Length欄位的前提條件是,伺服器傳送迴應之前,必須知道迴應的資料長度。

對於一些很耗時的動態操作來說,這意味著,伺服器要等到所有操作完成,才能傳送資料,顯然這樣的效率不高。更好的處理方法是,產生一塊資料,就傳送一塊,採用"流模式"(stream)取代"快取模式"(buffer)。

因此,1.1版規定可以不使用Content-Length欄位,而使用"分塊傳輸編碼"(chunked transfer encoding)。只要請求或迴應的頭資訊有Transfer-Encoding欄位,就表明迴應將由數量未定的資料塊組成。


Transfer-Encoding: chunked

每個非空的資料塊之前,會有一個16進位制的數值,表示這個塊的長度。最後是一個大小為0的塊,就表示本次迴應的資料傳送完了。下面是一個例子。


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

25
This is the data in the first chunk

1C
and this is the second one

3
con

8
sequence

0

新增了請求方式PUT、PATCH、OPTIONS、DELETE等

另外,客戶端請求的頭資訊新增了Host欄位,用來指定伺服器的域名。在HTTP1.0中認為每臺伺服器都繫結一個唯一的IP地址,因此,請求訊息中的URL並沒有傳遞主機名(hostname)。但隨著虛擬主機技術的發展,在一臺物理伺服器上可以存在多個虛擬主機(Multi-homed Web Servers),並且它們共享一個IP地址。


Host: www.example.com

有了Host欄位,就可以將請求發往同一臺伺服器上的不同網站,為虛擬主機的興起打下了基礎。(實現了在一臺WEB伺服器上可以在同一個IP地址和埠號上使用不同的主機名來建立多個虛擬WEB站點。也即是說,web server上的多個虛擬站點可以共享同一個ip和埠。)且請求訊息中如果沒有Host頭域會報告一個錯誤(400 Bad Request)。

        雖然1.1版允許複用TCP連線,但是同一個TCP連線裡面,所有的資料通訊是按次序進行的。服務端是按佇列順序處理請求的,伺服器只有處理完一個迴應,才會進行下一個迴應。假如前面的請求處理時間很長,後面就會有許多請求排隊等著,這樣就造成了“隊頭阻塞”的問題;同時HTTP是無狀態的連線,因此每次請求都需要新增重複的欄位,降低了頻寬的利用率。

多路複用帶來一個新的問題是,在連線共享的基礎之上有可能會導致關鍵請求被阻塞。SPDY允許給每個request設定優先順序,這樣重要的請求就會優先得到響應。比如瀏覽器載入首頁,首頁的html內容應該優先展示,之後才是各種靜態資原始檔,指令碼檔案等載入,這樣可以保證使用者能第一時間看到網頁內容。

為了避免這個問題,只有兩種方法:一是減少請求數,二是同時多開持久連線。這導致了很多的網頁優化技巧,比如合併指令碼和樣式表、將圖片嵌入CSS程式碼、域名分片(domain sharding)等等。如果HTTP協議設計得更好一些,這些額外的工作是可以避免的。

100(Continue) Status(節約頻寬)

HTTP/1.1加入了一個新的狀態碼100Continue)。客戶端事先發送一個只帶頭域的請求,如果伺服器因為許可權拒絕了請求,就回送響應碼401(Unauthorized);如果伺服器接收此請求就回送響應碼100,客戶端就可以繼續傳送帶實體的完整請求了。100 (Continue) 狀態程式碼的使用,允許客戶端在發request訊息body之前先用request header試探一下server,看server要不要接收request body,再決定要不要發request body。

HTTP/1.1在1.0的基礎上加入了一些cache的新特性,當快取物件的Age超過Expire時變為stale物件,cache不需要直接拋棄stale物件,而是與源伺服器進行重新啟用(revalidation)。

HTTP 1.1支援只發送header資訊(不帶任何body資訊),如果伺服器認為客戶端有許可權請求伺服器,則返回100,否則返回401。客戶端如果接受到100,才開始把請求body傳送到伺服器。這樣當伺服器返回401的時候,客戶端就可以不用傳送請求body了,節約了頻寬。

HTTP1.1還有身份認證機制,許多web站點要求使用者提供一個使用者名稱—口令對才能訪問存放在其伺服器中的文件,這種要求稱為身份認證(authentication)。HTTP提供特殊的狀態碼和頭部來幫助Web站點執行身份認證。

HTTP支援傳送內容的一部分。這樣當客戶端已經有一部分的資源後,只需要跟伺服器請求另外的部分資源即可。這是支援檔案斷點續傳的基礎。

HTTP/1.1支援檔案斷點續傳RANGE:bytes,HTTP/1.0每次傳送檔案都是從檔案頭開始,即0位元組處開始。RANGE:bytes=XXXX表示要求伺服器從檔案XXXX位元組處開始傳送,斷點續傳。即返回碼是206(Partial Content)

在HTTP1.1中新增了24個錯誤狀態響應碼,如409(Conflict)表示請求的資源與資源的當前狀態發生衝突;410(Gone)表示伺服器上的某個資源被永久性的刪除。

HTTP/2.0

        為了解決1.1版本利用率不高的問題,提出了HTTP/2.0版本。增加雙工模式,即不僅客戶端能夠同時傳送多個請求,服務端也能同時處理多個請求,解決了隊頭堵塞的問題(HTTP2.0使用了多路複用的技術,做到同一個連線併發處理多個請求,而且併發請求的數量比HTTP1.1大了好幾個數量級;HTTP請求和響應中,狀態行和請求/響應頭都是些資訊欄位,並沒有真正的資料,因此在2.0版本中將所有的資訊欄位建立一張表,為表中的每個欄位建立索引,客戶端和服務端共同使用這個表,他們之間就以索引號來表示資訊欄位,這樣就避免了1.0舊版本的重複繁瑣的欄位,並以壓縮的方式傳輸,提高利用率。

        另外也增加伺服器推送的功能,即不經請求服務端主動向客戶端傳送資料

當前主流的協議版本還是HTTP/1.1版本。

二進位制協議

HTTP/1.1 版的頭資訊肯定是文字(ASCII編碼),資料體可以是文字,也可以是二進位制。HTTP/2 則是一個徹底的二進位制協議,頭資訊和資料體都是二進位制,並且統稱為"幀"(frame):頭資訊幀和資料幀。

二進位制協議的一個好處是,可以定義額外的幀。HTTP/2 定義了近十種幀,為將來的高階應用打好了基礎。如果使用文字實現這種功能,解析資料將會變得非常麻煩,二進位制解析則方便得多。

多工

HTTP/2 複用TCP連線,在一個連線裡,客戶端和瀏覽器都可以同時傳送多個請求或迴應,而且不用按照順序一一對應,這樣就避免了"隊頭堵塞"。

舉例來說,在一個TCP連線裡面,伺服器同時收到了A請求和B請求,於是先回應A請求,結果發現處理過程非常耗時,於是就傳送A請求已經處理好的部分, 接著迴應B請求,完成後,再發送A請求剩下的部分。

這樣雙向的、實時的通訊,就叫做多工(Multiplexing)。

資料流

因為 HTTP/2 的資料包是不按順序傳送的,同一個連線裡面連續的資料包,可能屬於不同的迴應。因此,必須要對資料包做標記,指出它屬於哪個迴應。

HTTP/2 將每個請求或迴應的所有資料包,稱為一個數據流(stream)。每個資料流都有一個獨一無二的編號。資料包傳送的時候,都必須標記資料流ID,用來區分它屬於哪個資料流。另外還規定,客戶端發出的資料流,ID一律為奇數,伺服器發出的,ID為偶數。

資料流傳送到一半的時候,客戶端和伺服器都可以傳送訊號(RST_STREAM幀),取消這個資料流。1.1版取消資料流的唯一方法,就是關閉TCP連線。這就是說,HTTP/2 可以取消某一次請求,同時保證TCP連線還開啟著,可以被其他請求使用。

客戶端還可以指定資料流的優先順序。優先順序越高,伺服器就會越早迴應。

頭資訊壓縮

HTTP 協議不帶有狀態,每次請求都必須附上所有資訊。所以,請求的很多欄位都是重複的,比如CookieUser Agent,一模一樣的內容,每次請求都必須附帶,這會浪費很多頻寬,也影響速度。

HTTP/2 對這一點做了優化,引入了頭資訊壓縮機制(header compression)。一方面,頭資訊使用gzipcompress壓縮後再發送;另一方面,客戶端和伺服器同時維護一張頭資訊表,所有欄位都會存入這個表,生成一個索引號,以後就不傳送同樣欄位了,只發送索引號,這樣就提高速度了。

伺服器推送

HTTP/2 允許伺服器未經請求,主動向客戶端傳送資源,這叫做伺服器推送(server push)。

意思是說,當我們對支援HTTP2.0的web server請求資料的時候,伺服器會順便把一些客戶端需要的資源一起推送到客戶端,免得客戶端再次建立連線傳送請求到伺服器端獲取。這種方式非常合適載入靜態資源

伺服器端推送的這些資源其實存在客戶端的某處地方,客戶端直接從本地載入這些資源就可以了,不用走網路,速度自然是快很多的。

常見場景是客戶端請求一個網頁,這個網頁裡面包含很多靜態資源。正常情況下,客戶端必須收到網頁後,解析HTML原始碼,發現有靜態資源,再發出靜態資源請求。其實,伺服器可以預期到客戶端請求網頁後,很可能會再請求靜態資源,所以就主動把這些靜態資源隨著網頁一起發給客戶端了。

服務端推送把客戶端所需要的資源伴隨著index.html一起傳送到客戶端,省去了客戶端重複請求的步驟。正因為沒有發起請求,建立連線等操作,所以靜態資源通過服務端推送的方式可以極大地提升速度。

普通的客戶端請求過程:

服務端推送的過程:

HTTP 效能優化的關鍵並不在於高頻寬,而是低延遲。TCP 連線會隨著時間進行自我「調諧」,起初會限制連線的最大速度,如果資料成功傳輸,會隨著時間的推移提高傳輸的速度。這種調諧則被稱為 TCP 慢啟動(擁塞控制)。由於這種原因,讓原本就具有突發性和短時性的 HTTP 連線變的十分低效。
HTTP/2 通過讓所有資料流共用同一個連線,可以更有效地使用 TCP 連線,讓高頻寬也能真正的服務於 HTTP 的效能提升。

二、HTTP響應模型

        伺服器收到HTTP請求之後,會有多種方法響應這個請求,下面是HTTP響應的四種模型:

        單程序I/O模型

服務端開啟一個程序,一個程序僅能處理一個請求,並且對請求順序處理;

        多程序I/O模型

服務端並行開啟多個程序,同樣的一個程序只能處理一個請求,這樣服務端就可以同時處理多個請求;

        複用I/O模型

服務端開啟一個程序,但是呢,同時開啟多個執行緒,一個執行緒響應一個請求,同樣可以達到同時處理多個請求,執行緒間併發執行;

        複用多執行緒I/O模型

服務端並行開啟多個程序,同時每個程序開啟多個執行緒,這樣服務端可以同時處理程序數M*每個程序的執行緒數N個請求。