HTTP屬于老話題了疙渣,在項(xiàng)目中我們經(jīng)常需要往服務(wù)端發(fā)POST或者GET請求,但是對于HTTP的了解不應(yīng)只局限于此糕殉。千里之行亩鬼,始于足下殖告。越想走的遠(yuǎn)阿蝶,基本原理就應(yīng)該了解的透徹全面一些,僅僅停留在使用ASIHttpRequest或者AFNetWorking傳個(gè)參數(shù)發(fā)個(gè)請求的程度上是不夠的黄绩。
通過本文你能收獲哪些內(nèi)容:
- 完整HTTP請求與響應(yīng)包含的必要元素
- HTTP不同版本之間的差異
- HTTP羡洁、Socket、TCP的區(qū)別(易混)
一爽丹、HTTP協(xié)議
HTTP本質(zhì)上是一種協(xié)議筑煮,全稱是Hypertext Transfer Protocol,即超文本傳輸協(xié)議粤蝎。從名字上可以看出該協(xié)議用于規(guī)定客戶端與服務(wù)端之間的傳輸規(guī)則真仲,所傳輸?shù)膬?nèi)容不局限于文本(其實(shí)可以傳輸任意類型的數(shù)據(jù))。
二初澎、HTTP請求與響應(yīng)的內(nèi)容
當(dāng)我們往服務(wù)端發(fā)送一條HTTP請求時(shí)都發(fā)送了哪些東西過去呢秸应?
先看一個(gè)POST請求的示例圖:
注:本文使用Paw來模擬發(fā)送HTTP請求,使用Charles抓包碑宴,Charles選中Request以及Raw選項(xiàng)就可以看到請求的全部內(nèi)容
注:本文使用Paw來模擬發(fā)送HTTP請求软啼,使用Charles抓包,Charles選中"Request"以及"Raw"選項(xiàng)就可以看到請求的全部內(nèi)容
1延柠、請求行
請求行包含請求方法(Method)祸挪、請求統(tǒng)一資源標(biāo)識符(URI)、HTTP版本號贞间,如圖2.1第一行所示:
- 請求方法就是我們所熟悉的POST贿条、GET、HEAD增热、PUT等
- URI就是URL中排除掉Host剩下的部分整以,也就是資源在服務(wù)器本地上的路徑
- HTTP版本號,目前主流的版本是1.1(1999年開始采用)钓葫,最新的版本是2.0(2015年5月發(fā)布)悄蕾。不同版本之間差異下面會(huì)再展開
2、請求頭
請求頭主要存放對客戶端想給服務(wù)端的附加信息础浮,下圖框框的部分就是請求頭:
HTTP請求在iOS中用NSURLRequest與NSMutableRequest表示帆调;HTTP響應(yīng)用NSHTTPURLResponse表示。
- Host: 目標(biāo)服務(wù)器的網(wǎng)絡(luò)地址
- Accept: 讓服務(wù)端知道客戶端所能接收的數(shù)據(jù)類型豆同,如text/html /
- Content-Type: body中的數(shù)據(jù)類型番刊,如application/json; charset=UTF-8
- Accept-Language: 客戶端的語言環(huán)境,如zh-cn
- Accept-Encoding: 客戶端支持的數(shù)據(jù)壓縮格式影锈,如gzip
- User-Agent: 客戶端的軟件環(huán)境芹务,我們可以更改該字段為自己客戶端的名字蝉绷,比如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才開始有的熔吗,用來告訴服務(wù)端這是一個(gè)持久連接,“請服務(wù)端不要在發(fā)出響應(yīng)后立即斷開TCP連接”佳晶。關(guān)于該字段的更多解釋將在后面的HTTP版本簡介中展開桅狠。
- Content-Length: body的長度,如果body為空則該字段值為0轿秧。該字段一般在POST請求中才會(huì)有中跌。POST請求的body請求體也有可能是空的,因此POST中Content-Length也有可能為0
- Cookie: 記錄者用戶信息的保存在本地的用戶數(shù)據(jù)菇篡,如果有會(huì)被自動(dòng)附上
值得一提的是漩符,在iOS中當(dāng)你發(fā)送一個(gè)任意請求時(shí),不管你愿不愿意驱还,NSURLRequest都會(huì)自動(dòng)幫你記錄你所訪問的URL上設(shè)置的cookie嗜暴。在iOS中用NSHTTPCookieStorage表示,是一個(gè)單例铝侵。通過
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [cookieJar cookies]) {
NSLog(@"%@", cookie);
}
可以獲取目前被自動(dòng)保存的所有cookie灼伤。對cookie的操作感興趣的請移步iOS中http請求使用cookie這篇文章。
以上就是我們?nèi)粘i_發(fā)中比較經(jīng)常遇到的請求頭咪鲜,其實(shí)還有其他的field狐赡,但篇幅所限無法一一列出,想了解所有請求頭請看這里請求頭響應(yīng)頭列表疟丙。那在iOS中如何設(shè)置添加這些field呢颖侄?可以使用-[NSMutableURLRequest addValue: forHTTPHeaderField:]方法,獲取當(dāng)前請求已經(jīng)設(shè)置的field可以用-[NSURLRequest allHTTPHeaderFields]享郊。也就是我們可以通過以上接口定制我們所需要的請求頭览祖,但是有些field是不能改的,我們看一下iOS的描述:
從文檔中我們可以看到炊琉,在iOS中不應(yīng)當(dāng)對Authorization Connection Host WWW-Authenticate這幾個(gè)header field做更改展蒂。
3、請求體
真正需要發(fā)給服務(wù)端的數(shù)據(jù)苔咪,在使用POST-multipart上傳請求中請求體就是上傳文件的二進(jìn)制NSData類型數(shù)據(jù)锰悼;在GET請求中請求體為空;在普通的POST請求中請求體就是一些表單數(shù)據(jù)团赏。在iOS中一般用NSURLRequest與NSMutableURLRequest的HTTPBody屬性表示箕般,添加body用-[NSMutableURLRequest setHTTPBody:]。
4舔清、響應(yīng)狀態(tài)行
狀態(tài)行是服務(wù)端返回給客戶端的狀態(tài)信息丝里,包含HTTP版本號曲初、狀態(tài)碼、狀態(tài)碼對應(yīng)的英文名稱杯聚。
以下就是典型的正確狀態(tài)行:
HTTP/1.1 200 OK
這個(gè)部分需要講的是錯(cuò)誤碼臼婆。事實(shí)上HTTP請求錯(cuò)誤碼可以根據(jù)錯(cuò)誤碼從左往右第一個(gè)數(shù)字大致分為以下幾類:
- 1XX:信息提示。不代表成功或者失敗械媒,表示臨時(shí)響應(yīng)目锭,比如100表示繼續(xù),101表示切換協(xié)議
- 2XX: 成功
- 3XX: 重定向
- 4XX:客戶端錯(cuò)誤纷捞,很有可能是客戶端發(fā)生問題,如親切可愛的404表示未找到文件被去,說明你的URL是有問題的主儡,服務(wù)器機(jī)子上該目錄是沒有該文件的;414URL太長
- 5XX: 服務(wù)器錯(cuò)誤惨缆,比如504網(wǎng)關(guān)超時(shí)
錯(cuò)誤碼是不用去記的糜值,出錯(cuò)了再查對應(yīng)的錯(cuò)誤碼含義就行。但是知道上面的分類有助于第一時(shí)間做出大體的判斷坯墨,起碼你能清楚是服務(wù)端還是客戶端的原因寂汇。
5、響應(yīng)頭與響應(yīng)實(shí)體
這部分與請求部分差異不大捣染,響應(yīng)頭的字field會(huì)有稍許不同骄瓣,響應(yīng)頭中的header field同樣移步請求頭響應(yīng)頭列表。
三耍攘、HTTP版本
這里我把HTTP版本簡單分為三類:1.1之前榕栏,1.1,2.0蕾各,針對這三類做個(gè)主要差異的介紹:
HTTP 1.1之前
- 不支持持久連接扒磁。一旦服務(wù)器對客戶端發(fā)出響應(yīng)就立即斷開TCP連接
- 無請求頭跟響應(yīng)頭
- 客戶端的前后請求是同步的。下一個(gè)請求必須等上一個(gè)請求從服務(wù)端拿到響應(yīng)后才能發(fā)出式曲,有點(diǎn)類似多線程的同步機(jī)制妨托。
HTTP 1.1(主流版本)
與1.1之前的版本相比,做了以下性能上的提升
- 增加請求頭跟響應(yīng)頭
- 支持持久連接吝羞±忌耍客戶端通過請求頭中指定Connection為keep-alive告知服務(wù)端不要在完成響應(yīng)后立即釋放連接。HTTP是基于TCP的脆贵,在HTTP 1.1中一次TCP連接可以處理多次HTTP請求
- 客戶端不同請求之間是異步的医清。下一個(gè)請求不必等到上一個(gè)請求回來后再發(fā)出,而可以連續(xù)發(fā)出請求卖氨,有點(diǎn)類似多線程的異步處理会烙。
HTTP 2.0
本著向下兼容的原則负懦,1.1版本有的特性2.0都具備,也使用相同的API柏腻。但是2.0將只用于https網(wǎng)址纸厉。由于2.0的普及還需要比較長的一段時(shí)間,這里不展開五嫂,更多新特性請參考這篇文章颗品。
我們重點(diǎn)關(guān)注一下當(dāng)前1.1版本所做幾點(diǎn)改變。支持持久連接有什么好處呢沃缘?HTTP是基于TCP連接的躯枢,如果連接被頻繁地啟動(dòng)然后斷開就會(huì)花費(fèi)很多資源在TCP三次握手以及四次揮手上,效率低下槐臀。以請求一個(gè)網(wǎng)頁為例锄蹂,我們知道,一個(gè)html網(wǎng)頁上的圖片資源并不是直接嵌入在網(wǎng)頁上水慨,而只是提供url得糜,圖片仍需要額外發(fā)HTTP 請求去下載。一個(gè)網(wǎng)頁從請求到最終加載到本地往往需要經(jīng)過過個(gè)HTTP請求晰洒。在1.1版本之前請求一個(gè)網(wǎng)頁就需要發(fā)生多次"握手-揮手"的過程朝抖,每次連接之間相互獨(dú)立;而1.1及之后的版本最少只需要一次就夠谍珊。
再來就是請求異步治宣,其好處參考多線程異步處理,在此不展開抬驴。
以上特性可以用圖2.3表示:
我們可以看到:1炼七、N次請求其實(shí)只建立了1次TCP連接,2布持、N次請求連續(xù)異步發(fā)出豌拙。
四、HTTP题暖、Socket按傅、TCP的區(qū)別
這三個(gè)概念經(jīng)常被談到,也是比較容易被混掉的概念胧卤。在回顧之前我們先看一下這三者在TCP/IP協(xié)議族中的位置關(guān)系:
HTTP是應(yīng)用層的協(xié)議唯绍,更靠近用戶端;TCP是傳輸層的協(xié)議枝誊;而socket是從傳輸層上抽象出來的一個(gè)抽象層况芒,本質(zhì)是接口。所以本質(zhì)上三種還是很好區(qū)分的叶撒。盡管如此绝骚,有時(shí)候你可能會(huì)懵逼耐版,HTTP連接、TCP連接压汪、socket連接有什么區(qū)別粪牲?好吧,如果上面的圖解釋的還是不夠清楚的話止剖,我們繼續(xù)往下看腺阳。
1、TCP連接與HTTP連接的區(qū)別
上文提過穿香,HTTP是基于TCP的亭引,客戶端往服務(wù)端發(fā)送一個(gè)HTTP請求時(shí)第一步就是要建立與服務(wù)端的TCP連接,也就是先三次握手扔水,“你好痛侍,你好,你好”魔市。從HTTP 1.1開始支持持久連接,也就是一次TCP連接可以發(fā)送多次的HTTP請求赵哲。
小總結(jié):HTTP基于TCP
2待德、TCP連接與Socket連接的區(qū)別
在圖4.1中我們提到,socket層只是在TCP/UDP傳輸層上做的一個(gè)抽象接口層枫夺,因此一個(gè)socket連接可以基于連接将宪,也有可能基于UDP∠鹋樱基于TCP協(xié)議的socket連接同樣需要通過三次握手建立連接较坛,是可靠的;基于UDP協(xié)議的socket連接不需要建立連接的過程扒最,不過對方能不能收到都會(huì)發(fā)送過去丑勤,是不可靠的,大多數(shù)的即時(shí)通訊IM都是后者煎源。
小總結(jié):Socket也基于TCP
3南窗、HTTP連接與Socket連接的區(qū)別
區(qū)分這兩個(gè)概念是比較有意義的榜揖,畢竟TCP看不見摸不著,HTTP與Socket是實(shí)實(shí)在在能用到的岔霸。
- HTTP是短連接,Socket(基于TCP協(xié)議的)是長連接俯渤。盡管HTTP1.1開始支持持久連接呆细,但仍無法保證始終連接。而Socket連接一旦建立TCP三次握手八匠,除非一方主動(dòng)斷開絮爷,否則連接狀態(tài)一直保持趴酣。
- HTTP連接服務(wù)端無法主動(dòng)發(fā)消息,Socket連接雙方請求的發(fā)送先后限制略水。這點(diǎn)就比較重要了价卤,因?yàn)樗鼘Q定二者分別適合應(yīng)用在什么場景下。HTTP采用“請求-響應(yīng)”機(jī)制渊涝,在客戶端還沒發(fā)送消息給服務(wù)端前慎璧,服務(wù)端無法推送消息給客戶端。必須滿足客戶端發(fā)送消息在前跨释,服務(wù)端回復(fù)在后胸私。Socket連接雙方類似peer2peer的關(guān)系,一方隨時(shí)可以向另一方喊話鳖谈。
4岁疼、問題來了:什么時(shí)候該用HTTP,什么時(shí)候該用socket
這個(gè)問題的提出是很自然而然的缆娃。當(dāng)你接到一個(gè)與另一方的網(wǎng)絡(luò)通訊需求捷绒,自然會(huì)考慮用HTTP還是用Socket。
- 用HTTP的情況:雙方不需要時(shí)刻保持連接在線贯要,比如客戶端資源的獲取暖侨、文件上傳等。
- 用Socket的情況:大部分即時(shí)通訊應(yīng)用(QQ崇渗、微信)字逗、聊天室、蘋果APNs等
在iOS中宅广,發(fā)HTTP請求一般用原生的NSURLConnection葫掉、NSURLSession或者開源的AFNetWorking(推薦)、ASIHttpRequest(已停止更新)跟狱。連接Socket連接我用的比較多是robbiehanson大神的CocoaAsyncSocket(XMPPFramework也是出自他手)俭厚。