免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
《TCP/IP協(xié)議 詳解》思考總結(jié) · 一

前言

從去年年底開始大約花了半年時(shí)間去啃《TCP/IP協(xié)議 詳解》這本書。雖然整體過了一遍,也給了我一些基礎(chǔ)能夠參與到網(wǎng)絡(luò)相關(guān)話題下的討論,但對(duì)于這樣一本全面詳細(xì)的指導(dǎo)書,我并沒有掌握的很好。拋開沒有記住的內(nèi)容,很多問題在第一次閱讀的時(shí)候會(huì)以理所當(dāng)然的態(tài)度給跳過。

最近重新拿起這本書,審視一些第一遍沒有考慮到的問題。我選擇將一些思考的問題記錄下來(lái),分節(jié)整理。當(dāng)然,要捋完整本書需要一些時(shí)間,一篇文章的篇幅也有限,這篇文章主要講述了五個(gè)問題字?jǐn)?shù)已經(jīng)過萬(wàn),所以后續(xù)我會(huì)分篇一一來(lái)總結(jié)。更新的內(nèi)容目錄會(huì)添加到《TCP/IP協(xié)議 詳解》思考總結(jié)目錄里,有興趣的朋友可以在這里搜索找到對(duì)應(yīng)的文章。

因?yàn)楸救怂接邢蓿皇且幻胀ǖ能浖_發(fā)而非專業(yè)的網(wǎng)工人員,文章難免有所缺漏。如果讀者有任何疑問,望不吝賜教。

有任何問題歡迎評(píng)論留言,我會(huì)盡力給予回答。


網(wǎng)絡(luò)分層

計(jì)算機(jī)網(wǎng)絡(luò)采用了分層結(jié)構(gòu),但是不同地方關(guān)于劃分的標(biāo)準(zhǔn)并不是完全一致。主流的模型分別是TCP/IP五層模型(在TCP/IP協(xié)議詳解中分為四層,硬件相關(guān)的數(shù)據(jù)鏈路層和物理層被合并在一起)和OSI的七層模型。兩者的差異就在于會(huì)話層和表示層的缺失上。

會(huì)話層的功能是會(huì)話控制和同步,表示層是解決兩個(gè)系統(tǒng)間交換信息的語(yǔ)法和語(yǔ)義的問題,以及數(shù)據(jù)表示轉(zhuǎn)化,加解密和壓縮解壓縮的功能。在OSI七層模型中將這兩層從廣泛意義上的應(yīng)用層里獨(dú)立出來(lái),主要的目的還是讓這兩層的邏輯代碼可以為所有的應(yīng)用程序共享,同時(shí)瘦身應(yīng)用程序。

但是現(xiàn)實(shí)的情況是這兩層在各種應(yīng)用程序當(dāng)中很難設(shè)定一個(gè)統(tǒng)一的標(biāo)準(zhǔn),每一個(gè)不同的程序關(guān)于表示和會(huì)話的需求各不相同。這意味著相關(guān)的邏輯和代碼是無(wú)法復(fù)用的,那么獨(dú)立出來(lái)也就無(wú)從談起了。這部分的邏輯最終都交給應(yīng)用開發(fā)者在應(yīng)用層決策實(shí)現(xiàn)了。

關(guān)于網(wǎng)絡(luò)分層的原因,實(shí)際大部分軟件系統(tǒng)都是分層架構(gòu),這一切都是為了工程上實(shí)現(xiàn)/調(diào)試/維護(hù)的方便。之前在一個(gè)關(guān)于為什么Linux為什么還要堅(jiān)持使用宏內(nèi)核的問題下我看到一段話非常的有意思。

因?yàn)長(zhǎng)inus可以把這些亂七八糟的東西全都一個(gè)人寫了,一遍寫對(duì)了,還能穩(wěn)定跑起來(lái)無(wú)bug,而我們這些渣渣做不到,只能依靠保護(hù)模式來(lái)防止幾百個(gè)工程師寫出來(lái)的那一坨垃圾動(dòng)不動(dòng)藍(lán)屏,自己弱卻去質(zhì)疑天才的做法,和明知自己弱還要模仿天才的做法,都是認(rèn)不清現(xiàn)實(shí)的表現(xiàn)。
==============================================================================
工程這個(gè)東西是很有意思的,我們說科學(xué)是掌握規(guī)律,技術(shù)是利用規(guī)律克服大自然的限制,而工程,卻是利用技術(shù)來(lái)克服人自身的限制。技術(shù)會(huì)告訴你,造個(gè)金字塔,把石頭壘成四棱錐就行了,如果你是個(gè)力大無(wú)窮的巨人,或者是個(gè)能意念移物的魔法師,你就啪啪啪把石頭搬過來(lái)堆起來(lái)就完事了。但我們是凡人,我們力量很小,我們很弱,所以我們需要滾木,需要滑輪,需要繩索來(lái)幫忙,做了許多額外的麻煩事情,只為了克服我們?nèi)怏w的自身限制。體力上有限制,智力上同樣有限制。軟件工程很大程度上就是解決我們?nèi)祟愔橇ι舷拗频膯栴},軟件工程師在面對(duì)不知所謂的kernel dump的時(shí)候會(huì)無(wú)助,會(huì)哭泣,在面對(duì)無(wú)休止的接口變動(dòng)的時(shí)候會(huì)歇斯底里,面對(duì)改一行代碼系統(tǒng)就全掛的窘境束手無(wú)策,所以我們需要微內(nèi)核、微服務(wù)這樣的框架來(lái)約束系統(tǒng),降低系統(tǒng)的復(fù)雜性,讓我們所有犯的錯(cuò)誤都能保持在可控的范圍內(nèi),讓因?yàn)槲覀兊挠薇慷鴮懗龅挠衎ug的代碼也能勉勉強(qiáng)強(qiáng)運(yùn)行起來(lái)而不是分分鐘crash,哪怕這些方法額外增加了許多工作量、還降低了效率。但是,總是有超人存在的,我們?nèi)艘煲粋€(gè)紀(jì)念碑,設(shè)計(jì)一堆方案,superman會(huì)說,哈?這個(gè)事情,不是只要我去把那個(gè)石頭舉起來(lái),然后飛過來(lái),放在這里不就好了嘛?體力上差距這么大的超人也許不存在,但智力上差距這么大的超人卻是存在的,所以要記住工程方法只是為了拯救我們這些凡人,對(duì)于超人來(lái)說,他們是不需要這些的,他們要做的僅僅是“搬起來(lái),放下去”而已。
可以多人開發(fā)這件事情在軟件工程上是至關(guān)重要的,如果一個(gè)工程必須每個(gè)程序員都完全理解整個(gè)工程的架構(gòu)才能著手開發(fā),那這個(gè)工程一定做不成,不說完全理解一個(gè)龐大項(xiàng)目本身不可能這個(gè)問題,大家理解也會(huì)有偏差,寫出來(lái)代碼合不到一起,這就是為什么需要有嚴(yán)格的模塊拆分,而且接口必須控制在解耦的、可以理解的、數(shù)量盡量少的范圍內(nèi),而且通過框架來(lái)避免錯(cuò)誤相互擴(kuò)散。

