TCP協議

TCP詳解算法

一、前言緩存

傳輸控制協議(Transmission Control Protocol,TCP)是一種面向鏈接的、可靠的、基於字節流的傳輸層通訊協議,由IETF的RFC 793定義。服務器

二、TCP詳解網絡

2.1TCP報文頭部併發

來源鏈接端口(16bit)\目的鏈接端口(16bit):計算機上的進程要和其餘進程通訊是要經過計算機端口的,而一個計算機端口某個時刻只能被一個進程佔用,因此經過指定源端口和目標端口,就能夠知道是哪兩個進程須要通訊。源端口、目標端口是用16位表示的,可推算計算機的端口個數爲2^16個負載均衡

序列號碼(seq,32bit)socket

若是含有同步化旗標(SYN),則此爲最初序列號;第一個數據比特的序列號爲本序列加1。tcp

若是沒有同步化旗標(SYN),則此爲第一個數據比特的序列碼。性能

確認號碼(ack,32bit):指望收到的數據的開始序列號,也便是接收端收到的數據的字節長度加1。spa

數據偏移(4bit):表示TCP報文段的首部長度,共4位,因爲TCP首部包含一個長度可變的選項部分,須要指定這個TCP報文段到底有多長。它指出TCP報文段的數據起始處距離TCP報文段的起始處有多遠。該字段的單位是32位(即4個字節爲計算單位),4位二進制最大表示15,因此數據偏移也就是TCP首部最大60字節。

保留(3bit):置0

NS(ECN-nonce,ECN顯式擁塞通知(Explicit Congestion Notification)):是對TCP的擴展,定義於RFC 3540第5節;ECN容許擁塞控制的端對端通知而避免丟包。ECN爲一項可選功能,若是底層網絡設備支持,則可能被啓用ECN的兩個端點使用。當有序數據包到達時,ECN-nonce接收器會維護隨機數的和,並在每一個確認中返回當前的隨機數的和。在標記分組的狀況下,接收機可能不知道一個或多個隨機數值,在這種狀況下,當計算總和時,接收端將忽略丟失的隨機數值,並將ECN-Echo置位向發送端發出阻塞信號。

CWR(Congestion Window Reduced,減小擁塞窗口):當具備ECN功能的TCP發送方處於某種緣由(因爲重傳超時、快速重傳或相應ECN通知)減小其擁塞窗口時,TCP發送方會在第一個新數據的TCP頭部中設置CWR標識後,減小其隨後發送的報文。若是該報文在網絡中被丟棄,則發送方的TCP將不得再也不次減小擁塞窗口,並從新發送丟棄的數據包。(RFC 3168)

ECE(ECN-Echo):發送端在接收到ECE置位的TCP包時,發送端會將CWR進行置位,以確認接收到ECN-echo標識並對其作出反應。(RFC 3168)

(注:

在兩端協商創建ECN關係時,發送方A將CWR與ECE同時置位,造成ECN SYN包併發送給接收方B;接收方B在接收到ECN SYN包時,只將ECE位置位,CWR不置位,造成ECN SYN-Ack包發送給發送方A。

在發生擁塞時,接收端B在接收到一組TCP包,計算ECN-nonce接收器中隨機數總和,並與確認包中的隨機數總和進行比較,若是兩個數值不同,則發送端A端判斷髮生擁塞,會將發送給發送端的第一個TCP包中ECE進行置位;在發送端A接收到這個ECE位被置位的數據包以後,它會將CWR進行置位,併發送給你接收端B;在接收端B接收到一個CWR置位的數據包,它會減小向發送端A的數據包發送。)

USG:標識緊急指針是否有效

ACK:標識確認序號是否有效

PSH:用來標識接收端應用程序馬上將數據從TCP緩衝區讀取

RST:要求從新創建鏈接,咱們把還有RST標識的報文稱爲復位報文段

SYN:發出創建鏈接,咱們把還有SYN標識的報文稱爲同步報文段

FIN:通知對端,本段即將關閉,咱們把含有FIN標識的報文稱爲結束報文段

窗口大小:佔用16bit,表示從確認號開始,本報文的發送方能夠接收的字數,即接收窗口大小,用於流量控制。窗口大小爲滑動計算,由Window size value * Window size scaling factor(此值在三次握手階段TCP選項Windowscale協商獲得)得出此值。

校驗和:佔用16bit,由發送端填充,檢驗形式有CRC校驗等,若是接收端校驗不經過,則認爲數據有問題,此處的校驗和不光包含TCP首部,也包含TCP數據部分,以16位字進行計算所得,這是一個強制字段。

緊急指針:佔用16bit,用來標識數據哪一個部分有問題,只在USG位被置位時使用。

選項:最多40字節。每一個選項的開始時1字節的kind字段,說明選項的類型

0:選項表結束(1字節)

1:無操做(1字節),用於選項字段之間的字邊界對齊

2:最大報文長度(4字節,Maximum Segment Size,MSS)一般在建立鏈接而設置SYN標識的數據包中指明這個選項,指明本段所能接受的最大的報文段。一般將MSS設置爲(MTU-40)字節,攜帶TCP報文段的IP數據包的長度就不會超過MTU(MTU最大長度爲1518字節,最短位爲64字節),從而避免本機發生IP分片,只能出如今同步報文段中,不然將被忽略。

MTU和MSS值的關係:MTU=MSS+IP Header+TCP Header

通訊雙方最終的MSS值=較小MTU-IP Header-TCP Header

3:窗口擴大因子(4字節,wscale),取值0-14.用來把TCP的窗口的值左移的位數,使窗口值乘倍。只能出如今同步報文段中,不然將被忽略。這是由於如今的TCP接收數據緩衝區(接收窗口)的長度一般大於65535字節。

4:sackOK,發送端支持並贊成使用SACK選項

5:SACK實際工做的選項

6:時間戳(10字節,TCP Timestamps Option,TSopt)

發送端的時間戳(Timestamp Value field,TSval,4字節)

時間戳回顯應答(Timestamp Echo Reply field,TSecr,4字節)

 

二、鏈接管理機制

正常狀況下,TCP須要通過三次握手創建鏈接,四次揮手端口鏈接。

2.1TCP創建鏈接

三次握手過程,以下圖:

詳細通過:

剛開始,客戶端與服務器都處於CLOSED狀態;此時,客戶端向服務器主動發出鏈接,服務器被動接收鏈接:

1)TCP服務器進程先建立傳輸控制模塊TCB,時刻準備接收客戶端進程的鏈接,此時服務器就進入了LISTEN(監聽)狀態

2)TCP客戶端進程也是先建立傳輸控制模塊TCB,而後向服務器發出鏈接報文,此時報文首部中的同步表示位SYN=1,同時選擇一個初始序列號seq=x,此時,TCP客戶端進程進入SYN-SENT(同步已發送)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但須要消耗掉一個序號。

3)TCP服務器收到鏈接報文後,若是贊成鏈接,則發出確認報文。確認報文中的ACK=1,SYN=1,確認序號是x+1,同時選擇一個初始序列號seq=x,此時,TCP服務器進程進入SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但一樣要消耗一個序號。

4)在TCP客戶端進程收到確認包後,還要向服務器給出確認收到SYN-ACK的確認包。確認報文的AC=1,確認序列號=y+1,本身的序列號=x+1。

5)此時,TCP鏈接創建,客戶端進入ESTABLISHED(已創建鏈接)狀態。當服務器收到客戶端的確認後也進去ESTABLISHED狀態,此後雙方就能夠進行數據傳輸。

 

問題一:爲何不用兩次握手,創建鏈接?

答:假想一下,若是咱們去掉第三次握手?由於咱們不進行第三次握手,因此在服務端對客戶端進行迴應(第二次握手)後,就會理所固然的認爲鏈接已創建,而若是客戶端沒有收到服務端的此次迴應,那麼,客戶端會認爲鏈接沒有創建,可是服務端會對以前的鏈接保存必定的系統資源,若是出現大量的狀況,那麼服務端就會由於系統資源耗盡,而致使會崩潰。

 

問題二:爲何不是四次?

答:由於在TCP經過三次握手後,客戶端和服務端至少能夠確認以前的狀況,可是沒法肯定以後的狀況,基於此理論,不管4次仍是5次都是沒法肯定的該報是否已經收到,因此即使再多的握手包也都是徒勞的。

 

2.2TCP斷開鏈接

四次揮手過程,以下圖:

模式一:

 

詳細過程:

數據傳輸完畢後,雙方均可以釋放鏈接,此時客戶端和服務器都處於ESTABLISHED狀態,而後客戶端主動斷開鏈接,服務器被動斷開鏈接。

1)客戶端進程發出釋放鏈接報文,而且中止發送數據。釋放數據報文首部,FIN=1,其序列號seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時客戶端進入FIN-WAIT-1(終止等待1)狀態。TCP規定,FIN報文段計是不攜帶數據,也要消耗一個序號。

2)服務器收到釋放鏈接報文,發出確認報文,ACK=1,ack=u+1,並帶上本身的序號seq=v,此時,服務端進入CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送,可是服務器若發送數據,客戶端依然要接收。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。

3)客戶端收到服務器的確認斷開鏈接後,此時客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送鏈接釋放報文(在這以前還需接受服務器發送的最好的數據報文)。

4)服務器將最後的數據發送完畢後,就向客戶端發送鏈接釋放報文,FIN=1,ack=u+1,因爲在半關閉狀態,服務器極可能又發送了一些數據,假定此時的序列號位seq=w,此時,服務器就進入LAST-ACK(最後確認),等待客戶端的確認。

5)客戶端收到服務器的鏈接釋放報文後,必須發出確認,ACK=1,ack=w+1,而本身的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP尚未釋放,必須通過2*MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。

6)服務器只要收到了客戶端發出的確認,當即進入CLOSED狀態,同時,撤銷進程產生的TCB,這就結束了此次TCP的鏈接。服務端會比客戶端提前結束此次TCP鏈接。

 

模式二

數據傳輸完畢後,雙方均可以釋放鏈接,此時客戶端和服務器都處於ESTABLISHED狀態,而後雙方都主動斷開。

1)兩端同時向對端傳送釋放鏈接報文,並中止發送數據。釋放數據報文首部,FIN=1,ACK=1,序列號分seq別是c與s,此時兩端同時進入FIN-WAIT-1(種植等待1)狀態。

2)在兩端分別收到了對端發送本身的釋放鏈接報文後,併發出確認報文。確認數據報文首部,ACK=1,ack分別時s+1和c+1,並攜分別帶本身的本身的序列號seq是c+1和s+1,而且本身的狀態轉換爲CLOSING(雙方同時嘗試關閉,等待確認)狀態。

3)在兩端收到對端的確認報文以後,狀態從CLOSING(雙方同時嘗試關閉,等待確認)狀態,轉變爲TIME-WAIT(時間等待)狀態,此時須要通過2*MSL(最長報文段壽命)的使時間後,雙方分別撤銷了相應的TCB後,雙方纔會進入CLOSED狀態。

 

三、有限TCP狀態機

TCP協議的操做可使用11鍾狀態的有限狀態機。

CLOSED:關閉狀態,沒有連接活動或正在進行

LISTEN:監聽狀態,服務器正在等待鏈接進入

SYN_SENT:已發出連接報文,等待確認。

SYN_RCVD:收到一個連接報文,並已發出確認連接報文,還沒有確認對方是否已收到。

ESTABLISHED:連接創建,正常數據傳輸狀態。

FIN_WAIT_1:(主動關閉)已發送關閉鏈接報文,等待確認

FIN_WAIT_2:(主動關閉)收到對方關閉確認,等到對方關閉鏈接報文

TIMED_WAIT:完成雙向關閉,等待2*MSL(最長報文時間)

CLOSING:在發出FIN後,又收到對方發送給的FIN後,進入等待對方對己方的鏈接終止(FIN)的確認(ACK)的狀態。

CLOSE_WAIT:(被動關閉)收到對方關閉請求,已經確認

LAST_ACK:(被動關閉)等待最後一個關閉確認,並等待全部分組死掉

 

四、客戶端得典型狀態轉移

一、客戶端經過connect系統調用主動與服務器創建鏈接connect系統調用首先給服務器發送一個同步報文段,使鏈接轉移到SYN_SENT狀態

此後connect系統調用可能由於以下兩個緣由失敗返回:

1)若是connect鏈接的目標端口不存在(未被任何進程監聽),或者該端口仍被處於TIME_WAIT狀態的鏈接所佔用,則服務器將給客戶端發送一個復位報文段,connect調用失敗。

2)若是目標端口存在,但connect在超時時間內未收到服務器的確認報文段,則connect調用失敗。

二、connect調用失敗將使鏈接當即返回到初始的CLOSED狀態。若是客戶端成功收到服務器的同步報文段和確認,則connect調用成功返回,鏈接轉移至ESTABLISHED狀態。

三、當客戶端執行主動關閉時,它將向服務器發送一個結束報文段,同時鏈接進入FIN_WAIT_1狀態。若此時客戶端收到服務器專門用於確認目的的確認報文段,則鏈接轉移至FIN_WAIT_2狀態。當客戶端處於FIN_WAIT_2狀態時,服務器處於CLOSE_WAIT狀態,這一對狀態是可能發生半關閉的狀態。此時若是服務器也關閉鏈接(發送結束報文段),則客戶端將給予確認並進入TIME_WAIT狀態

四、客戶端從FIN_WAIT_1狀態可能直接進入TIME_WAIT狀態(不通過FIN_WAIT_2狀態),前提是處於FIN_WAIT_1狀態的服務器直接收到帶確認信息的結束報文段(而不是先收到確認報文段,再收到結束報文段)。

五、處於FIN_WAIT_2狀態的客戶端須要等待服務器發送結束報文段,才能轉移至TIME_WAIT狀態,不然它將一直停留在這個狀態。若是不是爲了在半關閉狀態下繼續接收數據,鏈接長時間地停留在FIN_WAIT_2狀態並沒有益處。鏈接停留在FIN_WAIT_2狀態的狀況可能發生在:客戶端執行半關閉後,未等服務器關閉鏈接就強行退出了。此時客戶端鏈接由內核來接管,可稱之爲孤兒鏈接(和孤兒進程相似)

 

五、TCP重傳超時

1)異常網絡情況下(開始出現超時或丟包),TCP控制數據傳輸以保證其承諾的可靠服務;

2)TCP服務必須可以重傳超時時間內未收到確認的TCP報文段。爲此,TCP模塊爲每一個TCP報文段都維護一個重傳定時器,該定時器在TCP報文段第一次被髮送時啓動。若是超時時間內未收到接收方的應答,TCP模塊將重傳TCP報文段並重置定時器。至於下次重傳的超時時間如何選擇,以及最多執行多少次重傳,就是TCP的重傳策略;

3)與TCP超時重傳相關的兩個內核參數:

/proc/sys/net/ipv4/tcp_retries1,指定在底層IP接管以前TCP最少執行的重傳次數,默認值是3

/proc/sys/net/ipv4/tcp_retries2,指定鏈接放棄前TCP最多能夠執行的重傳次數,默認值15(通常對應13~30min)

 

六、擁塞控制

1)網絡中的帶寬、交換結點中的緩存和處理機等,都是網絡的資源。在某段時間,若對網絡中某一資源的需求超過了該資源所能提供的可承受的能力,網絡的性能就會變壞。此狀況稱爲擁塞。

2)TCP爲提升網絡利用率,下降丟包率,並保證網絡資源對每條數據流的公平性。即所謂的擁塞控制。

3)TCP擁塞控制的標準文檔是RFC 5681,其中詳細介紹了擁塞控制的四個部分:慢啓動(slow start)、擁塞避免(congestion avoidance)、快速重傳(fast retransmit)和快速恢復(fast recovery)。擁塞控制算法在Linux下有多種實現,好比reno算法、vegas算法和cubic算法等。它們或者部分或者所有實現了上述四個部分。

 4)當前所使用的擁塞控制算法

/proc/sys/net/ipv4/tcp_congestion_control

 

七、KeepAlive包

        TCP的KeepAlive是側重於保持客戶端和服務器的鏈接,一方會按期發送心跳包給另外一方,當一方斷掉的時候,沒有斷掉的定時發送幾個心跳包,若是間隔發送幾回,對方都返回的RST,而不是ACK,那麼就釋放當前鏈接。假想一下,若是TCP層沒有keepAlive的機制,當一方斷開鏈接卻沒有發送FIN給另一方,那麼另一方會認爲這個鏈接仍是存在的,,相似的鏈接一多,時間一長,那麼這對服務器資源是一種很大的影響。

7.2 爲何要有keepAlive?

        首先明確,在TCP是沒有「請求」一說。TCP是一種通訊的方式,「請求」一詞是事務上的概念。在TCP鏈接創建以後,若是應用程序或者承載在TCP上面的協議一直不發送數據,或者隔很長時間才發送一次數據,當連接好久沒有數據報文傳輸時,如何肯定對方還在線?對方到底掉線了仍是沒有數據傳輸,連接還需不須要保持?這種狀況在TCP協議設計中時須要考慮的。

       TCP協議經過一種巧妙的方式去解決這個問題,當沒有傳輸數據,超時一段時間後,TCP自動發送一個數據爲全0的1字節的報文,若是對方迴應了這個報文,說明對方在線,TCP連接就能夠繼續保持,若是對方沒有報文迴應,而且重試了屢次以後,則本端認爲連接丟失,沒有必要繼續保持連接。

7.3 怎麼開啓KeepAlive?

在Linux上,默認不開啓KeepAlive,而且沒有一個全局的選項去開啓TCP的KeepAlive。須要開啓KeepAlive的應用必須在TCP的socket中單獨開啓。Linux Kernel有三個選項影響到KeepAlive的行爲:

在/proc/sys/net/ipv4/目錄下:

tcp_keepalive_time 7200 #距離上次傳送數多少時間未收到新報文判斷爲開始檢測,單位秒,默認7200s

tcp_keepalive_intvl 75 #檢測開始多長時間發送心跳包,單位秒,默認75s

tcp_keepalive_probes 9 #發送幾回心跳包對方沒有迴應則close連接,默認9次

TCP socket也有三個選項和內核對應,經過setsockopt系統調用針對單獨的socket進行設置

TCPKEEPCNT:覆蓋tcp_keepalive_probes

TCPKEEPDLE:覆蓋tcp_keepalive_time

TCPKEEPTVL:覆蓋tcp_keepalive_intvl

舉個例子,若是個人系統默認keepalive設置,若是我在應用程序中針對socket開啓了keepalive,而後設置的TCP_KEEPIDLE爲60,那麼TCP協議棧在發現TCP連接空閒了60s沒有傳輸數據,那麼系統就會發送第一個KeepAlive探測報文。

7.4 KeepAlive的不足

KeepAlive只能檢測連接是否存活,不能檢測連接是否可用。例如,某一方發生了死鎖,沒法在連接上進行任何讀寫操做,可是操做系統仍然能夠相應網絡的KeepAlive的探測包。

TCP KeepAlive機制依賴於操做系統的實現,靈活性不足,而且默認關閉。且默認的KeepAlive心跳時間是2個小時,時間較長。而且,代理或者負載均衡,會讓TCP KeepAlive失效。

 

八、基本TCP調優參數

1)Linux爲了防止孤兒鏈接長時間存留在內核中,定義了兩個內核參數:

/proc/sys/net/ipv4/tcp_max_orphans 指定內核能接管的孤兒鏈接數目

/proc/sys/net/ipv4/tcp_fin_timeout 指定孤兒鏈接在內核中生存的時間

 2)Linux爲了保證tcp鏈接得必定數量,定義了兩個內核參數:

/proc/sys/net/ipv4/tcp_max_syn_backlog 未完成鏈接隊列大小,建議調整大小爲1024以上

/proc/sys/net/core/somaxconn 完成鏈接隊列大小,建議調整大小爲1024以上

相關文章
相關標籤/搜索