91视频免费?看_蜜芽MY188精品TV在线观看_国产免费无遮挡在线观看视频_深夜国产_亚洲精品欧洲精品_欧美黑人粗暴多交

全面講解傳輸層協(xié)議 | UDP和TCP

Hello,大家好!我是木榮。今天給大家分享一份網(wǎng)絡(luò)中傳輸層UDP和TCP相關(guān)的知識(shí),在實(shí)際的工作中,網(wǎng)絡(luò)編程是十分重要的一個(gè)知識(shí)點(diǎn),尤其是TCP和UDP通信,也是我們編程中打交道最多的。本文非常詳細(xì)地講解了UDP和TCP通信中各個(gè)知識(shí)點(diǎn),希望對(duì)小伙伴們有幫助!

一. 應(yīng)用層和傳輸層的聯(lián)系

程序員在應(yīng)用層要做的事情

在網(wǎng)絡(luò)通信 的過(guò)程中, 應(yīng)用層描述了應(yīng)用程序如何理解和使用網(wǎng)絡(luò)中的通信數(shù)據(jù), 和程序員打交道最多的就是應(yīng)用層了, 針對(duì)不同的業(yè)務(wù)場(chǎng)景, 很多時(shí)候程序員需要去自定義應(yīng)用層協(xié)議, 自定義協(xié)議主要需要完成下面的兩件事情:

  1. 結(jié)合業(yè)務(wù)場(chǎng)景和需求, 分析清楚, 客戶端和服務(wù)器之間(請(qǐng)求/響應(yīng))要傳遞哪些信息.

  2. 明確傳遞的信息要以什么樣的格式來(lái)組織, 可以是普通文本的方式, 也可以一些廣泛使用的數(shù)據(jù)格式, 比如: XML, json, protobuffer.

其中xml和json都是按照文本的方式來(lái)組織的, 優(yōu)點(diǎn)是可讀性好, 用戶不需要借助其他工具, 肉眼就能看懂?dāng)?shù)據(jù)的含義, 缺點(diǎn)是要額外傳很多的標(biāo)簽或鍵名, 占用較多的網(wǎng)絡(luò)帶寬, 影響效率; 而 protobuffer會(huì)將文本數(shù)據(jù)壓縮 為二進(jìn)制數(shù)據(jù)傳輸, 特點(diǎn)是肉眼無(wú)法解析, 但占用空間更小小, 傳輸占用的帶寬也就降低了.

應(yīng)用層也有知名并廣泛使用的成品協(xié)議, 就比如 : HTTP協(xié)議.

傳輸層和應(yīng)用層的聯(lián)系

除了最上層的應(yīng)用層, 下面的傳輸層, 網(wǎng)絡(luò)層, 數(shù)據(jù)鏈路層, 物理這四層都是已經(jīng)在系統(tǒng)內(nèi)核/驅(qū)動(dòng)程序/硬件中已經(jīng)實(shí)現(xiàn)好了, 不許要我們?nèi)?shí)現(xiàn), 傳輸層是緊接著應(yīng)用層的一層, 雖然傳輸層是操作系統(tǒng)內(nèi)核實(shí)現(xiàn)好了, 但是我們?cè)趯?xiě)應(yīng)用層代碼的時(shí)候, 是要調(diào)用系統(tǒng)的socket API去完成網(wǎng)絡(luò)編程, 所以需要我們了解這里傳輸層的一些關(guān)鍵協(xié)議UDP和TCP.

端口號(hào)的使用注意

端口號(hào)是傳輸層協(xié)議的概念, TCP和UDP協(xié)議的報(bào)頭中都會(huì)包含源端口和目的端口, 并且都是使用 2 個(gè)字節(jié), 16bit來(lái)表示端口號(hào), 范圍也就是 0 -> 65535; 但是我們?nèi)粘?xiě)的程序使用的端口號(hào)一般都是從 1024 開(kāi)始的, 因?yàn)? -> 1023這個(gè)范圍的端口號(hào)也稱為 “知名端口號(hào)/具名端口號(hào)”, 這些端口號(hào)系統(tǒng)已經(jīng)分配給了一些知名并廣泛使用的應(yīng)用程序.

這里我們并不是完全不能使用0 -> 1023這個(gè)范圍的端口號(hào), 只是建議使用, 雖然這些端口被分配給了特定程序, 但是這些程序是否在主機(jī)運(yùn)行著, 主機(jī)上是否安裝了這些程序都是不一定的, 要使用0 -> 1023這些端口, 需要注意 2 點(diǎn) :

  1. 要確定這個(gè)端口沒(méi)有和程序綁在一起.
  2. 要擁有管理員權(quán)限.

二. UDP協(xié)議

UDP是User Datagram Protocol的縮寫(xiě), UDP的特點(diǎn)是無(wú)連接, 不可靠傳輸, 面向數(shù)據(jù)報(bào), 全雙工, UDP使用起來(lái)簡(jiǎn)單高效, 但它的數(shù)據(jù)載荷較小, 一般適用于以下場(chǎng)景:

  • 包總量較少的通信(DNS、SNMP等)
  • 視頻、音頻等多媒體通信(即時(shí)通信)
  • 限定于LAN等特定網(wǎng)絡(luò)中的應(yīng)用通信
  • 廣播通信(廣播、多播)

UDP協(xié)議的格式 :

上面是教科書(shū)中的畫(huà)法, 這樣畫(huà)是為了排版方便, 實(shí)際上畫(huà)成下面這樣更合理一些, 不過(guò)這并不影響理解.

UDP會(huì)把從應(yīng)用層拿到數(shù)據(jù)(就是網(wǎng)絡(luò)編程中應(yīng)用層調(diào)用socket API, send()發(fā)送的數(shù)據(jù))的基礎(chǔ)上再前面拼裝上 8 個(gè)字節(jié)的報(bào)頭.

下面就來(lái)分析一下報(bào)頭中所包含的這些屬性, 首先UDP報(bào)頭一共是 8 個(gè)字節(jié), 有 4 個(gè)部分分別占 2 個(gè)字節(jié), 源端口號(hào)和目的端口號(hào)不必多說(shuō), 要完成一次網(wǎng)絡(luò)通信, 涉及到源IP, 源端口, 目的IP, 目的端口, 協(xié)議類型這五元組, 其中端口信息是是由傳輸層負(fù)責(zé)的.

UDP的包長(zhǎng)度, 也叫報(bào)文長(zhǎng)度(報(bào)頭+載荷), 這個(gè)屬性表示了一個(gè)UDP數(shù)據(jù)報(bào)的大小, 單位為字節(jié), 2字節(jié)表示 0->65535這個(gè)范圍, 也就是說(shuō)一個(gè)UDP數(shù)據(jù)報(bào)最大不超過(guò)64KB.

那如果應(yīng)用層的數(shù)據(jù)報(bào)大于64KB怎么辦呢?

這里有兩種方案:

  1. 一是可以在應(yīng)用層的代碼層面將應(yīng)用層的數(shù)據(jù)報(bào)手動(dòng)進(jìn)行分包, 這樣拆分成多個(gè)小的包通過(guò)多個(gè)UDP數(shù)據(jù)報(bào)進(jìn)行傳輸.
  2. 第二種方案是就不用UDP協(xié)議了, 改用TCP協(xié)議, TCP沒(méi)有這樣的限制的, 后文會(huì)介紹到.

而且使用TCP也是更方便的, 如果使用第一種方案, 應(yīng)用層的代碼實(shí)現(xiàn)起來(lái)也比較復(fù)雜, 代碼也得進(jìn)行很多的測(cè)試, 畢竟代碼復(fù)雜了, 隨之bug也會(huì)更多更難處理一些, 這就好比叫貨拉拉搬家, 如果 東西很多, 一般的或者裝不下, 如果采用方案一, 就需要多約幾輛車, 這樣約車, 裝車, 清點(diǎn)都比較麻煩; 而采用方案二, 之間叫一個(gè)大一點(diǎn)的貨車就好了.

最后一個(gè)屬性就是校驗(yàn)和, 作用是驗(yàn)證傳輸?shù)臄?shù)據(jù)是否是正確的, 網(wǎng)絡(luò)傳輸, 本質(zhì)上是在傳輸光信號(hào)/電信號(hào), 在傳輸過(guò)程中, 可能會(huì)受到一些物理環(huán)境的干擾等, 在這些干擾下就可能出現(xiàn) “ 比特翻 轉(zhuǎn) ” 的情況, 0 會(huì)變成 1 , 1會(huì)變成 0.