即使是一次看似非常簡(jiǎn)單的數(shù)據(jù)傳輸,背后實(shí)際需要做的工作也非常多。我經(jīng)常會(huì)感嘆網(wǎng)絡(luò)的強(qiáng)大,無(wú)論需要傳輸?shù)氖且欢挝淖?,一張圖片,一段視頻,又或是無(wú)論我和通信的對(duì)方相距多遠(yuǎn),數(shù)據(jù)都可以穩(wěn)當(dāng)?shù)乃瓦_(dá)給對(duì)方。如何打包處理大小不同用戶的數(shù)據(jù);如何去和對(duì)方建立連接;如何在不可靠的信道上提供可靠的數(shù)據(jù)傳輸?處理這里面繁雜的邏輯,是一個(gè)大問題。前輩們給出的解決辦法是將大問題分割成若干個(gè)小問題,交由不同的層去解決。每一層相互獨(dú)立,互不干擾,只關(guān)心自身的任務(wù);處理結(jié)束之后將結(jié)果交由下一層繼續(xù)處理。

這樣做的好處非常明顯:
  • 每一層只需要專注自己本身,而不必關(guān)心整個(gè)網(wǎng)絡(luò)的結(jié)構(gòu),這讓問題簡(jiǎn)化了不少,每一層的自由度也很高;
  • 其他層面的操作對(duì)于本層而言可以視作一個(gè)黑盒,它隱藏了具體的實(shí)現(xiàn)細(xì)節(jié),只提供了一套可供調(diào)用的API,這樣只要外部接口不變,內(nèi)部做修改對(duì)其他地方毫無(wú)影響;
  • 相關(guān)邏輯的代碼可以重復(fù)被使用,提高了開發(fā)的效率;
  • 同時(shí)也給調(diào)試提供了便捷,允許逐層排查確認(rèn)問題。
那么劃分層級(jí)的標(biāo)準(zhǔn)又是如何呢?
單個(gè)網(wǎng)絡(luò)

上圖我們可以看出應(yīng)用層的程序是用戶的一個(gè)進(jìn)程,而下層則是交由操作系統(tǒng)(內(nèi)核)處理的,盡管這不是絕對(duì)的,但大多數(shù)情況都是這樣的,我們可以簡(jiǎn)單的這樣去認(rèn)為。應(yīng)用層的程序更多的是關(guān)心業(yè)務(wù)邏輯的處理,而不是數(shù)據(jù)在網(wǎng)絡(luò)中的傳輸活動(dòng);而下層對(duì)應(yīng)用程序毫不知情,但它們需要處理所有的通信細(xì)節(jié)。

鏈路層(或者說數(shù)據(jù)鏈路層和物理層)是和硬件設(shè)備以及相關(guān)的驅(qū)動(dòng)打交道的這一部分

如果只看這張圖,運(yùn)輸層和網(wǎng)絡(luò)層之間的區(qū)別并不是那么的明顯。百度百科上給出的答案是:

  • 網(wǎng)絡(luò)層:使用權(quán)數(shù)據(jù)路由經(jīng)過大型網(wǎng)絡(luò);
  • 運(yùn)輸層:提供終端到終端的可靠連接。

這樣的說法是完全錯(cuò)誤的!因?yàn)檫@是基于一個(gè)大的前提:網(wǎng)絡(luò)層的協(xié)議是IP協(xié)議,而運(yùn)輸層的協(xié)議是TCP協(xié)議。但實(shí)際情況是在網(wǎng)絡(luò)發(fā)展初期,各種協(xié)議層出不窮,TCP/IP也只是其中非常普通的一個(gè)部分,至少在OSI模型(五層模型)落地的時(shí)候誰(shuí)也不會(huì)料到今天TCP/IP會(huì)占據(jù)主流地位。所以這并不是劃分運(yùn)輸層和網(wǎng)絡(luò)層的理由。

要理解這兩層的含義,我們需要把視野從單個(gè)網(wǎng)絡(luò)放大到一組網(wǎng)絡(luò)

組網(wǎng)絡(luò)

