www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > 小林coding
[導(dǎo)讀]大家好,我是小林。最近有朋友跟我說,他在看面經(jīng)的時候,到哪都有我的影子。這個挺讓我意外的,沒想到我的圖解網(wǎng)絡(luò)和圖解系統(tǒng)已經(jīng)慢慢傳開了,形成一定的口碑了。不知道有多少讀者是通過別人文章認(rèn)識我的,哈哈。這個事情就說到了,剎個車。上周有個讀者問我,這么個問題:TCP是面向字節(jié)流的協(xié)議,...

大家好,我是小林。最近有朋友跟我說,他在看面經(jīng)的時候,到哪都有我的影子。

這個挺讓我意外的,沒想到我的圖解網(wǎng)絡(luò)和圖解系統(tǒng)已經(jīng)慢慢傳開了,形成一定的口碑了。不知道有多少讀者是通過別人文章認(rèn)識我的,哈哈。這個事情就說到了,剎個車。

上周有個讀者問我,這么個問題:

TCP 是面向字節(jié)流的協(xié)議,UDP 是面向報文的協(xié)議?這里的「面向字節(jié)流」和「面向報文」該如何理解。

這是個好問題,剛好我的《圖解網(wǎng)絡(luò)》系列里沒有說過這個事情,這次就來補一補。

如何理解字節(jié)流?

之所以會說 TCP 是面向字節(jié)流的協(xié)議,UDP 是面向報文的協(xié)議,是因為操作系統(tǒng)對 TCP 和 UDP 協(xié)議的發(fā)送方的機制不同,也就是問題原因在發(fā)送方。

先來說說為什么 UDP 是面向報文的協(xié)議?

當(dāng)用戶消息通過 UDP 協(xié)議傳輸時,操作系統(tǒng)不會對消息進(jìn)行拆分,在組裝好 UDP 頭部后就交給網(wǎng)絡(luò)層來處理,所以發(fā)出去的 UDP 報文中的數(shù)據(jù)部分就是完整的用戶消息,也就是每個 UDP 報文就是一個用戶消息的邊界,這樣接收方在接收到 UDP 報文后,讀一個 UDP 報文就能讀取到完整的用戶消息。

你可能會問,如果收到了兩個 UDP 報文,操作系統(tǒng)是怎么區(qū)分開的?

操作系統(tǒng)在收到 UDP 報文后,會將其插入到隊列里,隊列里的每一個元素就是一個 UDP 報文,這樣當(dāng)用戶調(diào)用 recvfrom() 系統(tǒng)調(diào)用讀數(shù)據(jù)的時候,就會從隊列里取出一個數(shù)據(jù),然后從內(nèi)核里拷貝給用戶緩沖區(qū)。


再來說說為什么 TCP 是面向字節(jié)流的協(xié)議?

當(dāng)用戶消息通過 TCP 協(xié)議傳輸時,消息可能會被操作系統(tǒng)分組成多個的 TCP 報文,也就是一個完整的用戶消息被拆分成多個 TCP 報文進(jìn)行傳輸。

這時,接收方的程序如果不知道發(fā)送方發(fā)送的消息的長度,也就是不知道消息的邊界時,是無法讀出一個有效的用戶消息的,因為用戶消息被拆分成多個 TCP 報文后,并不能像 UDP 那樣,一個 UDP 報文就能代表一個完整的用戶消息。

舉個實際的例子來說明。

發(fā)送方準(zhǔn)備發(fā)送 「Hi.」和「I am Xiaolin」這兩個消息。

在發(fā)送端,當(dāng)我們調(diào)用 send 函數(shù)完成數(shù)據(jù)“發(fā)送”以后,數(shù)據(jù)并沒有被真正從網(wǎng)絡(luò)上發(fā)送出去,只是從應(yīng)用程序拷貝到了操作系統(tǒng)內(nèi)核協(xié)議棧中。

