一、TCP三次握手
在建立連接的過程中,雙方的狀態變化時序圖如下所示 圖
剛開始的時候,客戶端和服務器都處於 CLOSED 狀態,先是服務端主動監聽某個端口,處於 LISTEN 狀態。
- 第一次握手:A發送SYN包(SYNC=j)到達B,並進入SYN_SEND狀態,等待服務器B確認
- 第二次握手:B收到SYN包后,也會發送一個SYN包給A,這個包里面帶有ACK=j+1用來確認A的SYN,和B自己的SYN=k,B進入SYN_RECV狀態
- 第三次握手:A收到B的SYN+ACK包,向B發送確認包ACK(ACK=k+1),發送完畢,A和B進入ESTABLISHED狀態,完成三次握手
二、為什么要三次握手
答案1
假如只進行一次握手,客戶端發送連接請求后,沒有收到服務端的應答,是沒法判斷連接是否成功的。
假如只進行兩次握手,客戶端發送連接請求后,會等待服務器端的應答。但是會出現的問題是,假如客戶端的SYN遲遲沒有到達服務器端,此時客戶端超時后,會重新發送一次連接,假如重發的這次服務器端收到了,且應答客戶端了,連接建立了。但是建立后,第一個SYN也到達服務端了,這時服務端會認為這是一個新連接,會再給客戶端發送一個ACK,這個ACK當然會被客戶端丟棄。但是此時服務器端已經為這個連接分配資源了,而且服務器端會一直維持着這個資源,會造成浪費
三次握手,兩次握手的問題在於服務器端不知道SYN的有效性,所以如果是三次握手,服務器端會等待客戶端的第三次握手,如果第三次握手遲遲不來,服務器端就會釋放相關資源。但是有人會問,假如第三次握手沒有到達服務器端呢?但是這時客戶端認為連接已經建立了。但是其實這種情況下,只要客戶端向服務器端寫數據,就會收到服務器端的RST應答,這時客戶端就能知道出現問題了。
————————————————
答案2
為了實現可靠數據傳輸, TCP 協議的通信雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程即是通信雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟。
如果只是兩次握手, 至多只有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認 (可以接受到序號,但是沒有確認序號是否准確)
————————————————
答案3
謝希仁版《計算機網絡》中的例子是這樣的,“已失效的連接請求報文段”的產生在這樣一種情況下:client發出的第一個連接請求報文段並沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后,就誤認為是client再次發出的一個新的連接請求。於是就向client發出確認報文段,同意建立連接。假設不采用“三次握手”,那么只要server發出確認,新的連接就建立了。由於現在client並沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送ack包。(校注:此時因為client沒有發起建立連接請求,所以client處於CLOSED狀態,接受到任何包都會丟棄,謝希仁舉的例子就是這種場景。但是如果服務器發送對這個延誤的舊連接報文的確認的同時,客戶端調用connect函數發起了連接,就會使客戶端進入SYN_SEND狀態,當服務器那個對延誤舊連接報文的確認傳到客戶端時,因為客戶端已經處於SYN_SEND狀態,所以就會使客戶端進入ESTABLISHED狀態,此時服務器端反而丟棄了這個重復的通過connect函數發送的SYN包,見第三個圖。而連接建立之后,發送包由於SEQ是以被丟棄的SYN包的序號為准,而服務器接收序號是以那個延誤舊連接SYN報文序號為准,導致服務器丟棄后續發送的數據包)但server卻以為新的運輸連接已經建立,並一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。采用“三次握手”的辦法可以防止上述現象發生。例如剛才那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連接。
- 在謝希仁著《計算機網絡》第四版中講“三次握手”的目的是“為了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤”。
- 在另一部經典的《計算機網絡》(Andrew S.Tanenbaum著,第四版)一書中講“三次握手”的目的是為了解決“網絡中存在延遲的重復分組”的問題。這兩種不同的表述其實闡明的是同一個問題