閱讀需要對TCP報文頭部字段以及它們的字段有一定了解。
一. 原理
TCP是全雙工通信,因此每一方的滑動窗口都包括了接收窗口+發送窗口,接收窗口負責處理自己接收到的數據,發送窗口負責處理自己要發送出去的數據。滑動窗口的本質其實就是維護幾個變量,通過這些變量將TCP處理的數據分為幾類,同時在發送出一個報文、接收一個報文對這些變量做一定的處理維護。
發送窗口如上圖 :
(1)N是發送窗口的起始字節,也就是說:字節序號 < N的字節都已經發送出去且已經收到ack,確認無誤了;
(2)nextSeq就是下一次發送報文的首部Seq字段(Seq即b第一個字節的序號,這些這里不講了),表示字節序號在 [N,nextSeq)區間的都已經使用過,發送出去了,但是還未收到ack確認;
(3) N+size就是窗口的最后一個可用字節序號,size是發送窗口的大小,就是每次接收到的報文中的Win字段的值,Win字段其實就是對方接收窗口的大小。
如何讓維護這幾個值呢?
(1)每接收到一個一個報文要做如下事情:檢查接收報文的ack,將N 置為 ack,即往前移到ack這個值;讀取報文中的Win字段值,即對方的最新接收窗口大小,從而更新N+size的值。
(2)每發送一個報文,就更改nextSeq的值,發送了多少個字節就把nextSeq往前移多少,但是不要超出N+size。
下面看接收窗口:
同樣是維護幾個關於字節序號的變量,與發送窗口類似,只不過接收窗口的字節序號都是接收到的字節。幾個變量的意義如下:
(1)J1表示:字節序號 < J1的字節已經接收到了,即已經發出ack確認了,也可以說可以給程序使用了。程序讀取接收緩沖區k個字節,J1會增加k。
(2)J2表示:[J1,J2)區間的字節已經完整、有序的接收到了,但還在緩沖區,沒有發出ack進行確認,隨時可以供程序讀取。
(3)J3就是接收窗口的最后一個可接收字節,由J1+接收窗口的大小算出。超過J3的字節發來是拒絕接收的(一般也不會收到,因為發送窗口的大小是根據接收窗口來的,不可能會超過)。
如何維護呢?
(1)發送一個報文時:將J1的值填入報文首部的ack字段;將 (J3-J2) 的差值填入首部Win字段,告訴對方我還有多 大空間可以接收。
(2)接收到一個報文時,如果報文順序沒出錯,則將移動J2,接收到多少字節就移動多少。如果報文按序到達,下一個收到的報文的Seq值就應該等於J2,如果不相等說明中間有報文丟失了,就不移動J2,從 而接下來發送的報文ack一直是同一個:J2,也就是重復確認,相應的又有快重傳(扯遠了,不詳細說了)。
滑動窗口原理總結
因為窗口的起始值在開始后就會慢慢的增加,也就是右移,所以這也是滑動窗口名字的由來。實際上就是在接收報文、發送報文維護幾個關於ack 、字節序號、seq(報文起始字節序號)的變量值。關於ack、seq、WinSize總結如下:
(1)發送報文ack是怎么來的,接收到報文時ack又是怎么用的:
發送報文時從接收窗口拿J2的值填到報文首部的 ack;接收報文時拿到ack后將發送窗口的起始值N更新為ack。
(2)發送報文seq是怎么來的,接收到報文時seq又是怎么用的:
發送報文時從發送窗口拿到nextSeq的值填到報文首部的seq字段;接收到報文后查看報文的seq字段是否是接收窗口的J2,是就將J2連續右移報文長度個單位。
(3)發送報文Win是怎么來的,接收到報文時Win又是怎么用的:
發送報文時,從接收窗口拿到 (J3-J2)的這個差值填到首部的Win字段;接收到報文時拿到首部的Win字段的值,假設為size,則更新發送窗口的結束位置為 N+size
二. 作用
我們知道,網絡層實際上就是一個數據報網絡,本身是不面向連接的,也不提供可靠有序完整的服務。udp直接使用數據報網絡,所以它只是提供盡力交付的服務,在傳輸過程中數據包可能會丟失。那么為什么TCP同樣是使用數據報網絡,卻能夠實現面向連接的可靠傳輸服務呢?實際上,是因為TCP的可靠傳輸不是依靠下層的網絡層完成的,完全是在傳輸層上,在軟件層面完成的,具體體現在TCP協議有收到確認、有超時重傳、有重復確認-快重傳等額外步驟。
正是因為收發數據報相比於udp多了這么多限制,所以才實現了可靠傳輸的服務,這些額外的步驟也帶來了不少問題:需要在首部字段多設置不少的字段用來完成那些步驟,多做的步驟會有時間、資源開銷等。這些首部字段最重要的就是 ack 、字節序號、seq,這是保證能正確的重傳丟失報文的基礎。
但是,有了這些也只能這樣做:發送報文,等待對方確認,收到確認后繼續發下一報文,效率非常低。而有了滑動窗口,通信雙方就不用發送一個報文后,收到此報文的確認后再發送下一個報文,而是可以連續發送多個報文,只要別超過窗口大小限制;還有就是:TCP開銷比udp大,一旦網絡擁塞或報文丟失又會造成報文重發,而這些重發又加重了擁塞,所以TCP里要嚴格控制發送速率防止網絡擁塞,滑動窗口根據接收方的Win大小很好的限制了發送方的發送速率。
總結就是:(1)滑動窗口允許發送方連續發送多個報文(2)根據對方接收窗口大小限制發送方的發送速率,防止擁塞
(注意:實際中並不一定按對方接收窗口rwnd大小來決定發送速率,因為沒有考慮網絡擁塞情況。擁塞控制中同樣會決定發送窗口cwnd大小,最后發送時取 MIN(cwnd,rwnd))
三. 過程示例
給個書上的示例:
B發送的的rwnd就是報文頭部Win字段的值。解釋個小問題:為什么前面說發送窗口的結束位置是N+size,而不是nextSeq+size?因為[N,nextSeq)這些字節是發送出去但未收到確認的,是隨時有可能重發的,因此可發送的區間要從N算起,到N+size。