TCP協議
特點
- 面向連接, 使用TCP傳輸數據前必須建立連接,完畢後須釋放連接。
- 點對點, 每個TCP連接只能有兩個端點。
- 可靠傳輸, 通過TCP的超時重傳和捎帶確認機制保證傳輸的數據能夠按序正確到達。
- 流量控制和擁塞控制,在網絡情況較差時可控制傳輸速率。
- 雙工通信, TCP連接的兩端都設有發送緩存和接收緩存, 會在合適的時機處理數據。
- 面向字節流, TCP流指流入到進程或從進程流出的字節序列,TCP吧應用程序交下來的數據堪稱是一串連續的字節流處理。
socket套接字
TCP把連接作爲最基本的抽象,因此每一條連接都需要有兩個端點,而套接字(socket)就是TCP連接的端點。
格式
套接字 socket = (IP地址 : 端口號)
每一條TCP連接唯一地被兩個套接字確定。
同一個IP地址可以有不同的TCP連接, 同一個端口號也可有不同的TCP連接。
注:
socket也指操作系統提供的一套應用編程接口 API。
由於TCP/IP的核心內容被封裝在操作系統中,如果應用程序要使用TCP/IP,則需要使用操作系統提供的socket API來實現。
TCP報文
- 源端口和目的端口 各2字節
用來寫入源端口號和目的端口號。
- 序號 4字節
4字節共2^ 32個序號。序號增加到 2^32 - 1 (32個1)後又回到0。
用來爲字節流中的每一個字節都按序編號,序號在連接建立時設置。
首部中的序號表示該數據報中發送的第一個字節的序號。
- 確認號 4字節
表示期望收到對方下一個報文段的數據的第一個字節的序號。例如A向B發送了序號爲300,長度爲100的數據報,B期望繼續接受數據,則需要將發送給A的確認報文中把確認號設置爲401。
- 數據偏移 4位
指出TCP報文段首部的長度。 因爲首部中還有長度不確定的選項字段, 因此需要額外用數據偏移字段記錄首部的長度。
數據偏移字段單位爲32bit(4字節)。 4位能表示最大二進制數爲15, 因此該字段最大能表示60字節,這也是TCP首部的最大長度。
- 保留 6位
6個控制位
- 緊急URG 1位
URG = 1 時表示緊急指針字段有效。它告訴系統次報文中有緊急數據,應當儘快傳送,而不按原來的排隊順序傳送。
- 確認ACK 1位
當ADC = 1時確認號字段纔有效, 在TCP連接建立後所有傳送的報文都必須把ACK設爲1.
- 推送PSH1位
PSH = 1表示推送操作, 立即創建一個表文段發送出去, 接收方TCP收到PSH = 1的報文段時就儘快的交付給接收應用進程。
- 復位RST1位
當RST = 1 時,表明TCP連接中出現嚴重差錯,必須釋放連接,然後重新建立連接。 它還用來拒絕非法的報文段或拒絕打開一個連接。
- 同步SYN1位
在建立連接時用來同步序號。
當SYN = 1 且 ACK = 0時 表明這是一個連接請求報文段。 對方若同意連接, 則應在響應的報文段中使SYN = 1 和 ACK = 1,。
- 終止FIN1位
FIN = 1是表示發送方的數據發送完畢, 將要釋放連接。
窗口 2字節
最大 2^16 - 1, 接受方的緩存大小有限, 所以需要使用該字段表示發送方的接收窗口大小。
窗口值經常在動態變化着.
- 校驗和2字節
校驗範圍包括首部和數據這兩部分. 和UDP一樣需要加僞首部.
- 緊急指針 2字節
指出本報文段中的緊急數據的字節數.
- 選項
長度可變, 最長可達40字節, 沒有選項時首部長20字節,
TCP連接建立
三次握手
A爲客戶端, B爲服務器, 最初二者都爲CLOSE關閉狀態。
B服務器進程先創建TCB(Transmission Control Block)傳輸控制塊, 進入Listen狀態準備接受客戶進程的連接請求。
- 客戶端A創建TCB, 然後向客戶端發送請求報文, 其中SYN = 1, 表示建立連接信號有效, ACK = 0 , 因爲是第一次請求還沒有確認號所以確認信號無效,seq = x 設置一個初始序號。 TCP規定SYN = 1 的報文不能攜帶數據, 但要消耗一個序列號。 發送完成後A客戶端進入SYN-SENT狀態。
- 服務器B收到A的連接請求後,如同意連接則需要向A發送確認,使 SYN = 1,表示連接請求, ACK = 1 , 因爲已經收到了A的seq序號, 將要請求下一個序號,所以ACK生效, 並且確認號ack = x + 1。
同時B還要帶上自己設置的初始序號seq = y。 發送完成後B服務端進入SYN-RCVD。 - 客戶端A收到服務端B的確認後, 需要再向服務端B發送一個確認。 設置seq = x + 1,ACK = 1, 確認號ack = y + 1(SYN = 0),向B進行確認。 發送後A進入ESTABLISHED狀態。服務端B收到後也進入ESTABLISHED狀態。
抓包
第一次握手
爲什麼要三次握手
需要建立可靠的連接, 則雙方都需要確認雙方的接收請求和發送請求功能都正常。
- 第一次握手,服務端B收到A的請求, 則B可以確認A的發送功能和B的接收功能都正常。
- 第二次握手,客戶端A收到B的確認, 則A可以確認A和B的發送和接收功能都正常。
- 第三次握手, 服務端B收到A的確認,則B可以確認A的接收功能和B的發送功能都正常。
因此, 三次握手才能夠建立可靠的連接。
只有兩次握手帶來的問題
- 網絡中客戶端發送的滯留失效報文在延時的情況下到達服務端時, 服務器返回一次確認後便認爲連接已經建立, 而此時客戶端在CLOSE狀態並沒有發起連接, 則此時服務端進入無效的等待, 浪費資源.
- 當服務器回答客戶端發送的滯留失效的延時報文時, 如果客戶端又恰好發起連接進入SYN-SENT狀態, 客戶端則會收到延時報文的確認, 從而會導致雙方得到了錯誤的的序號和確認號, 導致大量的數據包會被丟棄,造成系統資源的浪費。
TCP連接釋放
四次揮手
首先A和B都處於ESTABLISHED連接狀態
- A的應用進程先向其TCP發出連接釋放報文, 設置 FIN = 1 , seq = u, u爲前面傳送過的最後一個字節的序號加1, 並停止發送數據, 主動關閉連接, 進入FIN-WAIT-1狀態。
- B服務端收到連接釋放報文後發出確認, 確認序號ack = u + 1, seq = v 等於B之前傳送的報文的最後一個字節加1, 然後B進入CLOSE-WAIT狀態。此時連接處於半關閉狀態,A已經沒有數據要發送了, 但B若發送輸出A仍要接收。 這個狀態可能持續一段時間。A在收到B的確認後進入FIN-WAIT-2狀態,等待B的釋放連接報文。
- 若B已經沒有要向A發送的數據,其應用進程就通知TCP釋放連接。這時B發出連接釋放報文,設置FIN = 1 ,seq = w (表示半關閉狀態發送的最後一個字節序號加1), 重複上次的確認號ack = u + 1, 之後B進入LAST-ACK狀態, 等待A的確認。
- A在收到B的連接釋放報文段後,需對B進行回覆確認,在確認報文段中設置 ACK = 1, ack = w + 1, seq = u + 1(FIN = 1 消耗序列號), 之後進入TIME-WAIT狀態。 此時TCP連接還沒有被釋放掉, 必須經過2MSL後A才能進入CLOSE狀態。 MSL(Maxinmum Segment Lifetime)最長報文段壽命,RFC 793建議設置爲2分鐘,可根據具體情況對時長進行設置。所以進入TIME-WAIT後客戶端還要經過4分鐘才能進入CLOSE狀態。 服務端接接收到客戶端的確認報文後也進入CLOSE狀態。
抓包
TIME-WAIT
爲什麼客戶端A必須在TIME-WAIT狀態等待2MSL呢?
- 第四次揮手, A發送最後一個ACK報文段有可能丟失, 處在LAST-ACK狀態的B收不到最後一個確認報文,導致B不能正常關閉連接進入CLOSE狀態。 而如果A等待2MSL,則B在收不到確認報文時會超時重傳第三次揮手的FIN + ACK報文, A在2MSL內可以繼續接受B發送的該報文,並重傳一次確認,重新啓動2MSL計時器。這樣就能夠保證B也能正常釋放TCP連接。
- 2MSL可以使本連接持續時間內所產生的所有報文段都在網絡中消失。這樣可以使下一個新的連接不會出現舊的連接報文段。
保活計時器
如果客戶端與服務器建立了TCP連接後,客戶端突然發生了故障。使用保活計時器可以防止服務器繼續無效等待下去。
服務器每收到一次客戶的數據,就重新設置保活計時器,通常是兩小時。兩小時沒有收到客戶的數據,則服務器每隔75分鐘發送一個探測報文段。若連續發送10個仍無客戶響應,服務器就認爲客戶端出了故障,並關閉該鏈接。
TCP可靠傳輸
TCP通過超時重傳實現可靠傳輸。
當一個已經發送的報文在一定時間內未收到確認,則重傳該報文, 該時間成爲超時時間。
一個報文從發發送到收到確認所需的時間稱爲往返時間 RTT, 對RTT進行加權平均的往返時間成爲RTTs(平滑往返時間)。
其中,0 ≤ a < 1,RTTs 隨着 a 的增加更容易受到 RTT 的影響。
超時時間應該略大於RTTs值,TCP使用的超時時間:
其中 RTTd 爲偏差的加權平均值。
TCP滑動窗口
發送緩存用來存放:
- 應用程序準傳送給TCP準備發送的數據
- TCP已經發送但尚未收到確認的數據
接收緩存用來存放:
- 按序到達的、但尚未被應用程序讀取的數據
- 未按序到達的數據
滑動窗口是緩存的一部分, 用來暫時存放字節流。接收方通過TCP報文中的窗口字段告訴發送方自己的窗口大小,接收方通過該字段設置自己的發送窗口大小。
發送窗口裏的數據:
- 已經發送但未確認的數據
- 允許發送但還未來得及發送的數據
接收窗口裏的數據:
- 未按序到達的數據
- 可以被可以接收的數據
TCP爲全雙工通信,通信的雙方都有自己的發送窗口和接收窗口,也在同時發送和接收數據。
TCP流量控制
如圖, 如果發送方數據發送過快, 接收方的應用程序接收按序到達的數據太慢,就會導致接收緩存漸漸被填滿,造成數據的丟失。
利用滑動窗口機制可以方便地在TCP連接上實現對發送方的流量控制。
接收方發送的確認報文中的窗口字段可以用來控制發送方窗口大小,從而影響發送方的發送速率。
如果設置發送方的窗口爲0, 則發送方會啓動一個計時器, 定時發送探測報文, 從而防止產生僵局。
TCP擁塞控制
在計算機網絡中的帶寬, 交換節點的緩存和處理機等都是網絡的資源, 如果在某一時間對這些資源的需求超過了可提供的部分, 就會產生擁塞, 網絡性能就會變差, 。
因此當出現擁塞時, 發送方應當控制其傳送速率。
TCP 主要通過四個算法來進行擁塞控制:慢開始、擁塞避免、快重傳、快恢復。
慢開始與擁塞避免
發送方需要維護一個叫做擁塞窗口(cwnd)的狀態變量,注意擁塞窗口與發送方窗口的區別:擁塞窗口只是一個狀態變量,實際決定發送方能發送多少數據的是發送方窗口。
慢開始:令cwnd = 1, 發送方只能發送一個報文段, 在收到確認後,cwnd翻倍, cwnd = 2, 依次進行,cwnd的數量依次翻倍,2,4,8,16 …
擁塞避免: cwnd因爲成指數倍增長,增長速率非常快, 爲了避免增長過快導致的網絡擁塞, 需要設置一個慢開始門限 ssthresh, 當cwnd >= ssthresh時, 進入擁塞避免, 每個輪次只講cwnd增1。
如果出現了超時,則ssthresh 設置爲超時時的 cwnd / 2 , 再重新執行慢開始。
快重傳與快恢復
快重傳: 快重傳不需要接收方等待自己發送數據時稍待確認, 而是在接收數據後立即發送確認。 即使收到了失序的報文段也要立即發送正確的確認。
如圖, 接收方收到了M1, M2 的報文段後, M3報文段丟失,之後又收到了三個報文段M4,M5和M6, 併發送了三個M2的確認, 接收方收到三個隊M2的重複確認後立即重傳M3。
這種情況下, 只是出現意外丟失個別的報文段,而不是網絡擁塞, 因此執行快恢復,零ssthresh = cwnd / 2, cwnd = ssthresh, 並直接進入擁塞避免。