一旦數(shù)據(jù)變了, 對(duì)于數(shù)據(jù)含義的解析可能就致命的, 舉一個(gè)典型的例子, 程序中經(jīng)常使用 1 表示某個(gè)功能開(kāi)啟, 0表示關(guān)閉, 本來(lái)網(wǎng)絡(luò)數(shù)據(jù)報(bào)是想開(kāi)啟功能, 結(jié)果因?yàn)榉D(zhuǎn), 就導(dǎo)致變成了關(guān)閉了.

像這樣的現(xiàn)象是客觀存在不可避免了, 我們能做的只是及時(shí)的識(shí)別出當(dāng)前的數(shù)據(jù)是否出現(xiàn)了問(wèn)題, 因此就引入了校驗(yàn)和來(lái)進(jìn)行鑒別, 校驗(yàn)和是針對(duì)數(shù)據(jù)的內(nèi)容進(jìn)行一系類的運(yùn)算(每一個(gè)比特位都會(huì)參與運(yùn)算)得到一個(gè)比較短的結(jié)果, 我們可以認(rèn)為, 數(shù)據(jù)內(nèi)容一定, 得到的校驗(yàn)和就是相同的, 如果我們的數(shù)據(jù)變了, 那么的校驗(yàn)和就變了, 如此即可驗(yàn)證得到數(shù)據(jù)是否準(zhǔn)確.

比如發(fā)送方要發(fā)送的數(shù)據(jù)是 “反射導(dǎo)彈”, 計(jì)算出來(lái)的校驗(yàn)和也會(huì)放到報(bào)頭中發(fā)送, 當(dāng)接收方拿到數(shù)據(jù)就會(huì)有兩種情況了, 第一種情況是接收方拿到數(shù)據(jù)后重新計(jì)算一個(gè)校驗(yàn)和, 得到的結(jié)果如果和拿到的校驗(yàn)和相同, 就認(rèn)為數(shù)據(jù)是正確的, 不同, 則認(rèn)為數(shù)據(jù)不正確.

還有就是說(shuō), 數(shù)據(jù)傳輸過(guò)程出錯(cuò)了, 但是可能計(jì)算得到的校驗(yàn)和和之前的校驗(yàn)和恰好─樣, 這種情況理論上是存在的, 但在工程實(shí)踐中出現(xiàn)這種情況的概率極小, 就忽略不計(jì)了.

針對(duì)網(wǎng)絡(luò)傳輸中, 生成校驗(yàn)和的算法有很多種, 這里簡(jiǎn)單介紹一下下面幾個(gè)比較知名的:

  1. CRC(循環(huán)冗余校驗(yàn)): 這種方法簡(jiǎn)單粗暴, 就是報(bào)數(shù)據(jù)的每個(gè)字節(jié)循環(huán)往上累加, 如果累加溢出, 高位就不要了, 這種方法比較好算, 但是校驗(yàn)的效果不夠理想, 萬(wàn)一你的數(shù)據(jù)同時(shí)變動(dòng)了兩個(gè)bit位(前一個(gè)字節(jié)少1, 后一個(gè)字節(jié)多1), 就會(huì)出現(xiàn)內(nèi)容變了, CRC沒(méi)變這樣的情況.
  2. MD5: MD5是使用一系列公式來(lái)進(jìn)行更復(fù)雜的數(shù)學(xué)運(yùn)算, MD5算法得到的結(jié)果具有下面幾個(gè)特點(diǎn).
  • 定長(zhǎng):無(wú)論原始數(shù)據(jù)多, 得到的MD5值都是固定長(zhǎng)度(4/8字節(jié)).
  • 沖突概率很小: 原始數(shù)據(jù)即使變動(dòng)了一點(diǎn)點(diǎn), 算出來(lái)的MD5值都會(huì)差別差別很大(讓MD5結(jié)果更分散了).
  • 不可逆: 通過(guò)原始數(shù)據(jù)計(jì)算出MD5比較容易, 但是通過(guò)MD5還原成原始數(shù)據(jù)很難, 理論上是不可實(shí)現(xiàn)的(計(jì)算量極大).

基于MD5的這些特點(diǎn), 讓MD5適用于更多場(chǎng)景, 計(jì)算校驗(yàn)和, 作為哈希函數(shù)計(jì)算Hash值, 用于加密等; 網(wǎng)上會(huì)看到一些解密MD5的方法, 都不是真正的解密, 因?yàn)檎嬲牡南雽?shù)據(jù)還原回去憑借現(xiàn) 在的計(jì)算機(jī)是做不到的, 這里的解密實(shí)質(zhì)上是將一些常見(jiàn)的字符串的MD5值進(jìn)行匯總成一張表, 解密的過(guò)程相當(dāng)于查表, 總不可能把所有的字符串都列出來(lái)吧...

  1. SHA1: SHA1和MD5是類似的, 只是運(yùn)算過(guò)程不一樣吧, 這就屬于數(shù)學(xué)問(wèn)題了...

三. TCP協(xié)議

1. TCP報(bào)頭介紹

TCP, 即Transmission Control Protoco, TCP協(xié)議相比于UDP協(xié)議要更復(fù)雜, TCP的特點(diǎn)是有連接, 可靠傳輸, 面向字節(jié)流, 全雙工.

TCP協(xié)議格式:

可以看到TCP報(bào)頭中報(bào)還的屬性還是很多的, 下面就來(lái)介紹這里的關(guān)鍵數(shù)屬性.

首先對(duì)于端口號(hào)和校驗(yàn)和這幾個(gè)屬性這里就不做介紹了, 和UDP是一樣的.

數(shù)據(jù)偏移和選項(xiàng)(option), 選項(xiàng)可有可無(wú)也可以有多個(gè), 用于對(duì)TCP一些功能的擴(kuò)展和TCP中的一些屬性進(jìn)行解釋說(shuō)明, 可能包括 “窗口擴(kuò)大因子”, “時(shí)間戳” 等選項(xiàng), 數(shù)據(jù)偏移表示TCP數(shù)據(jù)起始處與TCP報(bào)文起始處之間的距離, 也就是 4 個(gè)比特位( 0 到 15 )表示TCP首部報(bào)頭的長(zhǎng)度, 單位是 4 字節(jié);

正是由于TPC當(dāng)中有了數(shù)據(jù)偏移和選項(xiàng)這兩個(gè)屬性, 致使使TCP的報(bào)頭長(zhǎng)度是可變的, 不像UDP一樣固定是 8 字節(jié), 選項(xiàng)之前的部分是固定的長(zhǎng)度( 20 字節(jié)), 選項(xiàng)長(zhǎng)度 = 首部長(zhǎng)度 - 20 字節(jié), 通過(guò)首部長(zhǎng)度就可以去調(diào)節(jié)選項(xiàng)長(zhǎng)度; 如果首部長(zhǎng)度值是 5 , 表示整個(gè)TCP報(bào)頭是 20 字節(jié)(相當(dāng)于沒(méi)有選項(xiàng)), 如果首部長(zhǎng)度值是 15 , 表示整個(gè)TCP報(bào)頭是 60 字節(jié)(選項(xiàng)部分就是 40 字節(jié)), 填充是為了保證選項(xiàng)為 32 比特的整數(shù)倍.

保留項(xiàng), 數(shù)據(jù)偏移后面還有 6 位保留項(xiàng), 這里保留項(xiàng)的存在是為了未來(lái)TCP協(xié)議的拓展升級(jí)準(zhǔn)備的, 網(wǎng)絡(luò)協(xié)議的拓展升級(jí)是一件成本極高的事情, 比如現(xiàn)在UDP協(xié)議報(bào)文長(zhǎng)度是最大是 2 字節(jié)(64KB), 如果想要升級(jí)一下讓UDP的報(bào)文支持更大的長(zhǎng)度, 在技術(shù)上可以實(shí)現(xiàn), 但實(shí)際上要想讓世界上所有能上網(wǎng)的設(shè)備所安裝的各式的操作系統(tǒng)都能夠同步完成升級(jí), 支持新的UDP, 這是不現(xiàn)實(shí)的; 一種系統(tǒng)升級(jí)了, 其他系統(tǒng)不升級(jí), 就辦法進(jìn)行通信了...

