linux高性能服務器編程學習筆記三:TCP協議詳解

1、和IP協議相比,TCP協議處於傳輸層,更靠近應用層,因此在應用程序中具有更強的可操作性。

2、一般來說,TCP主要處理端到端的通信,因此頭部信息主要包括源端口號,目的端口號。TCP協議是面向連接、面向字節流和可靠傳輸的協議。因此頭部還需要一些字段來管理TCP連接以及控制兩個方向的數據流。

3、TCP服務的特點:使用TCP協議通信必須先建立連接,然後才能開始數據的讀寫。並且通信雙方都需要在內核維護必要的數據結構和資源用於管理連接的狀態和保證數據的傳輸。TCP是全雙工的,也就是通信雙方只需在一個連接上傳輸數據即可。完成數據的交換之後,還需關閉連接以釋放資源。TCP協議是一對一的,也就是廣播和多播並不能使用TCP服務,UDP則非常適合於廣播和多播。

4、TCP協議面向字節流的概念:使用TCP協議的時候,應用程序連續多次執行寫操作時,TCP模塊先將這些數據拷貝到TCP發送緩衝區中。當TCP模塊真正發送數據時,可能會對處於發送緩衝區的數據進行封裝(封裝成一個或多個)成TCP報文段再發出。也就是應用程序執行寫操作的次數和TCP模塊發出的TCP報文段的個數並無固定關係。同理,接收端的TCP模塊接收到TCP報文段並按照TCP報文段的序號放入接收緩衝區後,通知應用程序讀取數據,應用程序讀取數據的次數可能是一次,也可能是多次,取決於應用程序設置的讀數據緩衝區的大小。因此TCP模塊接收TCP報文段的個數和應用程序讀取數據的次數並無固定關係。應用程序對數據的發送和接收是沒有邊界概念的,這就是面向字節流的含義。

5、UDP面向報文數據報的概念:發送端應用程序每執行一次寫操作,UDP模塊就必須將其封裝成一個UDP數據報併發送之。接收端的UDP模塊也必須及時的接收到這個UDP數據報並且通知應用程序針對每一個UDP數據報進行讀操作(recvfrom系統調用),否則可能會丟包。這裏要注意,假如接收端的應用程序沒有足夠大的讀緩衝區,可能造成UDP數據被截斷。


6、TCP傳輸可靠主要是因爲發送應答機制和超時重傳機制這兩個機制保證。發送應答機制就是每發送一個TCP報文段就必須收到接收方的應答,才認爲這個TCP報文段傳輸成功。超時重傳是每次傳輸一個TCP報文段都會開啓定時功能,假如在規定時間內沒有收到接收方對這個TCP報文段的應答,則重新發送該報文段。同時,由於TCP報文段最終是以IP數據報發送的,因此到達接收方時是亂序、重複的,所以TCP協議還會對接收到的TCP報文段進行重排、整理,再交付給應用層。

7、TCP固定頭部結構

這裏重點講解以下幾個字段:

(1)6位標誌位:

   URG標誌:表示緊急指針是否有效

   ACK標誌:表示確認號是否有效。一般稱攜帶ACK標誌的TCP報文段爲確認報文段

   PSH標誌:提示接收端的應用程序趕緊讀取TCP接收緩衝區中的數據,以騰出空間爲後續數據準備

   RST標誌:表示要求對方重新建立連接。一般稱攜帶RST標誌的TCP報文段爲復位報文段

   SYN標誌:表示請求建立一個連接。一般稱攜帶SYN標誌的TCP報文段爲同步報文段。

   FIN標誌:表示通知對方本端要關閉連接了。一般稱攜帶FIN標誌的TCP報文段爲結束報文段。

(2)16位窗口大小字段:由發送端填充,告訴對方本段的接收緩衝區還能接收多少字節的數據。這是一種流量控制手段。這樣對方就可以控制發送數據的速度

(3)16位校驗和:由發送端填充,與IP數據包不同的是,TCP報文段的檢驗和是檢驗整個報文段(包括TCP頭部),這也是TCP可靠傳輸的一個保障。

(4)16位緊急指針字段:是一個正的偏移量,它和序號字段的值相加就是最後一個緊急數據的下一個字節的序號。因此又稱爲緊急偏移,用於發送緊急數據的方法

8、TCP頭部選項

(1)TCP頭部的選項字段最多有40字節。典型的TCP頭部選項結構見:


Kind字段表明選項的類型。第二個字段length指定該選項的總長度。第三個字段info是選項的具體信息。

(2)常見的TCP選項有7種,見下圖:


kind=1是空操作選項,一般用於將TCP選項的總長度設置爲4的倍數。

   Kind=2是最大報文段長度選項。TCP連接初始化時,通信雙方使用該選項來協商最大報文段的數據部分長度(MSS)。TCP模塊一般將其設置爲(MTU-40)字節(減掉20字節的TCP頭部和20字節的IP頭部)。這樣攜帶TCP報文段的IP數據報的長度就不會超過MTU而避免本機對其進行分片(假如TCP和IP都不包含選項字段)。

   Kind=3表示窗口擴大因子選項。TCP連接初始化時,通信雙方使用該選項協商接收通告窗口的擴大因子。在TCP的頭部當中,接收通告窗口爲16位,那麼最大爲65535字節,但實際上TCP允許的最大接收窗口遠不止這個數,這就需要窗口擴大因子來解決此問題。假設TCP接收通告窗口大小爲N,窗口擴大因子爲M,則TCP報文段實際的接收通告窗口大小爲N*2M(左移M位)。M的取值範圍爲0-14。可以在/proc/sys/net/ipv4/tcp_window_scaling內核變量來啓用或關閉窗口擴大因子。注意!和MSS選項一樣,窗口擴大因子只能出現在同步(攜帶SYN標誌)報文段中,否則被忽略。

   Kind=4很重要!表示是否支持選擇性確認(SACK)選項。TCP通信時,假如某個TCP報文段丟失,則TCP模塊會重傳最後被確認的TCP報文段的後續的所有報文段。但實際上有些不連續的報文段已經在接收端的緩衝區當中,這樣重傳所有後續報文段會降低TCP性能。SACK技術就是爲改善這種情況而產生。選擇性確認同樣是用在連接初始化時。可以通過修改/proc/sys/net/ipbv/tcp_sack內核變量來啓用或關閉選擇性確認選項

   Kind=5是sack實際工作的選項。該選項的參數告訴發送方本端已經接收到並緩存的不連續的數據塊,從而讓發送端以此檢查並重傳丟失的數據塊。每個塊邊沿參數包含一個4字節的序號,左邊沿表示不連續塊第一個數據的序號,右邊沿表示不連續塊的最後一個數據的序號的下一個序號,這一對參數之間的數據就是沒有收到的。因爲選項減去前兩個字節(kind和length)。最長爲38個字節。而一個完整的塊(包括左邊沿塊和右邊沿塊)爲8個字節。那麼最多可以存儲4個這樣的不連續的數據塊。

   Kind=8是時間戳選項。該選項提供了準確的計算通信雙方之間的迴路時間(RTT)的方法。從而爲TCP流量控制提供了重要的信息。可以通過修改/proc/sys/net/ipv4/tcp_timestamps內核變量來啓用或關閉時間戳選項。

9、連接超時

(1)假如在連接的時候,由於網絡堵塞或者是訪問一個距離特別遠的服務器,導致服務器對於客戶端發出的同步報文段(連接請求)沒有應答,會嘗試多重連幾次,到達一定次數會通知應用程序連接超時

(2)重連的報文段一般時間間隔是1s、2s、4s、8s、16s、32s。在5次重連均失敗的情況下,TCP模塊放棄連接並通知應用程序。相關次數操作可以改變/proc/sys/net/ipv4/tcp_syn_retries內核變量。

10、半關閉狀態:TCP連接是全雙工的,這不僅代表通信雙方可以在一條連接上來回通信,也表明通信雙方可以獨立關閉連接。首先發送結束報文段的一方會使的整個TCP連接處於半連接狀態,也就是說先關閉連接的一方是在告訴對方我不會再發送數據了,但是我可以接收數據直到你也關閉連接。

11、服務器和客戶端判斷對方是否已經關閉連接的方法是:read系統調用返回0(收到結束報文段)。

12、TCP狀態轉移:

(1)先說服務器的狀態轉移:首先通過listen系統調用處於監聽狀態,被動等待對方的連接請求(被動打開)。服務器一旦監聽到連接請求就將該連接放入內核的等待隊列當中,並向對端發送帶SYN的確認報文段,此時該連接處於SYN_RCVD狀態。然後再此收到客戶端發過來的確認報文段,此時處於ESTABLISHED狀態,這個狀態意味着雙方的連接建立完畢。當客戶端關閉連接併發送帶FIN的結束報文段,服務器通過返回確認報文段而進入CLOSE_WAIT狀態,此狀態表明等待服務器應用程序關閉連接。當服務器應用程序關閉連接之後,該連接處於LAST_ACK狀態,此狀態表明等待確認客戶端發送的最後一次結束報文段。一旦確認完成,連接就徹底關閉了。

