1. 程式人生 > >iOS HTTP、Socket、TCP的區別

iOS HTTP、Socket、TCP的區別

HTTP屬於老話題了,在專案中我們經常需要往服務端發POST或者GET請求,但是對於HTTP的瞭解不應只侷限於此。千里之行,始於足下。越想走的遠,基本原理就應該瞭解的透徹全面一些,僅僅停留在使用ASIHttpRequest或者AFNetWorking傳個引數發個請求的程度上是不夠的。這篇文章就是帶你全方面回顧一下HTTP。

通過本文你能收穫哪些內容:

完整HTTP請求與響應包含的必要元素HTTP不同版本之間的差異HTTP、Socket、TCP的區別(易混)

一、HTTP協議

HTTP本質上是一種協議,全稱是Hypertext Transfer
Protocol,即超文字傳輸協議。從名字上可以看出該協議用於規定客戶端與服務端之間的傳輸規則,所傳輸的內容不侷限於文字(其實可以傳輸任意型別的資料)。

圖1.1傳輸示意圖.png

二、HTTP請求與響應的內容

當我們往服務端傳送一條HTTP請求時都發送了哪些東西過去呢?

先看一個POST請求的示例圖:

圖2.1 HTTP_POST請求示例.png
注:本文使用Paw來模擬傳送HTTP請求,使用Charles抓包,Charles選中”Request”以及”Raw”選項就可以看到請求的全部內容

以上示例圖中其實已經包含了一個HTTP請求所必備的幾大要素:請求行、請求頭(headerField)、請求體(body);同理,響應也有狀態行、響應頭、實體內容。接下來我們逐個展開。

1、請求行

請求行包含請求方法(Method)、請求統一資源識別符號(URI)、HTTP版本號,如圖2.1第一行所示:
圖2.2 請求行.png

請求方法就是我們所熟悉的POST、GET、HEAD、PUT等
URI就是URL中排除掉Host剩下的部分,也就是資源在伺服器本地上的路徑HTTP版本號,目前主流的版本是1.1(1999年開始採用),最新的版本是2.0(2015年5月釋出)。不同版本之間差異下面會再展開

2、請求頭

請求頭主要存放對客戶端想給服務端的附加資訊,下圖框框的部分就是請求頭:
圖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表示,是一個單例。通過

NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage]; for (NSHTTPCookie *cookie in [cookieJar cookies]) { NSLog(@"%@", cookie); }

可以獲取目前被自動儲存的所有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做更改。

3、請求體

真正需要發給服務端的資料,在使用POST-multipart上傳請求中請求體就是上傳檔案的二進位制NSData型別資料;在GET請求中請求體為空;在普通的POST請求中請求體就是一些表單資料。在iOS中一般用NSURLRequest與NSMutableURLRequest的HTTPBody屬性表示,新增body用-[NSMutableURLRequest setHTTPBody:]。

4、響應狀態行

狀態行是服務端返回給客戶端的狀態資訊,包含HTTP版本號、狀態碼、狀態碼對應的英文名稱。

以下就是典型的正確狀態行:

HTTP/1.1 200 OK

這個部分需要講的是錯誤碼。事實上HTTP請求錯誤碼可以根據錯誤碼從左往右第一個數字大致分為以下幾類:

1XX:資訊提示。不代表成功或者失敗,表示臨時響應,比如100表示繼續,101表示切換協議

2XX: 成功

3XX: 重定向

4XX:客戶端錯誤,很有可能是客戶端發生問題,如親切可愛的404表示未找到檔案,說明你的URI是有問題的,伺服器機子上該目錄是沒有該檔案的;414URI太長

5XX: 伺服器錯誤,比如504閘道器超時

錯誤碼是不用去記的,出錯了再查對應的錯誤碼含義就行。但是知道上面的分類有助於第一時間做出大體的判斷,起碼你能清楚是服務端還是客戶端的原因。

5、響應頭與響應實體

這部分與請求部分差異不大,響應頭的字field會有稍許不同,響應頭中的header field同樣移步請求頭響應頭列表。

三、HTTP版本簡介