而像TCP這樣引入了 “保留位”, 如果在未來(lái)想要引入一些新的功能, 就可以使用這些保留位, 這樣對(duì)于TPC本來(lái)的報(bào)頭結(jié)構(gòu)影響是比較小的, 針對(duì)不升級(jí)的設(shè)備也更容易兼容就版本的TPC.

剩下的屬性和TCP內(nèi)部的工作機(jī)制有關(guān), 在后文的內(nèi)容中會(huì)引入介紹, 這里先簡(jiǎn)單概述一下; 32位序號(hào)和 32 位確認(rèn)序號(hào)和TCP確認(rèn)應(yīng)答機(jī)制有關(guān); 16位窗口和滑動(dòng)窗口, 流量控制機(jī)制有關(guān); 16 位緊急指針要配合URG控制位一起使用的, 用于指明緊急數(shù)據(jù)之后正常數(shù)據(jù)的起始位置; 6位控制位從左至右分別為 , URG, ACK, PSH, RST, SYN, FIN, 含義如下:

  • URG(Urgent Flag)

該位為 1 時(shí), 表示包中有需要緊急處理的數(shù)據(jù).

  • ACK(Acknowledgement Flag)

該位為 1 時(shí), 確認(rèn)應(yīng)答的字段變?yōu)橛行? TCP規(guī)定除了最初建立連接時(shí)的SYN包之外該位必須設(shè)置為1.

PSH(Push Flag)

該位為 1 時(shí), 表示需要將受到的數(shù)據(jù)立刻傳給上層應(yīng)用協(xié)議, PSH為 0 時(shí), 不需要立即傳而是先進(jìn)行緩存.

  • RST(Reset Flag)

該位為 1 時(shí)表示TCP連接中出現(xiàn)異常必須強(qiáng)制斷開(kāi)連接.

  • SYN(Synchronize Flag)

用于建立連接, SYN為 1 表示希望建立連接, 并在其序列號(hào)的字段進(jìn)行序列號(hào)初始值的設(shè)定(Synchronize本身有同步的意思, 也就意味著建立連接的雙方, 序列號(hào)和確認(rèn)應(yīng)答號(hào)要保持同步).

  • FIN(Fin Flag)

該位為 1 時(shí), 表示今后不會(huì)再有數(shù)據(jù)發(fā)送, 希望斷開(kāi)連接.

2. TCP實(shí)現(xiàn)可靠傳輸?shù)暮诵臋C(jī)制

2.1 確認(rèn)應(yīng)答

確認(rèn)應(yīng)答是實(shí)現(xiàn)TCP可靠傳輸最核心的機(jī)制, 這里的可靠并不是指發(fā)送方能夠百分百能將數(shù)據(jù)發(fā)送到接收方, 可靠傳輸是要盡可能的把數(shù)據(jù)發(fā)過(guò)去, 發(fā)送方能夠準(zhǔn)確的知道接收方是否收到了數(shù)據(jù).

比如, 女神跟你關(guān)系是非常要好的, 你發(fā)的消息她一定立刻會(huì)回, 當(dāng)你給你的女神發(fā)消息說(shuō)要請(qǐng)她吃麻辣燙, 當(dāng)你看到她的回復(fù)的時(shí)候, 你就知道消息她是收到了的.

這里女神回復(fù)的好啊好啊就稱為 “應(yīng)答報(bào)文”, 也叫ACK(acknowledge), TCP進(jìn)行可靠傳輸最主要就是靠這個(gè)確認(rèn)應(yīng)答機(jī)制來(lái)保證的, A給B發(fā)一個(gè)消息, B收到之后就會(huì)返回一個(gè)ACK, A收到這個(gè)應(yīng)答之后, 就知道了發(fā)的數(shù)據(jù)順利到達(dá)了(沒(méi)有丟包), 如果沒(méi)有收到ACK就說(shuō)明A發(fā)的數(shù)據(jù)消息大概率不見(jiàn)了(丟包).

考慮更復(fù)雜的情況, 如果多條消息同時(shí)發(fā)送, 可能會(huì)發(fā)生 “后發(fā)先至” 的情況, 比如你問(wèn)你的女神是否吃麻辣燙后緊接著又問(wèn)一句: “女神, 女神, 你做我女朋友好嗎?”, 就可能會(huì)是下面的情況:

在網(wǎng)絡(luò)通信中, 兩個(gè)主機(jī)之間, 傳輸路線存在多條, 數(shù)據(jù)報(bào) 1 和數(shù)據(jù)報(bào) 2 可能走的都是不同路線, 而且設(shè)備的轉(zhuǎn)發(fā)速率也是有快有慢, 受這樣的網(wǎng)絡(luò)環(huán)境的影響, 這種后發(fā)先至的情況是很常見(jiàn)也是無(wú)法避免的, 收到消息的順序就會(huì)存在變數(shù), 這樣應(yīng)答錯(cuò)亂后, 解析數(shù)據(jù)的含義就出現(xiàn)歧義了, 就如上圖, 如果按照正常的邏輯解析的話就是 “吃麻辣燙->滾, 做女朋友->好啊好啊”, 而實(shí)際上女神沒(méi)想做你女朋友...

為了解決上述先發(fā)后至的問(wèn)題, TCP中就引入了序號(hào)和確認(rèn)序號(hào).

上面發(fā)送方的序號(hào)就是序號(hào), 接收方的序號(hào)就是確認(rèn)序號(hào), 這樣即使出現(xiàn)先發(fā)后至的情況導(dǎo)致消息的順序錯(cuò)位了, 也能區(qū)分清楚應(yīng)答報(bào)文是針對(duì)哪一條消息做出的應(yīng)答.

這里的序號(hào)和確認(rèn)序號(hào)就是對(duì)應(yīng)與TCP報(bào)頭中的序號(hào)與確認(rèn)序號(hào), 它們都是 32 位大小, ACK同樣也在報(bào)頭中占據(jù) 1 個(gè)比特位.

要注意任何一條數(shù)據(jù)(包括應(yīng)答報(bào)文)都是有序號(hào)的, 但確認(rèn)序號(hào)只有應(yīng)答報(bào)文有, 是否為應(yīng)答報(bào)文取決于ACK這個(gè)標(biāo)志位是否為1, 如果為 1 就表示是應(yīng)答報(bào)文, 如果是 0 就表示不是應(yīng)答報(bào)文.

實(shí)際上的TCP序號(hào)并不是上面舉例那樣簡(jiǎn)單的以1, 2這種方式編號(hào), TCP是面向字節(jié)流的, 在實(shí)際的TCP傳輸中, 是針對(duì)每一個(gè)字節(jié)都進(jìn)行了編號(hào), 假設(shè)我們的一條數(shù)據(jù)長(zhǎng)度是 1000 個(gè)字節(jié), 傳輸?shù)臄?shù)據(jù)第一個(gè)字節(jié)的序號(hào)是1, 由于這 1000 個(gè)字節(jié)都是屬于同一TCP報(bào)文的, 因此就將一條數(shù)據(jù)中的第一個(gè)字節(jié)作為了TCP報(bào)頭里記錄的序號(hào)也就是1, 代表要發(fā)送的這一條數(shù)據(jù).

確認(rèn)序號(hào)的取值是收到的數(shù)據(jù)的最后一個(gè)字節(jié)的序號(hào)+1, 接收方收到數(shù)據(jù)后會(huì)返回一個(gè) 1001 做為確認(rèn)序號(hào), 這里的確認(rèn)序號(hào)表示著兩個(gè)含義:

  1. 小于 1001 的數(shù)據(jù)都已經(jīng)確認(rèn)收到了
  2. 發(fā)送方接下來(lái)應(yīng)該從 1001 這個(gè)序號(hào)開(kāi)始發(fā)送數(shù)據(jù)(接收放向A索要 1001 開(kāi)始的數(shù)據(jù))

