http權威指南 第一部分(基礎)

http使用的是可靠的數據傳輸,因此即使數據從地球一端傳遞到另外一端也能保證數據的正確性。對於開發人員來說這是一件好事,因爲就不必擔心數據完整性的問題了。http屬於應用層的協議,在使用http的時候無需擔心網絡細節,因爲聯網的細節都交給了它更低一層的TCP/IP因特網傳輸協議。

URI、URL、URN

URI稱作統一資源標識符,URL爲統一資源定位符,URN爲統一資源名。其中URI包含了URL和URN。但是目前URN還處於試驗階段,未大範圍使用,所以可以理解爲URI就是URL了。
URL是根據資源的絕對位置來進行資源的查找,比如說http://www.baidu.com/file/index.html就是在主機地址爲www.baidu.com下的文件夾file查找index.html。但是如果該文件被轉移到了其他地址,那麼再次訪問就會出現404錯誤訪問不到。這就是URN來解決的問題,URN叫做統一資源名,作爲資源的唯一名稱存在,與資源所處的位置無關。通過這樣就可以把資源四處搬移。

http報文格式:

http報文分爲請求報文和響應報文。http報文包括以下三個部分的內容:起始行、首部字段、主體。
請求報文的語法格式如下:

  <method> <request-URL> <version>
  <headers>

  <entity-body>  

響應報文的格式如下:

<version> <status> <reason-phrase>
<headers>

<entity-body>

下面是各部分的簡要介紹:
< method>方法:是指客戶端希望服務器對資源執行的操作,是一個單獨的詞,有如下方法:

方法名 解釋
GET 從服務端獲取一份文檔,無主體(< entity-entry>)部分.
POST 向服務的發送需要處理的數據,通常用它來支持HTML的表單處理,有主體
PUT 將請求的主體部分存儲到服務端,有主體
HEAD 只從服務端獲取文檔的首部,可以在不獲得資源的情況下了解資源的情況,可以通過查看響應中的狀態碼看某個對象是否存在,可以通過查看首部來測試資源是否被修改,無主體
TRACE 對可能經過代理服務器傳送到服務器上去的報文進行追蹤,報文行程的最後一站的服務器會彈回一個響應消息,並在響應消息的主體中攜帶請求報文,而這個報文中便攜帶者報文經過的路徑,無主體
OPTIONS 決定在服務器上執行哪些方法,無主體
DELETE 從服務器上刪除一份文檔,無主體

< version> :版本號,格式爲HTTP/x.y的形式,因爲每個http版本所遵循的協議不同,通過這個就可以將應用程序所支持的協議告知對方。
< status>:狀態碼,用返回服務端處理請求的狀況。

整體範圍 已定義範圍 分類
100-199 100-101 信息提示
200-299 200-206 成功
300-399 300-305 重定向
400-499 400-415 客戶端錯誤
500-509 500-505 服務端錯誤

< reason-phrase>:用於描述響應狀態,人類可讀,無固定標準。
< request-url>:請求的資源路徑或者是完整的URL。
< headers>:首部信息,分爲通用首部(請求報文和響應報文都可用)、請求首部(請求報文單用)、響應報文(響應報文單用)、實體首部(用來描述http實體部分的內容)、擴展首部(非標準首部)。對於首部的詳細信息,會在後面逐漸介紹到。

URL語法

大多數url的語法都建立在如下的通用格式上:

<scheme>://<user>:<password>@<host>:<port>/<path>;<param>?<query>#<frag>
  1. 方案< scheme>:使用什麼協議,如http,ftp等。
  2. 主機與端口:主機組件標識了要訪問的宿主機器,端口號標識下層的tcp協議所監聽的端口,http默認爲80。
  3. 用戶名和密碼:許多服務器需要輸入用戶名和密碼才能進行訪問,如ftp服務器,如果沒有輸入用戶名和密碼,則會插入一個默認的值。
  4. 路徑:指明資源所在的路徑。
  5. 參數:爲應用程序提供所需要的額外信息。比如:http://host/ham;sale=false/index.html;grphpics=true。目前還沒見過這種應用場景。
  6. 查詢字符串:就是在?後面傳遞的鍵值對。
  7. 片段:不會發送到服務端,用於應用程序如瀏覽器進行文檔內部的定位。

