第十五章:傳輸控制協議(TCP)

1:基礎

1:在UDP中,進程把已經定義好邊界的報文發送給UDP進行發送,從進程發過來的報文稱爲用戶數據報,並最終稱爲IP數據報,各個數據報之間不存在聯繫

2:在TCP中,進程以字節流的方式傳遞數據,TCP是全雙工通信,每一方都有自己的發送緩存和接收緩存,這些緩存是差錯控制,流量控制,擁塞控制的基礎

3:進程以字節流方式將數據發送給運輸層,運輸層將若干個字節組成一個報文段,發送給網絡層,並形成IP數據報發送出去

4:TCP通信要先建立一條虛連接,然後交換數據,最後終止連接

 

2:特點

1:字節號

1:TCP把要發送的數據按字節編號,編號不一定從0開始,而是0~2的32次方-1的一個隨機數開始,由於TCP是全雙工通信,兩個方向的編號是獨立的

2:序號

1:傳輸層將字節流分成報文段,每個報文段的序號就是報文段第一個字節的字節號

2:當一個報文段攜帶數據和控制信息(捎帶)時,使用一個序號,當報文段只攜帶控制信息時,可能會佔用一個序號,以便接收方確認,這種報文用於連接建立,連接終止,連接異常終止

3:確認號

1:報文段中的確認號是發送方希望收到的下一個分組的序號(也就是下一個數據字節號),確認號是累積的,也就是說確認號A表示A之前的數據都已經收到

4:流量控制,差錯控制,擁塞控制

1:流量控制,差錯控制都是面向字節的

2:發送方能發的數據量受流量控制和擁塞控制

 

3:TCP報文段格式

1:TCP首部長20~60字節,可變部分是選項和填充

2:字段包括:源端口地址,目的端口地址,32位序號,32位確認號,

首部長度:20~60字節之間

控制:,可以定義此報文段類別,如同步序號,包含確認號,緊急指針有效,終止連接等

窗口大小:16位,也就是最大65535字節,這個值被稱爲接收窗口,由接收方確定

檢驗和:同樣包含IP僞首部+TCP首部+數據部分,UDP檢驗和是可選的,但TCP是必須計算的

緊急指針:當控制中的緊急標誌置位時有效,此時報文段中含有緊急數據,此數值+報文段序號=報文段緊急數據最後一個字節的字節號

選項和填充:0~40字節,後面解釋

 

4:TCP建立連接

1:三向握手

1:客戶端發送SYN報文段,控制中的SYN標誌置1,此報文段作用是同步序號,客戶端選擇了一個隨機的序號,並作爲此報文段序號發送給服務端,此報文段不包含確認號和窗口大小,只有當一個報文段中包含了確認號時,定義窗口大小纔是有意義的,因爲要發送確認號,也就是接收到數據,接收窗口會產生改變,此時告訴對方自己的接收窗口大小纔有意義,此報文段還可以包含一些選項,SYN報文段是一個控制報文段,不攜帶數據,但它要消耗一個序號,以後發送數據時序號要加1

2:服務器發送SYN+ACK報文段,控制中的SYN和ACK標誌置1,此報文段同樣同步服務器序號,由於包含了確認ACK,則要告訴客戶端自己的接收窗口大小,SYN+ACK報文段不攜帶數據,但要消耗序號

3:客戶端發送ACK報文段,告訴服務端自己的接收窗口大小,此報文段一般不攜帶數據,所以不消耗序號,也就是使用同1所使用的一樣的序號;但在有些實現中,它攜帶客戶端的第一個數據塊,這種情況下會消耗序號

2:同時打開

當兩個進程都發出主動打開請求時,會有些特殊情況,後面解釋

3:SYN洪泛攻擊

1:原理:攻擊者僞造自己的IP,不斷向服務器發送SYN報文段,此時服務器不斷回SYN+ACK報文段,並分配了一些資源(如創建傳送控制塊(TCB)表,計時器),當數量足夠時,服務器沒有時間處理正常的連接,這類攻擊稱爲拒絕服務攻擊