上圖中我們可以劃分出一個(gè)端系統(tǒng)(兩邊的主機(jī))和中間系統(tǒng)(路由器)。在這張圖中可以明確看出應(yīng)用層和運(yùn)輸層的協(xié)議都是端對(duì)端協(xié)議,也就是只有端系統(tǒng)會(huì)使用這兩層協(xié)議的內(nèi)容;而網(wǎng)絡(luò)層的協(xié)議是逐跳協(xié)議,兩個(gè)端系統(tǒng)和每一個(gè)中間系統(tǒng)都需要使用到它。

不同就在于網(wǎng)絡(luò)層服務(wù)的對(duì)象不僅僅是端系統(tǒng),也包括中間節(jié)點(diǎn)。提供服務(wù)的目的是將分組盡可能快的從源節(jié)點(diǎn)傳輸?shù)侥康墓?jié)點(diǎn),但不為此提供任何可靠性保證。如果我們略去運(yùn)輸層直接將數(shù)據(jù)交給應(yīng)用層,是否可以?當(dāng)然可以!這也是為什么我們會(huì)覺得運(yùn)輸層和網(wǎng)絡(luò)層區(qū)分不明顯的原因。但這樣的做法是非常不值得推薦的,因?yàn)閿?shù)據(jù)經(jīng)過不可靠信道傳輸之后狀態(tài)是未知的,這一部分的數(shù)據(jù)在交由應(yīng)用程序之前我們還需要進(jìn)行處理,包括但不限于數(shù)據(jù)校驗(yàn),安全性檢查,可靠性傳輸服務(wù)的提供。所以我們要在網(wǎng)絡(luò)層和應(yīng)用層之間建立一個(gè)端到端的連接,允許我們?cè)谶@之間對(duì)數(shù)據(jù)能夠有操作的空間和余地。而運(yùn)輸層就是負(fù)責(zé)建立,管理和維護(hù)這一部分的連接的

在TCP/IP協(xié)議簇當(dāng)中,IP協(xié)議負(fù)責(zé)將分組從源節(jié)點(diǎn)傳輸?shù)侥康墓?jié)點(diǎn),而TCP在上層提供了可靠性服務(wù)。但這并不是說可靠性服務(wù)一定要由運(yùn)輸層完成。和TCP對(duì)應(yīng)的UDP是以不可靠傳輸聞名天下。我們可以將UDP看作一個(gè)最原始的TCP數(shù)據(jù)包,在去除了各項(xiàng)feature之后,UDP相比TCP有了輕便,簡(jiǎn)潔的優(yōu)勢(shì)。這讓它在一些網(wǎng)絡(luò)狀況不佳、及時(shí)性要求較高的業(yè)務(wù)場(chǎng)景中有了發(fā)揮的機(jī)會(huì),TCP的三次握手以及擁塞控制在這些情況下反而變成了一種負(fù)擔(dān)。如果我們需要一些可靠傳輸?shù)男枨蠖植幌胧褂么蠖腡CP,我們可以將這一部分的邏輯移交到應(yīng)用層來(lái)實(shí)現(xiàn)。對(duì)于應(yīng)用開發(fā)者而言,可以更加自由的控制數(shù)據(jù)報(bào)傳輸?shù)倪壿嫛?strong>Ethernet Card Driver IP UDP TFTP就是一個(gè)很好的例子。


談一談可靠性是什么

在談及計(jì)算機(jī)網(wǎng)絡(luò)的時(shí)候可靠性是一個(gè)無(wú)法回避的概念。

我們經(jīng)常會(huì)將運(yùn)輸層上的TCP和udp協(xié)議進(jìn)行比較:TCP是一個(gè)可靠傳輸?shù)膮f(xié)議,而UDP則是不可靠的。在很長(zhǎng)的一段時(shí)間里,我簡(jiǎn)單的把這個(gè)可靠性理解為:UDP通信有一定的概率失敗,TCP通信是肯定會(huì)成功的。這是一個(gè)錯(cuò)誤非常明顯的理解,非常容易舉出反例:如果你的電腦是脫機(jī)狀態(tài),那么無(wú)論選擇什么樣的網(wǎng)絡(luò)協(xié)議你都無(wú)法和外界進(jìn)行通信。

那么TCP提供的可靠性傳輸究竟是什么?

第一次意識(shí)到我的錯(cuò)誤,是在開發(fā)一款藍(lán)牙產(chǎn)品的時(shí)候。多個(gè)藍(lán)牙設(shè)備自組網(wǎng)絡(luò),我通過指定Mesh ID(類似IP地址,用于指明Mesh網(wǎng)絡(luò)中具體哪一個(gè)設(shè)備)發(fā)送消息給直連的藍(lán)牙設(shè)備,來(lái)控制任一一臺(tái)Mesh網(wǎng)絡(luò)中的藍(lán)牙設(shè)備。boss測(cè)試程序的時(shí)候給我提出了一個(gè)問題,為什么我明明點(diǎn)擊了這個(gè)按鈕發(fā)送了開的命令,應(yīng)用內(nèi)也顯示打開了這個(gè)設(shè)備,但實(shí)際這個(gè)設(shè)備并沒有打開?

問題其實(shí)很簡(jiǎn)單:這個(gè)產(chǎn)品有一個(gè)缺陷,在同時(shí)發(fā)送多條消息的時(shí)候可能會(huì)造成信道擁塞,發(fā)送的消息可能會(huì)丟失沒有送達(dá)。在點(diǎn)擊打開按鈕的同時(shí)程序會(huì)發(fā)送一條消息,但是因?yàn)楫?dāng)時(shí)底層可能正在頻繁通訊導(dǎo)致這條消息沒有送達(dá),應(yīng)用內(nèi)又將設(shè)備狀態(tài)置成打開,造成了這種錯(cuò)誤。最后解決的辦法是在底層限制了通訊的速率來(lái)避免這種情況的發(fā)生。那么讀者可能會(huì)問:為什么你不能讓設(shè)備返回你一條打開成功的消息之后再改變?cè)O(shè)備在應(yīng)用內(nèi)的狀態(tài)呢?這是因?yàn)镸esh網(wǎng)絡(luò)本身承載能力有限,為此所有的消息是沒有返回的,所以我無(wú)法等收到消息然后再在回調(diào)里修改設(shè)備的狀態(tài)。這就類似UDP的數(shù)據(jù)報(bào)一樣,發(fā)出以后,發(fā)送方就不會(huì)再去關(guān)心。

