HTTP 1.1 HTTP 2.0
HTTP站在TCP之上
理解http協(xié)議之前一定要對TCP有一定基礎(chǔ)的了解刊咳。HTTP是建立在TCP協(xié)議之上,TCP協(xié)議作為傳輸層協(xié)議其實(shí)離應(yīng)用層并不遠(yuǎn)。HTTP協(xié)議的瓶頸及其優(yōu)化技巧都是基于TCP協(xié)議本身的特性。比如TCP建立連接時(shí)三次握手有1.5個RTT(round-trip time)的延遲,為了避免每次請求的都經(jīng)歷握手帶來的延遲钱床,應(yīng)用層會選擇不同策略的http長鏈接方案。又比如TCP在建立連接的初期有慢啟動(slow start)的特性燕雁,所以連接的重用總是比新建連接性能要好诞丽。
基于tcp的長鏈接
http long-polling
http streaming
web socket
移動端HTTP現(xiàn)狀
iOS平臺
iOS系統(tǒng)是從iOS8開始才通過NSURLSession來支持SPDY的鲸拥,iOS9+開始自動支持http2.0拐格。實(shí)際上apple對http2.0非常有信心,推廣力度也很大刑赶。新版本ATS機(jī)制默認(rèn)使用https來進(jìn)行網(wǎng)絡(luò)傳輸捏浊。APN(Apple Push Notifiction)在iOS9上也已經(jīng)是通過http2.0來實(shí)現(xiàn)的了。iOS9 sdk里的NSURLSession默認(rèn)使用http2.0撞叨,而且對開發(fā)者來說是完全透明的金踪,甚至沒有api來知道到底是用的哪個版本的http協(xié)議。
對于開發(fā)者來說到底怎么去配置最佳的http使用方案呢牵敷?在我看來胡岔,因app而異,主要從兩方面來考慮:一是app本身http流量是否大而且密集枷餐,二是開發(fā)團(tuán)隊(duì)本身的技術(shù)條件靶瘸。http2.0的部署相對容易很多,客戶端開發(fā)者甚至不用做什么改動毛肋,只需要使用iOS9的SDK編譯即可怨咪,但缺點(diǎn)是http2.0只能適用于iOS9的設(shè)備。SPDY的部署相對麻煩一些润匙,但優(yōu)點(diǎn)是可以兼顧iOS6+的設(shè)備诗眨。iOS端的SPDY可以使用twitter開發(fā)的CocoaSPDY方案,但有一點(diǎn)需要特別處理:
由于蘋果的TLS實(shí)現(xiàn)不支持NPN孕讳,所以通過NPN協(xié)商使用SPDY就無法通過默認(rèn)443端口來實(shí)現(xiàn)匠楚。有兩種做法,一是客戶端和server同時(shí)約定好使用另一個端口號來做NPN協(xié)商厂财,二是server這邊通過request header智能判斷客戶端是否支持SPDY而越過NPN協(xié)商過程芋簿。第一種方法會簡單一點(diǎn),不過需要從框架層將所有的http請求都map到另一個port蟀苛,url mapping可以參考我之前的一篇文章益咬。twitter自己的網(wǎng)站twitter.com使用的是第二種方法。
瀏覽器端(比如Chrome),server端(比如nginx)都陸續(xù)打算放棄支持spdy了幽告,畢竟google官方都宣布要停止維護(hù)了梅鹦。spdy會是一個過渡方案,會隨著iOS9的普及會逐步消失冗锁,所以這部分的技術(shù)投入需要開發(fā)團(tuán)隊(duì)自己去衡量齐唆。
Android平臺
android和iOS情況類似,http2.0只能在新系統(tǒng)下支持冻河,spdy作為過渡方案仍然有存在的必要箍邮。
對于使用webview的app來說,需要基于chrome內(nèi)核的webview才能支持spdy和http2.0叨叙,而android系統(tǒng)的webview是從android4.4(KitKat)才改成基于chrome內(nèi)核的锭弊。
對于使用native api調(diào)用的http請求來說,okhttp是同時(shí)支持spdy和http2.0的可行方案擂错。如果使用ALPN味滞,okhttp要求android系統(tǒng)5.0+(實(shí)際上,android4.4上就有了ALPN的實(shí)現(xiàn)钮呀,不過有bug剑鞍,知道5.0才正式修復(fù)),如果使用NPN爽醋,可以從android4.0+開始支持蚁署,不過NPN也是屬于將要被淘汰的協(xié)議。
TCP是面向連接的蚂四,可靠的字節(jié)流的協(xié)議光戈。連接時(shí)需要三次握手,斷開時(shí)需要四次揮手证杭。這是因?yàn)門CP是全雙工的田度,數(shù)據(jù)可以在兩端傳遞,所以斷開時(shí)都需要在每端單獨(dú)進(jìn)行關(guān)閉解愤,一方向的關(guān)閉通常是半關(guān)閉的镇饺,所以一端關(guān)閉時(shí)需要發(fā)送FIN來終止連接,收到后需要確認(rèn)ACK給發(fā)送端送讲。
TCP數(shù)據(jù)在IP數(shù)據(jù)報(bào)中的封裝為20字節(jié)的IP首部加上20字節(jié)TCP首部和TCP數(shù)據(jù)奸笤。
TCP/IP協(xié)議族
1.Frame: 物理層 比特流
2.Ethernet II:數(shù)據(jù)鏈路層 以太網(wǎng)幀頭部信息
3.IP網(wǎng)絡(luò)層? IP Packet 頭部信息
4.傳輸層 TCP Data Segment 頭部信息
5.應(yīng)用層 HTTP
網(wǎng)絡(luò)層次劃分為 標(biāo)準(zhǔn)的OSI七層模型,還有 TCP/IP四層協(xié)議 以及 TCP/IP五層協(xié)議哼鬓。如圖:
Wireshark下
TCP協(xié)議
? TCP是一種面向連接的监右、可靠的基于字節(jié)流的傳輸層通信協(xié)議。TCP將用戶數(shù)據(jù)打包成報(bào)文段异希,它發(fā)送后啟動一個定時(shí)器健盒,另一端收到的數(shù)據(jù)進(jìn)行確認(rèn),對失序的數(shù)據(jù)重新排序、丟棄重復(fù)數(shù)據(jù)扣癣。
TCP報(bào)文首部惰帽,如下圖所示:
16位源端口號
16為目的端口號
Sequence number – 序號
Acknowledgment number – 確認(rèn)號
Flags – 標(biāo)志位
—Urgent pointer 緊急指針位
— Acknowledgment 確認(rèn)位
— Push 急迫位
— Reset 重置位
— Syn 同步位
— Fin 終止位
a. 第一次握手標(biāo)志位
localhost Seq=0 -> 博客地址
從標(biāo)志位看出,同步位有值父虑,在做請求(SYN):Syn 同步位為1
b. 第二次握手標(biāo)志位
博客地址 Seq=0 Ack=1 -> localhost
從標(biāo)志位看出该酗,確認(rèn)位、同步位有值士嚎,在做應(yīng)答(SYN+ACK):Syn 同步位為 1 呜魄、Acknowledgment 確認(rèn)位為 1
c. 第三次握手標(biāo)志位
localhost Seq=1 Ack=1 ->? (注: Seq=Seq+1)
從標(biāo)志位看出,只有確認(rèn)位有值莱衩,在做再次確認(rèn)(SYN):Acknowledgment 確認(rèn)位為 1
所以爵嗅,一個完整的三次握手就是:請求(SYN) — 應(yīng)答(SYN+ACK) — 再次確認(rèn)(SYN)。
三次握手:
為什么需要“三次握手”
在謝希仁著《計(jì)算機(jī)網(wǎng)絡(luò)》第四版中講“三次握手”的目的是“為了防止已失效的連接請求報(bào)文段突然又傳送到了服務(wù)端膳殷,因而產(chǎn)生錯誤”操骡。在另一部經(jīng)典的《計(jì)算機(jī)網(wǎng)絡(luò)》一書中講“三次握手”的目的是為了解決“網(wǎng)絡(luò)中存在延遲的重復(fù)分組”的問題。這兩種不同的表述其實(shí)闡明的是同一個問題赚窃。
謝希仁版《計(jì)算機(jī)網(wǎng)絡(luò)》中的例子是這樣的,“已失效的連接請求報(bào)文段”的產(chǎn)生在這樣一種情況下:client發(fā)出的第一個連接請求報(bào)文段并沒有丟失岔激,而是在某個網(wǎng)絡(luò)結(jié)點(diǎn)長時(shí)間的滯留了勒极,以致延誤到連接釋放以后的某個時(shí)間才到達(dá)server。本來這是一個早已失效的報(bào)文段虑鼎。但server收到此失效的連接請求報(bào)文段后辱匿,就誤認(rèn)為是client再次發(fā)出的一個新的連接請求。于是就向client發(fā)出確認(rèn)報(bào)文段炫彩,同意建立連接匾七。假設(shè)不采用“三次握手”,那么只要server發(fā)出確認(rèn)江兢,新的連接就建立了昨忆。由于現(xiàn)在client并沒有發(fā)出建立連接的請求,因此不會理睬server的確認(rèn)杉允,也不會向server發(fā)送數(shù)據(jù)邑贴。但server卻以為新的運(yùn)輸連接已經(jīng)建立,并一直等待client發(fā)來數(shù)據(jù)叔磷。這樣拢驾,server的很多資源就白白浪費(fèi)掉了。采用“三次握手”的辦法可以防止上述現(xiàn)象發(fā)生改基。例如剛才那種情況繁疤,client不會向server的確認(rèn)發(fā)出確認(rèn)。server由于收不到確認(rèn),就知道client并沒有要求建立連接稠腊“钙#”。 主要目的防止server端一直等待麻养,浪費(fèi)資源褐啡。
四次揮手:
流量控制(滑動窗口協(xié)議)
傳輸數(shù)據(jù)的時(shí)候,如果發(fā)送方傳輸?shù)臄?shù)據(jù)量超過了接收方的處理能力鳖昌,那么接收方會出現(xiàn)丟包备畦。為了避免出現(xiàn)此類問題,流量控制要求數(shù)據(jù)傳輸雙方在每次交互時(shí)聲明各自的接收窗口「rwnd」大小许昨,用來表示自己最大能保存多少數(shù)據(jù)懂盐,這主要是針對接收方而言的,通俗點(diǎn)兒說就是讓發(fā)送方知道接收方能吃幾碗飯糕档,如果窗口衰減到零莉恼,那么就說明吃飽了,必須消化消化速那,如果硬撐的話說不定會大小便失禁俐银,那就是丟包了。
flow control
接收方和發(fā)送方的稱呼是相對的端仰,如果站在用戶的角度看:當(dāng)瀏覽網(wǎng)頁時(shí)捶惜,數(shù)據(jù)以下行為主,此時(shí)客戶端是接收方荔烧,服務(wù)端是發(fā)送方吱七;當(dāng)上傳文件時(shí),數(shù)據(jù)以上行為主鹤竭,此時(shí)客戶端是發(fā)送方踊餐,服務(wù)端是接收方。
1臀稚、流量控制是管理兩端的流量吝岭,以免會產(chǎn)生發(fā)送過塊導(dǎo)致收端溢出,或者因收端處理太快而浪費(fèi)時(shí)間的狀態(tài)烁涌。用的是:滑動窗口苍碟,以字節(jié)為單位
2、窗口有3種動作:展開(右邊向右)撮执,合攏(左邊向右)微峰,收縮(右邊向左)這三種動作受接收端的控制。
合攏:表示已經(jīng)收到相應(yīng)字節(jié)的確認(rèn)了
展開:表示允許緩存發(fā)送更多的字節(jié)
收縮(非常不希望出現(xiàn)的抒钱,某些實(shí)現(xiàn)是禁止的):表示本來可以發(fā)送的蜓肆,現(xiàn)在不能發(fā)送颜凯;但是如果收縮的是那些已經(jīng)發(fā)出的,就會有問題仗扬;為了避免症概,收端會等待到緩存中有更多緩存空間時(shí)才進(jìn)行通信。
發(fā)端窗口的大小取決于收端的窗口大小rwnd(TCP報(bào)文的窗口大小字段)和擁塞窗口大小cwnd(見擁塞控制)
發(fā)端窗口大小 = min{ rwnd , cwnd };
3早芭、關(guān)閉窗口:窗口縮回有個例外彼城,就是發(fā)送rwnd=0表示暫時(shí)不愿意接收數(shù)據(jù)。這種情況下退个,發(fā)端不是把窗口收縮募壕,二是停止發(fā)送數(shù)據(jù)。(為了比避免死鎖语盈,會用一些探測報(bào)定時(shí)發(fā)送試探舱馅,見定時(shí)器一節(jié))
4、問題:某些時(shí)候刀荒,由于發(fā)端或收端的數(shù)據(jù)很慢代嗤,會引起大量的1字節(jié)數(shù)據(jù)痛惜,浪費(fèi)很多資源缠借。
(1)干毅、發(fā)端的進(jìn)程產(chǎn)生數(shù)據(jù)很慢時(shí)候,時(shí)不時(shí)的來個1字節(jié)數(shù)據(jù)烈炭,那么TCP就會1字節(jié)1字節(jié)的發(fā)送溶锭,效率很低。
解決方法(Nagle算法):
a符隙、將第一塊數(shù)據(jù)發(fā)出去
b、然后等到發(fā)送緩存有足夠多的數(shù)據(jù)(最大報(bào)文段長度)垫毙,或者等到收端確認(rèn)的ACK時(shí)再發(fā)送數(shù)據(jù)霹疫。
c、重復(fù)b的過程
(2)综芥、收端進(jìn)程由于消耗數(shù)據(jù)很慢丽蝎,所以可能會有這么一種情況,收端會發(fā)送其窗口大小為1的信息膀藐,然后有是1字節(jié)的傳輸
解決辦法(2種)
a屠阻、Clark方法:在接收緩存的一半變空,或者有足夠空間放最大報(bào)文長度之前额各,宣告接收窗口大小為0
b国觉、推遲確認(rèn):在對收到的報(bào)文段確認(rèn)之前等待到足夠的接收緩存,或者等待到一個時(shí)間段(現(xiàn)在一般定義500ms)
慢啟動
雖然流量控制可以避免發(fā)送方過載接收方虾啦,但是卻無法避免過載網(wǎng)絡(luò)麻诀,這是因?yàn)榻邮沾翱凇竢wnd」只反映了服務(wù)器個體的情況痕寓,卻無法反映網(wǎng)絡(luò)整體的情況。
為了避免過載網(wǎng)絡(luò)的問題蝇闭,慢啟動引入了擁塞窗口「cwnd」的概念呻率,用來表示發(fā)送方在得到接收方確認(rèn)前,最大允許傳輸?shù)奈唇?jīng)確認(rèn)的數(shù)據(jù)呻引±裾蹋「cwnd」同「rwnd」相比不同的是:它只是發(fā)送方的一個內(nèi)部參數(shù),無需通知給接收方逻悠,其初始值往往比較小元践,然后隨著數(shù)據(jù)包被接收方確認(rèn),窗口成倍擴(kuò)大蹂风,有點(diǎn)類似于拳擊比賽卢厂,開始時(shí)不了解敵情,往往是次拳試探惠啄,慢慢心里有底了慎恒,開始逐漸加大重拳進(jìn)攻的力度。
Slow Start
在慢啟動的過程中撵渡,隨著「cwnd」的增加融柬,可能會出現(xiàn)網(wǎng)絡(luò)過載,其外在表現(xiàn)就是丟包趋距,一旦出現(xiàn)此類問題粒氧,「cwnd」的大小會迅速衰減,以便網(wǎng)絡(luò)能夠緩過來节腐。
說明:網(wǎng)絡(luò)中實(shí)際傳輸?shù)奈唇?jīng)確認(rèn)的數(shù)據(jù)大小取決于「rwnd」和「cwnd」中的小值外盯。
T C P的 流 量 控 制 由 連 接 的 每 一 端 通 過 聲 明 的 窗 口 大 小 來 提 供 。 窗 口 大 小 為 字 節(jié) 數(shù) 翼雀, 起 始于確認(rèn)序號字段指明的值饱苟,這個值是接收端正期望接收的字節(jié)。窗口大小是一個16 bit字段狼渊,因 而 窗 口 大 小 最 大 為6 5 5 3 5字節(jié)箱熬。在2 4 . 4節(jié) 我 們 將 看 到 新 的 窗 口 刻 度 選 項(xiàng) , 它 允 許 這 個 值 按 比例變化以提供更大的窗口狈邑。檢驗(yàn)和覆蓋了整個的T C P報(bào)文段:T C P首部和T C P數(shù) 據(jù) 城须。 這 是 一 個 強(qiáng) 制 性 的 字 段 , 一 定 是由發(fā)端計(jì)算和存儲米苹,并由收端進(jìn)行驗(yàn)證糕伐。T C P檢驗(yàn)和的計(jì)算和U D P檢 驗(yàn) 和 的 計(jì) 算 相 似 , 使 用一 個 偽 首 部 驱入。只有當(dāng)U R G標(biāo)志置1時(shí) 緊 急 指 針 才 有 效 赤炒。 緊 急 指 針 是 一 個 正 的 偏 移 量 氯析, 和 序 號 字 段 中 的 值相加表示緊急數(shù)據(jù)最后一個字節(jié)的序號。T C P的 緊 急 方 式 是 發(fā) 送 端 向 另 一 端 發(fā) 送 緊 急 數(shù) 據(jù) 的一種方式莺褒。
擁塞避免