(2)客戶端的狀態轉移:客戶端通過系統調用connect發起連接請求,給服務端發送一個同步報文段(注意服務器會回覆一個帶SYN的確認報文段)此時連接處於SYN_SENT狀態。這裏注意一下connect系統調用可能會因爲這幾個原因失敗:服務端的端口不存在。或者該端口仍被處於TIME_WAIT狀態的連接所佔用,這兩種情況,服務器會給客戶端發送一個復位報文段告訴客戶端應該將連接斷開或者重連。還有一種情況就是端口存在,但是connect在超時時間內沒有收到服務器帶SYN的確認報文段,則connect調用失敗。Connect調用失敗會使連接立即返回CLOSED狀態。如果客戶端收到服務端帶SYN的確認報文段,則表明connect調用成功,然後再次發送確認報文段,此時連接處於ESTABLISHED狀態。當客戶端向服務器發送結束報文段,連接處於FIN_WAIT_1狀態。若又收到了服務端對此報文的確認,則進入FIN_WAIT_2狀態。(如果客戶端處於FIN_WAIT_2狀態,服務器處於CLOSE_WAIT狀態,此時應該處於半關閉狀態)。然後服務器發送結束報文段,則客戶端給予確認並進入TIME_WAIT狀態。這裏注意一下,有些意外狀態會迫使客戶端長期處於FIN_WAIT_2狀態:客戶端執行關閉之後,假如並不等服務器關閉就立即強行退出。此時客戶端連接會由內核來管理,此時的連接可以稱爲孤兒連接(類似於孤兒進程)。Linux爲了防止孤兒連接長時間留在內核中,定義了兩個內個變量/proc/sys/net/ipv4/tcp_max_orphans以及/proc/sys/net/ipv4/tcp_fin_timeout。前者規定內核接管的最大的孤兒連接數目,後者規定孤兒連接在內核中生存的時間。

13、TIME_WAIT狀態存在的原因:

(1)保證TCP連接能夠可靠的終止

(2)保證讓遲來的報文或者還在網絡流中的報文能夠有足夠的時間被丟棄或者在網絡流中消逝。

第一個原因是,假如用於確認服務器結束報文段的TCP報文段丟失了,那麼服務器會重發結束報文段,假如沒有TIME_WAIT這個狀態讓客戶端有足夠的時間處理重複收到的結束報文段(也就是再次發送確認報文段),那麼客戶端會立刻發送一個復位報文段給服務器,服務器會認爲這是一個錯誤。第二個原因是一個TCP端口任意時刻只能用於一個TCP連接。當存在TIME_WAIT狀態時,那麼可以讓整個連接可靠的結束而防止端口被再次利用(因爲端口在連接完整關閉以前仍然被佔用着)。假如沒有TIME_WAIT狀態,則應用程序能夠以此TCP端口建立一個新連接,假如在原先的舊連接仍然有數據或TCP報文段,將會被新的連接接收到,這顯然是錯誤的。當然我們可以通過socket選項的SO_REUSEADDR來強制進程使用處於TIME_WAIT狀態連接的端口。

讓TIME_WAIT堅持2MSL(MSL:報文段最大生存時間),是爲了確保雙向尚未被接收到的,遲到的報文能夠消失。

14、復位報文段

在以下幾種情況下,TCP連接的一端會向另一端發送攜帶RST標誌的報文段,即復位報文段。

(1)訪問不存在的端口,或者向服務器的某個端口發起連接,但該端口處於TIME_WAIT狀態

(2)異常終止連接,即給對方發送一個復位報文段。(一旦發送了復位報文段,發送端所有排隊等待發送的數據都將被丟棄),可以利用socket選項SO_LINGER來發送復位報文段

(3)考慮這樣一個情況,客戶端(服務器)關閉或異常終止了連接,而對端沒有接收到結束報文段,此時,服務器(客戶端)還維持者原來的連接,而客戶端(服務器)即使重啓,也沒有該連接的信息,這種情況稱之爲半打開狀態,假如服務器(客戶端)往半打開連接發送數據,對方會迴應一個復位報文段。

15、TCP超時重傳

(1)TCP服務必須在重傳時間內未收到重複確認的TCP報文段,爲此TCP模塊爲每個TCP報文段都維護一個重傳定時器,在每一次發送TCP報文段都會啓動。

(2)每一次需要重傳,其時間間隔都增加一倍,一般來說最多重傳5次,在5次均重傳失敗之後,底層的IP和ARP開始接管,直到telnet客戶端放棄連接爲止。

(3)linux的內核變量:/proc/sys/net/ipv4/tcp_retries1和/proc/sys/net/ipv4/tcp_retries2,前者指定最少的重傳次數,默認爲3,後者指定放棄連接前TCP最多可以執行的重傳次數。

