Tcp問題匯總


一 TCP三次握手



PS:TCP協議中,主動發起請求的一端稱為『客戶端』,被動連接的一端稱為『服務端』。不管是客戶端還是服務端,TCP連接建立完后都能發送和接收數據。

起初,服務器和客戶端都為CLOSED狀態。在通信開始前,雙方都得創建各自的傳輸控制塊(TCB)。 
服務器創建完TCB后遍進入LISTEN狀態,此時准備接收客戶端發來的連接請求。

第一次握手 
客戶端向服務端發送連接請求報文段。該報文段的頭部中SYN=1,ACK=0,seq=x。請求發送后,客戶端便進入SYN-SENT狀態。

  • PS1:SYN=1,ACK=0表示該報文段為連接請求報文。
  • PS2:x為本次TCP通信的字節流的初始序號。 
    TCP規定:SYN=1的報文段不能有數據部分,但要消耗掉一個序號。

第二次握手 
服務端收到連接請求報文段后,如果同意連接,則會發送一個應答:SYN=1,ACK=1,seq=y,ack=x+1。 
該應答發送完成后便進入SYN-RCVD狀態。

  • PS1:SYN=1,ACK=1表示該報文段為連接同意的應答報文。
  • PS2:seq=y表示服務端作為發送者時,發送字節流的初始序號。
  • PS3:ack=x+1表示服務端希望下一個數據報發送序號從x+1開始的字節。

第三次握手 
當客戶端收到連接同意的應答后,還要向服務端發送一個確認報文段,表示:服務端發來的連接同意應答已經成功收到。 
該報文段的頭部為:ACK=1,seq=x+1,ack=y+1。 
客戶端發完這個報文段后便進入ESTABLISHED狀態,服務端收到這個應答后也進入ESTABLISHED狀態,此時連接的建立完成!

為什么連接建立需要三次握手,而不是兩次握手? 
防止失效的連接請求報文段被服務端接收,從而產生錯誤。

PS:失效的連接請求:若客戶端向服務端發送的連接請求丟失,客戶端等待應答超時后就會再次發送連接請求,此時,上一個連接請求就是『失效的』。

若建立連接只需兩次握手,客戶端並沒有太大的變化,仍然需要獲得服務端的應答后才進入ESTABLISHED狀態,而服務端在收到連接請求后就進入ESTABLISHED狀態。此時如果網絡擁塞,客戶端發送的連接請求遲遲到不了服務端,客戶端便超時重發請求,如果服務端正確接收並確認應答,雙方便開始通信,通信結束后釋放連接。此時,如果那個失效的連接請求抵達了服務端,由於只有兩次握手,服務端收到請求就會進入ESTABLISHED狀態,等待發送數據或主動發送數據。但此時的客戶端早已進入CLOSED狀態,服務端將會一直等待下去,這樣浪費服務端連接資源。

 

二 TCP四次揮手

TCP連接的釋放一共需要四步,因此稱為『四次揮手』。 
我們知道,TCP連接是雙向的,因此在四次揮手中,前兩次揮手用於斷開一個方向的連接,后兩次揮手用於斷開另一方向的連接。

第一次揮手 
若A認為數據發送完成,則它需要向B發送連接釋放請求。該請求只有報文頭,頭中攜帶的主要參數為: 
FIN=1,seq=u。此時,A將進入FIN-WAIT-1狀態。

  • PS1:FIN=1表示該報文段是一個連接釋放請求。
  • PS2:seq=u,u-1是A向B發送的最后一個字節的序號。

第二次揮手 
B收到連接釋放請求后,會通知相應的應用程序,告訴它A向B這個方向的連接已經釋放。此時B進入CLOSE-WAIT狀態,並向A發送連接釋放的應答,其報文頭包含: 
ACK=1,seq=v,ack=u+1。

  • PS1:ACK=1:除TCP連接請求報文段以外,TCP通信過程中所有數據報的ACK都為1,表示應答。
  • PS2:seq=v,v-1是B向A發送的最后一個字節的序號。
  • PS3:ack=u+1表示希望收到從第u+1個字節開始的報文段,並且已經成功接收了前u個字節。

A收到該應答,進入FIN-WAIT-2狀態,等待B發送連接釋放請求。

第二次揮手完成后,A到B方向的連接已經釋放,B不會再接收數據,A也不會再發送數據。但B到A方向的連接仍然存在,B可以繼續向A發送數據。

第三次揮手 
當B向A發完所有數據后,向A發送連接釋放請求,請求頭:FIN=1,ACK=1,seq=w,ack=u+1。B便進入LAST-ACK狀態。