這里總的來(lái)說(shuō)就是, TCP可靠傳輸?shù)哪芰ψ钪饕峭ㄟ^(guò)確認(rèn)應(yīng)答機(jī)制保證的, 通過(guò)應(yīng)答報(bào)文不僅可以讓發(fā)送方清楚的知道是否傳輸成功, 而且通過(guò)序號(hào)和確認(rèn)序號(hào)對(duì)多組數(shù)據(jù)的應(yīng)答對(duì)應(yīng)關(guān)系進(jìn)行了詳細(xì)的區(qū)分.

2.2 超時(shí)重傳

上面說(shuō)的確認(rèn)應(yīng)答只是討論了數(shù)據(jù)順利傳輸?shù)那闆r, 那如果出現(xiàn)意外了呢? 比如網(wǎng)絡(luò)原因可能會(huì)導(dǎo)致發(fā)送數(shù)據(jù)或者返回的ACK丟包了又該如何呢?

也好解決, 出現(xiàn)了丟包的情況, 發(fā)送方重新再發(fā)一個(gè)就行了, 這就是TCP引入的超時(shí)重傳機(jī)制, 當(dāng)一個(gè)數(shù)據(jù)發(fā)送后, 如果在一個(gè)時(shí)間閾值內(nèi)沒(méi)有收到ACK, 就認(rèn)為是丟包了, 就會(huì)重發(fā)一份同樣的數(shù)據(jù).

觸發(fā)重傳有兩種情況, 一是數(shù)據(jù)包丟了, 此時(shí)發(fā)送方等待一段時(shí)間沒(méi)有收到ACK就會(huì)重新發(fā)送.

第二種情況是返回的ACK丟包了, 對(duì)于發(fā)送方是不知道是自己這邊發(fā)送的消息丟包了還是對(duì)方返回的ACK出問(wèn)題了, 此時(shí)就會(huì)按照最壞的情況, 重發(fā)數(shù)據(jù), 這就可能導(dǎo)致接受方重復(fù)收到多次同樣的消息, 但TCP針對(duì)這種重復(fù)數(shù)據(jù)的傳輸是有特殊處理的, TCP實(shí)現(xiàn)了一個(gè)去重機(jī)制, TCP中存在一個(gè)發(fā)送緩沖區(qū)和接收緩沖區(qū)(操作系統(tǒng)內(nèi)核里面的一段內(nèi)存), 接收方拿到數(shù)據(jù)后是將數(shù)據(jù)放到了接收緩沖區(qū)(有阻塞隊(duì)列功能, 但不限于阻塞隊(duì)列)中, 緩沖區(qū)會(huì)根據(jù)數(shù)據(jù)的序號(hào)進(jìn)行排序, 并且根據(jù)數(shù)據(jù)的序號(hào), TCP很容易識(shí)別當(dāng)前接收緩沖區(qū)里的這兩條數(shù)據(jù)是否是重復(fù)的, 如果重復(fù)就把后來(lái)的這份數(shù)據(jù)就直接丟棄了, 然后重新返回一個(gè)與之前相同的ACK.

然后就是說(shuō), 重傳也是有可能出現(xiàn)丟包的情況的, 可能會(huì)出現(xiàn)多次重傳都丟包的情況, 而在網(wǎng)絡(luò)正常的情況下丟包的概率是很小的, 兩次以及多次丟包的概率那就更加小了, 所以重發(fā)一定次數(shù)后就會(huì)降頻重發(fā)(), 還是不行就會(huì)認(rèn)為是網(wǎng)絡(luò)出現(xiàn)故障了, TCP就會(huì)嘗試重置連接(斷開(kāi)重連), 如果重置還是失敗, 就會(huì)徹底斷開(kāi)連接了.

關(guān)于超時(shí)重傳這里, 總的來(lái)說(shuō)就是, 由于去重和重新排序機(jī)制的存在, 發(fā)送方只要發(fā)現(xiàn)ACK沒(méi)有按時(shí)到達(dá), 就會(huì)重傳數(shù)據(jù), 即使數(shù)據(jù)的順序亂了, 重復(fù)了, 依賴于TCP報(bào)頭的序號(hào), 接收方都能很好進(jìn)行處理.

TCP的可靠傳輸就是通過(guò)確認(rèn)應(yīng)答和超時(shí)重傳來(lái)體現(xiàn)出來(lái)的, 其中確認(rèn)應(yīng)答描述的是傳輸順利的情況, 超時(shí)重傳描述的是傳輸出現(xiàn)問(wèn)題的情況, 這兩者相互配合, 共同支撐整體的TCP可靠性.

3. 連接管理(三次握手, 四次揮手)

TCP要完成通信是需要先建立連接的, 所以就有了連接管理的機(jī)制, 連接管理一定程度上也可以體現(xiàn)TCP的可靠性, 但保證可靠傳輸最核心的機(jī)制還是上面介紹的確認(rèn)應(yīng)答和超時(shí)重傳.

TCP這里的連接指的是由一個(gè)四元組(源IP, 源端口, 目的IP, 目的端口)來(lái)標(biāo)識(shí), 一個(gè)連接建立完成就表示通信雙方知曉對(duì)方的IP和端口信息, 就是通信雙方各自都維護(hù)著連接這樣的一個(gè)數(shù)據(jù)結(jié)構(gòu),雙方把對(duì)方的地址信息都保存下來(lái)就是完成了連接, 而斷開(kāi)連接就是把各自存儲(chǔ)的連接刪除掉.

對(duì)于TCP的連接管理就是建立連接(三次握手), 斷開(kāi)連接(四次揮手)了.

3.1 建立連接(三次握手)

客戶端與服務(wù)器之間進(jìn)行三次交互建立連接的過(guò)程被形象地稱為 “三次握手”, 在這三次交互中, 通信雙方要完成對(duì)彼此信息的記錄.還是你和你的女神聊天, 你想要讓女神做你的女朋友, 于是就有了下面的對(duì)話.

首先是你向女神表白, 女神接受了就表明, 你和女神就有了一個(gè)認(rèn)同, 女神是你的唯一, 但此時(shí)你和女神還沒(méi)有成為男女朋友的關(guān)系, 因?yàn)榕癫恢滥闶遣皇撬奈ㄒ?萬(wàn)一你腳踏好幾只呢...),然后女神也對(duì)你表白, 你有接收了, 此時(shí)就說(shuō)明你倆互為唯一了, 這就真正建立了男女朋友關(guān)系, 相當(dāng)于客戶端和服務(wù)器建立成功了.

把每次通信形象的稱為一次揮手, 上面的過(guò)程就是四次揮手了, 但在實(shí)際TCP的建立連接過(guò)程上是三次揮手, 其實(shí)就相等于于把上面女神的兩條消息合并成了一條消息.

所以, 三次握手本質(zhì)上是四次信息交互, 通信雙方各自需要發(fā)送一個(gè) “建立連接” 的請(qǐng)求, 然后再各自向返回一個(gè)ACK, 中間兩次是合并完成的, 而且必須是三次握手, 兩次握手是不行的(沒(méi)有最后一次, 女神就不知道你是不是她的唯一了...)

我們知道客戶端是主動(dòng)發(fā)出請(qǐng)求的一端, 服務(wù)器是被動(dòng)響應(yīng)的, 所以, TCP建立連接需要讓客戶端向服務(wù)器發(fā)送一個(gè)連接請(qǐng)求, 即SYN, 然后服務(wù)器收到請(qǐng)求后會(huì)給客戶端響應(yīng)一個(gè)ACK和SYN, 客戶端收到服務(wù)器的SYN后會(huì)立即發(fā)送一個(gè)ACK給服務(wù)器, 服務(wù)器收到客戶端的確認(rèn)應(yīng)答后,客戶端與服務(wù)器就連接成功了.

要注意理解的是, 服務(wù)器給客戶端發(fā)送的ACK和SYN這兩次通信是必須要進(jìn)行合并的, 因?yàn)榉庋b分用兩次一定是比一次成本要高的, 而建立連接的這四次信息交互是在純內(nèi)核中完成的, 服務(wù)器的系統(tǒng)內(nèi)核是可以做到收到SYN之后就立即同時(shí)發(fā)送ACK和SYN的, 也就必須是這樣做.

三次握手的作用不限于建立連接, 除此之外三次握手還能檢測(cè)發(fā)送能力與接收能力是否正常, 還是你和女神之間的日常, 你和女神帶著耳機(jī)連麥打游戲, 有如下場(chǎng)景:

