摘要:收到了,發(fā)送,要求下一個是,不幸又丟了。在對于包的確認(rèn)中,會同時攜帶一個窗口大小的字段。前面的滑動窗口是怕發(fā)送方把接收方緩存塞滿,而擁塞窗口,是怕把網(wǎng)絡(luò)塞滿。這里有一個公式可以看出,是擁塞窗口和滑動窗口共同控制發(fā)送的速度。
網(wǎng)絡(luò)協(xié)議 1 - 概述
網(wǎng)絡(luò)協(xié)議 2 - IP 是怎么來,又是怎么沒的?
網(wǎng)絡(luò)協(xié)議 3 - 從物理層到 MAC 層
網(wǎng)絡(luò)協(xié)議 4 - 交換機(jī)與 VLAN:辦公室太復(fù)雜,我要回學(xué)校
網(wǎng)絡(luò)協(xié)議 5 - ICMP 與 ping:投石問路的偵察兵
網(wǎng)絡(luò)協(xié)議 6 - 路由協(xié)議:敢問路在何方?
網(wǎng)絡(luò)協(xié)議 7 - UDP 協(xié)議:性善碰到城會玩
網(wǎng)絡(luò)協(xié)議 8 - TCP 協(xié)議(上):性惡就要套路深
????上次了解了 TCP 建立連接與斷開連接的過程,我們發(fā)現(xiàn),TCP 會通過各種“套路”來保證傳輸數(shù)據(jù)的安全。除此之外,我們還大概了解了 TCP 包頭格式所對應(yīng)解決的五個問題:順序問題、丟包問題、連接維護(hù)、流量控制、擁塞控制。今天,我們就來看下 TCP 又是用怎樣的套路去解決這五個問題的。
????在解決問題之前,咱們先來看看 TCP 是怎么成為一個“靠譜”的協(xié)議的。
“靠譜”協(xié)議 TCP????TCP 為了保證順序性,每個包都有一個 ID。這建立連接的時候,會商定起始 ID 的值,然后按照 ID一個個發(fā)送。
????為了保證不丟包,對于發(fā)送的包都要進(jìn)行應(yīng)答。但是這個應(yīng)答不是一個一個來的,而是會應(yīng)答某個之前的 ID,表示都收到了,這種模式稱為累計確認(rèn)和累計應(yīng)答。
為了記錄所有發(fā)送的包和接收的包,TCP 也需要發(fā)送端和接收端分別用緩存來保存這些記錄。發(fā)送端的緩存里是按照包的 ID 一個個排列,根據(jù)處理的情況分成四個部分:
第一部分:發(fā)送且已經(jīng)確認(rèn)的;
第二部分:發(fā)送尚未確認(rèn)的;
第三部分:沒有發(fā)送,但是已經(jīng)等待發(fā)送的;
第四部分:沒有發(fā)送,并且暫時還不會發(fā)送的。
????于是,發(fā)送端需要保持這樣的數(shù)據(jù)結(jié)構(gòu):
LastByteAcked:第一部分和第二部分的分界線
LastByteSent:第二部分和第三部分的分界線
LastByteAcked:第三部分和第四部分的分界線
對于接收端來講,它緩存記錄的內(nèi)容要簡單一些,分為以下三個部分:
第一部分:接收且確認(rèn)過的;
第二部分:還沒接收,但是馬上就能接收的;
第三部分:還沒接收,也沒空間接收的。
????對應(yīng)的數(shù)據(jù)結(jié)構(gòu)就像這樣:
MaxRcvBuffer:最大緩存量;
LastByteRead:這個值之后是已經(jīng)接收,但是還沒被應(yīng)用層讀取的;
NextByteExpected:第一部分和第二部分的分界線,下一個期待的包 ID。
????第二部分的窗口有多大呢?
????NextByteExpected 和 LastByteRead 的差起始是還沒被應(yīng)用層讀取的部分占用掉的 MaxRcvBuffer 的量,我們定義為 A,即:A = NextByteExpected - LastByteRead - 1。
????那么,窗口大小,AdvertisedWindow = MaxRcvBuffer - A。
????也就是:AdvertisedWindow = MaxRcvBuffer - (NextByteExpected - LastByteRead - 1)
????而第二部分和三部分的分界線 = NextByteExpected + AdvertisedWindow - 1 = MaxRcvBuffer + LastByteRead。
順序與丟包問題????接下來,我們結(jié)合上述圖例,用一個例子來看下 TCP 如何處理順序與丟包問題的。
還是剛才的圖,在發(fā)送端看來:
1、2、3 是已經(jīng)發(fā)送并確認(rèn)的;
4、5、6、7、8、9 都是發(fā)送未確認(rèn)的;
10、11、12 是還沒發(fā)出的;
13、14、15 是接收方?jīng)]有空間,不準(zhǔn)備發(fā)送的。
而在接收端看來:
1、2、3、4、5 是已經(jīng)完成 ACK,但還沒讀取的;
6、7 是等待接收的;
8、9 是已經(jīng)接收,但是沒有 ACK 的。
發(fā)送端和接收端當(dāng)前的狀態(tài)如下:
1、2、3 沒有問題,雙方達(dá)成了一致;
4、5 接收方發(fā)送 ACK 了,但是發(fā)送方還沒收到,有可能丟了,有可能還在路上;
6、7、8、9 肯定都發(fā)了,但是 8、9 已經(jīng)到了,6、7還沒打,出現(xiàn)了亂序,于是在緩存中存儲,但是沒有返回 ACK。
????根據(jù)這個例子,我們可以知道,順序問題和丟包問題都有了能發(fā)送,所以我們先來看確認(rèn)與重發(fā)的機(jī)制。
????假設(shè) 4 的確認(rèn)到了,不幸的是,5 的 ACK 丟了,并且 6、7 的數(shù)據(jù)包也丟了,這時候會怎么處理呢?
????一種方法是超時重試,也就是對每一個發(fā)送了,但是沒有 ACK 的包,都有設(shè)一個定時器,一旦超過了一定的時間,就重新嘗試。這個超時時間不宜過短,時間必須大于往返時間 RTT,否則就會引起不必要的重傳也不宜過長,這樣超時時間變長,訪問就變慢了。
????估計往返時間,需要 TCP 通過采樣 RTT 的時間,然后進(jìn)行加權(quán)平均,算出一個值,而且這個值還是要不斷變化的,因為網(wǎng)絡(luò)狀況不斷的變化。
????除了采樣 RTT,還要采樣 RTT 的波動范圍,計算出一個估計的超時時間。由于重傳時間是不斷變化的,我們稱為自適應(yīng)重傳算法(Adaptive Retransmission Algorithm)。
????如果過一段時間,5、6、7 都超時了,就會重新發(fā)送。接收方發(fā)現(xiàn) 5 原來接收過,于是就丟棄5。收到了6,發(fā)送 ACK,要求下一個是 7,7 不幸又丟了。
????當(dāng) 7 再次超時的時候,如果有需要重傳,TCP 的策略就是超時間隔加倍。每當(dāng)遇到一次超時重傳的實時,都會將下一次超時時間間隔設(shè)置為先前值的兩倍。兩次超時,就說明網(wǎng)絡(luò)環(huán)境差,不宜頻繁發(fā)送。
????可以看出,超時重發(fā)存在的問題是,超時周期可能較長。那是不是可以有更快的方式呢?
????有一個可以快速重傳的機(jī)制。當(dāng)接收方收到一個序號大于下一個所期望的報文段時,就檢測到了數(shù)據(jù)流中的一個間格,于是發(fā)送三個冗余的ACK,客戶端收到后,就在定時器過期之前,重傳丟失的報文段。
????例如,接收方發(fā)現(xiàn) 6、8、9 都已經(jīng)接收了,但是 7 沒來。于是發(fā)送三個 6 的 ACK,要求下一個是 7??蛻舳耸盏饺齻€,就會發(fā)現(xiàn) 7 的確丟了,不等超時,就馬上重發(fā)。
????除此之外,還有一種方式稱為 Selective Acknowledgment(SACK)。這種方式需要在 TCP 頭里加一個 SACK 的東西,可以將緩存的地圖發(fā)格發(fā)送方。例如發(fā)送 ACK6、SACK8、SACK9,有了地圖,發(fā)送方一下子就能看出來是 7 丟了,然后快速重發(fā)。
流量控制問題????接下來,我們再來看看流量控制機(jī)制。在對于包的確認(rèn)中,會同時攜帶一個窗口大小的字段。
????我們先假設(shè)窗口不變的情況,發(fā)送端窗口始終為 9。4 的確認(rèn)來的時候,LastByteAcked 會右移一個,這個時候,第 13 個包就可以發(fā)送了。
????這個時候,假設(shè)發(fā)送端發(fā)送過猛,將第三部分中的 10、11、12、13 全部發(fā)送,之后就停止發(fā)送,則此時未發(fā)送可發(fā)送部分為 0。
????當(dāng)對于包 5 的確認(rèn)到達(dá)的時候,在客戶端相當(dāng)于窗口再滑動了一格,這個時候,才可以有更多的包可以發(fā)送了,例如第 14 個包才可以發(fā)送。
????如果接收方處理的太慢,導(dǎo)致緩存中沒有空間了,可以通過確認(rèn)信息修改窗口的大小,甚至可以設(shè)置為 0,則發(fā)送方將暫時停止發(fā)送。
????我們可以假設(shè)一個極端情況,接收端的應(yīng)用一直不讀取緩存中的數(shù)據(jù),當(dāng)數(shù)據(jù)包 6 確認(rèn)后,窗口大小就不會再是 9,而是減少一個變?yōu)榱?8。
????為什么會變?yōu)?8?你看,下圖中,當(dāng) 6 的確認(rèn)消息到達(dá)發(fā)送端的時候,左邊的 LastByteAcked 右移一位,而右邊的未發(fā)送可發(fā)送區(qū)域因為已經(jīng)變?yōu)?0,因此左邊的 LastByteSend 沒有移動,因此,窗口大小就從 9 變成了 8。
????而如果接收端一直不處理數(shù)據(jù),則隨著確認(rèn)的包越來越多,窗口越來越小,直到為 0。
????當(dāng)這個窗口大小通過包 14 的確認(rèn)到達(dá)發(fā)送端的時候,發(fā)送端的窗口也調(diào)整為 0,于是,發(fā)送端停止發(fā)送。
????當(dāng)發(fā)生這樣的情況時,發(fā)送方會定時發(fā)送窗口探測數(shù)據(jù)包,看是否有機(jī)會調(diào)整窗口的大小。對于接收方來說,當(dāng)接收比較慢的時候,要防止低能窗口綜合征,別空出一個字節(jié)就趕緊告訴發(fā)送方,結(jié)果又被填滿了??梢栽诖翱谔〉臅r候,不更新窗口大小,直到達(dá)到一定大小,或者緩沖區(qū)一半為空,才更新窗口大小。
????這就是我們常說的流量控制。
擁塞控制問題????最后,我們來看一下?lián)砣刂频膯栴}。
????這個問題,也是靠窗口來解決的。前面的滑動窗口 rwnd 是怕發(fā)送方把接收方緩存塞滿,而擁塞窗口 cwnd,是怕把網(wǎng)絡(luò)塞滿。
這里有一個公式:
LastByteSent - LastByteAcked <= min{cwnd, rwnd}
????可以看出,是擁塞窗口和滑動窗口共同控制發(fā)送的速度。
????那發(fā)送方怎么判斷網(wǎng)絡(luò)是不是滿呢?這其實是個挺難的事情。因為對于 TCP 協(xié)議來講,它壓根不知道整個網(wǎng)絡(luò)路徑都會經(jīng)歷什么。TCP 發(fā)送包常被比喻為往一個水管里灌水,而 TCP 的擁塞控制就是在不堵塞、不丟包的情況下,盡量發(fā)揮帶寬。
????水管有粗細(xì),網(wǎng)絡(luò)有帶寬,也就是每秒鐘能夠發(fā)送多少數(shù)據(jù);
水管有長度,端到端有時延。在理想情況下:
水管里的水量 = 水管粗細(xì) x 水管長度
而對于網(wǎng)絡(luò)來講:
通道的容量 = 帶寬 x 往返延遲
????如果我們設(shè)置發(fā)送窗口,使得發(fā)送但未確認(rèn)的包的數(shù)量為通道的容量,就能夠撐滿整個管道。
如上圖所示:
假設(shè)往返時間為 8s,去 4s,回 4s,每秒發(fā)送一個包,每個包 1024 byte。
????那么在 8s 后,就發(fā)出去了 8 個包。其中前 4 個包已經(jīng)到達(dá)接收端,但是 ACK 還沒有返回,不能算發(fā)送成功。而 5-8 后四個包還在路上,沒被接收。
這個時候,整個管道正好撐滿。在發(fā)送端,已發(fā)送未確認(rèn)的為 8 個包,也就是:
帶寬 = 1024byte/s x 8s(來回時間)
????如果我們在這個基礎(chǔ)上再調(diào)大窗口,使得單位時間內(nèi)更多的包可以發(fā)送,會出現(xiàn)什么現(xiàn)象呢?
????原來發(fā)送一個包,從一端到另一端,假設(shè)一共經(jīng)過四個設(shè)備,每個設(shè)備處理一個包耗時 1s,所以到達(dá)另一端需要耗費(fèi) 4s。如果發(fā)送的更加快速,則單位時間內(nèi),會有更多的包到達(dá)這些中間設(shè)備,這些設(shè)備還是只能每秒處理一個包的話,多出來的包就會被丟棄,這不是我們希望看到的。
????這個時候,我們可以想其他的辦法。例如,這四個設(shè)備本來每秒處理一個包,但是我們在這些設(shè)備上加緩存,處理不過來的就在隊列里面排著,這樣包就不會丟失,但是缺點也是顯而易見的,增加了時延。這個緩存的包,4s 肯定到達(dá)不了接收端,如果時延達(dá)到一定程度,就會超時,這也不是我們希望看到的。
????針對上述兩種現(xiàn)象:包丟失和超時重傳。一旦出現(xiàn)了這些現(xiàn)象就說明,發(fā)送速度太快了,要慢一點。但是一開始,發(fā)送端怎么知道速度多快呢?怎么知道把窗口調(diào)整到合適大小呢?
????如果我們通過漏斗往瓶子里灌水,我們就知道,不能一桶水一下子全倒進(jìn)去,肯定會溢出來。一開始要慢慢的倒,然后發(fā)現(xiàn)都能夠倒進(jìn)去,就加快速度。這叫做慢啟動。
一個 TCP 連接開始
cwnd 設(shè)置為一個報文段,一次只能發(fā)送 1 個;
當(dāng)收到這一個確認(rèn)的時候,cwnd 加 1,于是一次能夠發(fā)送 2 個;
當(dāng)這兩個包的確認(rèn)到來的時候,每個確認(rèn)的 cwnd 加 1,兩個確認(rèn) cwnd 加 2,于是一次能夠發(fā)送 4 個;
當(dāng)這四個的確認(rèn)到來的時候,每個確認(rèn) cwnd 加 1,四個確認(rèn) cwnd 加 4,于是一次能夠發(fā)送 8 個。
????從上面這個過程可以看出,這是指數(shù)性的增長。
????但是漲到什么時候是個頭呢?一個值 ssthresh 為 65535 個字節(jié),當(dāng)超過這個值的時候,就會將將增長速度降下來。
????此時,每收到一個確認(rèn)后,cwnd 增加 1/cwnd。一次發(fā)送 8 個,當(dāng) 8 個確認(rèn)到來的時候,每個確認(rèn)增加 1/8,8個確認(rèn)一共增加 1,于是一次就能夠發(fā)送 9 個,變成了線性增長。
????即使增長變成了線性增長,還是會出現(xiàn)“溢出”的情況,出現(xiàn)擁塞。這時候一般就會直接降低倒水的速度,等待溢出的水慢慢滲透下去。
????擁塞的一種變現(xiàn)形式是丟包,需要超時重傳。這個時候,將 ssthresh 設(shè)為 cwnd/2,將 cwnd 設(shè)為 1,重新開始慢啟動。也就是,一旦超時重傳,馬上“從零開始”。
????很明顯,這種方式太激進(jìn)了,將一個高速的傳輸速度一下子停了下來,會造成網(wǎng)絡(luò)卡頓。
????前面有提過快速重傳算法。當(dāng)接收端發(fā)現(xiàn)丟了一個中間包的時候,發(fā)送三次前一個包的 ACK,告訴發(fā)送端要趕緊給我發(fā)下一個包,別等超時再重傳。TCP 認(rèn)為這種情況不嚴(yán)重,因為大部分沒丟,只丟了一小部分,cwnd 變?yōu)?cwnd/2,然后 sshthresh = cwnd。當(dāng)三個包返回的時候,cwnd = sshthresh + 3。
????可以看出這種情況下降速沒有那么激進(jìn),cwnd 還是在一個比較高的值,呈線性增長。下圖是兩者的對比。
????就像前面說的一樣,正是這種知進(jìn)退,使得時延在很重要的情況下,反而降低了速度。但是,我們仔細(xì)想一想,TCP 的擁塞控制主要用來避免的兩個現(xiàn)象都是有問題的。
????第一個問題是丟包。丟包并不一定表示通道滿了,也可能是管子本來就”漏水”。就像公網(wǎng)上帶寬不滿也會丟包,這個時候就認(rèn)為擁塞,而降低發(fā)送速度其實是不對的。
????第二個問題是 TCP 的擁塞控制要等到將中間設(shè)備都填滿了,才發(fā)送丟包,從而降低速度。但其實,這時候降低速度已經(jīng)晚了,在將管道填滿后,不應(yīng)該接著填,直到發(fā)生丟包才降速。
????為了優(yōu)化這兩個問題,后來就有了 TCP BBR 擁塞算法。它企圖找到一個平衡點,就是通過不斷的加快發(fā)送速度,將管道填滿,但是不要填滿中間設(shè)備的緩存,因為這樣時延會增加,在這個平衡點可以很好的達(dá)到高帶寬和低時延的平衡。
????下圖是 BBR 算法與普通 TCP 的對比:
小結(jié)順序問題、丟包問題、流量控制都是通過滑動窗口來解決的。這就相當(dāng)于領(lǐng)導(dǎo)和員工的備忘錄,布置過的工作要有編號,干完了有反饋,活不能派太多,也不能太少;
擁塞控制是通過擁塞窗口來解決的,相當(dāng)于往管道里面倒水,快了容易溢出,慢了浪費(fèi)帶寬,要摸著石頭過河,找到最優(yōu)值。
參考:
The TCP/IP Guide;
百度百科 - TCP詞條;
劉超 - 趣談網(wǎng)絡(luò)協(xié)議系列課;
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/40245.html
摘要:收到了,發(fā)送,要求下一個是,不幸又丟了。在對于包的確認(rèn)中,會同時攜帶一個窗口大小的字段。前面的滑動窗口是怕發(fā)送方把接收方緩存塞滿,而擁塞窗口,是怕把網(wǎng)絡(luò)塞滿。這里有一個公式可以看出,是擁塞窗口和滑動窗口共同控制發(fā)送的速度。 網(wǎng)絡(luò)協(xié)議 1 - 概述 網(wǎng)絡(luò)協(xié)議 2 - IP 是怎么來,又是怎么沒的? 網(wǎng)絡(luò)協(xié)議 3 - 從物理層到 MAC 層 網(wǎng)絡(luò)協(xié)議 4 - 交換機(jī)與 VLAN:辦公室太...
摘要:操作請求只能由啟動器設(shè)備發(fā)起事件被響應(yīng)端設(shè)備用于把有關(guān)它狀態(tài)的改變通知啟動器設(shè)備。會話將在啟動器設(shè)備請求操作事務(wù)時被關(guān)閉,并且使用響應(yīng)端設(shè)備發(fā)出的一個有效響應(yīng)成功結(jié)束該請求。標(biāo)準(zhǔn)協(xié)議節(jié)斷開事件。 歡迎關(guān)注公眾號: nullobject 。文章首發(fā)在個人博客 https://www.nullobject.cn,公眾號nullobject同步更新。 PTP/IP (PTP over IP)...
閱讀 2856·2021-11-24 09:39
閱讀 2838·2021-09-23 11:45
閱讀 3453·2019-08-30 12:49
閱讀 3428·2019-08-30 11:18
閱讀 2104·2019-08-29 16:42
閱讀 3398·2019-08-29 16:35
閱讀 1385·2019-08-29 11:21
閱讀 1984·2019-08-26 13:49