簡介
園子裡已經有不少介紹HTTP的的好文章。對HTTP的一些細節介紹的比較好,所以本篇文章不會對HTTP的細節進行深究,而是從夠高和更結構化的角度將HTTP協議的元素進行分類講解。
HTTP的定義和曆史
在一個網路中。傳輸資料需要面臨三個問題:
1.用戶端如何知道所求內容的位置。
2.當用戶端知道所求內容的位置後,如何擷取所求內容。
3.所求內容以何種形式組織以便被用戶端所識別。
對於WEB來說,回答上面三種問題分別採用三種不同的技術,分別為:統一資源定位器(URIs),超文字傳輸通訊協定 (HTTP)(HTTP)和超文字標記語言 (HTML)(HTML)。對於大多數WEB開發人員來說URI和HTML都是非常的熟悉。而HTTP協議在很多WEB技術中都被封裝的過多使得HTTP反而最不被熟悉。
HTTP作為一種傳輸協議,也是像HTML一樣隨著時間不斷演化的,目前流行的HTTP1.1是HTTP協議的第三個版本。
HTTP 0.9
HTTP 0.9作為HTTP協議的第一個版本。是非常弱的。請求(Request)只有一行,比如:
GET www.cnblogs.com
從如此簡單的請求體,沒有POST方法,沒有HTTP 頭可以看出,那個時代的HTTP用戶端只能接收一種類型:純文字。並且,如果得不到所求的資訊,也沒有404 500等錯誤出現。
雖然HTTP 0.9看起來如此弱,但已經能滿足那個時代的需求了。
HTTP 1.0
隨著1996年後,WEB程式的需求,HTTP 0.9已經不能滿足需求。HTTP1.0最大的改變是引入了POST方法,使得用戶端通過HTML表單向伺服器發送資料成為可能,這也是WEB應用程式的一個基礎。另一個巨大的改變是引入了HTTP頭,使得HTTP不僅能返回錯誤碼,並且HTTP協議所傳輸的內容不僅限於純文字,還可以是圖片,動畫等一系列格式。
除此之外,還允許保持串連,既一次TCP串連後,可以多次通訊,雖然HTTP1.0 預設是傳輸一次資料後就關閉。
HTTP 1.1
2000年5月,HTTP1.1確立。HTTP1.1並不像HTTP1.0對於HTTP0.9那樣的革命性。但是也有很多增強。
首先,增加了Host頭,比如訪問我的部落格:
GET /Careyson HTTP/1.1 Host: www.cnblogs.com
Get後面僅僅需要相對路徑即可。這看起來雖然僅僅類似文法糖的感覺,但實際上,這個提升使得在Web上的一台主機可以存在多個域。否則多個網域名稱指向同一個IP會產生混淆。
此外,還引入了Range頭,使得用戶端通過HTTP下載時只下載內容的一部分,這使得多線程下載也成為可能。
還有值得一提的是HTTP1.1 預設串連是一直保持的,這個概念我會在下文中具體闡述。
HTTP的網路層次
在Internet中所有的傳輸都是通過TCP/IP進行的。HTTP協議作為TCP/IP模型中應用程式層的協議也不例外。HTTP在網路中的層次如圖1所示。
圖1.HTTP在TCP/IP中的層次
可以看出,HTTP是基於傳輸層的TCP協議,而TCP是一個端到端的連線導向的協議。所謂的端到端可以理解為進程到進程之間的通訊。所以HTTP在開始傳輸之前,首先需要建立TCP串連,而TCP串連的過程需要所謂的“三向交握”。概念如圖2所示。
圖2.TCP串連的三向交握
在TCP三向交握之後,建立了TCP串連,此時HTTP就可以進行傳輸了。一個重要的概念是連線導向,既HTTP在傳輸完成之間並不斷開TCP串連。在HTTP1.1中(通過Connection頭設定)這是預設行為。所謂的HTTP傳輸完成我們通過一個具體的例子來看。
比如訪問我的部落格,使用Fiddler來截取對應的請求和響應。如圖3所示。
圖3.用fiddler抓取請求和相應
可以看出,雖然僅僅訪問了我的部落格,但鎖擷取的不僅僅是一個HTML而已,而是瀏覽器對HTML解析的過程中,如果發現需要擷取的內容,會再次發起HTTP請求去伺服器擷取,比如圖2中的那個common2.css。這上面19個HTTP請求,只依靠一個TCP串連就夠了,這就是所謂的持久串連。也是所謂的一次HTTP請求完成。
HTTP請求(HTTP Request)
所謂的HTTP請求,也就是Web用戶端向Web伺服器發送資訊,這個資訊由如下三部分組成:
1.請求行
2.HTTP頭
3.內容
一個典型的請求行比如:
GET www.cnblogs.com HTTP/1.1
請求行寫法是固定的,由三部分組成,第一部分是要求方法,第二部分是請求網址,第三部分是HTTP版本。
第二部分HTTP頭在HTTP請求可以是3種HTTP頭:1.要求標頭(request header) 2.普通頭(general header) 3.實體頭(entity header)
通常來說,由於Get請求往往不包含內容實體,因此也不會有實體頭。
第三部分內容只在POST請求中存在,因為GET請求並不包含任何實體。
我們截取一個具體的Post請求來看這三部分,我在一個普通的aspx頁面放一個BUTTON,當提交後會產生一個Post請求,如圖4所示。
圖4.HTTP請求由三部分組成
HTTP要求方法
雖然我們所常見的只有Get和Post方法,但實際上HTTP要求方法還有很多,比如: PUT方法,DELETE方法,HEAD方法,CONNECT方法,TRACE方法。這裡我就不細說了,自行Bing。
這裡重點說一下Get和Post方法,網上關於Get和Post的區別滿天飛。但很多沒有說到點子上。Get和Post最大的區別就是Post有上面所說的第三部分:內容。而Get不存在這個內容。因此就像Get和Post其名稱所示那樣,Get用於從伺服器上取內容,雖然可以通過QueryString向伺服器發資訊,但這違背了Get的本意,QueryString中的資訊在HTTP看來僅僅是擷取所取得內容的一個參數而已。而Post是由用戶端向伺服器端發送內容的方式。因此具有請求的第三部分:內容。
HTTP響應(HTTP Response)
當Web伺服器收到HTTP請求後,會根據請求的資訊做某些處理(這些處理可能僅僅是靜態返回頁,或是包含Asp.net,PHP,Jsp等語言進行處理後返回),相應的返回一個HTTP響應。HTTP響應在結構上很類似於HTTP請求,也是由三部分組成,分別為:
1.狀態行
2.HTTP頭
3.返回內容
首先來看狀態行,一個典型的HTTP狀態如下:
HTTP/1.1 200 OK
第一部分是HTTP版本,第二部分是響應狀態代碼,第三部分是狀態代碼的描述,因此也可以把第二和第三部分看成一個部分。
對於HTTP版本沒有什麼好說的,而狀態代碼值得說一下,網上對於每個具體的HTTP狀態代碼所代表的含義都有解釋,這裡我說一下分類。
資訊類 (100-199)
響應成功 (200-299)
重新導向類 (300-399)
用戶端錯誤類 (400-499)
服務端錯誤類 (500-599)
HTTP響應中包含的頭包括1.回應標頭(response header) 2.普通頭(general header) 3.實體頭(entity header)。
第三部分HTTP響應內容就是HTTP請求所請求的資訊。這個資訊可以是一個HTML,也可以是一個圖片。比如我訪問百度,HTTP Response如圖5所示。
圖5.一個典型的HTTP響應
圖5中的響應是一個HTML,當然還可以是其它類型,比如圖片,如圖6所示。
圖6.HTTP響應內容是圖片
這裡會有一個疑問,既然HTTP響應的內容不僅僅是HTML,還可以是其它類型,那麼瀏覽器如何正確對接收到的資訊進行處理。
這是通過媒體類型確定的(Media Type),具體來說對應Content-Type這個HTTP頭,比如圖5中是text/html,圖6是image/jpeg。
媒體類型的格式為:大類/小類 比如圖5中的html是小類,而text是大類。
IANA(The Internet Assigned Numbers Authority,互連網數字分配機構)定義了8個大類的媒體類型,分別是:
application— (比如: application/vnd.ms-excel.)
audio (比如: audio/mpeg.)
image (比如: image/png.)
message (比如,:message/http.)
model(比如:model/vrml.)
multipart (比如:multipart/form-data.)
text(比如:text/html.)
video(比如:video/quicktime.)
HTTP頭
HTTP頭僅僅是一個標籤而已,比如我在Aspx中加入代碼:
Response.AddHeader("測試頭","測試值");
對應的我們可以在fiddler抓到的資訊如圖7所示。
圖7.HTTP頭
不難看出,HTTP頭並不是嚴格要求的,僅僅是一個標籤,如果瀏覽器可以解析就會按照某些標準(比如瀏覽器自身標準,W3C的標準)去解釋這個頭,否則不識別的頭就會被瀏覽器無視。對伺服器也是同理。假如你編寫一個瀏覽器,你可以將上面的頭解釋成任何你想要的效果
下面我們說的HTTP頭都是W3C標準的頭,我不會對每個頭的作用進行詳細說明,關於HTTP頭作用的文章在網上已經很多了,請自行Bing。HTTP頭按照其不同的作用,可以分為四大類。
通用頭(General header)
通用頭即可以包含在HTTP請求中,也可以包含在HTTP響應中。通用頭的作用是描述HTTP協議本身。比如描述HTTP是否持久串連的Connection頭,HTTP發送日期的Date頭,描述HTTP所在TCP連線時間的Keep-Alive頭,用於緩衝控制的Cache-Control頭等。
實體頭(Entity header)
實體頭是那些描述HTTP資訊的頭。既可以出現在HTTP POST方法的請求中,也可以出現在HTTP響應中。比如圖5和圖6中的Content-Type和Content-length都是描述實體的類型和大小的頭都屬於實體頭。其它還有用於描述實體的Content-Language,Content-MD5,Content-Encoding以及控制實體緩衝的Expires和Last-Modifies頭等。
要求標頭(HTTP Request Header)
要求標頭是那些由用戶端發往服務端以便協助服務端更好的滿足用戶端請求的頭。要求標頭只能出現在HTTP請求中。比如告訴伺服器只接收某種響應內容的Accept頭,發送Cookies的Cookie頭,顯示請求主機域的HOST頭,用於緩衝的If-Ma