第一次通信, 當(dāng)女神聽(tīng)到你的聲音時(shí), 女神就知道了你的的麥克風(fēng)和女神的耳機(jī)是都是可以使用的, 而你現(xiàn)在還什么都判斷不了; 第二次通信, 當(dāng)你聽(tīng)到女神的回復(fù), 你就知道了你和女神的麥克風(fēng)和耳機(jī)都是可以使用的, 但此時(shí)女神還不知道你的耳機(jī)和她的麥克風(fēng)能不能使用; 第三次通信, 當(dāng)女神再次聽(tīng)到你的回復(fù)時(shí)間, 你和女神就都知道了你們雙方的耳機(jī)和麥克風(fēng)都是可以正常使用的,這里的麥克風(fēng)就對(duì)應(yīng)發(fā)送能力, 耳機(jī)就對(duì)應(yīng)接收能力.

再?gòu)膶?shí)際上TCP的三次握手來(lái)說(shuō), 當(dāng)發(fā)送方發(fā)出SYN后, 接收方都到發(fā)送方的SYN后, 此時(shí)接收方就能夠確定發(fā)送方的發(fā)送能力和接收方的接收能力是正常的, 然后接收方回應(yīng)ACK和SYN, 當(dāng)接收方收到ACK和SYN后, 就知道了發(fā)送方和接收方的發(fā)送能力, 接收能力都是正常的, 最后發(fā)送方回應(yīng)ACK給接收方, 此時(shí)接收方也確定了接收方和發(fā)送方的接收能力, 發(fā)送能力都是正常的.

所以, 總結(jié)一下三次握手的意義就是:

  1. 讓通信雙方各自建立對(duì)對(duì)方的 “認(rèn)同”.
  2. 驗(yàn)證通信雙方各自的發(fā)送能力和接收能力是否正常
  3. 在握手的過(guò)程中, 雙方還會(huì) “協(xié)商” 配置一些重要的參數(shù)(完成一些數(shù)據(jù)的同步).

發(fā)起建立建立連接請(qǐng)求的報(bào)文就稱為SYN, 也叫同步報(bào)文段, SYN是TCP首部控制控制位當(dāng)中的一位, 這個(gè)標(biāo)志位為 1 就表示是請(qǐng)求建立連接的報(bào)文, 其他控制位同樣如此, 用 0 和 1 來(lái)進(jìn)行控制.

在建立連接的過(guò)程中, 服務(wù)器與客戶端是存在著不同的狀態(tài)的, 不同的狀態(tài)體現(xiàn)了TCP當(dāng)前的工作, 具體如下:

  • CLOSED 表示客戶端或服務(wù)器處于關(guān)閉狀態(tài).
  • LISTEN 表示服務(wù)器已經(jīng)準(zhǔn)備就緒, 等待客戶端連接的狀態(tài).
  • SYN_SENT 表示客戶端連接請(qǐng)求已發(fā)送, 此時(shí)客戶端進(jìn)入阻塞等待服務(wù)器確認(rèn)應(yīng)答狀態(tài), 一般此狀態(tài)的存在時(shí)間很短.
  • SYN_RCVD 表示服務(wù)器已經(jīng)收到客戶端的連接請(qǐng)求, 發(fā)送ACK和SYN并進(jìn)入阻塞等待客戶端連接狀態(tài), 一般此狀態(tài)存在時(shí)間很短.
  • ESTABLISHED 表示客戶端或服務(wù)器已經(jīng)建立成功連接, 隨時(shí)可以進(jìn)行通信, 要注意理解, 兩次握手后, 從客戶端來(lái)看, 客戶端已經(jīng)把該發(fā)送的和該接收的都完成了, 此時(shí)客戶端就認(rèn)為進(jìn)行成功建立連接; 而對(duì)于服務(wù)器, 當(dāng)?shù)谌挝帐趾蟛拍苷J(rèn)為成功建立連接.

三次握手這里也能一定程度上保證TCP的可靠傳輸, 但只是輔助做作用, 真確保可靠傳輸個(gè)關(guān)鍵機(jī)制還是上面介紹的確認(rèn)應(yīng)答和超時(shí)重傳.

3.2 斷開(kāi)連接(四次揮手)

與三次握手類似, 客戶端與服務(wù)器通過(guò)四次交互斷開(kāi)連接的過(guò)程稱為 “四次揮手”, 通信雙方向?qū)Ψ桨l(fā)起一個(gè)斷開(kāi)連接的請(qǐng)求FIN, 再各自給對(duì)方一個(gè)回應(yīng)ACK, 這個(gè)時(shí)候你和女神的緣分就到了盡頭了, 出現(xiàn)了下面的場(chǎng)景:

斷開(kāi)連接的請(qǐng)求也被稱為FIN, 在TCP報(bào)文中也是一個(gè)控制位, 斷開(kāi)連接的請(qǐng)求可以是客戶端先發(fā)起, 也可以是服務(wù)器先發(fā)起, 這里以客戶端主動(dòng)斷開(kāi)連接為例繼續(xù)介紹.

這里與三次握手需要區(qū)別的是, 斷開(kāi)連接的過(guò)程中, 中間兩次通信通常是不能合并的, 這是因?yàn)镕IN的發(fā)起并不是由系統(tǒng)內(nèi)核控制的, 而是由應(yīng)用程序調(diào)用socket的close方法(或者進(jìn)程退出)才會(huì)觸發(fā)FIN, ACK則是由系統(tǒng)內(nèi)核控制的, 所以, 上圖描述的客戶端發(fā)出斷開(kāi)連接FIN請(qǐng)求, 然后服務(wù)器收到請(qǐng)求后立即響應(yīng)ACK(內(nèi)核返回ACK), 但此時(shí)并不能確定服務(wù)器應(yīng)用層程序到底什么時(shí)候才會(huì)調(diào)用socket.close()從而觸發(fā)返回給客戶端的FIN.

也就是說(shuō), 四次揮手不像三次握手那樣是在重內(nèi)核中完成的, 三次握手可以做到中間兩次交互的時(shí)機(jī)是相同的, 而四次揮手的中間兩次交互時(shí)機(jī)首否是相同的完全取決于服務(wù)器應(yīng)用層的代碼是怎么寫(xiě)的, 如果特殊情況下中間的兩次交互時(shí)機(jī)是相同的也有可能是合并完成的.

同樣斷開(kāi)連接過(guò)程中, 客戶端和服務(wù)器的的狀態(tài)如下:

  • FIN_WAIT_1 出現(xiàn)在主動(dòng)發(fā)起斷開(kāi)連接的一方, 主動(dòng)方發(fā)送FIN后, 進(jìn)入等待被動(dòng)方確認(rèn)斷開(kāi)響應(yīng)狀態(tài).
  • FIN_WAIT_2 1 出現(xiàn)在主動(dòng)發(fā)起斷開(kāi)發(fā)起連接的一方, 收到被動(dòng)方的ACK, 進(jìn)入等待被動(dòng)方FIN狀態(tài)
  • CLOSE_WAIT 出現(xiàn)在被動(dòng)發(fā)起斷開(kāi)連接的一方, 當(dāng)被動(dòng)方收到主動(dòng)方發(fā)送的FIN請(qǐng)求后, 被動(dòng)方響應(yīng)ACK, 然后等待關(guān)閉連接( 等待socket調(diào)用close方法).
  • LAST_ACK 出現(xiàn)在被動(dòng)發(fā)起斷開(kāi)連接的一方, 被動(dòng)方FIN發(fā)送后, 進(jìn)入等待最后一個(gè)ACK狀態(tài).
  • TIME_WAIT 出現(xiàn)在主動(dòng)發(fā)起斷開(kāi)發(fā)起連接的一方, 收到被動(dòng)方FIN, 發(fā)送最后一次ACK, 然后繼續(xù)保持當(dāng)前的TCP狀態(tài), 再等一會(huì)兒后釋放連接.