第四次揮手 
A收到釋放請求后,向B發送確認應答,此時A進入TIME-WAIT狀態。該狀態會持續2MSL時間,若該時間段內沒有B的重發請求的話,就進入CLOSED狀態,撤銷TCB。當B收到確認應答后,也便進入CLOSED狀態,撤銷TCB。

為什么A要先進入TIME-WAIT狀態,等待2MSL時間后才進入CLOSED狀態? 
為了保證B能收到A的確認應答。 
若A發完確認應答后直接進入CLOSED狀態,那么如果該應答丟失,B等待超時后就會重新發送連接釋放請求,但此時A已經關閉了,不會作出任何響應,因此B永遠無法正常關閉。

 

 

三 流量控制

1 滑動窗口

數據的傳送過程中很可能出現接收方來不及接收的情況,這時就需要對發送方進行控制以免數據丟失。利用滑動窗口機制可以很方便地在TCP連接上對發送方的流量控制。TCP的窗口單位是字節,不是報文段,發送方的發送窗口不能超過接受方給出的接收窗口的數值

說明: 使發送方暫停發送的狀態將持續到主機B重新發出一個新的窗口值為止,B向A發送的三個報文段都設置了ACK=1

考慮一種特殊的情況,接收方若沒有緩存足夠使用,就會發送零窗口大小的報文,此時發送方將發送窗口設置為0,停止發送數據; 之后接收方有足夠緩存,發送了非零窗口大小的報文,但是這個報文中途丟失,那么發送方的發送窗口就一直為0導致死鎖。為此,TCP為每一個連接設有一個持續計時器(Persistence Timer).當TCP連接的一方收到對方的零窗口通知時就啟動持續計數器。若持續計時器時間到期,就發送一個零窗口探測報文段(攜有1字節的數據),那么收到這個報文段的一方就重新設置持續計數器,給出現在的窗口值。

TCP規定,即使設置為零窗口,也必須接收以下幾種報文段: 零窗口探測報文段, 確認報文段和攜帶緊急數據的報文段

 

2 發送時機

  (1) TCP維持一個變量MSS,等於最大報文段長度。只要緩沖區存放的數據達到MSS字節時,就組裝成了一個TCP報文段發送出去。 
  (2) 由發送方的應用進程指明要發送的報文段,即:TCP支持推送操作。 
  (3) 發送方的一個計時器期限到了,這時就把當前已有的緩存數據裝入報文段(但長度不能超過MSS大小)發送出去。

 

3 Nagle算法

發送方把第一個數據字節發送出去,把后面到達的數據字節緩存起來。當發送方接收對第一個數據字符的確認后,再把發送緩存中的所有數據組裝成一個報文段再發送出去,同時繼續對隨后到達的數據進行緩存。只有在收到前一個報文段的確認后,才繼續發送下一個報文段。TCP規定一個連接最多只能有一個未被確認的未完成的小分組,在該分組的確認到達之前不能發送其他的小分組。當數據到達較快而網絡速率較慢時,用這樣的方法可明顯地減少所用的網絡帶寬。

Nagle算法還規定: 當到達的數據已達到發送窗口大小的一半或已經達到報文段的最大長度,就可立即發送一個報文段

 

 

4 延遲ACK:

如果tcp對每個數據包都發送一個ack確認,那么只是一個單獨的數據包為了發送一個ack代價比較高,所以tcp會延遲一段時間,如果這段時間內有數據發送到對端,則捎帶發送ack,如果在延遲ack定時器觸發時候,發現ack尚未發送,則立即單獨發送;

延遲ACK好處:

(1) 避免糊塗窗口綜合症;

(2) 發送數據的時候將ack捎帶發送,不必單獨發送ack;

(3) 如果延遲時間內有多個數據段到達,那么允許協議棧發送一個ack確認多個報文段;

 

5  當Nagle遇上延遲ACK:

試想如下典型操作,寫-寫-讀,即通過多個寫小片數據向對端發送單個邏輯的操作,兩次寫數據長度小於MSS,當第一次寫數據到達對端后,對端延遲ack,不發送ack,而本端因為要發送的數據長度小於MSS,所以nagle算法起作用,數據並不會立即發送,而是等待對端發送的第一次數據確認ack;這樣的情況下,需要等待對端超時發送ack,然后本段才能發送第二次寫的數據,從而造成延遲

 

 

 

