1 概述
- TCP提供可靠的運輸層。
- 可靠性保證之一:確認從另一端收到的數據。
- 但數據和確認都有可能會丟失。TCP通過在發送時設置一個定時器來解決這種問題。
- 如果當定時器溢出時還沒有收到確認,它就重傳該數據。
- TCP對於每個連接TCP管理4個不同的定時器:
- 重傳定時器:使用於當希望收到另一端的確認。
- 2MSL定時器:測量一個連接處於TIME_WAIT狀態的時間。
- 堅持(persist)定時器:使窗口大小信息保持不斷流動,即使另一端關閉了其接收窗口
- 保活(keepalive)定時器:用於檢測一個空閑連接的另一端何時崩潰或重啟。
2 TCP的狀態機
超時情況:
-
建立連接時SYN超時:
- client主動打開連接,發送SYN報文給server
- server收到后被動打開,發送SYN+ACK報文給client,此時client直接下線
- 導致server無法收到client的ACK報文,即該TCP連接既沒有建立也沒有斷開,所以server又會重傳SYN+ACK報文
- 在Linux下,默認重試次數為5次,重試的間隔時間從1s開始每次都翻倍,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s,總共31s,第5次發出后還要等32s都知道第5次也超時了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s,TCP才會把斷開這個連接。
-
SYN Flood:
- 惡意偽造大量的TCP連接並斷開Client,導致Server維持了大量的TCP連接,且需要63秒后才會斷開,即63秒內的TCP連接數足夠多時,Server將承受不住,導致正常的連接不能處理。
- 處理方案:
- 減少Server的重試次數
- 增大SYN的連接數
- 處理不過來的連接直接丟棄
-
ISN的初始化:
- ISN不是每次建立建立連接時都從1開始
- 問題:因為如果從1開始,client發送了30個segment給server,此時網絡斷開,過會client重連,又使用ISN=1來發送報文,但之前的報文已經送到了,即server認為client的ISN(初始化序列號)為30,但是實際上為1。
- 解決方法:ISN會和一個假的時鍾綁在一起,這個時鍾會在每4微秒對ISN做加一操作,直到超過2^32,又從0開始。
- MSL(Maximum Segment Lifetime):TCP段的最大存活時間,4ms*2^32=4.55小時,即TCP段存活時間不超過4.55小時就不會重用ISN。
-
MSL 和 TIME_WAIT:
- 在TCP的狀態圖中,從TIME_WAIT狀態到CLOSED狀態,有一個超時設置,這個超時設置是 2 * MSL。
- TIME_WAIT(2 * MSL):確保有足夠的時間讓對端收到了ACK,如果被動關閉的那方沒有收到Ack,就會觸發被動端重發FIN,一來一去正好不超過2 * MSL。
- 有足夠的時間讓這個連接不會跟后面的連接混在一起(有些路由器會緩存IP數據包,導致連接被重用)。
3 TCP重試機制(發送方)
(1)概述
TCP要保證所有的數據包都到達,必需有重傳機制。
發送端發了1,2,3,4,5一共五份數據,接收端收到了1,2,於是回ack 3,然后收到了4(注意此時3沒收到),此時的TCP會怎么辦?
(2)超時重傳機制
- 不回ACK,一直等待SN=3的到來,而發送方一直收不到3的ACK,就會超時重傳3.
- 這種方式會有嚴重的問題,那就是死等3,所以導致4和5已經被收到了,而發送方也完全不知道(沒有收到ACK),所以發送方可能會悲觀地認為也丟了(即有可能導致4和5的重傳)。
- 發送方的兩種選擇:
- 僅僅重傳timeout的包,節約寬帶,但是需要繼續等待后續報文ACK或者timeout重傳(效率低)。
- 重傳timeout后的所有包,浪費寬帶,而且可能做了無用功。
(3)快速重傳機制
- 如果發送方發出了1,2,3,4,5份數據,1先到了,於是就ack回2,結果2因為某些原因沒收到。3到達了,ack也回2,后面的4和5都到了,ack也回2(2沒有收到)。
- 於是發送端收到了三個ack=2的確認,知道了2還沒有送達,於是重傳2(或者2之后的所有包)。然后接收端收到了2,此時3,4,5都收到了,於是ack回6。
- 優點:不需要等待ACK超時,但是仍然沒有解決是重傳2還是重傳2與之后的所有包。
(4)SACK 方法
- SACK回復可以告知發送端需要排除哪些報文進行發送
- 該協議需要兩邊都支持。
強烈建議看看叔的文章