2:解決方法:

(1):限制單位時間連接次數

(2):過濾IP地址

(3):使用Cookie,做到推遲資源分配,直到服務器能夠證實連接請求來自合法的IP地址,SCTP採用了這種策略

 

5:TCP數據傳送

1:數據傳送是雙向的,一個報文段攜帶數據的同時也能攜帶確認號

2:如果一個報文段不攜帶數據,則它不佔用序號,比如:前一個報文段發送的數據字節號爲1000~2000,則下一個不攜帶數據的報文段序號爲2000

3:發送帶數據的報文段時,可將控制的PUSH標誌置1,以表示推送數據

4:推送數據:

(1);發送方TCP會定義報文段的長度,並緩存應用層傳輸過來的字節流,當緩存達到數據報長度,纔會發送一個報文(當然,這應該有其他規則,比如設置了計時器,防止一直達不到此長度就不能發送數據),接收方TCP也會將接收的數據緩存,並在接收TCP認爲方便的時候才讓進程拉取數據

(2):當進程間需要快速響應時,上面的策略就顯得不合理,推送數據置1表示發送方應當儘快發送數據不要等待更多的數據到達,接收方應該儘快準備好數據當應用層拉取數據而不是等待更多的數據到達

(3):TCP實現大多忽略了推送數據此功能

5:緊急數據

(1):如果一個數據段控制的緊急指針位置1,表示此數據段攜帶有緊急數據,緊急數據從此數據段第一個字節開始,首部中的緊急指針字段定義了緊急數據最後一個字節的字節號

(2):攜帶有緊急數據的數據段和普通數據段對於應用層以下的各層沒有任何的不同,它只起一個標誌作用,真正區別它的是應用層

 

6:TCP連接關閉

1:三向握手(全關閉)

1:A向B發送報文段請求關閉連接,此報文段控制的FIN位置1,此報文段可以攜帶數據,如果不攜帶數據,則佔用一個序號

2:B向A發送ACK+FIN報文段,同樣可攜帶數據,如果不攜帶數據,則佔用一個序號

3:A向B發送ACK報文段,不消耗序號,也不能攜帶數據

2:四向握手(半關閉)

A請求關閉,但B推遲發送ACK+FIN報文段,而是繼續發送數據報文段,A可以接收數據和發送ACK報文段,但不能發送數據報文段

1:同三向握手1

2:B發送ACK報文段,表示同意關閉連接,然後B繼續發送數據報文段,A回覆ACK報文段

3:同三向握手2

4:同三向握手3

 

7:連接復位

連接復位採用控制的RST(復位)標誌完成,它有以下功能

1:拒絕連接請求

A的TCP向一個不存在的端口發送連接請求,另一端TCP就可能發送RST==1的報文段拒絕這個請求

2:異常終止連接

A出現異常,並希望放棄一條在用的連接,則發送RST==1報文段終止這條連接

3:終止空閒連接

A發現一條連接長時間空閒,則發送RST==1報文段終止這條連接,該過程同異常終止連接是一樣的

 

8:狀態轉換圖

接下來從整體的角度說明連接的生命過程和特殊情況

1:連接建立(一般正常情況下)

A發送SYN,B回覆SYN+ACK,A回覆ACK

2:連接建立(雙方同時連接)

雙方同時發送SYN,雙方都受到SYN後,又都同時回覆SYN+ACK,此時連接建立

3:拒絕連接

A發送SYN,B回覆RST+ACK

4:連接關閉(四向握手半關閉)

1:A發送FIN,B回覆ACK,B繼續發送數據,A可以回覆ACK但不能發送數據,B發送FIN+ACK,A回覆ACK並啓動計時器,計時器超時就關閉連接,釋放資源

