講完了 IP 層以后,接下來我們開始講傳輸層追驴。傳輸層里比較重要的兩個協(xié)議械哟,一個是 TCP,一個是 UDP殿雪。對于不從事底層開發(fā)的人員來講暇咆,或者對于開發(fā)應(yīng)用的人來講,最常用的就是這兩個協(xié)議丙曙。由于面試的時候爸业,這兩個協(xié)議經(jīng)常會被放在一起問,因而我在講的時候亏镰,也會結(jié)合著來講扯旷。
TCP 和 UDP 有哪些區(qū)別?
一般面試的時候我問這兩個協(xié)議的區(qū)別索抓,大部分人會回答钧忽,TCP 是面向連接的,UDP 是面向無連接的逼肯。
什么叫面向連接耸黑,什么叫無連接呢?在互通之前篮幢,面向連接的協(xié)議會先建立連接大刊。例如,TCP 會三次握手三椿,而 UDP 不會奈揍。為什么要建立連接呢?你 TCP 三次握手赋续,我 UDP 也可以發(fā)三個包玩玩男翰,有什么區(qū)別嗎?
所謂的建立連接纽乱,是為了在客戶端和服務(wù)端維護(hù)連接蛾绎,而建立一定的數(shù)據(jù)結(jié)構(gòu)來維護(hù)雙方交互的狀態(tài),用這樣的數(shù)據(jù)結(jié)構(gòu)來保證所謂的面向連接的特性。
例如租冠,TCP 提供可靠交付鹏倘。通過 TCP 連接傳輸?shù)臄?shù)據(jù),無差錯顽爹、不丟失纤泵、不重復(fù)、并且按序到達(dá)镜粤。我們都知道 IP 包是沒有任何可靠性保證的捏题,一旦發(fā)出去,就像西天取經(jīng),走丟了、被妖怪吃了诗宣,都只能隨它去友题。但是 TCP 號稱能做到那個連接維護(hù)的程序做的事情,這個下兩節(jié)我會詳細(xì)描述。而 UDP 繼承了 IP 包的特性,不保證不丟失,不保證按順序到達(dá)绪钥。
再如,TCP 是面向字節(jié)流的关炼。發(fā)送的時候發(fā)的是一個流程腹,沒頭沒尾。IP 包可不是一個流盗扒,而是一個個的 IP 包跪楞。之所以變成了流,這也是 TCP 自己的狀態(tài)維護(hù)做的事情侣灶。而 UDP 繼承了 IP 的特性甸祭,基于數(shù)據(jù)報的,一個一個地發(fā)褥影,一個一個地收池户。
還有 TCP 是可以有擁塞控制的。它意識到包丟棄了或者網(wǎng)絡(luò)的環(huán)境不好了凡怎,就會根據(jù)情況調(diào)整自己的行為校焦,看看是不是發(fā)快了,要不要發(fā)慢點(diǎn)统倒。UDP 就不會寨典,應(yīng)用讓我發(fā),我就發(fā)房匆,管它洪水滔天耸成。
因而 TCP 其實(shí)是一個有狀態(tài)服務(wù)报亩,通俗地講就是有腦子的,里面精確地記著發(fā)送了沒有井氢,接收到?jīng)]有弦追,發(fā)送到哪個了,應(yīng)該接收哪個了花竞,錯一點(diǎn)兒都不行劲件。而 UDP 則是無狀態(tài)服務(wù)。通俗地說是沒腦子的约急,天真無邪的零远,發(fā)出去就發(fā)出去了。
我們可以這樣比喻烤宙,如果 MAC 層定義了本地局域網(wǎng)的傳輸行為遍烦,IP 層定義了整個網(wǎng)絡(luò)端到端的傳輸行為俭嘁,這兩層基本定義了這樣的基因:網(wǎng)絡(luò)傳輸是以包為單位的躺枕,二層叫幀,網(wǎng)絡(luò)層叫包供填,傳輸層叫段拐云。我們籠統(tǒng)地稱為包。包單獨(dú)傳輸近她,自行選路叉瘩,在不同的設(shè)備封裝解封裝,不保證到達(dá)粘捎∞泵澹基于這個基因,生下來的孩子 UDP 完全繼承了這些特性攒磨,幾乎沒有自己的思想泳桦。
UDP 包頭是什么樣的?
我們來看一下 UDP 包頭娩缰。
前面章節(jié)我已經(jīng)講過包的傳輸過程灸撰,這里不再贅述。當(dāng)我發(fā)送的 UDP 包到達(dá)目標(biāo)機(jī)器后拼坎,發(fā)現(xiàn) MAC 地址匹配浮毯,于是就取下來,將剩下的包傳給處理 IP 層的代碼泰鸡。把 IP 頭取下來债蓝,發(fā)現(xiàn)目標(biāo) IP 匹配,接下來呢盛龄?這里面的數(shù)據(jù)包是給誰呢饰迹?
發(fā)送的時候器虾,我知道我發(fā)的是一個 UDP 的包,收到的那臺機(jī)器咋知道的呢蹦锋?所以在 IP 頭里面有個 8 位協(xié)議兆沙,這里會存放,數(shù)據(jù)里面到底是 TCP 還是 UDP莉掂,當(dāng)然這里是 UDP葛圃。于是,如果我們知道 UDP 頭的格式憎妙,就能從數(shù)據(jù)里面库正,將它解析出來。解析出來以后呢厘唾?數(shù)據(jù)給誰處理呢褥符?
處理完傳輸層的事情,內(nèi)核的事情基本就干完了抚垃,里面的數(shù)據(jù)應(yīng)該交給應(yīng)用程序自己去處理喷楣,可是一臺機(jī)器上跑著這么多的應(yīng)用程序,應(yīng)該給誰呢鹤树?
無論應(yīng)用程序?qū)懙氖褂?TCP 傳數(shù)據(jù)铣焊,還是 UDP 傳數(shù)據(jù),都要監(jiān)聽一個端口罕伯。正是這個端口曲伊,用來區(qū)分應(yīng)用程序,要不說端口不能沖突呢追他。兩個應(yīng)用監(jiān)聽一個端口坟募,到時候包給誰呀?所以邑狸,按理說懈糯,無論是 TCP 還是 UDP 包頭里面應(yīng)該有端口號,根據(jù)端口號推溃,將數(shù)據(jù)交給相應(yīng)的應(yīng)用程序昂利。
當(dāng)我們看到 UDP 包頭的時候,發(fā)現(xiàn)的確有端口號铁坎,有源端口號和目標(biāo)端口號蜂奸。因?yàn)槭莾啥送ㄐ怕铮@很好理解硬萍。但是你還會發(fā)現(xiàn)扩所,UDP 除了端口號,再沒有其他的了朴乖。和下兩節(jié)要講的 TCP 頭比起來祖屏,這個簡直簡單得一塌糊涂爸蕖!
UDP 的三大特點(diǎn)
UDP 就像小孩子一樣袁勺,有以下這些特點(diǎn):
第一雹食,溝通簡單,不需要一肚子花花腸子(大量的數(shù)據(jù)結(jié)構(gòu)期丰、處理邏輯群叶、包頭字段)。前提是它相信網(wǎng)絡(luò)世界是美好的钝荡,秉承性善論街立,相信網(wǎng)絡(luò)通路默認(rèn)就是很容易送達(dá)的,不容易被丟棄的埠通。
第二赎离,輕信他人。它不會建立連接端辱,雖然有端口號梁剔,但是監(jiān)聽在這個地方,誰都可以傳給他數(shù)據(jù)掠手,他也可以傳給任何人數(shù)據(jù)憾朴,甚至可以同時傳給多個人數(shù)據(jù)狸捕。
第三喷鸽,愣頭青,做事不懂權(quán)變灸拍。不知道什么時候該堅持做祝,什么時候該退讓。它不會根據(jù)網(wǎng)絡(luò)的情況進(jìn)行發(fā)包的擁塞控制鸡岗,無論網(wǎng)絡(luò)丟包丟成啥樣了混槐,它該怎么發(fā)還怎么發(fā)。
UDP 的三大使用場景
基于 UDP 這種“小孩子”的特點(diǎn)轩性,我們可以考慮在以下的場景中使用声登。
第一,需要資源少揣苏,在網(wǎng)絡(luò)情況比較好的內(nèi)網(wǎng)悯嗓,或者對于丟包不敏感的應(yīng)用。這很好理解卸察,就像如果你是領(lǐng)導(dǎo)脯厨,你會讓你們組剛畢業(yè)的小朋友去做一些沒有那么難的項(xiàng)目,打一些沒有那么難的客戶坑质,或者做一些失敗了也能忍受的實(shí)驗(yàn)性項(xiàng)目合武。
我們在第四節(jié)講的 DHCP 就是基于 UDP 協(xié)議的临梗。一般的獲取 IP 地址都是內(nèi)網(wǎng)請求,而且一次獲取不到 IP 又沒事稼跳,過一會兒還有機(jī)會盟庞。我們講過 PXE 可以在啟動的時候自動安裝操作系統(tǒng),操作系統(tǒng)鏡像的下載使用的 TFTP汤善,這個也是基于 UDP 協(xié)議的茫经。在還沒有操作系統(tǒng)的時候,客戶端擁有的資源很少萎津,不適合維護(hù)一個復(fù)雜的狀態(tài)機(jī)卸伞,而且因?yàn)槭莾?nèi)網(wǎng),一般也沒啥問題锉屈。
第二荤傲,不需要一對一溝通,建立連接颈渊,而是可以廣播的應(yīng)用遂黍。咱們小時候人都很簡單,大家在班級里面俊嗽,誰成績好雾家,誰寫作好,應(yīng)該表揚(yáng)誰懲罰誰绍豁,誰得幾個小紅花都是當(dāng)著全班的面講的芯咧,公平公正公開。長大了人心復(fù)雜了竹揍,薪水敬飒、獎金要背靠背,和員工一對一溝通芬位。
UDP 的不面向連接的功能无拗,可以使得可以承載廣播或者多播的協(xié)議。DHCP 就是一種廣播的形式昧碉,就是基于 UDP 協(xié)議的英染,而廣播包的格式前面說過了。
對于多播被饿,我們在講 IP 地址的時候四康,講過一個 D 類地址,也即組播地址锹漱,使用這個地址箭养,可以將包組播給一批機(jī)器。當(dāng)一臺機(jī)器上的某個進(jìn)程想監(jiān)聽某個組播地址的時候哥牍,需要發(fā)送 IGMP 包毕泌,所在網(wǎng)絡(luò)的路由器就能收到這個包喝检,知道有個機(jī)器上有個進(jìn)程在監(jiān)聽這個組播地址。當(dāng)路由器收到這個組播地址的時候撼泛,會將包轉(zhuǎn)發(fā)給這臺機(jī)器挠说,這樣就實(shí)現(xiàn)了跨路由器的組播。
在后面云中網(wǎng)絡(luò)部分愿题,有一個協(xié)議 VXLAN损俭,也是需要用到組播,也是基于 UDP 協(xié)議的潘酗。
第三杆兵,需要處理速度快,時延低仔夺,可以容忍少數(shù)丟包琐脏,但是要求即便網(wǎng)絡(luò)擁塞,也毫不退縮缸兔,一往無前的時候日裙。記得曾國藩建立湘軍的時候,專門招出生牛犢不怕虎的新兵惰蜜,而不用那些“老油條”的八旗兵昂拂,就是因?yàn)榘似毂?jīng)歷的事情多,遇到敵軍不敢舍死忘生抛猖。
同理格侯,UDP 簡單、處理速度快樟结,不像 TCP 那樣养交,操這么多的心,各種重傳啊瓢宦,保證順序啊,前面的不收到灰羽,后面的沒法處理啊驮履。不然等這些事情做完了,時延早就上去了廉嚼。而 TCP 在網(wǎng)絡(luò)不好出現(xiàn)丟包的時候玫镐,擁塞控制策略會主動的退縮,降低發(fā)送速度怠噪,這就相當(dāng)于本來環(huán)境就差恐似,還自斷臂膀,用戶本來就卡傍念,這下更卡了矫夷。
當(dāng)前很多應(yīng)用都是要求低時延的葛闷,它們可不想用 TCP 如此復(fù)雜的機(jī)制,而是想根據(jù)自己的場景双藕,實(shí)現(xiàn)自己的可靠和連接保證淑趾。例如,如果應(yīng)用自己覺得忧陪,有的包丟了就丟了扣泊,沒必要重傳了,就可以算了嘶摊,有的比較重要延蟹,則應(yīng)用自己重傳,而不依賴于 TCP叶堆。有的前面的包沒到等孵,后面的包到了,那就先給客戶展示后面的嘛蹂空,干嘛非得等到齊了呢俯萌?如果網(wǎng)絡(luò)不好,丟了包上枕,那不能退縮啊咐熙,要盡快傳啊,速度不能降下來啊辨萍,要擠占帶寬棋恼,搶在客戶失去耐心之前到達(dá)。
由于 UDP 十分簡單锈玉,基本啥都沒做爪飘,也就給了應(yīng)用“城會玩”的機(jī)會。就像在和平年代拉背,每個人應(yīng)該有獨(dú)立的思考和行為师崎,應(yīng)該可靠并且禮讓;但是如果在戰(zhàn)爭年代椅棺,往往不太需要過于獨(dú)立的思考犁罩,而需要士兵簡單服從命令就可以了。
曾國藩說哪支部隊(duì)需要誘敵犧牲两疚,也就犧牲了床估,相當(dāng)于包丟了就丟了。兩軍狹路相逢的時候诱渤,曾國藩說上丐巫,沒有帶寬也要上,這才給了曾國藩運(yùn)籌帷幄,城會玩的機(jī)會递胧。同理如果你實(shí)現(xiàn)的應(yīng)用需要有自己的連接策略碑韵,可靠保證,時延要求谓着,使用 UDP泼诱,然后再應(yīng)用層實(shí)現(xiàn)這些是再好不過了。
基于 UDP 的“城會玩”的五個例子
我列舉幾種“城會玩”的例子赊锚。
“城會玩”一:網(wǎng)頁或者 APP 的訪問
原來訪問網(wǎng)頁和手機(jī) APP 都是基于 HTTP 協(xié)議的治筒。HTTP 協(xié)議是基于 TCP 的,建立連接都需要多次交互舷蒲,對于時延比較大的目前主流的移動互聯(lián)網(wǎng)來講耸袜,建立一次連接需要的時間會比較長,然而既然是移動中牲平,TCP 可能還會斷了重連堤框,也是很耗時的。而且目前的 HTTP 協(xié)議纵柿,往往采取多個數(shù)據(jù)通道共享一個連接的情況蜈抓,這樣本來為了加快傳輸速度,但是 TCP 的嚴(yán)格順序策略使得哪怕共享通道昂儒,前一個不來沟使,后一個和前一個即便沒關(guān)系,也要等著渊跋,時延也會加大腊嗡。
而 QUIC(全稱 Quick UDP Internet Connections,快速 UDP 互聯(lián)網(wǎng)連接)是 Google 提出的一種基于 UDP 改進(jìn)的通信協(xié)議拾酝,其目的是降低網(wǎng)絡(luò)通信的延遲燕少,提供更好的用戶互動體驗(yàn)。
QUIC 在應(yīng)用層上蒿囤,會自己實(shí)現(xiàn)快速連接建立客们、減少重傳時延,自適應(yīng)擁塞控制蟋软,是應(yīng)用層“城會玩”的代表镶摘。這一節(jié)主要是講 UDP,QUIC 我們放到應(yīng)用層去講岳守。
“城會玩”二:流媒體的協(xié)議
現(xiàn)在直播比較火,直播協(xié)議多使用 RTMP碌冶,這個協(xié)議我們后面的章節(jié)也會講湿痢,而這個 RTMP 協(xié)議也是基于 TCP 的。TCP 的嚴(yán)格順序傳輸要保證前一個收到了,下一個才能確認(rèn)譬重,如果前一個收不到拒逮,下一個就算包已經(jīng)收到了,在緩存里面臀规,也需要等著滩援。對于直播來講,這顯然是不合適的塔嬉,因?yàn)槔系囊曨l幀丟了其實(shí)也就丟了玩徊,就算再傳過來用戶也不在意了,他們要看新的了谨究,如果老是沒來就等著恩袱,卡頓了,新的也看不了胶哲,那就會丟失客戶畔塔,所以直播,實(shí)時性比較比較重要鸯屿,寧可丟包澈吨,也不要卡頓的。
另外寄摆,對于丟包谅辣,其實(shí)對于視頻播放來講,有的包可以丟冰肴,有的包不能丟屈藐,因?yàn)橐曨l的連續(xù)幀里面,有的幀重要熙尉,有的不重要联逻,如果必須要丟包,隔幾個幀丟一個检痰,其實(shí)看視頻的人不會感知包归,但是如果連續(xù)丟幀,就會感知了铅歼,因而在網(wǎng)絡(luò)不好的情況下公壤,應(yīng)用希望選擇性的丟幀。
還有就是當(dāng)網(wǎng)絡(luò)不好的時候椎椰,TCP 協(xié)議會主動降低發(fā)送速度厦幅,這對本來當(dāng)時就卡的看視頻來講是要命的,應(yīng)該應(yīng)用層馬上重傳慨飘,而不是主動讓步确憨。因而译荞,很多直播應(yīng)用,都基于 UDP 實(shí)現(xiàn)了自己的視頻傳輸協(xié)議休弃。
“城會玩”三:實(shí)時游戲
游戲有一個特點(diǎn)吞歼,就是實(shí)時性比較高∷快一秒你干掉別人篙骡,慢一秒你被別人爆頭,所以很多職業(yè)玩家會買非常專業(yè)的鼠標(biāo)和鍵盤丈甸,爭分奪秒糯俗。
因而,實(shí)時游戲中客戶端和服務(wù)端要建立長連接老虫,來保證實(shí)時傳輸叶骨。但是游戲玩家很多,服務(wù)器卻不多祈匙。由于維護(hù) TCP 連接需要在內(nèi)核維護(hù)一些數(shù)據(jù)結(jié)構(gòu)忽刽,因而一臺機(jī)器能夠支撐的 TCP 連接數(shù)目是有限的,然后 UDP 由于是沒有連接的夺欲,在異步 IO 機(jī)制引入之前跪帝,常常是應(yīng)對海量客戶端連接的策略。
另外還是 TCP 的強(qiáng)順序問題些阅,對戰(zhàn)的游戲伞剑,對網(wǎng)絡(luò)的要求很簡單,玩家通過客戶端發(fā)送給服務(wù)器鼠標(biāo)和鍵盤行走的位置市埋,服務(wù)器會處理每個用戶發(fā)送過來的所有場景黎泣,處理完再返回給客戶端,客戶端解析響應(yīng)缤谎,渲染最新的場景展示給玩家抒倚。
如果出現(xiàn)一個數(shù)據(jù)包丟失,所有事情都需要停下來等待這個數(shù)據(jù)包重發(fā)坷澡⊥信唬客戶端會出現(xiàn)等待接收數(shù)據(jù),然而玩家并不關(guān)心過期的數(shù)據(jù)频敛,激戰(zhàn)中卡 1 秒项郊,等能動了都已經(jīng)死了。
游戲?qū)?shí)時要求較為嚴(yán)格的情況下斟赚,采用自定義的可靠 UDP 協(xié)議着降,自定義重傳策略,能夠把丟包產(chǎn)生的延遲降到最低拗军,盡量減少網(wǎng)絡(luò)問題對游戲性造成的影響鹊碍。
“城會玩”四:IoT 物聯(lián)網(wǎng)
一方面厌殉,物聯(lián)網(wǎng)領(lǐng)域終端資源少食绿,很可能只是個內(nèi)存非常小的嵌入式系統(tǒng)侈咕,而維護(hù) TCP 協(xié)議代價太大;另一方面器紧,物聯(lián)網(wǎng)對實(shí)時性要求也很高耀销,而 TCP 還是因?yàn)樯厦娴哪切┰驅(qū)е聲r延大。Google 旗下的 Nest 建立 Thread Group铲汪,推出了物聯(lián)網(wǎng)通信協(xié)議 Thread熊尉,就是基于 UDP 協(xié)議的。
“城會玩”五:移動通信領(lǐng)域
在 4G 網(wǎng)絡(luò)里掌腰,移動流量上網(wǎng)的數(shù)據(jù)面對的協(xié)議 GTP-U 是基于 UDP 的狰住。因?yàn)橐苿泳W(wǎng)絡(luò)協(xié)議比較復(fù)雜,而 GTP 協(xié)議本身就包含復(fù)雜的手機(jī)上線下線的通信協(xié)議齿梁。如果基于 TCP催植,TCP 的機(jī)制就顯得非常多余,這部分協(xié)議我會在后面的章節(jié)單獨(dú)講解勺择。
小結(jié)
好了创南,這節(jié)就到這里了,我們來總結(jié)一下:
如果將 TCP 比作成熟的社會人省核,UDP 則是頭腦簡單的小朋友稿辙。TCP 復(fù)雜,UDP 簡單气忠;TCP 維護(hù)連接邻储,UDP 誰都相信;TCP 會堅持知進(jìn)退旧噪;UDP 愣頭青一個吨娜,勇往直前;
UDP 雖然簡單舌菜,但它有簡單的用法萌壳。它可以用在環(huán)境簡單、需要多播日月、應(yīng)用層自己控制傳輸?shù)牡胤礁の汀@?DHCP、VXLAN爱咬、QUIC 等尺借。
最后,給你留兩個思考題吧精拟。
都說 TCP 是面向連接的燎斩,在計算機(jī)看來虱歪,怎么樣才算一個連接呢?
你知道 TCP 的連接是如何建立栅表,又是如何關(guān)閉的嗎笋鄙?
歡迎你留言和討論。