這里重點(diǎn)要理解的是TIME_WAIT這個(gè)狀態(tài), 從上圖來(lái)看在客戶端看來(lái)它的將最后一次ACK發(fā)出去后, 四次揮手就已經(jīng)是完成了? 那為什么TIME_WAIT這里還要等待一會(huì)而不是立即釋放連接, 這是因?yàn)樽詈笠淮慰蛻舳税l(fā)送ACK后是可能存在丟包的情況的, 在三次握手和四次揮手的過(guò)程中, 同樣是存在超時(shí)重傳的, 如果丟包了, 服務(wù)器就會(huì)以最壞情況認(rèn)為自己的FIN丟了, 會(huì)重發(fā)FIN, 此時(shí)客戶端就需要等待以預(yù)防服務(wù)器重發(fā)FIN的這種情況, 因此使用TIME WAIT狀態(tài)保留一定的時(shí)間, 就是為了能夠處理最后一個(gè)ACK丟包的情況, 能夠在收到重傳的FIN之后, 進(jìn)行ACK響應(yīng).

TIME_WAIT這里等待的時(shí)間為2MSL , MSL表示報(bào)文最大生存時(shí)間(通常是60s), 也就是在兩個(gè)節(jié)點(diǎn)進(jìn)行網(wǎng)絡(luò)傳輸過(guò)程中消耗的最大時(shí)間, 如果TIME_WAIT維持了2MSL都沒(méi)用收到重傳的FIN, 就認(rèn)為最后一個(gè)ACK順利到達(dá)了, 服務(wù)器與客戶端就完全斷開(kāi)連接了.

4. 滑動(dòng)窗口

上面介紹的TCP機(jī)制都是再給TCP的可靠性提供支持, 但保證了可靠性其實(shí)就犧牲了一定的效率, 滑動(dòng)窗口做的事情就是在保證傳輸?shù)目煽啃缘幕A(chǔ)上, 盡量地去提高傳輸效率.

在進(jìn)行IO操作的時(shí)候, 時(shí)間成本主要是兩個(gè)部分, 一是等, 二是數(shù)據(jù)傳輸, 大多數(shù)情況下, IO花的時(shí)間成本大頭都是在等上面, 滑動(dòng)窗口本質(zhì)上就是降低了等待確認(rèn)應(yīng)答ACK消耗的時(shí)間.

對(duì)于基本的確認(rèn)應(yīng)答機(jī)制來(lái)說(shuō), 每發(fā)送一次數(shù)據(jù), 都需要等待ACK返回后才能進(jìn)行下一次發(fā)送, 這樣大部分的時(shí)間都用在等ACK上了.

滑動(dòng)窗口的本質(zhì)就是不進(jìn)行等待發(fā)送多條數(shù)據(jù), 然后使用一份時(shí)間來(lái)等待多個(gè)ACK返回.

把不需要等待, 就能直接發(fā)送的最大數(shù)據(jù)量, 稱為 “窗口大小”, 上圖中窗口的大小就是4000, 客戶端發(fā)送了 4 條數(shù)據(jù)之后并不是等到 4 個(gè)ACK都都返回后才能繼續(xù)發(fā)送, 而是每收到一次ACK就繼續(xù)發(fā)下一條數(shù)據(jù), 這樣就讓客戶端這里等待ACK的數(shù)據(jù)始終始終都是 4 條, 就如上圖, 客戶端發(fā)出1-1000,1001-2000, 2001-3000, 3001-4000這四條數(shù)據(jù)后, 客戶端收到1001, 緊接著就發(fā)送4001-5000 這條數(shù)據(jù), 收到2001, 就繼續(xù)發(fā)送5001-6000...

這里圖中本來(lái)等待ACK是1001-5000, 接下來(lái), 收到了 2001 這個(gè)ACK, 就說(shuō)明 2001 之前的數(shù)據(jù)(1001-2000)已經(jīng)被確認(rèn)了, 此時(shí)就可以立即發(fā)送5001-6000的數(shù)據(jù), 此時(shí)意味著等待ACK的范圍就是2001-6000, 這就相當(dāng)于一個(gè)大小始終不變的窗口, 但窗口框住的數(shù)據(jù)變了, 相當(dāng)于窗口向右滑動(dòng)了一格, 所以這里就形象的稱為 “滑動(dòng)窗口”.

上述是正常傳輸?shù)那闆r, 那如果丟包了應(yīng)該如何處理呢? 下面就來(lái)分析一下,

情況1, ACK丟了

這種情況下是不用做任何的處理的, 數(shù)據(jù)還是能夠正常傳輸, 比如 1001 丟了, 但實(shí)際上1-1000的數(shù)據(jù)是服務(wù)器是收到了的, 當(dāng)客戶端收到 2001 時(shí)就表明 2001 之前的數(shù)據(jù)都已經(jīng)確認(rèn)到達(dá)服務(wù)器, 就會(huì)接著再發(fā)送兩條數(shù)據(jù), 所以只要大部分的ACK沒(méi)有丟, 客戶端可以通過(guò)下一次或者后面的確認(rèn)應(yīng)答序號(hào)來(lái)進(jìn)行確認(rèn), 不處理也沒(méi)事.

情況2, 數(shù)據(jù)丟了

數(shù)據(jù)都丟了, 這就必須得處理了, 比如1-3000的數(shù)據(jù)中, 其中1001-2000的數(shù)據(jù)丟了, 那服務(wù)器每收到一個(gè)數(shù)據(jù), 都會(huì)返回1001, 表示讓客戶端重傳1001-2000這個(gè)數(shù)據(jù), 當(dāng)客戶端收到若干個(gè)個(gè)相同的確認(rèn)應(yīng)答序號(hào)時(shí), 就明白了, 數(shù)據(jù)丟了, 就會(huì)對(duì)丟失的數(shù)據(jù)進(jìn)行重傳, 直到服務(wù)器收到1001-2000的數(shù)據(jù), 就會(huì)返回最新的確認(rèn)應(yīng)答序號(hào), 當(dāng)然, 如果中間還有數(shù)據(jù)都丟包,返回的就是新丟的包的序號(hào)了, 然后還是上述操作.

這種丟包重傳的方式被稱作 “ 快速重傳 ”, 讓重傳操作只重傳了丟失的數(shù)據(jù), 可以視為是超時(shí)重傳機(jī)制在滑動(dòng)窗口下的變形; 如果當(dāng)前傳輸數(shù)據(jù)密集, 按照滑動(dòng)窗口的方式來(lái)傳輸, 此時(shí)按照快速重傳來(lái)處理丟包; 如果當(dāng)前傳輸數(shù)據(jù)稀疏, 就不再按照滑動(dòng)窗口方式了傳輸了, 此時(shí)還是按照之前的超時(shí)重傳處理丟包.

5. 流量控制

滑動(dòng)窗口機(jī)制是在提高TCP的傳輸效率, 窗口越大, 傳輸效率就越高, 但是窗口也是不能無(wú)限大下去的.

首先如果窗口無(wú)限大了, 那么一個(gè)窗口就把數(shù)據(jù)都發(fā)完了, 也就是完全沒(méi)有在等ACK了, 數(shù)據(jù)傳輸?shù)目煽啃跃偷貌坏奖U狭? 這就和TCP的初心背道而馳了, 而且窗口太大, 也會(huì)消耗大量的系統(tǒng)資源.

然后就是說(shuō), 窗口大了, 發(fā)送方的發(fā)送效率確實(shí)提高了, 但是接收方能接受得過(guò)來(lái)嗎? 但是如果發(fā)送速度過(guò)快, 接收方的接緩沖區(qū)滿了之后, 接收方就處理不過(guò)來(lái)了, 白發(fā)了...

所以并不是窗口大小越大, 傳輸效率就越高, 只有保證發(fā)送方發(fā)送與接收方接收的速率最大并保持一致時(shí), 傳輸效率才是最高的, 而流量控制要做的工作就是根據(jù)接收方的處理能力, 動(dòng)態(tài)協(xié)調(diào)發(fā)送方的發(fā)送速率.

接收方的處理能力是通過(guò)接收方緩沖區(qū)的剩余容量來(lái)衡量的, 接收方緩沖區(qū)的容量剩余多少, 下次發(fā)送方的窗口大小就是多少, 可以接收方的緩沖區(qū)想象成一個(gè)蓄水池, 那么發(fā)送方的工作就是注水, 接收方的工作就是使用水池中的水, 當(dāng)水位比較低(剩余空間大)那就注水的時(shí)候就快一點(diǎn), 水位比較高(剩余空間小)那就注水的時(shí)候就慢一點(diǎn), 池子滿了就暫時(shí)先停止注水.