2;之所以要設定計時器,是因爲B發送了FIN+ACK會啓動一個計時器並等待A的ACK(對於所有的報文段,都會啓動計時器,參考選擇重發協議),如果A一收到FIN+ACK發送ACK後就關閉自己,如果此ACK丟失,B的計時器就會不停的超時重發FIN+ACK但又永遠也等不到ACK,所以這裏A設定一個計時器,如果在計時器內沒有再收到FIN+ACK,則認爲對方已經收到ACK了

3:計時器超時時間是最大報文段壽命(MSL)的兩倍,MSL是一個報文段被丟棄之前在因特網中能夠生存的最大時間,MSL常用數值爲30~60秒

5:連接關閉(三向握手全關閉)

A發送FIN,B回覆FIN+ACK,A回覆ACK並設置MSL

6:連接關閉(同時關閉)

雙方都發送FIN,然後雙方都回復ACK並設置MSL,超時後都關閉

7:化身

AB通信,然後關閉連接,馬上又開始新連接,使用同樣的套接字(源IP,目的IP,源端口,目的端口),這樣的新連接稱爲舊連接的化身,則新的連接可能會收到上一次連接的數據,爲了避免這個問題,TCP協議規定化身必須在2MSL之後出現,但在一些實現中,如果化身使用的初始序號大於之前連接最後的序號,也可以不需要此限制

8:異常終止連接

A發送RST+ACK報文段,立即進入終止狀態,丟棄緩存中所有數據,B收到此報文段,同樣進入終止狀態,並丟棄緩存所有數據

 

9:TCP中的窗口

1:發送窗口

1:發送窗口會收縮,大小由接收方和擁塞控制決定的,TCP滑動窗口採用字節編號

2:在Windows中,參考我的其他文章,在發送窗口和進程緩衝區之間,還有一個緩存

3:TCP協議只使用一個計時器

2:接收窗口

1:接收窗口不會收縮,接收窗口能從發送方獲得的數據大小稱爲rwnd

2:rwnd=緩存大小-緩存中等待進程拉取的數據字節數,緩存大小一般實現爲幾千字節

3:TCP確認號機制是累積確認,在新版TCP中同時使用累積確認和選擇確認

4:補充說明一點:以Windows爲例,接收窗口的緩存大小就是起操作系統核心TCP層實現的緩存大小,看一些資料應該是17KB,接收窗口可能不需要應用層設置,它直接設置爲操作系統TCP層緩存最大值,這樣效率是最高的

 

10:流量控制

1:數據整個流動情況是:發送進程推送數據到運輸層,運輸層推送數據到接收方運輸層,接收方拉取運輸層數據到應用層,所以流量控制在兩方面:

(1):發送方運輸層反向控制發送方進程,通過對Send()函數返回失敗來控制

(2):接收方運輸層反向控制發送方運輸層

2:接收方指定rwnd值可能會導致發送窗口的縮小,此時發送窗口右壁向左收縮,有些實現強制發送窗口不能收縮(只是右壁不能向左移動),如果沒有此限定,要注意一個問題:

發送窗口右壁的收縮可能會導致已經發送出去的數據落在了發送窗口外面,要避免這種情況的一種方法是:接收方延遲提交接收窗口大小,直到窗口比較大爲止

3:窗口關閉:接收方可以發送接收窗口大小爲0的報文段用來暫時關閉窗口,窗口關閉一般用於接收方暫時不希望接收數據的情況,收到此報文段後,發送方不會真正將自己的窗口大小設置爲0,但會暫停發送數據,直到收到一個新的報文段爲止,這期間,其實發送方仍然能夠發送具有一個字節的報文段,稱爲探測,用於防止死鎖

 

11:糊塗窗口綜合症

1:名詞解釋

當發送方數據以很小的方式產生或者接收方數據以很小的方式消耗,對於每一個小量數據都會形成一個報文段,其中首部遠大於數據部分,這樣帶寬的利用率是非常低的,這就是糊塗窗口綜合症