這個(gè)Bug讓我第一次感受到了TCP的美好:每一條消息發(fā)送成功以后都會(huì)返回一條Ack來(lái)告知發(fā)送方已送達(dá)。我重新定義了一遍可靠性:能夠明確告知發(fā)送方發(fā)送結(jié)果。

就算沒有發(fā)送成功不會(huì)有Ack消息,我們也可以設(shè)置超時(shí)時(shí)間來(lái)認(rèn)定發(fā)送失敗

但很顯然這并不全面,TCP做的遠(yuǎn)不止這些

這一次幫助我意識(shí)到問題的是一個(gè)BugCocoaAsyncSocket UDP收發(fā)數(shù)據(jù)包大小限制,我開始重新審視TCP報(bào)文里的參數(shù)。

TCP/UDP 首部

上圖是TCP和UDP報(bào)文的格式。TCP的報(bào)文中包含了非常多的選項(xiàng)可以設(shè)置,而UDP非常的簡(jiǎn)單,除了目的地址和端口以外,只有Data Len和一個(gè)Checksum(如果為了追求極致的速度你甚至可以關(guān)閉Checksum這一選項(xiàng),這在TCP中是不可能的)。之前藍(lán)牙項(xiàng)目的消息有兩個(gè)特征:精短;每一條消息獨(dú)立互不干擾。那么數(shù)據(jù)報(bào)非常大(大到一條消息放置不下)的情況要如何處理?多個(gè)有序數(shù)據(jù)報(bào)在網(wǎng)絡(luò)中傳遞發(fā)生亂序,丟包該怎么辦?

了解TCP的朋友應(yīng)該知道TCP提供了擁塞避免,快速重傳等機(jī)制來(lái)應(yīng)對(duì)處理未知信道帶來(lái)的這些問題。當(dāng)然,這其中每一部分拿出來(lái)都可以說上很多,不在這個(gè)問題下反復(fù)糾結(jié)。但是我們應(yīng)該要明確的一點(diǎn)是,在應(yīng)用開發(fā)者看不見的地方,TCP已經(jīng)為我們解決了很多不可靠信道帶來(lái)的傳輸問題。關(guān)于可靠性我們應(yīng)該為它加上一點(diǎn):能夠自主應(yīng)對(duì)大部分?jǐn)?shù)據(jù)傳輸過程中出現(xiàn)的問題,包括但不限于丟包,亂序等等

以上我們討論的內(nèi)容其實(shí)都是圍繞消息是否送達(dá),但實(shí)際傳輸過程中我們還需要關(guān)心送達(dá)的內(nèi)容是否準(zhǔn)確

這就是為什么我們需要校驗(yàn)和的原因

在談?wù)撨@一部分之前,我們需要認(rèn)清網(wǎng)絡(luò)傳輸?shù)谋举|(zhì)是什么。無(wú)論你需要計(jì)算機(jī)傳輸?shù)氖鞘裁礃拥馁Y源文件,最后都會(huì)變成一串01000101010101...的比特流,在互聯(lián)網(wǎng)的血管里流淌?;叵胍幌旅枋鑫募笮〉膯挝?,就算一個(gè)簡(jiǎn)單的文件實(shí)際也是一串非常長(zhǎng)的數(shù)據(jù)。要想保證它在層層傳遞的過程中不出現(xiàn)差錯(cuò),這幾乎是不可能實(shí)現(xiàn)的。

這也就是為什么多層協(xié)議上都引入了數(shù)據(jù)校驗(yàn)這一部分。在數(shù)據(jù)鏈路層有FCS;網(wǎng)絡(luò)層IP協(xié)議和運(yùn)輸層的TCP/UDP都提供了Checksum。目的就是為了檢測(cè)出因?yàn)榫W(wǎng)卡軟硬件Bug、電纜不可靠、信號(hào)干擾而造成信號(hào)失真造成的數(shù)據(jù)錯(cuò)誤。

可是同樣一件事需要幾層同時(shí)去做嗎?

當(dāng)然!第一個(gè)原因在于鏈路層CRC不能完全檢測(cè)出錯(cuò)誤;第二,每一層校驗(yàn)覆蓋的數(shù)據(jù)范圍是不一樣的:IP協(xié)議Checksum只覆蓋IP首部,而傳輸層只針對(duì)自己的數(shù)據(jù)包。那么為什么設(shè)計(jì)成一次校驗(yàn)完成避免多次校驗(yàn)的浪費(fèi)?IP協(xié)議Checknum只覆蓋IP首部的原因在于IP首部的信息傳輸過程中會(huì)被多次修改的,包括TTL以及某些情況下對(duì)源地址的修改。其次,我們需要理解分而治之是網(wǎng)絡(luò)中的一個(gè)重要概念。因?yàn)槟銦o(wú)法保證執(zhí)行校驗(yàn)的上層一定需要這個(gè)校驗(yàn)的結(jié)果,即時(shí)性和可靠性是無(wú)法盡善盡美的,只能取一個(gè)平衡。最好的就是大家各自完成各自的任務(wù),就像一個(gè)可以自由組合的積木,還是那句話:分而治之。

有了校驗(yàn)可以認(rèn)為數(shù)據(jù)更加安全了嗎