當(dāng)發(fā)送方的數(shù)據(jù)到達(dá)接收方的時(shí)候, 接收方都會(huì)返回一個(gè)ACK,這個(gè)ACK除了確認(rèn)能夠確認(rèn)應(yīng)答, 還能告知接收方緩沖區(qū)的剩余容量, 然后發(fā)送方就會(huì)根據(jù)接收方緩沖區(qū)的剩余容量來(lái)控制發(fā)送速度(窗口大小), 當(dāng)接收方得知接收方緩沖區(qū)空間滿了的時(shí)候, 就暫時(shí)不會(huì)發(fā)送數(shù)據(jù)了, 而是會(huì)定期去給接收方發(fā)送一個(gè)探測(cè)窗口報(bào)文, 這個(gè)報(bào)文不攜帶具體的業(yè)務(wù)數(shù)據(jù), 只是為了觸發(fā)ACK查詢接收方緩沖區(qū)的剩余容量.

上面獲取接收方緩沖區(qū)的剩余容量, 是通過(guò)TCP報(bào)頭中的窗口大小來(lái)進(jìn)行獲取的, 占 16 個(gè)比特位(即64kb), 但這并不意味著窗口大小最大就是64kb, 因?yàn)門(mén)CP報(bào)頭的選項(xiàng)部分里面有一個(gè)窗口擴(kuò)大 因子M, 實(shí)際窗口大小是將窗口大小字段左移M位, 也就是擴(kuò)大 2 ^ M 倍, 比如窗口大小已經(jīng)是64kb, 如果擴(kuò)展因子M標(biāo)識(shí)為2, 最大的窗口大小就拓展為了256KB(64KB << 2).

6.擁塞控制

流量控制是考慮到了接收方的處理能力來(lái)調(diào)節(jié)發(fā)送方的窗口大小, 而實(shí)際上數(shù)據(jù)傳輸不單單是簡(jiǎn)單的從發(fā)送方直接到了接收方, 往往還有復(fù)雜的中間轉(zhuǎn)發(fā)過(guò)程(眾多交換機(jī)和路由器等節(jié)點(diǎn)), 擁塞控制描述的是傳輸過(guò)程中中間節(jié)點(diǎn)的處理能力, 同樣的如果中間轉(zhuǎn)發(fā)過(guò)程中鏈路的擁堵了, 那接收方的處理能力再快也是白搭的(木桶效應(yīng)).

擁塞控制本質(zhì)上就是通過(guò)實(shí)驗(yàn)的方式來(lái)逐漸找到一個(gè)合適的窗口大小(合適的發(fā)送速率).

接收方處理能力是好量化衡量的, 但是由于設(shè)備眾多, 數(shù)據(jù)每次傳輸路線也大概率是不相同的... 眾多影響因素導(dǎo)致中間節(jié)點(diǎn)的處理能力是不好量化衡量的, 因此擁塞控制采取了 “測(cè)試實(shí)驗(yàn)” 的方式逐漸調(diào)整不同情況下合適的發(fā)送速度.

初始的時(shí)候接收方會(huì)以較小的窗口進(jìn)行發(fā)送(0輪, 窗口大小是1, 但不是一個(gè)字節(jié)), 由于初始窗口比較小(發(fā)送速率慢), 每一輪不丟包都會(huì)使窗口大小擴(kuò)大一倍(指數(shù)增長(zhǎng)), 當(dāng)增長(zhǎng)速率達(dá)到閾值之后, 指數(shù)增長(zhǎng)就成為了線性增長(zhǎng), 再當(dāng)窗口達(dá)到一定的大小, 就會(huì)出現(xiàn)丟包的情況, 這就意味著鏈路就出現(xiàn)了 “擁堵”, 說(shuō)明此時(shí)發(fā)送的速率已經(jīng)接近網(wǎng)絡(luò)的極限的; 此時(shí)就會(huì)減小窗口的大小(速度很 快, 立馬縮成很小的值), 因?yàn)槿绻霈F(xiàn)丟包減小窗口大小的速度不夠大, 可能會(huì)出現(xiàn)持續(xù)性的丟包, 對(duì)網(wǎng)絡(luò)通信的質(zhì)量會(huì)造成很大的影響, 然后就是重復(fù)剛才指數(shù)增長(zhǎng)和線性增長(zhǎng)的過(guò)程了.

擁塞窗口不是固定數(shù)值, 而是一直動(dòng)態(tài)變化的, 隨著時(shí)間的推移, 逐漸達(dá)到一個(gè)動(dòng)態(tài)平衡的過(guò)程, 使窗口大小隨著網(wǎng)絡(luò)的動(dòng)態(tài)變化而動(dòng)態(tài)變化.

實(shí)際上的窗口大小是擁塞控制和流量控制共同決定的, 取的是擁塞窗口和流量控制窗口的較小值.

7. 延時(shí)應(yīng)答

延時(shí)應(yīng)答也是提升TCP效率的機(jī)制, 流量控制是為了在接收方能夠處理得了的前提下, 盡可能的把窗口大小放大一點(diǎn), 延時(shí)應(yīng)答相當(dāng)于流量控制的延伸, 想要在此基礎(chǔ)上, 讓窗口的大小盡量再大一點(diǎn).

就是接受方收到數(shù)據(jù)之后, 不是立即返回ACK了而是稍微等會(huì)再返回, 等待的時(shí)間里, 接收方的應(yīng)用程序就能夠把接收緩沖區(qū)的數(shù)據(jù)給再處理一波, 此時(shí)接收緩沖區(qū)的剩余容量就更大了.

實(shí)際上延時(shí)應(yīng)答采取的方式, 就是在滑動(dòng)窗口下, ACK不再每一條數(shù)據(jù)都返回了, 比如下圖就是隔一條返回一個(gè)ACK.

實(shí)際上接收緩沖區(qū)剩余空間大小的變化是一個(gè)復(fù)雜的過(guò)程, 既取決于發(fā)送方的發(fā)送也取決于接收方的處理.

8. 捎帶應(yīng)答

同樣捎帶應(yīng)答也是提升TCP效率的機(jī)制, 是延遲應(yīng)答的延伸, 由于延時(shí)應(yīng)答的存在, 接收方并不是立即就返回響應(yīng)ACK的, 而很多情況下, 客戶端服務(wù)器在應(yīng)用層也是 “一發(fā)一收” 的, 當(dāng)服務(wù)器的應(yīng)用層有業(yè)務(wù)數(shù)據(jù)要發(fā)送給客戶端時(shí), 就可以捎帶的將ACK一起發(fā)送, 此時(shí)應(yīng)用層代碼需要響應(yīng)的時(shí)機(jī)與ACK響應(yīng)時(shí)機(jī)重合的, 就可以將這兩個(gè)數(shù)據(jù)合二為一進(jìn)行發(fā)送, 結(jié)合下圖理解, 還是你和女神的日常,

圖中你給女神發(fā)送的ACK是由系統(tǒng)內(nèi)核返回的, 業(yè)務(wù)數(shù)據(jù)是由應(yīng)用程序發(fā)送的, 這兩條數(shù)據(jù)的發(fā)送本來(lái)是在不同的時(shí)機(jī)發(fā)送的, 由于延時(shí)應(yīng)答機(jī)制的存在, 就導(dǎo)致等待ACK的過(guò)程中, 接收方就要發(fā)送業(yè)務(wù)數(shù)據(jù)給發(fā)送方了, 此時(shí)就可以讓業(yè)務(wù)數(shù)據(jù)捎上這個(gè)ACK一起發(fā)過(guò)去就行了.

也就是說(shuō), 上面的ACK和業(yè)務(wù)數(shù)據(jù)本來(lái)是在不同的時(shí)機(jī)的, 但在延時(shí)應(yīng)答的情況下是可能成為相同時(shí)機(jī)的, 然后就合并發(fā)送了, 延時(shí)應(yīng)答是提高了這里合并的概率, 捎帶應(yīng)答就是針對(duì)這種能合并的情況進(jìn)行的特殊處理.

9. 面向字節(jié)流(粘包問(wèn)題)

