前言
一直忙著面試,拿到了比較喜歡的offer,終于空出時間繼續做做筆記,嗯,工作要換了,但學習停不了! 有機會也會出點這段時間遇到的奇葩面經~
概述
一般來說,我們總希望數據傳輸的更快一點。
但如果發送方把數據發送的過快,接收方就可能來不及接收,這就會造成 數據的丟失。
所謂 流量控制(flow control) 就是讓發送方的發送速率不要太快,要讓接收方來得及接收。
利用滑動窗口實現流量控制
利用 滑動窗口 機制可以很方便地在 TCP 連接上實現對發送方的 流量控制。
下面我會舉一個例子來說明如何利用滑動窗口機制進行流量控制。ok,來看看本帥畫的美圖:
假設 A 向 B 發送數據。在連接建立時, B 告訴了 A: “我的接收窗口 rwnd = 400” (這里的 rwnd 表示 receiver window)。 因此, 發送方的發送窗口不能超過接收方給出的接收窗口的數值。 請注意, TCP的 窗口單位是字節,不是報文段。 TCP 連接建立時的窗口協商過程在圖中沒有顯示出來。
再設每一個報文段為 100 字節長,而數據報文段序號的初始值為 1(見圖中第一個箭頭上面的序號 seq = 1。 圖中右邊的注釋可幫助理解整個流量控制的過程)。
請注意, 圖中箭頭上面大寫的 ACK 表示首部的確認為 ACK, 小寫 ack 表示確認字段的值。
這里我們可以看到, 接收方主機 B 進行了三次流量控制。 第一次把窗口減小到 rwnd = 300, 第二次又減到 rwnd = 100, 最后減到 rwnd = 0 ,即不允許發送方再發送數據了。 這種使 發送方暫停發送的狀態將持續到主機 B 重新發送一個新的窗口為止。
另外, 可以看到 B 主機發送了三個報文段 (ACK = 1, xxx) , 都設置了 ACK = 1 ,也是只有在 ACK = 1 時確認號字段才會有意義。
持續計時器
現在我們考慮一種情況,在上圖中,B 向 A 發送了零窗口的報文段不久后, B 的接收緩存又有了一些存儲空間。 于是 B 向 A 發送了 rwnd = 400 的報文段。 然而這個報文段在傳送過程中 丟失 了。 A 一直等待收到 B 發送的數據。如果沒有其它措施, 這種互相等待的 死鎖 局面將一直持續下去。
為了解決這個問題, TCP 為每一個連接設有一個 持續計時器(persistence timer)。 只要 TCP 連接的一方收到對方的零窗口通知,就啟動持續計時器。 若持續計時器設置的時間到期,就發送一個 探測報文段 (僅攜帶 1 字節的數據), 而對方就在確認這個探測報文時返回現在的窗口值。 如果窗口仍然為零, 那么收到這個報文段的一方就重新設置持續計時器。 如果窗口不為零, 那么死鎖的僵局就打破了。
必須考慮的傳輸效率
前面已經講過, 應用進程把數據傳送到 TCP 的發送緩存后, 剩下的發送任務就由 TCP 來控制了。可以使用不同的機制來控制 TCP 報文段的發送時機。 例如:
- 第一種機制是 TCP 維持一個變量, 它等于 最大報文段長度 MSS。 只要緩存中存放的數據達到 MSS 字節時, 就組裝成一個 TCP 報文段發送出去。
- 第二種機制是由發送方的應用進程指明要求發送報文段, 即 TCP 支持的 推送(push) 操作。
- 第三種機制是發送方的一個計時器期限到了, 這是就把當前已有的緩存數據裝入報文段(長度不能超過 MSS) 發送出去。
但是,如何控制 TCP 的發送時機仍然是個較為復雜的問題。
舉個例子
一個交互式用戶使用一條 TELNET 連接 (運輸層為 TCP 協議)。 假設用戶只發 1 個字符。加上 20 字節首部后, 得到 21 字節長的 TCP 報文段。再加上 20 字節的 IP 首部, 形成 41 字節長的 IP 數據報。
(1字符) + (20TCP首部) + (20IP首部) = (41IP數據報)
在接收方 TCP 立即發出確認,構成的數據報是 40 字節長(假定沒有數據發送)。若用戶要求遠程主機回送這一字符, 則又要發送 41 字節長的 IP數據報 和 40字節長的確認 IP 數據報。這樣,用戶僅發一個字符時線路上就需要傳輸總長度為 162 字節共 4 個報文段 ,噢吉納我的天,這也太多了把!?
這么看,當帶寬比較差的情況下,這種傳輸方式效率就很低下。因此適當的推遲回復確認報文,并盡量少用捎帶確認的方法。
Nagle 算法
在 TCP 中廣泛使用的 Nagle 算法。
若發送應用進程把要發送的數據逐個字節地發送到 TCP 的發送緩存。 則發送方就把第一個數據字節先發出去,把后面到達的數據字節都緩存起來。當發送方收到對第一個數據字符的確認后,再把發送緩存中的所有數據組裝成一個報文段發送,同時繼續對隨后到達的數據進行緩存。只有在收到對前一個報文段的確認后才繼續發送下一個報文段。當數據到達較快而網絡速率較慢時,用這樣的方法可明顯減少所用的網絡帶寬。
Nagle 算法還規定,當到達的數據已達到發送窗口大小的一半或者已達到報文段的最大長度時,就立即發送一個報文段。這樣做,就可以有效的提高網絡的吞吐量。
糊涂窗口綜合癥(silly window syndrome)
這個問題有時候也會使 TCP 性能變差。例如:
TCP 接收方的緩存已滿,而交互式的應用進程一次只從接收緩存中讀 1 個字節 (接收緩存騰出 1 個字節), 然后向發送方發送確認,并把窗口大小設置為 1 個字節(但發送的數據報是 40 個字節長)。接著,發送方又發來 1 個字節的數據(這里發送方的 IP 數據報是 41 字節長)。 接收方返回確認,仍然將窗口設置為 1 個字節。這樣下去就會嚴重影響到網絡傳輸的效率。
如何解決?
可以讓 接收方等待一段時間, 使得接收緩存已有足夠空間容納一個最長報文段,或者 等到接收緩存已有一半的空閑時間 。只要出現這兩種情況之一,接收方就發出確認報文,并向發送方通知當前窗口大小。此外,發送方也不要發送太小的報文段,而是把數據積累成足夠大的報文段,或達到接收方緩存空間的一半大小。
這兩種方法可以配合使用,使得在發送方不發送很小的報文段的同時,接收方也不要在緩存剛剛有了一點小的空間就急忙把這個很小的窗口大小信息通知給發送方。
總結
不知道還記不記得,優化http網絡速率的主要的兩個問題,就在于 延遲 和 帶寬 ,而本文可以得出,TCP 的流量控制其實就是在帶寬上做優化,怎么在更短的時間內讓接收方和發送方傳輸更多的數據?
重點還是在于 滑動窗口機制 、 避免數據傳輸浪費 和 避免短數據傳輸 的情況。
另外還有 TCP的擁塞控制 相關的知識我會后續記錄。
這里主要回顧了下大學的計算機網絡,~如果有影響到的地方,可以聯系我刪除:fzfz2007@163.com