2:發送方解決方案:Nagle

採用Nagle,在Windows平臺下的實現是:如果發送緩衝暫存的數據超過了最大報文段則立即發送,不然等待上一個ACK到達再發送下一個報文段

3:接收方產生原因

當接收緩存滿時,接收方進程Recv()一塊很小的數據,導致發送方同樣發送一塊很小的數據

4:接收方解決方案

(1):Clark:接收方只要數據到達就返回ACK,但設定一個闕值,低於此值返回的ACK窗口大小設置爲0

(2):推遲確認:接收方數據到達不立即發送ACK並設置一個計時器(500ms),讓接收緩存有機會釋放空間,當超時或者空間大於最大報文段時發送ACK,此方法如果和網絡狀況吻合能節約帶寬,如果不吻合有可能造成數據重發

5:Windows平臺下可能產生的例子

當設置發送緩衝爲0,啓用Nagle算法,應用層投遞20000個小包,則每一個小包都會對應一個報文段,並且要等待ACK才能發送下一個報文段,顯然這樣的效率是非常低下的

 

12:差錯控制

1:確認

1:控制報文段不攜帶數據,但要消耗一個序號,它也需要被確認

2:確認ACK不需要被確認,也不消耗序號

3:確認類型:

(1):累積確認ACK

(2):選擇確認SACK:SACK要報告失序的數據塊和重複的報文段塊,由於TCP首部沒有空閒放這些東西,所以SACK通過TCP首部末尾的選項來實現

2:產生確認的規則

不同的實現有不同的規則,以下這些是最常用的,順序不代表其重要性

1:A向B發送的數據報報文段,必須捎帶一個確認

2:A收到B的正確報文段,但A沒有想發給B的數據,同時前一個報文段也已經確認過了,那麼以下兩種情況會立即發送確認:

(1):又收到一個B的報文段

(2):超時(一般是500ms)

3:A收到一個正確的報文段,但前一個報文段還沒確認,立即發送ACK,也就是不能有兩個以上按序到達的報文段未被確認,這和2(1)實現方式不同,2(1)是隻要收到一個報文段,不管是不是按序到達的,都會發送確認

4:A收到比自己期望收到的序號S大的報文段,立即發送ACK(S),這會導致對丟失報文段的快重傳

5:當一個丟失的報文段到達,馬上發送ACK+自己期望的下一個序號

6:當一個重複的報文段到達,丟棄報文段,馬上發送ACK+自己期望的下一個序號

3:重傳

1:發送方爲每條連接設置一個重傳超時(RTO),當RTO超時,則發送序號最小的已發報文段並重啓計時器,RTO值是動態的,它根據報文段往返時間(RTT)更新

2:如果發送方收到三個相同的ACK,則立即發送報文段而不必等待RTO超時,這成爲快重傳

4:失序

TCP不會丟棄失序到達的報文段,也不會提交失序的報文段給進程

 

13:TCP數據傳送流程圖

1:正常運行

接收方收到一個包,如果沒有數據發送給發送方,則啓動ACK延遲計時器(Windows下200ms),在超時之前如果又收到報文段,則立即發送ACK;如果超時,同樣發送ACK

2:報文段丟失

發送方發送第一個報文段時,啓動RTO計時器(重傳計時器),如果RTO超時,則重傳序號最小的未被確認的報文段,RTO具體參考後面

3:快重傳

當RTO計時器超時之前,發送方發送了多個報文段,假如第一個報文段丟失,接收方接收後面幾個,每次都會收到報文段立即發送ACK,當發送方收到三個ACK並且具有相同的確認值,則立即重傳報文段並重置RTO

4:延遲的報文段

發送方發送報文段A,超時後重發A*,A*可能比A更先到達接收方,接收方接收了A*後,如果A到達,會被拋棄

