我們都知道,TCP和UDP協議的區別在于TCP可以提供可靠的網絡數據傳輸,但UDP不能。
為什么TCP比較可靠呢?可能有人會回答,TCP是面向連接的,而UDP不是。連接是什么呢?它是一個像水管一樣的東西,把所有的數據往連接里一放就保證了數據完整有序到達嗎?其實不是的。
TCP/IP詳解對整個數據傳輸過程有著詳細的說明。我試著把其中關鍵的部分抽取出來,解釋一下TCP協議到底是怎么保證傳輸的可靠性的。
建立連接
連接是什么
當提到連接,我們本能的會把它想成一根水管或者繩子,建立連接就是把這個水管或者繩子接起來。在通信的時候,我們會把連接想象成一個獨立的通道,建立連接后所有的數據都順著這個通道傳輸出去,對方就能收到有序完整的數據。但是實際上,因為各種各樣的問題(硬件故障、網絡阻塞、攻擊等)的存在,網絡傳輸通道本身就不可靠。
所以,TCP里的所謂連接不是一個通道,它只是通信雙方建立的一個一對一的邏輯關系,讓雙方都明確對方是自己的通信目標。
既然雙方只存在邏輯約定,數據仍然可能在傳輸過程中會出現錯誤、丟失等各種狀況。那建立連接的意義是什么呢?舉個例子:
如果一開始雙方沒有建立連接的那幾步溝通,乙不知道甲到底要干什么,也不知道甲說的內容從什么地方開始到什么地方結束,甲乙就都沒法確保這次通信的完整和正確。
更多的細節可以參考到底什么是TCP連接?
三次握手建立連接
TCP通過三次握手的方式建立連接,具體的過程見下圖:
從圖中可以看到,三次握手的過程其實是一個客戶端和服務器各向對方發送一個數seq,并接收對方的ACK(收到的seq+1)的過程。第二次握手的數據里同時包含了服務器給客戶端的ACK和服務器發出的seq。
三次握手以后,連接雙方就同時進入ESTABLISHED(連接成功)
狀態,準備開始數據傳輸。
如果想知道三次握手建立連接,或者四次揮手斷開連接的更多細節,可以看看簡析TCP的三次握手與四次分手這篇文章。
另外,知乎上為什么TCP是三次握手,而不是兩次或者四次握手這個問題,解釋了三次握手的設計初衷,值得一看。
傳輸數據
ACK機制
由于通信過程的不可靠性,傳輸的數據不可避免的會出現丟失、延遲、錯誤、重復等各種狀況,TCP協議為解決這些問題設計了一系列機制。
這個機制的核心,就是發送方向接收方發送數據后,接收方要向發送方發送ACK(回執)。如果發送方沒接收到正確的ACK,就會重新發送數據直到接收到ACK為止。
比如:發送方發送的數據序號是seq,那么接收方會發送seq + 1作為ACK,這樣發送方就知道接下來要發送序號為seq + 1的數據給接收方了。
我們來看看在不同的異常情況下,ACK機制是怎么工作的:
- 數據丟失或延遲。發送方發送數據seq時會起一個定時器,如果在指定時間內沒有接收到ACK seq + 1,就把數據seq再發一次。
- 數據亂序。接收方上一個收到的正確數據是seq + 4,它返回seq + 5作為ACK。這時候它收到了seq + 7,因為順序錯了,所以接收方會再次返回seq + 5給發送方。
- 數據錯誤。每一個TCP數據都會帶著數據的校驗和。接收方收到數據seq + 3以后會先對校驗和進行驗證。如果結果不對,則發送ACK seq + 3,讓發送方重新發送數據。
- 數據重復。接收方直接丟棄重復的數據即可。
ACK的優化
按照ACK機制,只要整個數據傳輸順利結束,接收方就能收到完整有序的數據了。但是,如果我們針對每一個數據包都發送ACK,就會有大量的網絡資源消耗在ACK的發送上,這不太劃算的。于是,TCP設計了延遲ACK的機制。
這個機制其實很簡單。客戶端一次給服務器發送多個數據包,當服務器收到客戶端的數據包時,不馬上發送ACK,而是稍微等一小段時間。在這個過程中服務器可能能收到后續幾個數據包,服務器就可以直接按照最后一個正確的數據發送ACK,減少發送ACK的總數。
除了延遲ACK的機制,TCP還做了很多對傳輸過程的優化,比如滑動窗口機制,比如慢啟動機制。由于跟本文的主題無關,我就不在這里多說了,有興趣的同學可以搜索來看看。
本人學識有限,文中難免有不嚴謹或者錯誤出現,希望各位讀者能幫忙指出,感謝。