這其實(shí)是把安全性和可靠性混淆在了一起。所有的校驗(yàn)操作是防君子而防不了小人,它可以檢測(cè)硬件故障、軟件bug、信號(hào)干擾、線路差、人為誤操作這些非主觀原因造成的錯(cuò)誤,但是卻無(wú)法防止別人惡意去篡改你的數(shù)據(jù)內(nèi)容。道理很簡(jiǎn)單,篡改了數(shù)據(jù)的同時(shí),可以將重新校驗(yàn)的結(jié)果覆蓋上去??煽啃蕴峁┑氖牵?strong>避免非主觀因素造成的數(shù)據(jù)錯(cuò)誤。

但是如何避免被別人攻擊篡改數(shù)據(jù)內(nèi)容,這是安全性相關(guān)的內(nèi)容,需要?jiǎng)e的機(jī)制來(lái)提供相關(guān)的服務(wù)。以我們熟悉的TCP舉例說明,它提供的是一個(gè)可靠但不安全的傳輸服務(wù)。


談一談分片

鏈路層對(duì)于數(shù)據(jù)幀的長(zhǎng)度都有一個(gè)限制,我們稱之為MTU。如果需要傳輸?shù)臄?shù)據(jù)報(bào)長(zhǎng)度大于鏈路層的MTU,那么我們就需要將數(shù)據(jù)報(bào)分成若干長(zhǎng)度小于MTU的數(shù)據(jù)報(bào),這個(gè)過程叫做分片。因?yàn)閿?shù)據(jù)報(bào)在發(fā)送的過程中需要經(jīng)過不同的網(wǎng)絡(luò),鏈路層的MTU不盡相同,所以分片不僅發(fā)生在源主機(jī)端,也可能會(huì)發(fā)生在中間路由上。

上述所說的中間路由發(fā)生分片,是IPV4的環(huán)境下;在IPV6中只在源端分片重復(fù),如果數(shù)據(jù)報(bào)長(zhǎng)度大于中間路由的MTU,路由會(huì)直接返回一條ICMPv6 too big 給源主機(jī)端。這一部分我們后面會(huì)再說到

我們首先要明確一點(diǎn)為什么要把分片放在IP層

發(fā)展至今,IP協(xié)議幾乎和網(wǎng)絡(luò)層劃上了等號(hào)。雖然網(wǎng)絡(luò)層仍有其他協(xié)議的存在,但是可以說能走到最終用戶面前的協(xié)議,網(wǎng)絡(luò)層都是選擇IP協(xié)議。所以分片操作放在IP協(xié)議上,可以為所有的上層協(xié)議服務(wù),而不必再依次去運(yùn)輸層的協(xié)議中實(shí)現(xiàn),相關(guān)的邏輯代碼復(fù)用效率很高。在這里為什么我們不再像討論Check num那樣去討論分而治之,原因在于分片的根源是MTU的限制,任何一種協(xié)議都無(wú)法避免大數(shù)據(jù)報(bào)傳輸過程中的分片。協(xié)議可以決策自己是否實(shí)現(xiàn)這部分邏輯,但必須保證這部分的邏輯一定要實(shí)現(xiàn)。而IP協(xié)議就是這一個(gè)必須實(shí)現(xiàn)的保障。

另外,IP協(xié)議比起上層協(xié)議還有一個(gè)很大的優(yōu)勢(shì)就在于它更貼近鏈路層,這讓IP協(xié)議可以感知到底層的MTU。而上層協(xié)議既不關(guān)心,也無(wú)法直接關(guān)心到底層MTU。

注意這里說的是直接關(guān)心,也就是說上層協(xié)議如果想要關(guān)心MTU,可以去設(shè)計(jì)獲得,但這個(gè)信息并不是必要的。

總結(jié)來(lái)說,IP層實(shí)現(xiàn)分片是一個(gè)成本最低的選項(xiàng)。因?yàn)槲锢韺尤プ龇制?,需要和硬件打交道,如果處理的?fù)雜或者不符合大眾標(biāo)準(zhǔn)很可能就是路越走越窄最后把自己走死了;而上層處理和我之前說的一樣,運(yùn)輸層去各自實(shí)現(xiàn)成本很高,應(yīng)用層自己去做對(duì)于應(yīng)用開發(fā)者來(lái)說非常痛苦。

作為一名應(yīng)用開發(fā)者,我承認(rèn)分片是一件簡(jiǎn)單但是折磨人的工作。在實(shí)現(xiàn)藍(lán)牙OTA升級(jí)功能的時(shí)候,我需要將升級(jí)包.bin文件拆開依次發(fā)送給設(shè)備,中間需要處理分片,對(duì)齊,填充等等工作。這是一份不難但是非??简?yàn)?zāi)托牡幕睢_@只是簡(jiǎn)單的點(diǎn)對(duì)點(diǎn)傳輸,如果情況復(fù)雜,需要處理的工作就會(huì)更多。要上層各自去實(shí)現(xiàn)分片,實(shí)在不是一個(gè)很好的選擇。

分包真的只有IP協(xié)議去實(shí)現(xiàn)了嗎

并不盡然。雖然我們說IP協(xié)議去做分片是一個(gè)成本較低的選擇,但實(shí)際還是有其他協(xié)議做了類似的操作。鏈路層中有類似Atm協(xié)議;在TCP協(xié)議中,MSS(Maximum Segment Size,最大報(bào)文長(zhǎng)度)實(shí)際就是一個(gè)分片機(jī)制。

MSS

鏈路層協(xié)議作者并不熟悉,實(shí)際也并未接觸。我們后續(xù)討論以TCP為對(duì)象。