TCP是面向字節(jié)流的, 在接收緩沖區(qū)其實(shí)是把多個(gè)數(shù)據(jù)都放到一起的, 這就導(dǎo)致應(yīng)用層去使用read()讀取緩沖區(qū)的數(shù)據(jù)時(shí), 會(huì)出現(xiàn)分不清讀到哪里才算是一個(gè)完整的應(yīng)用層數(shù)據(jù)報(bào), 由于TCP是面向字節(jié)流的, 那么一次讀 1 個(gè)字節(jié)或者讀N個(gè)字節(jié), 都是可以的, 這就導(dǎo)致一次讀到的數(shù)據(jù)可能是半個(gè)應(yīng)用層數(shù)據(jù)報(bào), 可能是一個(gè)應(yīng)用層數(shù)據(jù)報(bào), 也有可能是多個(gè)應(yīng)用層數(shù)據(jù)報(bào)...

也就是說(shuō)在TCP層次的socket API中是沒(méi)有告訴我們應(yīng)該讀幾個(gè)字節(jié)的, 具體怎么讀, 完全是由程序員自己負(fù)責(zé), 但我們所希望的是每次讀的是一個(gè)完整的應(yīng)用層數(shù)據(jù)報(bào), 這就是需要程序員自己去解決了.

其實(shí)解決方案也很簡(jiǎn)單, 程序員是可以控制應(yīng)用層協(xié)議 的, 只需要在應(yīng)用層代碼中約定好應(yīng)用層數(shù)據(jù)報(bào)和應(yīng)用層數(shù)據(jù)報(bào)之間的邊界就好了, 比如可以應(yīng)用層數(shù)據(jù)報(bào)結(jié)尾約定一個(gè)分隔符, 這樣在讀取的時(shí)候, 就能區(qū)分出一個(gè)完整的應(yīng)用層數(shù)據(jù)報(bào)了; 也可以約定好每個(gè)包的長(zhǎng)度, 讀取時(shí)先讀取長(zhǎng)度, 讓然后再讀取讀到長(zhǎng)度的字節(jié)數(shù)就能得到完整的數(shù)據(jù)報(bào)了.

10. TCP下的異常情況

在進(jìn)行TCP協(xié)議傳輸過(guò)程中會(huì)出現(xiàn)由于不可抗力導(dǎo)致的異常情況, 針對(duì)如下幾種進(jìn)行簡(jiǎn)單介紹:

????進(jìn)程崩潰了(進(jìn)程終止)

TCP連接是通過(guò)socket來(lái)進(jìn)行連接的, socket本質(zhì)上是進(jìn)程打開(kāi)維護(hù)的一個(gè)PCB, 進(jìn)程終止了, 對(duì)應(yīng)的PCB就沒(méi)了, 再對(duì)應(yīng)在文件描述表中的位置就釋放了, 就相當(dāng)于文件自動(dòng)關(guān)閉了, 這個(gè)過(guò)程和手動(dòng)調(diào)用socket.close方法沒(méi)有區(qū)別, 系統(tǒng)內(nèi)核依然會(huì)完成四次揮手的過(guò)程, 此時(shí)其實(shí)還時(shí)一個(gè)正常斷開(kāi)連接的流程.

主機(jī)關(guān)機(jī)(按照正常流程關(guān)機(jī))

主機(jī)關(guān)機(jī)首先終止的是進(jìn)程, 就和上面一樣的, 還是會(huì)觸發(fā)四次揮手, 然后正式關(guān)機(jī).

主機(jī)掉電

當(dāng)電源或網(wǎng)絡(luò)直接斷開(kāi)時(shí), 是沒(méi)有任何時(shí)間留給操作系統(tǒng)去反應(yīng)的, 所以根本來(lái)不及去完成四次揮手.

假設(shè)是接收方掉電了, 此時(shí)發(fā)送方仍然是繼續(xù)在發(fā)數(shù)據(jù)的, 發(fā)完數(shù)據(jù)要等待ACK返回, 接收方都掛了肯定時(shí)傳不了了, 那么發(fā)送方就會(huì)進(jìn)行超時(shí)重傳, 但不管怎么重傳, 都是收不到ACK的, 重傳了 幾次, 還是沒(méi)有收到ACK, 發(fā)送方就會(huì)嘗試重置TCP連接, 顯然這個(gè)重置也會(huì)失敗, 然后發(fā)送方就會(huì)單方面放棄連接了.

TCP重置連接的報(bào)文的是通過(guò)復(fù)位報(bào)文段來(lái)判斷的, 即RST, 也是TCP報(bào)頭中控制位中的一位.

再考慮發(fā)送方掉電的情況, 此時(shí)接收方會(huì)發(fā)現(xiàn), 發(fā)送方很差時(shí)間沒(méi)有數(shù)據(jù)發(fā)送過(guò)來(lái)了, 但從接受方的角度來(lái)看, 接受方不知道是發(fā)送方掛了還是發(fā)送方在組織數(shù)據(jù), 所以針對(duì)這種情況, 接受方會(huì)周

期性的給發(fā)送方發(fā)送一個(gè)探測(cè)報(bào)文, 觸發(fā)服務(wù)器的ACK, 如果沒(méi)有反應(yīng), 就說(shuō)明是發(fā)送掛了.

這樣的探測(cè)報(bào)文也被形象的叫做 “ 心跳包 ”, 用來(lái)確認(rèn)通信雙方是否處在正常的工作狀態(tài)中, 因?yàn)樾奶侵芷谛缘? 如果心跳沒(méi)了, 說(shuō)明就掛了, 心跳包是非常常見(jiàn)并且經(jīng)常用到的保活機(jī)制.

最后在這里對(duì)比一下UDP和TCP, TCP優(yōu)勢(shì)在于可靠傳輸, 在絕大部分場(chǎng)景中都需要進(jìn)行的是可靠傳輸; 而UDP優(yōu)勢(shì)在于高效率, 如果有些場(chǎng)景對(duì)于性能要求更苛刻使用UDP就很合適, 比如同一個(gè)機(jī)房?jī)?nèi)部的服務(wù)器之間通行就可以使用UDP, 因?yàn)檫@種場(chǎng)景下的網(wǎng)絡(luò)結(jié)構(gòu)相對(duì)簡(jiǎn)單, 網(wǎng)絡(luò)帶寬也是比較充裕的, 轉(zhuǎn)發(fā)設(shè)備也是比較好的設(shè)備, 整體丟包的可能性就比較小了, 這里就可以要求以更高的效率進(jìn)行傳輸; UDP還有一個(gè)天然的優(yōu)勢(shì)就是支持廣播, IP地址中有一種特殊的地址叫 “廣播IP”, 通過(guò)UDP往廣播IP上發(fā)送數(shù)據(jù)報(bào), 此時(shí)該局域網(wǎng)內(nèi)所有的設(shè)備都能收到數(shù)據(jù).

TCP是個(gè)非常復(fù)雜的協(xié)議, 上面所介紹的十大特性只是TCP中比較核心的特性, 其他的就不在這里介紹了, 傳輸層的協(xié)議也不只有UDP與TCP這兩個(gè), 比如還有KCP, QUIC等, 在游戲場(chǎng)景中經(jīng)常使用。

聲明:本內(nèi)容為作者獨(dú)立觀點(diǎn),不代表電子星球立場(chǎng)。未經(jīng)允許不得轉(zhuǎn)載。授權(quán)事宜與稿件投訴,請(qǐng)聯(lián)系:editor@netbroad.com
覺(jué)得內(nèi)容不錯(cuò)的朋友,別忘了一鍵三連哦!
贊 2
收藏 4
關(guān)注 37
成為作者 賺取收益
全部留言
0/200
成為第一個(gè)和作者交流的人吧
主站蜘蛛池模板: 平武县| 旅游| 益阳市| 湛江市| 喜德县| 当涂县| 丰台区| 竹北市| 罗平县| 清原| 博罗县| 都兰县| 安庆市| 奇台县| 邵阳市| 缙云县| 西畴县| 萝北县| 高碑店市| 增城市| 沙坪坝区| 和田市| 乐东| 沙雅县| 新乐市| 渝中区| 刚察县| 琼结县| 井陉县| 东方市| 富平县| 连城县| 惠东县| 越西县| 清流县| 林甸县| 建湖县| 阿拉善盟| 两当县| 乌审旗| 依兰县|