這裡我把HTTP版本簡單分為三類:1.1之前,1.1,2.0,針對這三類做個主要差異的介紹:

HTTP 1.1之前

不支援持久連線。一旦伺服器對客戶端發出響應就立即斷開TCP連線 無請求頭跟響應頭 客戶端的前後請求是同步的。下一個請求必須等上一個請求從服務端拿到響應後才能發出,有點類似多執行緒的同步機制。

與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表示:
<img alt="" http:="" www.2cto.com="" kf="" ware="" vc="" "="" target="_blank" class="keylink" style="box-sizing: border-box; border: medium none; vertical-align: middle; display: block; margin: 0px auto; max-width: 100%; width: 1229px; height: auto;">vcfrx/MuanBn" src="http://www.2cto.com/uploadfile/Collfiles/20160325/20160325145409228.jpg" title="\" />
我們可以看到:1、N次請求其實只建立了1次TCP連線,2、N次請求連續非同步發出。

四、HTTP、Socket、TCP的區別

這三個概念經常被談到,也是比較容易被混掉的概念。在回顧之前我們先看一下這三者在TCP/IP協議族中的位置關係:
圖4.1 層次關係.png

HTTP是應用層的協議,更靠近使用者端;TCP是傳輸層的協議;而socket是從傳輸層上抽象出來的一個抽象層,本質是介面。所以本質上三種還是很好區分的。儘管如此,有時候你可能會懵逼,HTTP連線、TCP連線、socket連線有什麼區別?好吧,如果上面的圖解釋的還是不夠清楚的話,我們繼續往下看。

1、TCP連線與HTTP連線的區別

上文提過,HTTP是基於TCP的,客戶端往服務端傳送一個HTTP請求時第一步就是要建立與服務端的TCP連線,也就是先三次握手,“你好,你好,你好”。從HTTP 1.1開始支援持久連線,也就是一次TCP連線可以傳送多次的HTTP請求。

小總結:HTTP基於TCP

2、TCP連線與Socket連線的區別

在圖4.1中我們提到,socket層只是在TCP/UDP傳輸層上做的一個抽象介面層,因此一個socket連線可以基於連線,也有可能基於UDP。基於TCP協議的socket連線同樣需要通過三次握手建立連線,是可靠的;基於UDP協議的socket連線不需要建立連線的過程,不過對方能不能收到都會發送過去,是不可靠的,大多數的即時通訊IM都是後者。

小總結:Socket也基於TCP

3、HTTP連線與Socket連線的區別

區分這兩個概念是比較有意義的,畢竟TCP看不見摸不著,HTTP與Socket是實實在在能用到的。

HTTP是短連線,Socket(基於TCP協議的)是長連線。儘管HTTP1.1開始支援持久連線,但仍無法保證始終連線。而Socket連線一旦建立TCP三次握手,除非一方主動斷開,否則連線狀態一直保持。
HTTP連線服務端無法主動發訊息,Socket連線雙方請求的傳送先後限制。這點就比較重要了,因為它將決定二者分別適合應用在什麼場景下。HTTP採用“請求-響應”機制,在客戶端還沒傳送訊息給服務端前,服務端無法推送訊息給客戶端。必須滿足客戶端傳送訊息在前,服務端回覆在後。Socket連線雙方類似peer2peer的關係,一方隨時可以向另一方喊話。
4、問題來了:什麼時候該用HTTP,什麼時候該用socket

這個問題的提出是很自然而然的。當你接到一個與另一方的網路通訊需求,自然會考慮用HTTP還是用Socket。

用HTTP的情況:雙方不需要時刻保持連線線上,比如客戶端資源的獲取、檔案上傳等。
用Socket的情況:大部分即時通訊應用(QQ、微信)、聊天室、蘋果APNs等
在iOS中,發HTTP請求一般用原生的NSURLConnection、NSURLSession或者開源的AFNetWorking(推薦)、ASIHttpRequest(已停止更新)。連線Socket連線我用的比較多是robbiehanson大神的CocoaAsyncSocket (XMPPFramework也是出自他手)。

五、The end