TCP協(xié)議中的MSS和分片有一些簡(jiǎn)單的不同:分片是因?yàn)閿?shù)據(jù)報(bào)長(zhǎng)度大于MTU所以被動(dòng)去分割數(shù)據(jù)報(bào);而MSS是三次握手過程中雙方協(xié)商的結(jié)果,提前分割數(shù)據(jù)避免分片發(fā)送。TCP之所以提供這樣一個(gè)option來(lái)避免IP層分片,相信有部分原因在于IP分片并不如我們所假設(shè)的那么完美。

我們之前提到IP協(xié)議去做分片確實(shí)復(fù)用率非常高,成本非常低,但實(shí)際情況中這些操作給負(fù)責(zé)分片和重組的主機(jī)和路由的cpu帶來(lái)了非常大的壓力。負(fù)責(zé)分組的終端的IP層需要去拆分?jǐn)?shù)據(jù)報(bào),用ID值相同的IP Fragmented Packet將數(shù)據(jù)發(fā)送出去;而重組終端的IP協(xié)議IP層根據(jù)ID,MF位,F(xiàn)ragment Offset信息進(jìn)行重組,得到完整數(shù)據(jù)提交給上層。需要著重強(qiáng)調(diào)的是我們無(wú)法保證分片后的數(shù)據(jù)按照順序依次到達(dá)目的終端,并且IP層沒有類似超時(shí)重傳的可靠性支持,其中任一一個(gè)分片數(shù)據(jù)丟失都會(huì)引起整個(gè)數(shù)據(jù)報(bào)的丟失!?。。?/strong>

亂序和丟包的問題確實(shí)非常的麻煩,這里的麻煩更多的在于出現(xiàn)問題需要解決的成本過高。類似UDP只依靠IP協(xié)議分片處理大數(shù)據(jù)報(bào)的,實(shí)際是非常不推薦的。

如果上層提前分割了數(shù)據(jù),IP層只要為每一個(gè)數(shù)據(jù)報(bào)加上IP頭發(fā)出即可。每一個(gè)數(shù)據(jù)相互獨(dú)立,由上層提供了可靠性支持。相比IP層分片的苦苦掙扎,這樣做確實(shí)簡(jiǎn)單了很多。

但是這并不能完全的避免分片的發(fā)生。如RFC 879所說:TCP provides an option that may be used at the time a connection is established (only) to indicate the maximum size TCP segment that can be accepted on that connection.。這是一個(gè)在三次握手過程中協(xié)商的結(jié)果,不保證通訊過程中一定不會(huì)發(fā)生變化。

那么是否還有其他的方式來(lái)避免分片

Path MTU Discovery(傳輸路徑MTU發(fā)現(xiàn))就是為此服務(wù)的。

在這一段的開始我們提到分片也可以發(fā)生在中間路由。舉一個(gè)簡(jiǎn)單的例子:我們從源主機(jī)發(fā)生了一個(gè)IP包 = 1500,在互聯(lián)網(wǎng)上逐跳傳遞,在中間的某臺(tái)路由發(fā)送出去的時(shí)候,因?yàn)樵摻涌贛TU只有1000 < 1500,那么這個(gè)包就被分成兩個(gè)IP分片發(fā)出去了。但是作為源主機(jī)對(duì)此毫不知情的,后續(xù)它仍然會(huì)按1500的大小發(fā)送IP包,那么每一個(gè)大于1000的IP包都會(huì)被分片。

為此我們可以在IP頭中設(shè)置DF(Don‘t Fragement)為1來(lái)讓中間路由不要分片,如果IP包的大小大于中間路由的mtu,那么直接丟棄并通過ICMP告知源主機(jī)。源主機(jī)再根據(jù)ICMP中提供的信息修改IP包的大小。

DF=1

如果后續(xù)還碰到更小的MTU怎么辦?旁友,你聽過遞歸不咯?

在ICMP發(fā)送至源主機(jī)的過程中,可能會(huì)被攔截或者丟失,那么這個(gè)IP包(包括后續(xù)大小超過中間路由MTU的IP包)就相當(dāng)于靜悄悄的被丟棄了,TCP連接會(huì)被中斷。為此要么修改配置允許相關(guān)type的ICMP包的通過,要么關(guān)閉PMTUD,畢竟允許分片的話雙方還是可以正常通信的。

IPv6中有關(guān)分片和IPv4有什么區(qū)別嗎

IPv6中只會(huì)在源端和目的端分片重組,拒絕中間節(jié)點(diǎn)來(lái)進(jìn)行分片。如果數(shù)據(jù)報(bào)大小小于中間節(jié)點(diǎn)的MTU,那么中間節(jié)點(diǎn)會(huì)以ICMPv6 type=2的消息來(lái)告訴源端這個(gè)情況。這個(gè)操作看起來(lái)就像IPv4中DF=1一樣。據(jù)此我們可以認(rèn)為:避免分片是一種共識(shí)。

根據(jù)Wiki - IPv6 packet中有關(guān)IPv6的描述,端節(jié)點(diǎn)負(fù)責(zé)PMTUD來(lái)查詢?cè)试S發(fā)送數(shù)據(jù)報(bào)的最大長(zhǎng)度,讓上層協(xié)議來(lái)限制Paylod的大小。如果上層協(xié)議不支持,那么發(fā)送長(zhǎng)度不大于1280的數(shù)據(jù)報(bào)來(lái)避免分片。

IPv6要求鏈路層的最小MTU是1280


TCP為什么需要三次握手

在思考為什么握手需要三次這個(gè)問題之前,我們應(yīng)該先考慮的是

為什么TCP的建立需要握手。

