作為客戶端開(kāi)發(fā)者,如果你的 App 中有圖片上傳功能考赛,而且某天測(cè)試人員拿著手機(jī)告訴你圖片總是上傳不上去颜骤,或者進(jìn)度條走的很慢干跛,你的第一反應(yīng)很有可能是「網(wǎng)絡(luò)不好?」阎姥。網(wǎng)絡(luò)到底是個(gè)什么概念记舆,網(wǎng)速為什么會(huì)不好御蒲,如何預(yù)估當(dāng)前網(wǎng)速是否合理衣赶,分析這類(lèi)問(wèn)題,背后需要建立全面且廣闊的技術(shù)視野厚满。
無(wú)論是上行數(shù)據(jù)通道(數(shù)據(jù)從客戶端發(fā)往服務(wù)器)還是下行通道(數(shù)據(jù)從服務(wù)器發(fā)回客戶端)府瞄,一次完整的網(wǎng)絡(luò)行為(比如 HTTP 請(qǐng)求)涉及到的硬件設(shè)備有很多,影響面也不會(huì)止于通訊的兩端。當(dāng)我們嘗試去分析是否因?yàn)榫W(wǎng)絡(luò)問(wèn)題導(dǎo)致 App 行為異常的時(shí)候遵馆,需要將各方面可能的因素納入考量鲸郊,一步一步抽絲剝繭直達(dá)真相。
Debug 的時(shí)候货邓,所有游戲的參與者都值得被懷疑秆撮。
iPhone
測(cè)試人員手里的 iPhone 設(shè)備是第一個(gè)值得懷疑的對(duì)象,作為網(wǎng)絡(luò)數(shù)據(jù)的發(fā)送方或者接收方换况,iPhone 有太多的方式去影響網(wǎng)絡(luò)行為了职辨,比如:
測(cè)試人員在 iPhone 的 Setting 中關(guān)閉了 App 的 Push,導(dǎo)致 Push 收不到戈二。
測(cè)試人員將 iPhone 設(shè)置了夜間模式舒裤,導(dǎo)致鎖屏收不到 Push。
測(cè)試人員將 iPhone 的開(kāi)發(fā)者模式打開(kāi)觉吭,并設(shè)置 100% 丟包腾供,所有請(qǐng)求失敗。
測(cè)試人員將 iPhone 連接了另一個(gè)不可用的 WIFI鲜滩。
測(cè)試人員將 iPhone 的自動(dòng)同步日期功能關(guān)閉台腥,并將手機(jī)設(shè)置成 10 年前的某個(gè)時(shí)間,導(dǎo)致所有的 HTTPS 請(qǐng)求證書(shū)校驗(yàn)失敗绒北,請(qǐng)求無(wú)法成功黎侈。
測(cè)試人員。闷游。峻汉。
作為網(wǎng)絡(luò)行為的第一環(huán)節(jié),iPhone 設(shè)備就有很多的可能性導(dǎo)致請(qǐng)求出現(xiàn)問(wèn)題脐往,優(yōu)秀的測(cè)試人員會(huì)竭盡所能去探索 App 邊際的異常行為休吠,工程師如何去快速定位排除各類(lèi)異常 case,只能靠年復(fù)一年的工程實(shí)踐和經(jīng)驗(yàn)積累业簿。
路由器
大多數(shù)時(shí)候瘤礁,網(wǎng)絡(luò)包離開(kāi) iPhone 后下一個(gè)目的地可以大致分為兩類(lèi),如果是 2G/3G/4G 梅尤,走較近的基站柜思,如果是 WIFI,則走房間里的路由器巷燥。
這兩類(lèi)場(chǎng)景需要分開(kāi)測(cè)試赡盘。
這幾年隨著國(guó)內(nèi) 4G 的普及,4G 的覆蓋面已經(jīng)相當(dāng)之廣缰揪,其穩(wěn)定性甚至已慢慢超過(guò)電信陨享、網(wǎng)通的 WIFI 線路,我相信并不是只有我一個(gè)人會(huì)偶爾在 WIFI 不穩(wěn)定的時(shí)候切換成 4G 看視頻。
另一個(gè)原因是由于 Cellular Network 和 WIFI Network 運(yùn)營(yíng)商不同抛姑,有時(shí)候網(wǎng)絡(luò)行為會(huì)存在差異赞厕。比如有些運(yùn)營(yíng)商會(huì)在 HTTP 請(qǐng)求中插入自己的代碼,有些運(yùn)營(yíng)商對(duì)于 TCP 長(zhǎng)連接設(shè)置較短的 NAT 超時(shí)時(shí)間定硝,導(dǎo)致心跳失效坑傅。
路由器可能引起的網(wǎng)絡(luò)問(wèn)題有兩類(lèi),一是信號(hào)干擾喷斋,二是路由器本身攜帶的安全機(jī)制⊙涠荆現(xiàn)在大部分寫(xiě)字樓的辦公環(huán)境下,都能一次性搜出十多個(gè) WIFI 信號(hào)星爪,如果外部的 WIFI 信號(hào)碰巧和你路由器在使用同一頻道浆西,即使信號(hào)不強(qiáng),也會(huì)造成數(shù)據(jù)的擁擠顽腾,進(jìn)而導(dǎo)致「網(wǎng)速不好」的感知近零。另外是路由器的安全機(jī)制,現(xiàn)在很多給企業(yè)用的路由器都自帶一鍵屏蔽 QQ抄肖,微信等 App 的安全功能久信,或者針對(duì)通訊協(xié)議進(jìn)行屏蔽,比如丟棄所有除 53 端口的 UDP 包漓摩,直接導(dǎo)致一些走 UDP 的 VOIP 和 VPN 軟件不可用裙士。
公網(wǎng)
網(wǎng)絡(luò)包離開(kāi)網(wǎng)關(guān),進(jìn)入公網(wǎng)后管毙,其網(wǎng)絡(luò)行為將變得難以預(yù)測(cè)腿椎,公網(wǎng)代表的是互聯(lián)網(wǎng)的基礎(chǔ)設(shè)施,就像是高速公路夭咬。一輛貨車(chē)在計(jì)劃好路線啃炸,預(yù)估好車(chē)速之后,也無(wú)法準(zhǔn)確的計(jì)算抵達(dá)目的地所耗費(fèi)的時(shí)間卓舵,因?yàn)槁飞蠒?huì)有意料之外的擁堵南用,事故或者計(jì)劃變更。
我們能做的是掏湾,理解網(wǎng)絡(luò)包傳輸?shù)恼麄€(gè)流程裹虫,理清所有可能的影響因子,再借助網(wǎng)絡(luò)工具分析忘巧,盡可能的接近網(wǎng)絡(luò)傳輸?shù)恼嫦唷?/p>
iOS 開(kāi)發(fā)工程師絕大多數(shù)時(shí)候都是在和 TCP 打交道恒界, 我們對(duì)于網(wǎng)絡(luò)請(qǐng)求的分析是否準(zhǔn)確取決于我們對(duì)于協(xié)議的理解程度睦刃。大多數(shù)請(qǐng)求(比如 HTTP 上傳圖片文件)都是從客戶端發(fā)起砚嘴,之后服務(wù)器響應(yīng),客戶端 App 更多的是充當(dāng)發(fā)送方的角色。TCP 連接的傳輸效率(主觀上對(duì)于網(wǎng)速的感知)受制于 Flow Control 和 Congestion Control际长,有很明顯的流量特征耸采,大致分為以下幾個(gè)階段。
階段一:三次握手工育。
階段二:慢啟動(dòng)虾宇。
階段三:到達(dá)最大發(fā)送窗口,遇到丟包如绸,發(fā)送窗口減小嘱朽。
階段四:n 個(gè) RTT 之后,發(fā)送窗口進(jìn)一步達(dá)到最大值怔接。
階段三與階段四之間往復(fù)搪泳。
上述幾步是簡(jiǎn)化抽象的描述,深入了解 TCP 流控和擁塞控制的細(xì)節(jié)后扼脐,才能對(duì)這幾個(gè)環(huán)節(jié)或者抓包的流量變化有更加準(zhǔn)確的了解岸军。
在設(shè)計(jì) TCP 的早期,協(xié)議的設(shè)計(jì)者就認(rèn)為互聯(lián)網(wǎng)的基礎(chǔ)設(shè)施相對(duì)于使用者來(lái)說(shuō)是不夠用的(總體帶寬不夠瓦侮,或者理解成自來(lái)水管道容量不夠)艰赞。因此為了避免管道出現(xiàn)擁塞,TCP 在剛開(kāi)始建立連接的時(shí)候肚吏,發(fā)送方會(huì)通過(guò) Slow Start 的方式去試探管道的容量方妖,以避免初期大流量所導(dǎo)致的整個(gè)互聯(lián)網(wǎng)管道擁塞。從 Slow Start 開(kāi)始探測(cè)容量到發(fā)送方逐步到達(dá)最大發(fā)送速率罚攀,中間有一套流程機(jī)制嚴(yán)格控制吁断。
發(fā)送方當(dāng)前能夠發(fā)送的數(shù)據(jù)包(能夠注入管道的流量)是由 Send Window (Send Buffer,以 Bytes 為單位)控制的坞生。而 Send Window 又受到以下幾個(gè)因素影響:
Congestion Window (cwnd)仔役,cwnd 的概念引入是為了推測(cè)或者感知當(dāng)前管道容量的大小,判斷是否出現(xiàn)了擁塞是己。如果有一個(gè)包被成功 ack又兵,那么可以推測(cè)管道容量夠用,那么我們可以增大 cwnd 的大小卒废,如果一個(gè)包發(fā)送超時(shí)沛厨,那么很有可能管道出現(xiàn)了擁塞,那么我們需要減小 cwnd 的大小摔认。顯然 Send Window 的大小不應(yīng)該超過(guò) cwnd 的值逆皮,因?yàn)槿绻霈F(xiàn)擁塞,發(fā)送再多的包也只會(huì)加重 Congestion参袱〉缫ィ可以說(shuō) cwnd 的值決定了發(fā)送方發(fā)送數(shù)據(jù)流量的變化秽梅。
cwnd 在連接的初期的變化有兩種,第一種是指數(shù)級(jí)增大(2->4->8)剿牺,第二種是加法增大(8->9->10)企垦。第一種方式即為 slow start,cwnd 有一個(gè)較小的初始值(這個(gè)值也有個(gè)計(jì)算公式晒来,感興趣的可以查看 rfc 5681)钞诡,但每一次 RTT 之后獲得一次指數(shù)級(jí)增大的機(jī)會(huì),這樣可以在三次握手之后快速的達(dá)到一個(gè)較大的 cwnd 值湃崩。第二種方式稱之為 congestion avoidence荧降,是為了避免 cwnd 增長(zhǎng)過(guò)快所帶來(lái)的流量壓力。兩種方式的分水嶺是 ssthresh(slow start threshold) 值:
- if cwnd < ssthresh攒读,則采用 slow start誊抛。
- if cwnd > ssthresh,則采用 congestion avoidence整陌。
- if cwnd == ssthresh拗窃,則采用 slow start 或者 congestion avoidence。
slow start 和 congestion avoidence 是連接初期 cwnd 的變化方式泌辫,連接穩(wěn)定之后随夸,如果出現(xiàn)丟包(RTO,或者連續(xù)收到三次重復(fù) Ack)震放,則認(rèn)為可能出現(xiàn)擁塞宾毒,cwnd 值會(huì)減小。此時(shí)根據(jù) TCP 版本的不同殿遂,cwnd 變化的方式也會(huì)不同诈铛,實(shí)際上 cwnd 值的控制存在非常多的版本,這里以 TCP Reno 的實(shí)現(xiàn)為例墨礁,cwnd 的值會(huì)減半幢竹,之后進(jìn)入 congestion avoidence,再次以 RTT 為時(shí)間單位逐步增大 cwnd 的值恩静。整個(gè)流程我們可以用下圖表示:
Receive window (rwnd)焕毫,rwnd 是另一個(gè)會(huì)影響 send window 大小的值,它表示的是接收方的接收窗口大小驶乾,在接收方的 ack 包里會(huì)帶回 receive window 的大小邑飒。顯然,send window 的大小不應(yīng)該超過(guò) rwnd 的值级乐,否則則超出了接收方處理包的能力疙咸。
send window 的大小總是取 cwnd 和 rwnd 中的較小值,這樣發(fā)送方在發(fā)包的時(shí)候风科,既考慮了網(wǎng)絡(luò)的擁塞情況撒轮,也不會(huì)超過(guò)接收方的接收能力乞旦。
理解了上述的 TCP 擁塞控制過(guò)程,我們可以得出幾個(gè)明顯的結(jié)論腔召。
結(jié)論一:高延遲會(huì)影響 TCP 的傳輸效率杆查。 TCP 在前期的 Slow Start 和中期丟包導(dǎo)致的發(fā)送窗口減小扮惦,都需要接收方 Ack 來(lái)逐步增大發(fā)送窗口臀蛛,每幾次 RTT 之后,發(fā)送方有一次機(jī)會(huì)去增大發(fā)送窗口崖蜜,如果兩地的物理距離長(zhǎng)浊仆,延遲高,那么 RTT 值也大豫领,自然 TCP 的傳輸效率也會(huì)受到影響抡柿。延遲越高,TCP 的傳輸效率或者說(shuō)吞吐量(Throughput)也會(huì)越低等恐。這也是為什么洲劣,即使在不擁擠的時(shí)段,出口帶寬足夠的情況下课蔬,訪問(wèn)國(guó)外網(wǎng)站或者下載文件囱稽,網(wǎng)速也很難達(dá)到國(guó)內(nèi)服務(wù)器的水準(zhǔn)。
結(jié)論二:丟包率會(huì)嚴(yán)重影響 TCP 的傳輸效率二跋。TCP Reno 的實(shí)現(xiàn)在確認(rèn)丟包的情況下战惊,會(huì)將發(fā)送方的發(fā)送窗口大小減半,只要有一個(gè)包丟失就會(huì)發(fā)生減半扎即,這種策略本身是站在整個(gè)互聯(lián)網(wǎng)的維度去保證整體效率吞获,但對(duì)單條鏈路來(lái)說(shuō),十分影響傳輸效率谚鄙,后續(xù)發(fā)送窗口的恢復(fù)需要更多健康的數(shù)據(jù)包發(fā)送各拷。即使是 2% 的丟包率,也可以導(dǎo)致 TCP 傳輸效率嚴(yán)重下降闷营。作為測(cè)試可以在 iPhone 的「設(shè)置-開(kāi)發(fā)者」里面設(shè)置 2% 的丟包率撤逢,再上傳圖片文件,和正常情況下丟包對(duì)比下網(wǎng)速體驗(yàn)粮坞。
這里值得一提的是對(duì)于丟包的檢測(cè)蚊荣,發(fā)送方在兩種場(chǎng)景下會(huì)認(rèn)為發(fā)生了丟包。第一種是發(fā)送方的 RTO (Retransimission Timeout)被觸發(fā)莫杈,發(fā)送方在一定的時(shí)間內(nèi)如果沒(méi)有收到對(duì)方的 Ack互例,則認(rèn)為發(fā)生了丟包。第二種是發(fā)送方收到了三次對(duì)方重復(fù)的 Ack筝闹,這種情況表明接收方收到了亂序的包媳叨,比如先收到 Packet 1腥光,Packet 2,再是 Packet 4糊秆,Packet 5武福,Packet 6,此時(shí)接收方會(huì)重復(fù)三次 Ack Packet 2痘番,接收方收到之后捉片,即認(rèn)為 Packet 3 被丟失了。兩種場(chǎng)景的丟包汞舱,對(duì)于發(fā)送窗口的影響也有差別伍纫,RTO 觸發(fā)會(huì)將發(fā)送窗口置為 1 MSS,3 個(gè)重復(fù) Ack 則導(dǎo)致窗口減半( TCP Reno 的做法昂芜,其他算法又不一樣)莹规。
了解這些技術(shù)細(xì)節(jié)的意義,在于抓包的時(shí)候能更準(zhǔn)確的感知流量的變化是否正常泌神。
服務(wù)器
客戶端請(qǐng)求數(shù)據(jù)包的最后一站是服務(wù)器良漱,一旦抵達(dá)服務(wù)器,我們能夠分析的數(shù)據(jù)就只能依賴于服務(wù)器返回的 return code 了欢际,或者是借助抓包工具分析服務(wù)器的回包行為母市。
結(jié)束語(yǔ)
學(xué)習(xí)網(wǎng)絡(luò)協(xié)議的意義在于能準(zhǔn)確分析可能出現(xiàn)的網(wǎng)絡(luò)問(wèn)題,網(wǎng)絡(luò)行為的參與者多幼苛,環(huán)節(jié)多窒篱,客戶端一旦遇到異常,需要一步步分析定位舶沿,接近問(wèn)題的真相墙杯。不熟悉網(wǎng)絡(luò)抓包的同學(xué),可以參考我博客上之前寫(xiě)的幾篇關(guān)于 tcpdump括荡,wireshark高镐,mitimproxy 的介紹文章。