HTTP屬於老話題了,在項目中咱們常常須要往服務端發POST或者GET請求,可是對於HTTP的瞭解不該只侷限於此。千里之行,始於足下。越想走的遠,基本原理就應該瞭解的透徹全面一些,僅僅停留在使用ASIHttpRequest或者AFNetWorking傳個參數發個請求的程度上是不夠的。這篇文章就是帶你全方面回顧一下HTTP。html
經過本文你能收穫哪些內容:git
完整HTTP請求與響應包含的必要元素github
HTTP不一樣版本之間的差別json
HTTP、Socket、TCP的區別(易混)瀏覽器
1、HTTP協議服務器
HTTP本質上是一種協議,全稱是Hypertext Transfer Protocol,即超文本傳輸協議。從名字上能夠看出該協議用於規定客戶端與服務端之間的傳輸規則,所傳輸的內容不侷限於文本(其實能夠傳輸任意類型的數據)。微信
圖1.1傳輸示意圖.pngcookie
2、HTTP請求與響應的內容網絡
當咱們往服務端發送一條HTTP請求時都發送了哪些東西過去呢?多線程
先看一個POST請求的示例圖:
圖2.1 HTTP_POST請求示例.png
注:本文使用Paw來模擬發送HTTP請求,使用Charles抓包,Charles選中"Request"以及"Raw"選項就能夠看到請求的所有內容
以上示例圖中其實已經包含了一個HTTP請求所必備的幾大要素:請求行、請求頭(headerField)、請求體(body);同理,響應也有狀態行、響應頭、實體內容。接下來咱們逐個展開。
一、請求行
請求行包含請求方法(Method)、請求統一資源標識符(URI)、HTTP版本號,如圖2.1第一行所示:
圖2.2 請求行.png
請求方法就是咱們所熟悉的POST、GET、HEAD、PUT等
URI就是URL中排除掉Host剩下的部分,也就是資源在服務器本地上的路徑
HTTP版本號,目前主流的版本是1.1(1999年開始採用),最新的版本是2.0(2015年5月發佈)。不一樣版本之間差別下面會再展開
二、請求頭
請求頭主要存放對客戶端想給服務端的附加信息,下圖框框的部分就是請求頭:
圖2.3 請求頭.png
HTTP請求在iOS中用NSURLRequest與NSMutableRequest表示;HTTP響應用NSHTTPURLResponse表示。
Host: 目標服務器的網絡地址
Accept: 讓服務端知道客戶端所能接收的數據類型,如text/html */*
Content-Type: body中的數據類型,如application/json; charset=UTF-8
Accept-Language: 客戶端的語言環境,如zh-cn
Accept-Encoding: 客戶端支持的數據壓縮格式,如gzip
User-Agent: 客戶端的軟件環境,咱們能夠更改該字段爲本身客戶端的名字,好比QQ music v1.11,好比瀏覽器Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/600.8.9 (KHTML, like Gecko) Maxthon/4.5.2
Connection: keep-alive,該字段是從HTTP 1.1纔開始有的,用來告訴服務端這是一個持久鏈接,「請服務端不要在發出響應後當即斷開TCP鏈接」。關於該字段的更多解釋將在後面的HTTP版本簡介中展開。
Content-Length: body的長度,若是body爲空則該字段值爲0。該字段通常在POST請求中才會有。
POST請求的body請求體也有多是空的,所以POST中Content-Length也有可能爲0
Cookie: 記錄者用戶信息的保存在本地的用戶數據,若是有會被自動附上
值得一提的是,在iOS中當你發送一個任意請求時,無論你願不肯意,NSURLRequest都會自動幫你記錄你所訪問的URL上設置的cookie。在iOS中用NSHTTPCookieStorage表示,是一個單例。經過
1 2 3 4 |
|
能夠獲取目前被自動保存的全部cookie。對cookie的操做感興趣的請移步iOS中http請求使用cookie這篇文章。
以上就是咱們平常開發中比較常常遇到的請求頭,其實還有其餘的field,但篇幅所限沒法一一列出,想了解全部請求頭請看這裏請求頭響應頭列表。那在iOS中如何設置添加這些field呢?可使用-[NSMutableURLRequest addValue: forHTTPHeaderField:]方法,獲取當前請求已經設置的field能夠用-[NSURLRequest allHTTPHeaderFields]。也就是咱們能夠經過以上接口定製咱們所須要的請求頭,可是有些field是不能改的,咱們看一下iOS的描述:
圖2.4 iOS請求頭接口描述.png
從文檔中咱們能夠看到,在iOS中不該當對Authorization Connection Host WWW-Authenticate這幾個header field作更改。
三、請求體
真正須要發給服務端的數據,在使用POST-multipart上傳請求中請求體就是上傳文件的二進制NSData類型數據;在GET請求中請求體爲空;在普通的POST請求中請求體就是一些表單數據。在iOS中通常用NSURLRequest與NSMutableURLRequest的HTTPBody屬性表示,添加body用-[NSMutableURLRequest setHTTPBody:]。
四、響應狀態行
狀態行是服務端返回給客戶端的狀態信息,包含HTTP版本號、狀態碼、狀態碼對應的英文名稱。
如下就是典型的正確狀態行:
1 |
|
這個部分須要講的是錯誤碼。事實上HTTP請求錯誤碼能夠根據錯誤碼從左往右第一個數字大體分爲如下幾類:
1XX:信息提示。不表明成功或者失敗,表示臨時響應,好比100表示繼續,101表示切換協議
2XX: 成功
3XX: 重定向
4XX:客戶端錯誤,頗有多是客戶端發生問題,如親切可愛的404表示未找到文件,說明你的URI是有問題的,服務器機子上該目錄是沒有該文件的;414URI太長
5XX: 服務器錯誤,好比504網關超時
錯誤碼是不用去記的,出錯了再查對應的錯誤碼含義就行。可是知道上面的分類有助於第一時間作出大致的判斷,起碼你能清楚是服務端仍是客戶端的緣由。
五、響應頭與響應實體
這部分與請求部分差別不大,響應頭的字field會有稍許不一樣,響應頭中的header field一樣移步請求頭響應頭列表。
3、HTTP版本簡介
這裏我把HTTP版本簡單分爲三類:1.1以前,1.1,2.0,針對這三類作個主要差別的介紹:
HTTP 1.1以前
不支持持久鏈接。一旦服務器對客戶端發出響應就當即斷開TCP鏈接
無請求頭跟響應頭
客戶端的先後請求是同步的。下一個請求必須等上一個請求從服務端拿到響應後才能發出,有點相似多線程的同步機制。
HTTP 1.1(主流版本)
與1.1以前的版本相比,作了如下性能上的提高
增長請求頭跟響應頭
支持持久鏈接。客戶端經過請求頭中指定Connection爲keep-alive告知服務端不要在完成響應後當即釋放鏈接。HTTP是基於TCP的,在HTTP 1.1中一次TCP鏈接能夠處理屢次HTTP請求
客戶端不一樣請求之間是異步的。下一個請求沒必要等到上一個請求回來後再發出,而能夠連續發出請求,有點相似多線程的異步處理。
HTTP 2.0
本着向下兼容的原則,1.1版本有的特性2.0都具有,也使用相同的API。可是2.0將只用於https網址。因爲2.0的普及還須要比較長的一段時間,這裏不展開,更多新特性請參考這篇文章。
咱們重點關注一下當前1.1版本所作幾點改變。支持持久鏈接有什麼好處呢?HTTP是基於TCP鏈接的,若是鏈接被頻繁地啓動而後斷開就會花費不少資源在TCP三次握手以及四次揮手上,效率低下。以請求一個網頁爲例,咱們知道,一個html網頁上的圖片資源並非直接嵌入在網頁上,而只是提供url,圖片仍須要額外發HTTP 請求去下載。一個網頁從請求到最終加載到本地每每須要通過過個HTTP請求。在1.1版本以前請求一個網頁就須要發生屢次"握手-揮手"的過程,每次鏈接之間相互獨立;而1.1及以後的版本最少只須要一次就夠。
再來就是請求異步,其好處參考多線程異步處理,在此不展開。
以上特性能夠用圖2.3表示:
圖3.1 異步請求.jpg
咱們能夠看到:一、N次請求其實只創建了1次TCP鏈接,二、N次請求連續異步發出。
4、HTTP、Socket、TCP的區別
這三個概念常常被談到,也是比較容易被混掉的概念。在回顧以前咱們先看一下這三者在TCP/IP協議族中的位置關係:
圖4.1 層次關係.png
HTTP是應用層的協議,更靠近用戶端;TCP是傳輸層的協議;而socket是從傳輸層上抽象出來的一個抽象層,本質是接口。因此本質上三種仍是很好區分的。儘管如此,有時候你可能會懵逼,HTTP鏈接、TCP鏈接、socket鏈接有什麼區別?好吧,若是上面的圖解釋的仍是不夠清楚的話,咱們繼續往下看。
一、TCP鏈接與HTTP鏈接的區別
上文提過,HTTP是基於TCP的,客戶端往服務端發送一個HTTP請求時第一步就是要創建與服務端的TCP鏈接,也就是先三次握手,「你好,你好,你好」。從HTTP 1.1開始支持持久鏈接,也就是一次TCP鏈接能夠發送屢次的HTTP請求。
小總結:HTTP基於TCP
二、TCP鏈接與Socket鏈接的區別
在圖4.1中咱們提到,socket層只是在TCP/UDP傳輸層上作的一個抽象接口層,所以一個socket鏈接能夠基於鏈接,也有可能基於UDP。基於TCP協議的socket鏈接一樣須要經過三次握手創建鏈接,是可靠的;基於UDP協議的socket鏈接不須要創建鏈接的過程,不過對方能不能收到都會發送過去,是不可靠的,大多數的即時通信IM都是後者。
小總結:Socket也基於TCP
三、HTTP鏈接與Socket鏈接的區別
區分這兩個概念是比較有意義的,畢竟TCP看不見摸不着,HTTP與Socket是實實在在能用到的。
HTTP是短鏈接,Socket(基於TCP協議的)是長鏈接。儘管HTTP1.1開始支持持久鏈接,但仍沒法保證始終鏈接。而Socket鏈接一旦創建TCP三次握手,除非一方主動斷開,不然鏈接狀態一直保持。
HTTP鏈接服務端沒法主動發消息,Socket鏈接雙方請求的發送前後限制。這點就比較重要了,由於它將決定兩者分別適合應用在什麼場景下。HTTP採用「請求-響應」機制,在客戶端還沒發送消息給服務端前,服務端沒法推送消息給客戶端。必須知足客戶端發送消息在前,服務端回覆在後。Socket鏈接雙方相似peer2peer的關係,一方隨時能夠向另外一方喊話。
四、問題來了:何時該用HTTP,何時該用socket
這個問題的提出是很天然而然的。當你接到一個與另外一方的網絡通信需求,天然會考慮用HTTP仍是用Socket。
用HTTP的狀況:雙方不須要時刻保持鏈接在線,好比客戶端資源的獲取、文件上傳等。
用Socket的狀況:大部分即時通信應用(QQ、微信)、聊天室、蘋果APNs等
在iOS中,發HTTP請求通常用原生的NSURLConnection、NSURLSession或者開源的AFNetWorking(推薦)、ASIHttpRequest(已中止更新)。鏈接Socket鏈接我用的比較可能是robbiehanson大神的CocoaAsyncSocket(XMPPFramework也是出自他手)。
5、The end
以上就是關於HTTP相關概念的回顧,適合菜鳥也適合有經驗同窗一塊兒回顧。