TCP是一種可靠傳輸控制協(xié)議,它的主要任務(wù)是在可靠傳輸數(shù)據(jù)的基礎(chǔ)之上,盡可能的提高傳輸?shù)男?/strong>。但是問題在于傳輸?shù)男诺啦⒉豢煽?,我們面?duì)的是一個(gè)未知的網(wǎng)絡(luò)環(huán)境,無(wú)法確定信道的可靠性。假如說信道百分百的可靠,那么完全不需要握手的過程,我們只需要按照UDP的方式簡(jiǎn)單的將消息發(fā)出即可,因?yàn)槲覀冎罒o(wú)論何時(shí)何地我們只要發(fā)出消息對(duì)方一定能夠收到。但是在廣域網(wǎng)中這種完美的情況幾乎不存在,為了解決在不可靠的信道上完成可靠傳輸這樣一個(gè)問題,那么我們必須要在雙方傳輸數(shù)據(jù)之前,就某些問題達(dá)成一致?;氐絋CP中來(lái)解釋也就是通信雙方約定起始的Seq。

為什么需要的次數(shù)是三次,而不是兩次、四次或者更多?

首先我們先明確一點(diǎn),在雙方通信的過程當(dāng)中,一條消息單方面的確認(rèn)需要兩次通信,也就是一次單向握手的過程。過程如下

單向握手

當(dāng)消息已收到傳遞到A之后,那么作為A這一方就可以確定消息已經(jīng)被B接收到了。但是這個(gè)時(shí)候作為B,它成功收到了A發(fā)來(lái)的消息但是它并不知道消息已送達(dá)這條消息是否成功抵達(dá)A處。

回到TCP中來(lái)看,建立連接的開始需要握手:我們需要驗(yàn)證通信雙方之間的信道是否通暢,雖然中間只有一條信道,但是這條信道是雙向的(非常簡(jiǎn)單的例子,一條馬路允許雙向的通行),我們需要同時(shí)驗(yàn)證A->B以及B->A都是通暢的,雙方各有一個(gè)Seq需要和對(duì)方確認(rèn)。

那么也就是說兩次握手肯定是沒有辦法實(shí)現(xiàn)連接建立前雙方協(xié)商在某些方面達(dá)成一致的需求,因?yàn)檫@只能滿足一方確認(rèn)消息,另一方無(wú)法確認(rèn)。如果需要雙方都確認(rèn)某一條消息,那么必然需要兩次單向握手的過程。從TCP的角度來(lái)解釋,發(fā)送方SYN ACK之后,響應(yīng)方要初始化信道必須也要一次SYN ACK。

那么三次握手是如何來(lái)的呢。出于優(yōu)化的目的,響應(yīng)方將對(duì)發(fā)送方SYN的ACK和自身的SYN合并成了一條。所以說三次握手這個(gè)說法雖然貼切的描述了握手的過程,但并不準(zhǔn)確。事實(shí)其實(shí)是雙方各一次的握手,各一次的確認(rèn),只不過其中一次握手和確認(rèn)合并在一起。因?yàn)檫@樣一個(gè)合并導(dǎo)致雙向握手 雙向確認(rèn)的過程變成了三次握手。

四次揮手為什么不優(yōu)化成三次揮手?

理由也非常的簡(jiǎn)單:通信是雙向的,如果響應(yīng)方接受了發(fā)送方的連接請(qǐng)求,那么必然也要發(fā)起一個(gè)單向握手來(lái)確認(rèn)和發(fā)送方的連接;但是連接的關(guān)閉是允許半關(guān)閉的!任何一方都可以拆除自己發(fā)往對(duì)方數(shù)據(jù)的那個(gè)通道,而同時(shí)還要保證對(duì)方發(fā)往自己的數(shù)據(jù)能被正常處理。也就是說拆除階段的第二次單向握手并不一定是要在第一次單線握手之后立馬執(zhí)行的,那么中間兩次FIN ACK的合并也就無(wú)從談起了。


為什么應(yīng)用層還需要設(shè)計(jì)心跳

當(dāng)TCP連接建立成功以后,它可以長(zhǎng)時(shí)間處于空閑狀態(tài)沒有任何數(shù)據(jù)的交流。這個(gè)時(shí)間短則幾分鐘,長(zhǎng)則幾個(gè)小時(shí),幾天。這段時(shí)間里只要雙方的主機(jī)沒有重啟進(jìn)程依然存在,無(wú)論中間網(wǎng)絡(luò)發(fā)生什么樣的情況連接都不會(huì)被中斷。

這樣一個(gè)說法似乎很難被理解,因?yàn)檫@和我們生活接觸的實(shí)際例子不太一樣。比如水管斷開會(huì)漏水,電線斷開會(huì)停電。而TCP連接實(shí)質(zhì)是通信雙方的主機(jī)保持的一個(gè)狀態(tài),中間的連接是一個(gè)虛構(gòu)的存在,這樣一個(gè)鏈接無(wú)論是發(fā)生波動(dòng)抑或某一端異常斷開,通信的雙方是沒有辦法感知的。

上面所說的情況在實(shí)際情況中經(jīng)常會(huì)發(fā)生。我們假設(shè)通信的雙方是服務(wù)器-客戶端,提供服務(wù)的一端我們認(rèn)為是服務(wù)器,服務(wù)器通常會(huì)一直保持運(yùn)行狀態(tài)并同時(shí)為多個(gè)客戶端服務(wù);發(fā)出請(qǐng)求的一方是客戶端。通常來(lái)說客戶端關(guān)閉程序或是手動(dòng)關(guān)機(jī)的時(shí)候系統(tǒng)會(huì)為我們主動(dòng)斷開連接,發(fā)送一個(gè)FIN包給服務(wù)器。但是如果客戶端突然崩潰或客戶直接強(qiáng)制關(guān)閉了計(jì)算機(jī),這個(gè)時(shí)候系統(tǒng)來(lái)不及發(fā)送FIN包,就會(huì)留下一個(gè)半開放連接在這里。如果服務(wù)器再次向這個(gè)已經(jīng)非正常關(guān)閉的客戶端發(fā)送消息,那么它會(huì)收到一個(gè)RST的回復(fù)。但是如果恰巧服務(wù)器正處在等待客戶端回應(yīng)的狀態(tài),那么它會(huì)一直等待下去

