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