6 糊塗窗口綜合症 
TCP接收方的緩存已滿,而交互式的應用進程一次只從接收緩存中讀取1字節(這樣就使接收緩存空間僅騰出1字節),然后向發送方發送確認,並把窗口設置為1個字節(但發送的數據報為40字節的的話)。然后,發送方又發來1個字節的數據(發送方的IP數據報是41字節),接收方發回確認,仍然將窗口設置為1個字節。這樣,網絡的效率很低。要解決這個問題,可讓接收方等待一段時間,使得或者接收緩存已有足夠空間容納一個最長的報文段或者等到接收方緩存已有一半的空閑空間。只要出現這兩種情況,接收方就發回確認報文,並向發送方通知當前的窗口大小。此外,發送方也不要發送太小的報文段,而是把數據報積累成足夠大的報文段,或達到接收方緩存的空間的一半大小。

 

 

四 擁塞控制

網絡擁塞現象是指到達通信子網中的某一部分數量過多,使得該部分網絡來不及處理,以致引起這部分乃至整個網絡性能下降的現象,嚴重時導致網絡通信業務陷入停頓出現死鎖現象。擁塞控制時通過擁塞窗口處理網絡擁塞現象的一種機制

發送報文段速率確定: 
   [1]. 全局考慮防止擁塞 <- - 擁塞窗口 (Congestion Window) - -> 發送端流量控制,發送端根據自己估計的網絡擁塞程度而設置的窗口值; 
   [2]. 接收端的接收能力 <- - 接收窗口 (Reciver Window) - -> 接收端流量控制,接收端根據目前的接收緩存大小所許諾的最新窗口值;

    發送方窗口的上限值 = Min [ rwind, cwind ] 
  rwind < cwind 時,接收方的接收能力限制發送方窗口的最大值。 
  當cwind < rwind 時,網絡的擁塞限制發送方窗口的最大值。

針對擁塞控制共有4種算法: 慢啟動 擁塞避免 快重傳 快恢復。我們假定:(1) 數據單方向傳送,而另外一個方向只傳送確認。  (2)接收方總是有足夠大的緩存空間,因為發送窗口的大小由網絡的擁塞程度來決定

 1 慢啟動

發送方維護一個擁塞窗口cwind的狀態變量,擁塞窗口的大小取決於網絡的擁塞程度,動態變化。通過逐漸增加cwind的大小來探測可用的網絡容量,防止連接開始時采用不合適的發送量導致網絡擁塞。

當主機開始發送數據時,如果通過較大的發送窗口立即將全部數據字節都注入到網絡中,由於不清楚網絡狀況,有可能引起網絡擁塞。較好的方法是試探,從小到大逐漸增大發送端擁塞窗口的cwind數值。

例子: 開始發送方先設置cwnd=1,發送第一個報文段M1,接收方接收到M1后,ACK返回給發送端,發送端將cwnd增加到2,接着發送方發送M2,M3,再次接受到ACK后將cwnd增加到4...慢啟動算法每經過一個傳輸輪次,擁塞窗口cwind就加倍。

當rwind足夠大時,為防止擁塞窗口cwind的增長引起網絡擁塞,還需要另外一個變量,慢開始門限ssthresh

當cwind < ssthresh時,使用上述慢啟動算法;

當cwind > ssthresh時,停止使用慢啟動算法,改為擁塞避免算法

 

2 擁塞避免

讓擁塞窗口cwind緩慢地增大,沒經過一個往返時間RTT就把發送方的擁塞窗口cwind+1, 而不是加倍。這樣擁塞窗口cwind線性緩慢增長,比慢開始算法的擁塞窗口增長速率緩慢地多。

無論慢啟動開始階段還是在擁擠避免階段,只要發送方判斷網絡出現擁塞(沒收到ACK),就把慢啟動門限ssthresh設置為出現擁塞時的cwind的一半。然后把擁塞窗口cwind重新設置為1,執行慢啟動算法。目的是迅速減少主機發送到網絡中的分組樹,使得發生阻塞的路由器有足夠的時間把隊列中積壓的分組處理完畢

控制過程(見上圖):

(1) TCP連接初始化,將擁塞窗口cwind設置為1個報文段,即cwind = 1

(2) 執行慢開始算法,cwind按指數規律增長,直到cwind == ssthresh時,開始擁塞避免算法,cwind按線性規律增長

(3) 當網絡發生阻塞,把ssthresh值更新為擁塞前cwind的一半(12 = 24/2),cwind重新設置為1,再按照(2)執行

 

注意⚠️:

擁塞避免是由指數增長拉低到線性增長,降低出現擁塞的可能,並不是能完全避免網絡擁塞

慢開始算法只是在TCP連接建立和網絡出現超時時才使用

 

3 快重傳和快恢復

一條TCP連接有時會因等待重傳計時器的超時而空閑較長的時間,慢開始和擁塞避免無法很好地解決這類問題,因此提出了快重傳和快恢復的擁塞控制方法