16、擁塞控制

(1)一般原理:發生擁塞控制的原因:資源(帶寬、交換節點的緩存、處理機)的需求>可用資源。作用:擁塞控制就是爲了防止過多的數據注入到網絡中,這樣可以使網絡中的路由器或者鏈路不至於過載。擁塞控制要做的都有一個前提:就是網絡能夠承受現有的網絡負荷。

對比流量控制:擁塞控制是一個全局的過程,涉及到所有的主機、路由器、以及降低網絡相關的所有因素。流量控制往往指點對點通信量的控制。是端對端的問題。

(2)流量控制:設主機A向主機B發送數據。雙方確定的窗口值是400.再設每一個報文段爲100字節長,序號的初始值爲seq=1,大寫ACK表示首部中的卻認爲爲ACK,小寫ack表示確認字段的值。 
接收方的主機B進行了三次流量控制。第一次把窗口設置爲rwind=300,第二次減小到rwind=100最後減到rwind=0,即不允許發送方再發送過數據了。這種使發送方暫停發送的狀態將持續到主機B重新發出一個新的窗口值爲止。 

假如,B向A發送了零窗口的報文段後不久,B的接收緩存又有了一些存儲空間。於是B向A發送了rwind=400的報文段,然而這個報文段在傳送中丟失了。A一直等待收到B發送的非零窗口的通知,而B也一直等待A發送的數據。這樣就死鎖了。爲了解決這種死鎖狀態,TCP爲每個連接設有一個持續計時器。只要TCP連接的一方收到對方的零窗口通知,就啓動持續計時器,若持續計時器設置的時間到期,就發送一個零窗口探測報文段(僅攜帶1字節的數據),而對方就在確認這個探測報文段時給出了現在的窗口值。

(2)主要有四個部分:慢啓動、擁塞避免、快重傳、快恢復

慢開始和擁塞避免 
發送報文段速率的確定,既要根據接收端的接收能力,又要從全局考慮不要使網絡發生擁塞,這由接收窗口和擁塞窗口兩個狀態量確定。接收端窗口(Reciver Window)又稱通知窗口(Advertised Window),是接收端根據目前的接收緩存大小所許諾的最新窗口值,是來自接收端的流量控制。擁塞窗口cwnd(Congestion Window)是發送端根據自己估計的網絡擁塞程度而設置的窗口值,是來自發送端的流量控制。一般來說,發送數據爲擁塞窗口和通知窗口當中的較小值。

1)當主機開始發送數據時,如果立即將較大的發送窗口的全部數據字節都注入到網絡中,那麼由於不清楚網絡的情況,有可能引其網絡擁塞 
2)比較好的方法是試探一下,即從小到達逐漸增大發送端的擁塞控制窗口數值 
3)通常在剛剛開始發送報文段時可先將擁塞窗口cwnd設置爲一個最大報文段的MSS的數值。在每收到一個對新報文段確認後,將擁塞窗口增加至多一個MSS的數值,當rwind足夠大的時候,爲了防止擁塞窗口cwind的增長引起網絡擁塞,還需要另外一個變量—慢開始門限ssthresh 。

擁塞控制具體過程爲:

1)TCP連接初始化,將擁塞窗口設置爲1 
2)執行慢開始算法,cwind按指數規律增長,知道cwind == ssthress開始執行擁塞避免算法,cwnd按線性規律增長 
3)當網絡發生擁塞,把ssthresh值更新爲擁塞前ssthresh值的一半,cwnd重新設置爲1,按照步驟(2)執行。 
快重傳和快恢復 
一條TCP連接有時會因等待重傳計時器的超時而空閒較長的時間,慢開始和擁塞避免無法很好的解決這類問題,因此提出了快重傳和快恢復的擁塞控制方法。 
快重傳算法並非取消了重傳機制,只是在某些情況下更早的重傳丟失的報文段(如果當發送端接收到三個重複的確認ACK時,則斷定分組丟失,立即重傳丟失的報文段,而不必等待重傳計時器超時)。慢開始算法只是在TCP建立時才使用。 
快恢復算法有以下兩個要點: 
1)當發送方連續收到三個重複確認時,就執行“乘法減小”算法,把慢開始門限減半,這是爲了預防網絡發生擁塞。 
2)由於發送方現在認爲網絡很可能沒有發生擁塞,因此現在不執行慢開始算法,而是把cwnd值設置爲慢開始門限減半後的值,然後開始執行擁塞避免算法,使擁塞窗口的線性增大。

總結: 
沒有發生擁塞之前使用前兩種算法,發生擁塞之後(即三次重複確認)使用後兩種算法。
發佈了35 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章