url中的字符:由於需要兼容全世界各地的字符請求,所以對一些特殊字符,如~空格等都會進行轉義,轉義的方式就是%後面跟着兩個標識字符ASCII的十六進制數。

連接管理

http下層利用的是tcp/ip可靠的數據連接。http連接的性能在很大程度上取決於tcp的連接,最常見的tcp相關時延包括:

  • tcp連接建立握手
  • tcp慢啓動擁塞控制(tcp連接會隨着時間進行自我調諧,起初會限制最大速度,如果傳輸成功便會逐漸提高自己的傳輸速度)
  • 數據聚集的nagle算法(該算法試圖在tcp發送前將tcp數據綁定在一起以提高網絡效率。首先小的http請求可能無法填滿分組,會因爲等待永遠無法到來的數據而產生時延,其次該算法會阻止數據的發送,直到有確認的分組抵達爲止。)
  • 用於捎帶確認的tcp延遲確認算法
  • time_wait時延和端口耗盡(當tcp斷開連接時,會在內存中維護一個小控制塊,用來記錄最近所關閉連接的IP地址和端口號,這個維護會持續一小段時間,此時關閉的IP地址和端口號是無法使用的,所以端口號的數量限制了每秒請求的次數。)

Connection首部字段的理解

Connection首部字段有一個逗號分隔的連接標籤列表,這些標籤爲此連接指定了不會傳遞到其他連接中的選項,比如可以用Connection:close來說明下一條報文以後必須關閉連接。

對於HTTP/1.0所支持的持久連接,可以通過包含Connection:Keep-Alive首部請求將一條連接保持打開的狀態,也可以通過Keep-Alive:max=5,timeout=120指定持久連接所處理的最大報文數量和延續的最長時間。但是卻存在一種啞代理的狀況。

客戶端發送保持長連接的http請求後,如果下一跳爲一個代理,而該代理並不能理解Connection字段的含義,所以該代理就會把報文原封不動地向後發送,直到服務器接收。服務器接收後發現有Connection:keep-alive首部,然後他就會認爲客戶端需要保持長連接,所以返回一個包含Connection:keep-alive的長連接的響應報文,同樣,那個不支持的代理會原封不動地轉發數據,最終客戶端和服務器都認爲已經建立了長連接,但是代理卻完全不知道是怎麼回事,當客戶端發送下一個請求時,因爲代理在轉發完報文後在等待關閉,所以他會忽略客戶端發送來的報文,導致客戶端一直處於掛起狀態,直到連接超時關閉。

對此的一個解決方案是如下:
添加一個Proxy-connection字段代替Connection字段的長連接請求,當代理遇到這個字段時,如果支持Connection字段,則會把Proxy-connection改爲Connection字段進行傳輸,如果服務端遇到Connection字段,那麼證明代理已經把Proxy-connection進行了處理,如果服務端遇到Proxy-connection,則直接拒接長連接請求。
但是這種方案對於中間有一個代理的情況可以解決,但是對於多個代理的情況就無法解決了。

對於HTTP/1.1,長連接是個默認值,除非響應中包含了Conneciont:close,不然http就會維持長連接的狀態。

管道化連接

這是相對於持續連接的一個優化,當第一條請求的響應還沒返回之前,持續發送第二條第三條數據,這樣做降低了網絡迴環的時間,提高性能。對於管道化連接,有幾條限制:

  • 如果客戶端無法確認連接是持久的,則不應該使用管道
  • 必須按照與請求相同的順序回送http相應。
  • http客戶端必須做好連接隨時中斷的準備
  • http不應該用管道化的方法發送回產生副作用的請求,比如post。

對於第四點做一個解釋,對於get請求,如果管道關閉了,可以進行重複請求。但是對於post,提交請求後,管道關閉,但是並不知道服務器到底是否已經處理了這個請求,如果重複提交,可能存在處理多個表單的情況,比如京東買東西的時候多下了一次單。所以,管道請求需要的是冪等事務,即不管重複多少次和重複一次的效果是一樣的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章