這樣的情況顯然是不可以接受的。因?yàn)榉?wù)器打開一個(gè)鏈接的同時(shí),會(huì)以一個(gè)客戶的身份占用著某一部分的資源,這樣的一個(gè)半開放鏈接會(huì)導(dǎo)致這一部分的資源永遠(yuǎn)得不到釋放。為了應(yīng)對(duì)解決這一個(gè)問題,TCP設(shè)計(jì)了?;罟δ埽簿褪荢O_KEEPALIVE選項(xiàng)。

TCP的心跳包是一個(gè)有爭(zhēng)議的選項(xiàng),這只是一個(gè)option,而不是一個(gè)必須實(shí)現(xiàn)的標(biāo)準(zhǔn)。通常情況下我們是在服務(wù)器打開這個(gè)選項(xiàng),客戶端被動(dòng)響應(yīng),默認(rèn)間隔時(shí)間7200s。但這不是絕對(duì)的,如果有需求客戶端同樣可以打開。之所以在服務(wù)器設(shè)置這個(gè)選項(xiàng)是因?yàn)樗枰L(zhǎng)時(shí)間保持在工作狀態(tài),并且同時(shí)為多個(gè)客戶端服務(wù),而客戶端作為個(gè)人使用會(huì)經(jīng)常(異常)關(guān)閉。檢測(cè)出半開放的連接并刪除它,釋放資源對(duì)于服務(wù)器是非常必要的。

NFS雙方都打開了SO_KEEPALIVE,而Telnet和Rlogin則只有服務(wù)器打開了這個(gè)選項(xiàng)。

TCP?;顧C(jī)制的缺陷

?;顧C(jī)制是非常有必要的,但關(guān)于是否應(yīng)該在TCP中提供一直爭(zhēng)論不休。在Host Requirement中提供了3個(gè)不使用保活定時(shí)器的理由

  • 在出現(xiàn)短暫差錯(cuò)的情況下,可能會(huì)使一個(gè)運(yùn)行正常的連接釋放掉。(比如中間路由崩潰并重新啟動(dòng)時(shí)會(huì)發(fā)送一個(gè)保活探查,會(huì)讓TCP誤認(rèn)客戶端已經(jīng)崩潰)
  • 耗費(fèi)不必要的帶寬
  • 在按分組計(jì)費(fèi)的情況下會(huì)在互聯(lián)網(wǎng)下花費(fèi)更多的金錢

拋開歷史的包袱來(lái)看,帶寬和計(jì)費(fèi)相關(guān)的問題已經(jīng)不再需要我們擔(dān)心,但TCP的?;顧C(jī)制仍然是一個(gè)不被推薦的選項(xiàng)。

首先我們需要明確的是,TCP的?;?strong>只能夠檢測(cè)連接是否存活,但是否可用是未知的。做一個(gè)簡(jiǎn)單的比方,TCP的保活就像水道工,它只關(guān)心水管是否暢通能否通水,但水廠能否供水它無(wú)法保證?;氐絋CP上來(lái)解釋就是進(jìn)程可能死鎖或者擁塞,操作系統(tǒng)是正常收發(fā)TCP消息的,但服務(wù)端繁忙無(wú)法提供服務(wù)。

其次,默認(rèn)檢測(cè)的時(shí)間是7200s=120min=2h。這個(gè)間隔實(shí)際上是非常長(zhǎng)的,這么遲緩的響應(yīng)能力在大部分的應(yīng)用場(chǎng)景下是無(wú)法接受的。當(dāng)然我們可以手動(dòng)去修改SO_KEEPLIVE選項(xiàng)的參數(shù),但這是系統(tǒng)級(jí)的變量,修改意味著會(huì)影響所有運(yùn)行的程序。

有朋友提到這個(gè)參數(shù)在socket中可以為pre socket單獨(dú)設(shè)置,類似Buffer。

除此之外,還有一個(gè)比較棘手的問題是keep alive的數(shù)據(jù)包有可能會(huì)被運(yùn)營(yíng)商攔截。如果僅僅依賴TCP的?;顧C(jī)制,那么在這種情況服務(wù)器可能會(huì)釋放掉一個(gè)運(yùn)行正常的連接。

總結(jié)

考慮到以上的問題,很多上層的協(xié)議都提供了心跳機(jī)制來(lái)維持連接(比如MQTT中的PINGREQPINGRESP)。這并不是一種重復(fù)設(shè)計(jì)或者浪費(fèi)!我們必須要明確的是TCP作為運(yùn)輸層的協(xié)議,它提供的是一個(gè)host級(jí)別的可靠傳輸服務(wù),TCP所有任務(wù)實(shí)際的本質(zhì)就是傳輸(就像我們提到的只是檢測(cè)連接是否存活)。如果在業(yè)務(wù)場(chǎng)景中有需要心跳機(jī)制來(lái)處理的邏輯,這部分的實(shí)現(xiàn)應(yīng)該交由應(yīng)用層來(lái)完成。作為應(yīng)用層的開發(fā)人員,應(yīng)當(dāng)把TCP簡(jiǎn)單看成一個(gè)負(fù)責(zé)網(wǎng)絡(luò)傳輸?shù)耐獠緼PI,雖然它被廣泛應(yīng)用但并不完美可靠,應(yīng)用層關(guān)于自身差錯(cuò)糾錯(cuò)的邏輯是不應(yīng)該被省略的。


本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服