5:重複的報文段

同4基本一樣,丟棄後到的報文段

6:自動糾正丟失的ACK

由於TCP的ACK是累積確認,當前一個ACK丟失,後一個ACK可能會確認更大的數值,這樣的效果是前一個ACK的丟失是完全沒影響

7:因確認丟失而產生的死鎖

接收方發送ACK並設置rwnd=0,請求發送方暫時關閉窗口停止發送,當接收方希望恢復發送時,會發送一個rwnd!=0的ACK,但此ACK可能會丟失,導致發送方在等待此ACK,接收方在等待數據的死鎖情況

死鎖:雙方都在等待對方的響應

解決方法:設置持續計時器,後面討論

8:備註

如上討論,發送方有RTO計時器,接收方有ACK延遲計時器,當通信是雙向時,一方根據實際情況可能會有相應計時器產生

 

14:擁塞控制

 擁塞控制分爲開環擁塞控制和閉環擁塞控制

1:擁塞窗口

1:除接收方能控制發送方發送窗口外,網絡是控制發送方窗口大小的第二個實體

2:真正發送窗口大小=min(rwnd,cwnd);rwnd:接收窗口;cwnd:擁塞窗口

2:擁塞策略

1:擁塞策略處於三個階段:慢開始,擁塞避免,擁塞檢測

2:綜述:慢開始階段,發送方以非常小的速度開始發送數據,但很快把速度增加到一個門限值,然後進入擁塞避免階段,數據率增長開始放慢,最後,只要一檢測到擁塞,發送方又回到慢開始或者擁塞避免階段

3:慢開始:擁塞窗口大小從1個最大報文段的長度(MSS)開始,MSS的數值是在建立連接時同名選項確認的,每當到達一個ACK,cwnd就加1,發送窗口就會擴大,由此可知,cwnd會以指數增長,當到達ssthresh(慢開始門限)時,此階段結束

4:擁塞避免(加法增加):擁塞窗口大小按照加法規律增長,直到檢測到擁塞爲止;每當一個窗口的數據被確認後,擁塞窗口才+1;和慢開始不同的時,此算法針對窗口,而慢開始針對單獨數據報

5:擁塞檢測(乘法減少):發送方能夠檢測到擁塞已經發生的唯一現象就是需要重傳一個報文段,可能是RTO超時或者收到連續三個相同ACK

當一個報文段被路由器丟棄後,會發送ICMP差錯報文,但這裏不通過ICMP檢測報文的丟失,而是通過RTO超時

此時TCP實現一般會有以下兩種反應:

(1):如果是RTO超時,擁塞可能性很大,TCP有以下強烈反應

A:門限值設定爲當前窗口大小的一半

B:把cwnd重新設置爲一個報文段

C:狀態轉移到慢開始

(2):如果是收到3個ACK,擁塞可能性較小,TCP有以下較弱反應

A:門限值設定爲當前窗口大小的一半

B:把cwnd設爲門限值(也就是當前窗口大小的一般)

C:啓動擁塞避免階段

 

15:TCP的計時器

1:RTO重傳計時器

按照書上的理解是:當發送發送窗口中第一個報文段時,啓動此計時器,當這個計時器超時後,重發發送窗口第一個未被確認的報文段,當超時時發送窗口爲空,則停止此計時器

但是我想不通的是:如果發送一直在進行,隨着窗口滑動,發送窗口一直有數據,那將肯定會超時,這時是如何處理的,留着這個疑問在這裏了

1:RTT,報文段往返時間即RTT,在TCP中,任何時刻都只能有一個正在進行的RTT測量,測量RTT需要用到選項的時間戳

2:RTT採用加權平均進行計算,RTO=RTT加權平均+RTT偏差平均

3:Karn算法:RTT由於要採集報文段往返時間,如果一個報文段被超時重傳,當收到ACK時不知道是舊報文段的ACK還是重傳報文段的ACK,Karn採用一種簡單的方法:在計算RTO時不採用重傳的RTT

4:指數退避:如果發生重傳,RTO的數值就加倍

2:持續計時器

1:接收方發送rwnd=0的ACK給發送方,通知發送方暫時關閉窗口,停止發送報文段,當接收方想繼續接收取報文段時,發送rwnd!=0的ACK,由於確認報文段不能被再次確認,並且不會設置RTO重傳計時器,所以當此ACK丟失後,會造成死鎖

2:爲了解決死鎖問題,當發送方收到rwnd=0的報文段時,啓動持續計時器,超時默認值爲RTO大小,當超時時,發送1字節數據的探測報文段,此報文段有序號,但此序號不需要被確認,並且以後發送數據也可以重用此序號,如果接收方已經發送了rwnd!=0的ACK,當接收方收到探測報文段時,會重發此ACK,如果接收方想維持rwnd=0,則不響應,此時,發送方會將持續計時器的值加倍並重啓計時器,如果往返,當持續計時器值達到一個門限則不再增加(通常60s),在這以後,發送方每隔60秒發送一次探測報文段,直到窗口被打開

3:保活計時器

1:保活計時器用於在連接長時間靜默,確認連接是否有效

2:服務器設置保活計時器,當收到客戶端一塊數據時重置此計時器,一般爲2個小時

3:超時後,服務器發送探測報文段,若連續發送10個探測報文段沒收到響應,則終止這個連接

4:TIME-WAIT計時器

連接終止時使用,防止最後一個ACK丟失,當發送ACK出去後,還有FIN到達,則再次發送ACK

 

16:選項

1:最大報文段長度MSS

最大報文段長度選項定義了能夠被接受的最大TCP報文段數據部分長度,該值16位,MSS在連接建立階段確認,如果一方沒有定義,則使用默認值536字節,連接期間不能改變該值

2:窗口擴大因子

首部窗口大小字段定義了滑動窗口大小,長16字節,但可能不夠用,所以窗口擴大因子作用爲:實際窗口值=首部窗口值*2的窗口擴大因子次方,窗口擴大因子最大255,但在TCP/IP中最大值爲14,也就是2的30次方,因爲窗口大小不能超過序號最大值

在數據傳輸過程中,窗口大小可以改變,但窗口擴大因子不能改變

3:時間戳

請求連接的一方SYN中宣佈一個時間戳,SYN+ACK如果也能收到時間戳,則表示允許使用時間戳,否則不再使用

時間戳選項有兩個應用:測量往返時間防止序號繞回

1:測量往返時間RTT:

 發送方準備好一個報文段,並讀取系統時間,將此時間插入到時間戳字段中,接收方收到這個字段後,複製出來,當累積確認包含了對此報文段的確認時,就將此時間戳複製到選項的時間戳回送回答中,發送方收到報文段後獲得當前系統時間,並減去時間戳回送回答的時間,這就是RTT

2:防止序號繞回PAWS:

序號爲32位,當數據量很大時,可能發生繞回,使用序號+時間戳可以唯一的標識一個報文段

3:允許SACK和SACK選項:

TCP採用累積確認方式,但當報文段丟失,失序到達,重複到達時,累積確認不能報告這些情況,於是提出選擇確認(SACK),它通過選項來報告這些錯誤,以使發送方能更精確的應對

此方法詳情見書上

 

17:TCP軟件包

對於每條連接,都會存儲在一張表中,並新建一行,這個表稱爲傳輸控制塊TCB

TCB包含的字段包括:

狀態:連接此時的狀態

進程:使用該連接的進程

本地IP,本地端口,遠程IP,遠程端口

本地窗口,遠程窗口

發送序號,接收序號

已發送ACK號

往返時間:保存多個RTT

超時值:多個計時器的超時值

緩存大小

緩存指針

發佈了90 篇原創文章 · 獲贊 5 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章