1 三次握手
TCP是面向連接的,無論哪一方向另一方發送數據之前,都必須在雙方之間建立一個連接。
在TCP/IP協議中,TCP協議提供可靠的連接服務,連接是通過三次握手進行初始化的。三次握手的目的是同步連接雙方的序列號和確認號,并交換TCP窗口大小信息。
名詞解釋:
SYN:synchronous 同步。SYN是6個控制位中的一個,值為1時表示當前報文段的作用是同步序號。
seq:Sequence Number 序號。第一個字節的編號,在“TCP報文結構“中有介紹。
ACK:Acknowledgement 確認字符。ACK是6個控制位中的一個,值為1時表示前一個報文段已確認收到。
ack:小寫的ack表示確認號。即接收方期望接收的下一個字節的編號。
第一次握手:
客戶端發送SYN報文段,將SYN位置為1,seq為X(X為一個隨機數);然后客戶端進入SYN_SEND狀態,等待服務器的確認。
注意:這個報文段中不包括確認號,也沒有定義窗口大小。SYN報文段是一個控制報文段,它不攜帶任何數據。但是它消耗了一個序號,當數據傳送開始時,序號就應當加1。
第二次握手:
服務器發送SYN+ACK報文段,其中SYN和ACK這兩個控制位都置為1。
這個報文段有兩個目的:
服務器使用這個報文段來同步它的初始序號(seq),以便從服務器向客戶端發送字節。
服務器通過ACK標志告訴客戶端已經收到來自客戶端的SYN報文段。同時設置ack表示希望從客戶端收到的下一個字節編號,也因為設置了ack,服務器還定義了接收窗口的大小(rwnd)。
此時服務器進入SYN_RECV狀態。
注意:SYN+ACK報文段不攜帶任何數據,但要消耗一個序號。
第三次握手:
客戶端發送ACK報文段。
ACK標志和確認號(ack)用于告訴服務器已經收到來自服務器的SYN+ACK報文段。
序號(seq)和第一次握手的SYN報文段的序號一樣,也就是說這個ACK報文段不消耗任何序號。但在某些實現中,連接階段的第三個報文段(ACK報文段)可以攜帶客戶端的第一個數據塊,此時,第三個報文段必須有一個新的序號來表示數據中的第一個字節編號。結論就是:ACK報文段如果不攜帶數據就不消耗序號。
此時客戶端和服務器都進入了ESTABLISHED狀態,完成TCP三次握手。(單詞解釋:establish:建立)
為什么要三次握手?
為了防止已經失效的連接請求報文段突然又傳送到了服務器,因而產生錯誤。
具體例子:
client發出的第一個連接請求報文段并沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以后的某個時間才到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段后,就誤認為是client再次發出的一個新的連接請求。于是就向client發出確認報文段,同意建立連接。假設不采用“三次握手”,那么只要server發出確認,新的連接就建立了。由于現在client并沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送數據。但server卻以為新的運輸連接已經建立,并一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。采用“三次握手”的辦法可以防止上述現象發生。
2 四次揮手
客戶端和服務器通過三次握手建立了TCP連接,當數據傳送完畢,需要斷開連接,對于TCP的斷開連接,有“四次揮手過程”。
第一次揮手:
主動方(可以是客戶端或者服務器),設置seq=x,向被動方發送一個FIN報文段(FIN報文段可以包含要發送的最后一塊數據);此時,主動方進入FIN_WAIT_1狀態;這表示主動方已經沒有數據要發送給被動方了。
第二次揮手:
被動方收到了主動方的FIN報文段,向主動方回了一個ACK報文段,ack=x+1;此時主動方進入FIN_WAIT_2狀態;這表示被動方告訴主動方,我“同意”你的關閉請求。
第三次揮手:
被動方向主動方發送FIN報文段,請求關閉連接;此時被動方進入LAST_ACK狀態。
第四次揮手:
主動方收到被動方的FIN報文段,向被動方發送ACK報文段,然后主動方進入TIME_WAIT狀態;被動方收到主動方的ACK報文段后,就關閉連接;此時主動方等待2MSL(MSL:Maximum Segment Lifetime,報文段最大生存時間)后依然沒有收到回復,則證明被動方已正常關閉,那么主動方也可以關閉了。
為什么要四次揮手?
TCP協議是一種面向連接的、可靠的、基于字節流的運輸層通信協議。TCP是全雙工模式,這就意味著,當主機1發出FIN報文段時,只是表示主機1已經沒有數據要發送了,主機1告訴主機2,它的數據已經全部發送完畢了;但是,這個時候主機1還是可以接受來自主機2的數據;當主機2返回ACK報文段時,表示它已經知道主機1沒有數據發送了,但是主機2還是可以發送數據到主機1的;當主機2也發送了FIN報文段時,這個時候就表示主機2也沒有數據要發送了,就會告訴主機1,我也沒有數據要發送了,之后彼此就會愉快的中斷這次TCP連接。
為什么要等待2MSL?
原因有二:
保證TCP協議的全雙工連接能夠可靠的關閉
保證這次連接的重復數據段從網絡中消失
第一點:如果主機1直接CLOSED了,那么由于IP協議的不可靠性或者是其它網絡原因,導致主機2沒有收到主機1最后回復的ACK。那么主機2就會在超時之后繼續發送FIN,此時由于主機1已經CLOSED了,就找不到與重發的FIN對應的連接。所以,主機1不是直接進入CLOSED,而是要保持TIME_WAIT狀態。當再次收到FIN的時候,能夠保證對方收到ACK,最后正確的關閉連接。
第二點:如果主機1直接CLOSED,然后又再向主機2發起一個新連接,我們不能保證這個新連接與剛關閉的連接的端口號是不同的。也就是說有可能新連接和老連接的端口號是相同的。一般來說不會發生什么問題,但是還是有特殊情況出現:假設新連接和已經關閉的老連接端口號是一樣的,如果前一次連接的某些數據仍然滯留在網絡中(稱為Lost Duplicate),這些延遲數據在建立新連接之后才到達主機2,由于新連接和老連接的端口號是一樣的,TCP協議就認為那個延遲的數據是屬于新連接的,這樣就和真正的新連接的數據包發生混淆了。所以TCP連接要在TIME_WAIT狀態等待2倍MSL,保證本次連接的所有數據都從網絡中消失。
以上內容摘自:
https://github.com/LRH1993/android_interview/blob/master/computer-networks/tcpip.md
《TCP/IP協議族》第四版