話說,UDP比TCP快嗎? ? 相信就算不是八股文老手,也會下意識的脫口而出:"是"。 ? 這要追問為什么,估計大家也能說出個大概。 ? ? ? 但這也讓人好奇,用UDP就一定比用TCP快嗎?什么情況下用UDP會比用TCP慢? ? 我們今天就來聊下這個話題。 ?
使用socket進(jìn)行數(shù)據(jù)傳輸
作為一個程序員,假設(shè)我們需要在A電腦的進(jìn)程發(fā)一段數(shù)據(jù)到B電腦的進(jìn)程,我們一般會在代碼里使用socket進(jìn)行編程。 ? socket就像是一個電話或者郵箱(郵政的信箱)。當(dāng)你想要發(fā)送消息的時候,撥通電話或者將信息塞到郵箱里,socket內(nèi)核會自動完成將數(shù)據(jù)傳給對方的這個過程。 ? 基于socket我們可以選擇使用TCP或UDP協(xié)議進(jìn)行通信。 ? 對于TCP這樣的可靠性協(xié)議,每次消息發(fā)出后都能明確知道對方收沒收到,就像打電話一樣,只要"喂喂"兩下就能知道對方有沒有在聽。 ? 而UDP就像是給郵政的信箱寄信一樣,你寄出去的信,根本就不知道對方有沒有正常收到,丟了也是有可能的。 ? 回到socket編程的話題上。 ? 創(chuàng)建socket的方式就像下面這樣。 ?
fd?=?socket(AF_INET,?具體協(xié)議,0);
注意上面的"具體協(xié)議",如果傳入的是SOCK_STREAM,是指使用字節(jié)流傳輸數(shù)據(jù),說白了就是TCP協(xié)議。 ?
TCP是什么 ? 如果傳入的是SOCK_DGRAM,是指使用數(shù)據(jù)報傳輸數(shù)據(jù),也就是UDP協(xié)議。 ?
UDP是什么 ? 返回的fd是指socket句柄,可以理解為socket的身份證號。通過這個fd你可以在內(nèi)核中找到唯一的socket結(jié)構(gòu)。 ? 如果想要通過這個socket發(fā)消息,只需要操作這個fd就行了,比如執(zhí)行 send(fd, msg, ...),內(nèi)核就會通過這個fd句柄找到socket然后進(jìn)行發(fā)數(shù)據(jù)的操作。 ? 如果一切順利,此時對方執(zhí)行接收消息的操作,也就是 recv(fd, msg, ...),就能拿到你發(fā)的消息。 ?
udp發(fā)送接收過程 ?
對于異常情況的處理
但如果不順利呢? ? 比如消息發(fā)到一半,丟包了呢? ?
丟包的原因有很多,之前寫過的《用了TCP協(xié)議,就一定不會丟包嗎?》有詳細(xì)聊到過,這里就不再展開。
那UDP和TCP的態(tài)度就不太一樣了。 ? UDP表示,"哦,是嗎?然后呢?關(guān)我x事" ? TCP態(tài)度就截然相反了,"???那可不行,是不是我發(fā)太快了呢?是不是鏈路太堵被別人影響到了呢?不過你放心,我肯定給你補發(fā)" ? TCP老實人石錘了。我們來看下這個老實人在背后都默默做了哪些事情。 ? 重傳機制 ? 對于TCP,它會給發(fā)出的消息打上一個編號(sequence),接收方收到后回一個確認(rèn)(ack)。發(fā)送方可以通過ack的數(shù)值知道接收方收到了哪些sequence的包。 ? 如果長時間等不到對方的確認(rèn),TCP就會重新發(fā)一次消息,這就是所謂的重傳機制。 ?
TCP重傳 ? 流量控制機制 ? 但重傳這件事本身對性能影響是比較嚴(yán)重的,所以是下下策。 ? 于是TCP就需要思考有沒有辦法可以盡量避免重傳。 ? 因為數(shù)據(jù)發(fā)送方和接收方處理數(shù)據(jù)能力可能不同,因此如果可以根據(jù)雙方的能力去調(diào)整發(fā)送的數(shù)據(jù)量就好了,于是就有了發(fā)送和接收窗口,基本上從名字就能看出它的作用,比如接收窗口的大小就是指,接收方當(dāng)前能接收的數(shù)據(jù)量大小,發(fā)送窗口的大小就指發(fā)送方當(dāng)前能發(fā)的數(shù)據(jù)量大小。TCP根據(jù)窗口的大小去控制自己發(fā)送的數(shù)據(jù)量,這樣就能大大減少丟包的概率。 ?
流量控制機制 ? 滑動窗口機制 ? 接收方的接收到數(shù)據(jù)之后,會不斷處理,處理能力也不是一成不變的,有時候處理的快些,那就可以收多點數(shù)據(jù),處理的慢點那就希望對方能少發(fā)點數(shù)據(jù)。畢竟發(fā)多了就有可能處理不過來導(dǎo)致丟包,丟包會導(dǎo)致重傳,這可是下下策。因此我們需要動態(tài)的去調(diào)節(jié)這個接收窗口的大小,于是就有了滑動窗口機制。 ? 看到這里大家可能就有點迷了,流量控制和滑動窗口機制貌似很像,它們之間是啥關(guān)系?我總結(jié)一下。其實現(xiàn)在TCP是通過滑動窗口機制來實現(xiàn)流量控制機制的。 ?
滑動窗口機制 ? 擁塞控制機制 ? 但這還不夠,有時候發(fā)生丟包,并不是因為發(fā)送方和接收方的處理能力問題導(dǎo)致的。而是跟網(wǎng)絡(luò)環(huán)境有關(guān),大家可以將網(wǎng)絡(luò)想象為一條公路。馬路上可能堵滿了別人家的車,只留下一輛車的空間。那就算你家有5輛車,目的地也正好有5個停車位,你也沒辦法同時全部一起上路。于是TCP希望能感知到外部的網(wǎng)絡(luò)環(huán)境,根據(jù)網(wǎng)絡(luò)環(huán)境及時調(diào)整自己的發(fā)包數(shù)量,比如馬路只夠兩輛車跑,那我就只發(fā)兩輛車。但外部環(huán)境這么復(fù)雜,TCP是怎么感知到的呢? ? TCP會先慢慢試探的發(fā)數(shù)據(jù),不斷加碼數(shù)據(jù)量,越發(fā)越多,先發(fā)一個,再發(fā)2個,4個…。直到出現(xiàn)丟包,這樣TCP就知道現(xiàn)在當(dāng)前網(wǎng)絡(luò)大概吃得消幾個包了,這既是所謂的擁塞控制機制。 ? 不少人會疑惑流量控制和擁塞控制的關(guān)系。我這里小小的總結(jié)下。流量控制針對的是單個連接數(shù)據(jù)處理能力的控制,擁塞控制針對的是整個網(wǎng)絡(luò)環(huán)境數(shù)據(jù)處理能力的控制。 ?
? 分段機制 ? 但上面提到的都是怎么降低重傳的概率,似乎重傳這個事情就是無法避免的,那如果確實發(fā)生了,有沒有辦法降低它帶來的影響呢? ? 有。當(dāng)我們需要發(fā)送一個超大的數(shù)據(jù)包時,如果這個數(shù)據(jù)包丟了,那就得重傳同樣大的數(shù)據(jù)包。但如果我能將其分成一小段一小段,那就算真丟了,那我也就只需要重傳那一小段就好了,大大減小了重傳的壓力,這就是TCP的分段機制。 ? 而這個所謂的一小段的長度,在傳輸層叫MSS(Maximum Segment Size),數(shù)據(jù)包長度大于MSS則會分成N個小于等于MSS的包。
MSS分包 ? 而在網(wǎng)絡(luò)層,如果數(shù)據(jù)包還大于MTU(Maximum Transmit Unit),那還會繼續(xù)分包。 ?
MTU分包 ? 一般情況下,MSS=MTU-40Byte,所以TCP分段后,到了IP層大概率就不會再分片了。 ?
MSS和MTU的區(qū)別 ? 亂序重排機制 ? 既然數(shù)據(jù)包會被分段,鏈路又這么復(fù)雜還會丟包,那數(shù)據(jù)包亂序也就顯得不奇怪了。比如發(fā)數(shù)據(jù)包1,2,3。1號數(shù)據(jù)包走了其他網(wǎng)絡(luò)路徑,2和3數(shù)據(jù)包先到,1數(shù)據(jù)包后到,于是數(shù)據(jù)包順序就成了2,3,1。這一點TCP也考慮到了,依靠數(shù)據(jù)包的sequence,接收方就能知道數(shù)據(jù)包的先后順序。 ? 后發(fā)的數(shù)據(jù)包先到是吧,那就先放到專門的亂序隊列中,等數(shù)據(jù)都到齊后,重新整理好亂序隊列的數(shù)據(jù)包順序后再給到用戶,這就是亂序重排機制。 ?
亂序隊列等待數(shù)據(jù)包的到來 ? 連接機制 ? 前面提到,UDP是無連接的,而TCP是面向連接的。 ? 這里提到的連接到底是啥? ? TCP通過上面提到的各種機制實現(xiàn)了數(shù)據(jù)的可靠性。這些機制背后是通過一個個數(shù)據(jù)結(jié)構(gòu)來實現(xiàn)的邏輯。而為了實現(xiàn)這套邏輯,操作系統(tǒng)內(nèi)核需要在兩端代碼里維護(hù)一套復(fù)雜的狀態(tài)機(三次握手,四次揮手,RST,closing等異常處理機制),這套狀態(tài)機其實就是所謂的"連接"。這其實就是TCP的連接機制,而UDP用不上這套狀態(tài)機,因此它是"無連接"的。 ? 網(wǎng)絡(luò)環(huán)境鏈路很長,還復(fù)雜,數(shù)據(jù)丟包是很常見的。 ? 我們平常用TCP做各種數(shù)據(jù)傳輸,完全對這些事情無感知。 ? 哪有什么歲月靜好,是TCP替你負(fù)重前行。 ? 這就是TCP三大特性"面向連接、可靠的、基于字節(jié)流"中"可靠"的含義。 ? 不信你改用UDP試試,丟包那就是真丟了,丟到你懷疑人生。 ?
用UDP就一定比用TCP快嗎?
這時候UDP就不服了:"正因為沒有這些復(fù)雜的TCP可靠性機制,所以我很快啊" ? 嗯,這也是大部分人認(rèn)為UDP比TCP快的原因。 ? 實際上大部分情況下也確實是這樣的。這話沒毛病。 ? 那問題就來了。 ? 有沒有用了UDP但卻比TCP慢的情況呢? ? 其實也有。 ? 在回答這個問題前,我需要先說下UDP的用途。 ? 實際上,大部分人也不會嘗試直接拿裸udp放到生產(chǎn)環(huán)境中去做項目。 ? 那UDP的價值在哪? ? 在我看來,UDP的存在,本質(zhì)是內(nèi)核提供的一個最小網(wǎng)絡(luò)傳輸功能。 ? 很多時候,大家雖然號稱自己用了UDP,但實際上都很忌憚它的丟包問題,所以大部分情況下都會在UDP的基礎(chǔ)上做各種不同程度的應(yīng)用層可靠性保證。比如王者農(nóng)藥用的KCP,以及最近很火的QUIC(HTTP3.0),其實都在UDP的基礎(chǔ)上做了重傳邏輯,實現(xiàn)了一套類似TCP那樣的可靠性機制。 ? 教科書上最愛提UDP適合用于音視頻傳輸,因為這些場景允許丟包。但其實也不是什么包都能丟的,比如重要的關(guān)鍵幀啥的,該重傳還得重傳。除此之外,還有一些亂序處理機制。舉個例子吧。 ? 打音視頻電話的時候,你可能遇到過丟失中間某部分信息的情況,但應(yīng)該從來沒遇到過亂序的情況吧。 ? 比如對方打網(wǎng)絡(luò)電話給你,說了:"我好想給小白來個點贊在看!" ? 這時候網(wǎng)絡(luò)信號不好,你可能會聽到"我….點贊在看"。 ? 但卻從來沒遇到過"在看小白好想贊"這樣的亂序場景吧? ? 所以說,雖然選擇了使用UDP,但一般還是會在應(yīng)用層上做一些重傳機制的。 ? 于是問題就來了,如果現(xiàn)在我需要傳一個特別大的數(shù)據(jù)包。 ? 在TCP里,它內(nèi)部會根據(jù)MSS的大小分段,這時候進(jìn)入到IP層之后,每個包大小都不會超過MTU,因此IP層一般不會再進(jìn)行分片。這時候發(fā)生丟包了,只需要重傳每個MSS分段就夠了。 ?
TCP分段 ? 但對于UDP,其本身并不會分段,如果數(shù)據(jù)過大,到了IP層,就會進(jìn)行分片。此時發(fā)生丟包的話,再次重傳,就會重傳整個大數(shù)據(jù)包。 ?
UDP不分段 ? 對于上面這種情況,使用UDP就比TCP要慢。 ? 當(dāng)然,解決起來也不復(fù)雜。這里的關(guān)鍵點在于是否實現(xiàn)了數(shù)據(jù)分段機制,使用UDP的應(yīng)用層如果也實現(xiàn)了分段機制的話,那就不會出現(xiàn)上述的問題了。 ?
總? 結(jié)
TCP為了實現(xiàn)可靠性,引入了重傳機制、流量控制、滑動窗口、擁塞控制、分段以及亂序重排機制。而UDP則沒有實現(xiàn),因此一般來說TCP比UDP慢。 ? TCP是面向連接的協(xié)議,而UDP是無連接的協(xié)議。這里的"連接"其實是,操作系統(tǒng)內(nèi)核在兩端代碼里維護(hù)的一套復(fù)雜狀態(tài)機。 ? 大部分項目,會在基于UDP的基礎(chǔ)上,模仿TCP,實現(xiàn)不同程度的可靠性機制。比如王者農(nóng)藥用的KCP其實就在基于UDP在應(yīng)用層里實現(xiàn)了一套重傳機制。 ? 對于UDP+重傳的場景,如果要傳超大數(shù)據(jù)包,并且沒有實現(xiàn)分段機制的話,那數(shù)據(jù)就會在IP層分片,一旦丟包,那就需要重傳整個超大數(shù)據(jù)包。而TCP則不需要考慮這個,內(nèi)部會自動分段,丟包重傳分段就行了。這種場景下,其實TCP更快。 ? ? ? ? ?
編輯:黃飛
?
評論
查看更多