TCP是面向連接的、可靠的、基於字節流(應用層傳下來的報文看成字節流,把字節流組織成大小不等的數據塊)的、擁有流量控制的協議。在一個TCP連接中。僅有連接雙方進行通信。TCP為應用層提供全雙工服務,數據能在兩個方向上獨立地進行傳輸。是一對一的通信。TCP可以表述為一個沒有選擇確認或否認的滑動窗口協議。
TCP將用戶數據打包構成報文段;它發送數據后啟動一個定時器(超時重傳);另一端對收到的數據進行確認,對失序的數據重新排序,丟棄重復數據;提供端到端的流量控制,並計算和驗證一個強制性的端到端檢驗和。
報文格式
- 16位源端口號 /16位目的端口號:兩個值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個TCP連接。一個IP地址和一個端口號也稱為一個插口( socket)。
- 32位序號:序號用來標識從TCP發端向TCP收端發送的數據字節流,表示第一個字節的序號。如果將字節流看作在兩個應用程序間的單向流動,則TCP用序號對每個字節進行計數。溢出后又重0開始。當建立一個新的連接時, SYN標志變1。序號字段包含由這個主機選擇的該連接的初始序號ISN(Initial Sequence Number)。該主機要發送數據的第一個字節序號為這個ISN加1,因為SYN標志消耗了一個序號。
- 32位確認序號:確認序號包含發送確認的一端所期望收到的下一個序號。確認序號應當是上次已成功收到數據字節序號加1。只有A C K標志為1時確認序號字段才有效。在TCP中,ACK是累積的—它們表示接收方已經正確收到了一直到確認序號減1的所有字節,如確認4097,說明1到4096均已正確收到。
- 4位首部長度:給數首部中有多少個32bit。
- 6個標志比特:URG(urgent pointer)緊急指針有效;ACK確認序號有效;PSH接收方應該盡快將收到的數據交給應用進程;RST重建連接;SYN同步序號用來發起一個連接;FIN發端完成發送任務。
- 16位窗口大小:TCP的流量控制由連接的每一端通過聲明的窗口大小來提供。窗口大小為字節數,起始於確認序號字段指明的值,這個值是接收端正期望接收的字節。窗口大小是一個16 bit字段,因而窗口大小最大為65535字節。
- 16位檢驗和:覆蓋TCP首部+TCP數據。TCP的檢驗和與UDP的檢驗和相似,同樣會有一個偽首部。
- 16位緊急指針:只有當URG標志置1時緊急指針才有效。緊急指針是一個正的偏移量,和序號字段中的值相加表示緊急數據最后一個字節的序號。TCP的緊急方式是發送端向另一端發送緊急數據的一種方式。
- 選項:如下圖。最常見的可選字段是最長報文大小,又稱為MSS (Maximum Segment Size),指明本端所能接收的最大長度的報文段。當一個連接建立時,連接的雙方都要通告各自的MSS。
復位報文段
當一個報文段發往基准的連接出現錯誤, TCP都會發出一個復位報文段,RST置1的報文。
引發的原因有:
- 到不存的端口的連接請求。產生復位的一種常見情況是當連接請求到達時,目的端口沒有進程正在聽。對於UDP,當一個數據報到達目的端口時,該端口沒在使用,它將產生一個ICMP端口不可達的信息。
- 異常終止一個連接。丟棄任何待發數據並立即發送復位報文段,RST的接收方會區分另一端執行的是異常關閉還是正常關閉。
- 檢測半打開連接。如果一方已經關閉或異常終止連接而另一方卻還不知道,我們將這樣的TCP連接稱為半打開的。
TCP的ICMP差錯報文
TCP能夠遇到的最常見的ICMP差錯就是源站抑制、主機不可達和網絡不可達。
- 一個接收到的源站抑制引起擁塞窗口cwnd被置為1個報文段大小來發起慢啟動,但是慢啟動門限ssthresh沒有變化,所以窗口將打開直至它或者開放了所有的通路(受窗口大小和往返時間的限制)或者發生了擁塞。
- 一個接收到的主機不可達或網絡不可達實際上都被忽略,因為這兩個差錯都被認為是短暫現象。
TCP建立連接:三次握手
-
首先服務器B處於LISTEN(監聽)狀態,等待客戶的連接請求。
-
客戶端A向B發送連接請求報文,SYN=1,ACK=0,選擇一個初始的序號seq=x。
-
B收到連接請求報文,如果同意建立連接,則向A發送連接確認報文,SYN=1,ACK=1,確認號為ack=x+1,同時也選擇一個初始的序號seq=y。
-
A收到B的連接確認報文后,還要向B發出確認,確認號為y+1,序號為x+1。
-
B收到A的確認后,連接建立。
三次握手的原因
第三次握手是為了防止失效的連接請求到達服務器,讓服務器錯誤打開連接。客戶端發送的連接請求如果在網絡中滯留,那么就會隔很長一段時間才能收到服務器端發回的連接確認。客戶端等待一個超時重傳時間之后,就會重新請求連接。但是這個滯留的連接請求最后還是會到達服務器,如果不進行三次握手,那么服務器就會打開兩個連接。如果有第三次握手,客戶端會忽略服務器之后發送的對滯留連接請求的連接確認,不進行第三次握手,因此就不會再次打開連接。
TCP斷開連接:四次揮手
- 客戶端A發送連接釋放報文,FIN=1。
- 服務器B收到之后發出確認,此時TCP屬於半關閉狀態,B能向A發送數據但是A不能向B發送數據。
- 當B不再需要連接時,發送連接釋放報文,FIN=1。
- A收到后發出確認,進入TIME-WAIT狀態,等待2MSL(最大報文存活時間)后釋放連接。
- B收到A的確認后釋放連接。
四次揮手的原因
客戶端發送了FIN連接釋放報文之后,服務器收到了這個報文,就進入了CLOSE-WAIT狀態。這個狀態是為了讓服務器端發送還未傳送完畢的數據,傳送完畢之后,服務器會發送FIN連接釋放報文。
TIME_WAIT
客戶端接收到服務器端的FIN報文后進入此狀態,此時並不是直接進入CLOSED狀態,還需要等待一個時間計時器設置的時間2MSL。這么做有兩個理由:
-
確保最后一個確認報文能夠到達。如果B沒收到A發送來的確認報文,那么就會重新發送連接釋放請求報文,A等待一段時間就是為了處理這種情況的發生。
-
等待一段時間是為了讓本連接持續時間內所產生的所有報文都從網絡中消失,使得下一個新的連接不會出現舊的連接請求報文。
同時打開和關閉
TCP是特意設計為了可以處理同時打開,對於同時打開它僅建立一條連接而不是兩條連接。需要每一方使用一個對方熟知的端口作為本地端口。一個同時打開的連接需要交換4個報文段,比正常的三次握手多一個。
TCP協議也允許同時關閉。
完整狀態轉移圖
限制端口
TCP的交互數據流和成塊數據流
TCP的通信量如果按照分組數量計算,約有一半的TCP報文段包含成塊數據(如FTP、電子郵件和Usenet新聞),另一半則包含交互數據(如Telnet和Rlogin)。如果按字節計算,則成塊數據與交互數據的比例約為90%和10%。這是因為成塊數據的報文段基本上都是滿長度的(通常為512字節的用戶數據),而交互數據則小得多。TCP需要同時處理這兩類數據,但使用的處理算法則有所不同。
通道的容量計算:帶寬時延積 = 帶寬 x 時延(是來回的時間RTT)。
TCP可靠傳輸(超時重傳)
TCP的管理有4個定時器:1、重傳定時器,用於等待對方的確認,超時重傳保證可靠性;2、堅持定時器,使窗口大小信息保持不斷流動,即使另一端關閉了其接收窗口;3、保活定時器,可檢測到一個空閑連接的另一端何時崩潰或重啟;4、2MSL定時器,測量一個連接處於TIMEWAIT狀態的時間。
TCP超時重傳中最重要的部分就是對一個給定連接的往返時間( RTT)的測量。
最初的TCP規范使TCP使用低通過濾器來更新一個被平滑的RTT估計器:RTT =αRTT + (1-α)M,RTT表示使用的估計時間往返值,M表示新測量的時間往返值。α是一個推薦值為0.9的平滑因子。每次進行新測量的時候,這個被平滑的RTT將得到更新。每個新估計的90%來自前一個估計,而10%則取自新的測量。
該算法在給定這個隨RTT的變化而變化的平滑因子的條件下, RFC 793推薦的重傳超時時間RTO(Retransmission TimeOut)的值應該設置為RTO = RTTβ,這里的β是一個推薦值為2的時延離散因子。
除了被平滑的RTT估計器,所需要做的還有跟蹤RTT的方差。在往返時間變化起伏很大時,基於均值和方差來計算RTO,將比作為均值的常數倍數來計算RTO能提供更好的響應。M為最新往返時間測量值,A為被平滑的RTT值,Err為時間偏差,g為起加權作用的增量,h為偏差的增益,D為均值偏差。
當TCP超時重傳時,它不一定要重傳同樣的報文段,允許進行重新分組而發送一個較大的報文段,這將有助於提高性能。因為TCP是使用字節序號而不是報文段序號來進行識別它所要發送的數據和進行確認。
TCP流量控制(滑動窗口)
TCP所使用的被稱為滑動窗口協議的另一種形式的流量控制方法。由於發送方不必每發一個分組就停下來等待確認,因此該協議可以加速數據的傳輸。
窗口是緩存的一部分,用來暫時存放字節流。發送方和接收方各有一個窗口,接收方通過 TCP 報文段中的窗口字段告訴發送方自己的窗口大小,發送方根據這個值和其它信息設置自己的窗口大小。
發送窗口內的字節都允許被發送,接收窗口內的字節都允許被接收。如果發送窗口左部的字節已經發送並且收到了確認,那么就將發送窗口向右滑動一定距離,直到左部第一個字節不是已發送並且已確認的狀態;接收窗口的滑動類似,接收窗口左部字節已經發送確認並交付主機,就向右滑動接收窗口。
接收窗口只會對窗口內最后一個按序到達的字節進行確認,例如接收窗口已經收到的字節為 {31, 34, 35},其中 {31} 按序到達,而 {34, 35} 就不是,因此只對字節 31 進行確認。發送方得到一個字節的確認之后,就知道這個字節之前的所有字節都已經被接收。
窗口左右沿的運動有三種:1、窗口左邊沿向右邊沿靠近為窗口合攏。這種現象發生在數據被發送和確認時;2、窗口右邊沿向右移動時將允許發送更多的數據,稱為窗口張開。這種現象發生在另一端的接收進程讀取已經確認的數據並釋放了TCP的接收緩存時;3、右邊沿向左移動時,我們稱之為窗口收縮。因為窗口的左邊沿已經是確認接收的數據了,因此不可能向左邊移動。
TCP擁塞控制
當數據到達一個大的管道(如一個快速局域網)並向一個較小的管道(如一個較慢的廣域網)發送時便會發生擁塞。當多個輸入流到達一個路由器,而路由器的輸出流小於這些輸入流的總和時也會發生擁塞。如果網絡出現擁塞,分組將會丟失,此時發送方會繼續重傳,從而導致網絡擁塞程度更高。因此當出現擁塞時,應當控制發送方的速率。這一點和流量控制很像,但是出發點不同。流量控制是為了讓接收方能來得及接收,而擁塞控制是為了降低整個網絡的擁塞程度。
TCP主要通過四個算法來進行擁塞控制:慢啟動、擁塞避免、快重傳、快恢復。維護兩個狀態變量:一個擁塞窗口cwnd和一個慢啟動門限ssthresh(到達這個上限值后不再使用慢啟動中的策略)。慢開始和快恢復的快慢指的是 cwnd 的設定值,而不是 cwnd 的增長速率。慢開始 cwnd 設定為 1,而快恢復 cwnd 設定為 ssthresh。
慢啟動與擁塞避免
擁塞避免算法和慢啟動算法是兩個目的不同、獨立的算法。但是當擁塞發生時,我們希望降低分組進入網絡的傳輸速率,於是可以調用慢啟動來作到這一點。在實際中這兩個算法通常在一起實現。
慢啟動通過觀察到新分組進入網絡的速率應該與另一端返回確認的速率相同而進行工作。cwnd被初始化為1個報文段,每收到一個ACK,擁塞窗口就指數增加報文段( cwnd以字節為單位,但是慢啟動以報文段大小為單位進行增加)。發送方取擁塞窗口與通告窗口中的最小值作為發送上限。擁塞窗口是發送方使用的流量控制,而通告窗口則是接收方使用的流量控制。發送方開始時發送一個報文段,然后等待ACK。當收到該ACK時,擁塞窗口從1增加為2,即可以發送兩個報文段。當收到這兩個報文段的ACK時,擁塞窗口就增加為4。這是一種指數增加的關系。
擁塞避免在到達慢啟動門限之前使用慢啟動。流程如下:
- 對一個給定的連接,初始化cwnd為1個報文段,ssthresh為65535個字節。TCP輸出例程的輸出不能超過cwnd和接收方通告窗口的大小中的最小值。
-
當 cwnd >= ssthresh時,進入擁塞避免,收到ACK后,每個輪次只將 cwnd 加 1。如果出現了超時,則ssthresh被設置為當前窗口大小的一半( cwnd和接收方通告窗口大小的最小值,但最少為2個報文段),然后重新執行慢開始。
快重傳與快恢復
在接收方,要求每次接收到報文段都應該對最后一個已收到的有序報文段進行確認。例如已經接收到 M1和M2,此時收到M4,應當發送對M2的確認。由於我們不知道一個重復的ACK是由一個丟失的報文段引起的,還是由於出現了幾個報文段的重新排序,所以當收到2個重復的ACK時不能確定報文丟失,需要收到連續3及以上重復的ACK時可以確定報文丟失。在發送方,如果收到3個重復確認,那么可以知道下一個報文段丟失,此時執行快重傳,立即重傳下一個報文段。例如收到三個 M2,則 M3丟失立即重傳 M3。
在這種情況下,只是丟失個別報文段,而不是網絡擁塞。因此執行快恢復,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此時直接進入擁塞避免,這就是快恢復。
TCP堅持定時器
由於TCP只對數據進行確認,而不對ACK進行確認。所以如果接收方回傳的關於更新窗口的一個確認丟失了,則雙方就有可能因為等待對方而使連接終止:接收方等待接收數據(因為它已經向發送方通告了一個非0的窗口),而發送方在等待允許它繼續發送數據的窗口更新。為防止這種死鎖情況的發生,發送方使用一個堅持定時器來周期性地向接收方查詢,以便發現窗口是否已增大,這些從發送方發出的報文段稱為窗口探查。
TCP保活定時器
如過沒有保活的情況下,我們可以啟動一個客戶與服務器建立一個連接,然后離去數小時、數天、數個星期或者數月,而連接依然保持。中間路由器可以崩潰和重啟,電話線可以被掛斷再連通,但是只要兩端的主機沒有被重啟,則在兩端看來連接依然保持建立。
一般保活由應用層實現,在連接空閑兩個小時后,在一個連接上發送一個探查分組來完成保活功能。可能會發生4種不同的情況:對端仍然運行正常、對端已經崩潰、對端已經崩潰並重新啟動以及對端當前無法到達。
TCP vs. UDP
- TCP和UDP均工作在傳輸層,檢驗和的計算相似,均有偽首部。
- TCP的檢驗和是必須的;UDP的檢驗和是可選的。
- TCP是面向連接的,一對一之間的通信;UDP是面向無連接的。
- TCP是基於字節流服務;UDP是基於報文;
- TCP具有超時重傳,差錯控制,流量控制;UDP沒有。
參考
《TCP/IP詳解卷1》