至于什么時候真正被發(fā)送,取決于發(fā)送窗口、擁塞窗口以及當(dāng)前發(fā)送緩沖區(qū)的大小等條件。也就是說,我們不能認(rèn)為每次 send 調(diào)用發(fā)送的數(shù)據(jù),都會作為一個整體完整地消息被發(fā)送出去。

如果我們考慮實際網(wǎng)絡(luò)傳輸過程中的各種影響,假設(shè)發(fā)送端陸續(xù)調(diào)用 send 函數(shù)先后發(fā)送 「Hi.」和「I am Xiaolin」 報文,那么實際的發(fā)送很有可能是這幾種情況。

第一種情況,這兩個消息被分到同一個 TCP 報文,像這樣:

第二種情況,「I am Xiaolin」的部分隨 「Hi」 在一個 TCP 報文中發(fā)送出去,像這樣:

第三種情況,「Hi.」 的一部分隨 TCP 報文被發(fā)送出去,另一部分和 「I am Xiaolin」 一起隨另一個 TCP 報文發(fā)送出去,像這樣。

類似的情況還能舉例很多種,這里主要是想說明,我們不知道 「Hi.」和 「I am Xiaolin」 這兩個用戶消息是如何進(jìn)行 TCP 分組傳輸?shù)摹?/p>因此,我們不能認(rèn)為一個用戶消息對應(yīng)一個 TCP 報文,正因為這樣,所以 TCP 是面向字節(jié)流的協(xié)議。

當(dāng)兩個消息的某個部分內(nèi)容被分到同一個 TCP 報文時,就是我們常說的 TCP 粘包問題,這時接收方不知道消息的邊界的話,是無法讀出有效的消息。

要解決這個問題,要交給應(yīng)用程序

如何解決粘包?

粘包的問題出現(xiàn)是因為不知道一個用戶消息的邊界在哪,如果知道了邊界在哪,接收方就可以通過邊界來劃分出有效的用戶消息。

一般有三種方式分包的方式:

  • 固定長度的消息;

  • 特殊字符作為邊界;

  • 自定義消息結(jié)構(gòu)。

固定長度的消息
這種是最簡單方法,即每個用戶消息都是固定長度的,比如規(guī)定一個消息的長度是 64 個字節(jié),當(dāng)接收方接滿 64 個字節(jié),就認(rèn)為這個內(nèi)容是一個完整且有效的消息。

但是這種方式靈活性不高,實際中很少用。

特殊字符作為邊界

我們可以在兩個用戶消息之間插入一個特殊的字符串,這樣接收方在接收數(shù)據(jù)時,讀到了這個特殊字符,就把認(rèn)為已經(jīng)讀完一個完整的消息。

HTTP 是一個非常好的例子。

HTTP 通過設(shè)置回車符、換行符作為 HTTP 報文協(xié)議的邊界。

有一點要注意,這個作為邊界點的特殊字符,如果剛好消息內(nèi)容里有這個特殊字符,我們要對這個字符轉(zhuǎn)義,避免被接收方當(dāng)作消息的邊界點而解析到無效的數(shù)據(jù)。

自定義消息結(jié)構(gòu)

我們可以自定義一個消息結(jié)構(gòu),由包頭和數(shù)據(jù)組成,其中包頭包是固定大小的,而且包頭里有一個字段來說明緊隨其后的數(shù)據(jù)有多大。

比如這個消息結(jié)構(gòu)體,首先 4 個字節(jié)大小的變量來表示數(shù)據(jù)長度,真正的數(shù)據(jù)則在后面。

struct?{?
????u_int32_t?message_length;?
????char?message_data[];?
}?message;
當(dāng)接收方接收到包頭的大?。ū热?4 個字節(jié))后,就解析包頭的內(nèi)容,于是就可以知道數(shù)據(jù)的長度,然后接下來就繼續(xù)讀取數(shù)據(jù),直到讀滿數(shù)據(jù)的長度,就可以組裝成一個完整到用戶消息來處理了。

沒想到吧,又是深夜技術(shù)文

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