# Transmission Control Protocol (TCP) ###### tags: `Technology` `Internet` ## 概述 TCP為 **[傳輸層](https://hackmd.io/gJIzD0-VTi6VtW2JbpkMcg)** 的一種協定,因為是可靠傳輸,相較於 **[UDP](https://hackmd.io/KZ_bS79pRFKTY69kPtNrzg)** 複雜許多,他有以下幾個機制: 1. **連線導向(Connection-oriented)** 2. **全雙工服務(Full-duplex service)** 3. **可靠資料傳輸(Reliable data transfer)** * 估計來回時間 * 倍增逾時間隔 * 快速重送(Fast Retransmit) 4. **流量控制(Flow Control)** 5. **壅塞控制(Congestion Control)** 假設某台主機上的行程(**用戶端行程**)想要與另一台主機(**伺服端行程**)開啟連線,而建立TCP連線大概的步驟如下: 1. 先透過**三次握手(three-way handshake)** 建立連線,TCP連線建立後就可以開始傳送資料 2. 用戶端行程透過socket送出資料至**傳送緩衝區(send buffer)** 3. 從緩衝區中抓出資料放入**區段(segment)** 中,要注意的是資料量有限制,受限於**最大區段大小(Maximum Segment Size, MSS)** ,而MSS的大小通常又是由最大連結層訊框,又稱為**最大傳輸單元(Maximum transmission unit, MTU)** 決定。 所以當TCP要傳送大型檔案時,通常會將檔案切割成大小為MSS的片段。 4. 建立**TCP區段(TCP segment)** ,為資料加上標頭後送入網路層。 5. 另一端收到後,資料會先放在**接收緩衝區(Receive buffer)** 。 6. 應用程式再從緩衝區讀出資料。 ![TCP_flow](https://i.imgur.com/1rvlEpY.png) ## TCP區段結構 (TCP segment format) TCP標頭通常是20個位元組,結構如下所示: ![TCP_segment_format](https://i.imgur.com/7e8oYOI.png) 1. **來源端與目的端埠號(SrcPort and DstPort number)** 2. **序號(SequenceNum)** - 32 bits 紀錄第一個位元組的編號。假設現在要傳送30000位元組的檔案而MSS為1000位元組,所以檔案會被分成30個segment,而第一個segment的序號為0,第二個segment為1000,第三個為2000,以此類推。 3. **確認編號(Acknowledgment)** - 32 bits //TODO 4. **標頭長度(HdrLen)** - 4 bits 紀錄標頭的長度,通常Options是空的,所以一般TCP標頭長度為20個bytes。 5. **FLAG** - 6 bits URG、ACK、PSH、RST、SYN、FIN 6個bits。 6. **接收窗格(AdvertisedWindow)** 7. **檢查和(Checksum)** ## TCP的建立與關閉 Connection Establishment and Termination ### 建立連線 TCP透過**三次握手(three-way handshake)** 建立連線,有以下3個步驟 * **Step 1** 用戶端傳送一個不含資料的TCP區段到伺服端,該區段的FLAG欄位中的SYN位元為1,且會隨機選定一個初始序號(client_isn) * **Step 2** 伺服端收到後配置TCP緩衝區與變數,並送出連許可區段給用戶端 * 確認客戶端的序號:ACK=1,且Acknowledgment欄位為client_isn+1。 * 傳送自己的初始序號:SYN=1,序號欄位為自己的初始序號server_isn。 * **Step 3** 用戶端收到連線許可區段後,配置緩衝區與變數給該筆TCP連線,SYN=0、ACK=1,Acknowledgment欄位為server_isn+1。 ![establish](https://i.imgur.com/MAZhF0k.png) 這三個步驟都完成後,雙方就可以開始互相傳送資料了。之後的資料,SYN位元都會被設定成0。 ### 關閉連線 傳輸資料一段時間後,任一方都可結束該筆連線,假設現在用戶端想結束,步驟如下: 1. 用戶端送出特殊TCP區段,FLAG內的FIN位元為1。 2. 伺服端收到後,傳回確認區段給用戶端。 3. 接著伺服端再送出FIN位元為1的TCP區段給用戶端。 4. 用戶端收到後傳回確認區段結束這筆連線。 ![termination](https://i.imgur.com/FcnMveF.png) ## 來回時間的評估 在逾時就重送的機制哩,設定逾時間隔是很值得討論的議題,他要比來回時間(RTT)長,但又不能太長,所以估計RTT就變得很重要。TCP的RTT估計如下所示: 1. 當收到ACK時,TCP就會記下發出packet跟收到ACK的時間間隔,記為$SampleRTT$。 2. 任意時間的$SampleRTT$都有可能不同,所以TCP會持續更新一個估計值$EstimatedRTT$: $$ EstimatedRTT=(1-\alpha)\times EstimatedRTT+\alpha \times SampleRTT $$ 根據RFC6298,$\alpha$的建議值為0.125。 3. TCP也會估計RTT的變動程度$DevRTT$,這個是SampleRTT跟EstimatedRTT的差距: $$ DevRTT=(1-\beta )\times DevRTT+\beta \times |SampleRTT-EstimatedRTT| $$ $\beta$ 的建議值為0.25。 4. 逾時間隔$TimeoutInterval$就被定義為: $$ TimeoutInterval=EstimatedRTT+4\times DevRTT $$ ![RTT](https://i.imgur.com/RkqTedH.png)