為使發送方及早知道有報文沒有達到對方,快重傳算法首先要求接受方每收到一個報文段后就立即發出重復確認。快重傳算法並非取消了重傳機制,只是在某些情況下更早地重傳丟失的報文段。即,當TCP源端收到3個相同的ACK確認時,即認為有數據包丟失,則源端重傳丟失的數據包,而不必等待RTO(Retransmission Timeout)超時。由於發送方盡早重傳未被確認的報文段。因此,采用快重傳后可以使整個網絡吞吐量提高20%

快重傳算法要求首先接收方收到一個失序的報文段后就立刻發出重復確認,而不要等待自己發送數據時才進行捎帶確認。接收方成功的接受了發送方發送來的M1、M2並且分別給發送了ACK,現在接收方沒有收到M3,而接收到了M4,顯然接收方不能確認M4,因為M4是失序的報文段。如果根據可靠性傳輸原理接收方什么都不做,但是按照快速重傳算法,在收到M4、M5等報文段的時候,不斷重復的向發送方發送M2的ACK,如果接收方一連收到三個重復的ACK,那么發送方不必等待重傳計時器到期,由於發送方盡早重傳未被確認的報文段。

控制過程(見上圖):

(1) 當發送方連續收到3個重復確認時,執行"乘法減小"算法,慢啟動門限減半,為了預防網絡發生擁塞

(2) 由於發送方現在認為網絡很可能沒有發生擁塞,因此不執行慢啟動。而是把cwind值設為新的門限值,然后執行擁塞避免算法,cwind值線性增大,避免了當網絡擁塞不夠嚴重時采用"慢啟動"算法而造成過大地減小發送窗口尺寸的現象,這就是快恢復。

 

 

 

五 其他問題

1 三次握手/四次揮手幾個字段含義

在TCP/IP協議中,有個FLAGS字段,這個字段有以下幾個標識: SYN, FIN, ACK, PSH, RST, URG, seq, ack等

SYN(synchronous)         表示建立連接

FIN(finish)                     表示關閉連接

ACK(acknowledgement)  表示響應

PSH(push)                     表示有數據傳輸

RST(reset)                     表示連接重置

URG(urgent)                  表示緊急

seq(sequence number)    表示確認號碼

ack(acknowledge number) 表示確認號碼

 

2 長連接 VS 短連接

短連接:
  • 建立連接——數據傳輸——關閉連接
  • 建立連接——數據傳輸——關閉連接
長連接:
  • 建立連接——數據傳輸...(保持連接)...數據傳輸——關閉連接 

長連接可以省去較多TCP建立和關閉的操作,減少浪費,節約資源。對於頻繁請求資源的客戶來說,適合使用長連接。不過這里存在一個問題,存活功能的探測周期太長,還有就是它知識探測TCP連接的存活,遇到惡意的連接時,保活功能就不夠使了。在長連接的應用場景下,client端一般不會主動關閉它們之間的連接,client與server之前的連接如果一直不關閉的話,會存在一個問題,隨着客戶端連接越來越多,server會扛不住,這時候server端需要采取一些策略:

(1) 關閉一些長時間沒有讀寫事件發生的連接,這樣可以避免一些惡意連接導致server端服務受損

(2) 如果條件再允許可以以"客戶端機器"粒度,限制每個客戶端的最長連接數,這樣可以完全避免某個蛋疼的客戶端連累后端服務

相比之下,短鏈接對於服務端來說管理比較簡單,存在的連接都是有用的連接,不需要額外的控制手段。但如果客戶請求頻繁,將在TCP的建立和關閉操作上浪費時間和帶寬。

 

 

3 長/短連接的應用場景

長連接多用於操作頻繁,點對點的通訊,而且連接數不能太多的情況。每個TCP連接都需要三次握手,這需要時間,如果每個操作都是先連接,再操作的話那么處理速度會降低很多,所以每個操作完后都不斷開,再次處理時直接發送數據包就OK了,不用建立TCP連接。例如: 數據庫的連接用長連接,如果用短連接頻繁的通信會造成socket錯誤,而且頻繁的socket創建也是對資源的浪費。

而像WEB網站的http服務一般都用短鏈接,因為長連接對於服務端來說會耗費一定的資源,而像WEB網站這么頻繁的成千上萬甚至上億客戶端的連接用短連接會更省一些資源,如果用長連接,而且同時有成千上萬的用戶,如果每個用戶都占用一個連接的話,那可想而知吧。所以並發量大,但每個用戶無需頻繁操作情況下需用短連好。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM