TCP報文與三次握手四次揮手TCP報文與三次握手四次揮手
簡介
TCP是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議,在傳送資料前,通訊雙方必須在彼此間建立一條連線。所謂的“連線”,其實是客戶端和服務端儲存的一份關於對方的資訊,如ip地址、埠號等。TCP可以看成是一種位元組流,它會處理IP層或以下的層的丟包、重複以及錯誤問題。在連線的建立過程中,雙方需要交換一些連線的引數。這些引數可以放在TCP頭部。一個TCP連線由一個4元組構成,分別是兩個IP地址和兩個埠號。一個TCP連線通常分為三個階段:連線、資料傳輸、退出(關閉)。通過三次握手建立一個連結,通過四次揮手來關閉一個連線。當一個連線被建立或被終止時,交換的報文段只包含TCP頭部,而沒有資料。
1. TCP報文的頭部結構
在瞭解TCP連線之前先來了解一下TCP報文的頭部結構。
上圖中有幾個欄位需要重點介紹下:
(1)序號:seq序號,佔32位,用來標識從TCP源端向目的端傳送的位元組流,發起方傳送資料時對此進行標記。
(2)確認序號:ack序號,佔32位,只有ACK標誌位為1時,確認序號欄位才有效,ack=seq+1。
(3)標誌位:共6個,即URG、ACK、PSH、RST、SYN、FIN等,具體含義如下:
- ACK:確認序號有效。
- FIN:釋放一個連線。
- PSH:接收方應該儘快將這個報文交給應用層。
- RST:重置連線。SYN:發起一個新連線。
- URG:緊急指標(urgent pointer)有效。需要注意的是:不要將確認序號ack與標誌位中的ACK搞混了。確認方ack=發起方seq+1,兩端配對。
2. 三次握手
三次握手的本質是確認通訊雙方收發資料的能力首先,我讓信使運輸一份信件給對方,對方收到了,那麼他就知道了我的發件能力和他的收件能力是可以的。於是他給我回信,我若收到了,我便知我的發件能力和他的收件能力是可以的,並且他的發件能力和我的收件能力是可以。然而此時他還不知道他的發件能力和我的收件能力到底可不可以,於是我最後回饋一次,他若收到了,他便清楚了他的發件能力和我的收件能力是可以的。這,就是三次握手。
- 第一次握手:客戶端要向服務端發起連線請求,首先客戶端隨機生成一個起始序列號ISN(比如是100),那客戶端向服務端傳送的報文段包含SYN標誌位(也就是SYN=1),序列號seq=100。
- 第二次握手:服務端收到客戶端發過來的報文後,發現SYN=1,知道這是一個連線請求,於是將客戶端的起始序列號100存起來,並且隨機生成一個服務端的起始序列號(比如是300)。然後給客戶端回覆一段報文,回覆報文包含SYN和ACK標誌(也就是SYN=1,ACK=1)、序列號seq=300、確認號ack=101(客戶端發過來的序列號+1)。
- 第三次握手:客戶端收到服務端的回覆後發現ACK=1並且ack=101,於是知道服務端已經收到了序列號為100的那段報文;同時發現SYN=1,知道了服務端同意了這次連線,於是就將服務端的序列號300給存下來。然後客戶端再回復一段報文給服務端,報文包含ACK標誌位(ACK=1)、ack=301(服務端序列號+1)、seq=101(第一次握手時傳送報文是佔據一個序列號的,所以這次seq就從101開始,需要注意的是不攜帶資料的ACK報文是不佔據序列號的,所以後面第一次正式傳送資料時seq還是101)。當服務端收到報文後發現ACK=1並且ack=301,就知道客戶端收到序列號為300的報文了,就這樣客戶端和服務端通過TCP建立了連線。
3. 四次揮手
四次揮手的目的是關閉一個連線
比如客戶端初始化的序列號ISA=100,服務端初始化的序列號ISA=300。TCP連線成功後客戶端總共傳送了1000個位元組的資料,服務端在客戶端發FIN報文前總共回覆了2000個位元組的資料。
- 第一次揮手:當客戶端的資料都傳輸完成後,客戶端向服務端發出連線釋放報文(當然資料沒發完時也可以傳送連線釋放報文並停止傳送資料),釋放連線報文包含FIN標誌位(FIN=1)、序列號seq=1101(100+1+1000,其中的1是建立連線時佔的一個序列號)。需要注意的是客戶端發出FIN報文段後只是不能發資料了,但是還可以正常收資料;另外FIN報文段即使不攜帶資料也要佔據一個序列號。
- 第二次揮手:服務端收到客戶端發的FIN報文後給客戶端回覆確認報文,確認報文包含ACK標誌位(ACK=1)、確認號ack=1102(客戶端FIN報文序列號1101+1)、序列號seq=2300(300+2000)。此時服務端處於關閉等待狀態,而不是立馬給客戶端發FIN報文,這個狀態還要持續一段時間,因為服務端可能還有資料沒發完。
- 第三次揮手:服務端將最後資料(比如50個位元組)傳送完畢後就向客戶端發出連線釋放報文,報文包含FIN和ACK標誌位(FIN=1,ACK=1)、確認號和第二次揮手一樣ack=1102、序列號seq=2350(2300+50)。
- 第四次揮手:客戶端收到服務端發的FIN報文後,向服務端發出確認報文,確認報文包含ACK標誌位(ACK=1)、確認號ack=2351、序列號seq=1102。注意客戶端發出確認報文後不是立馬釋放TCP連線,而是要經過2MSL(最長報文段壽命的2倍時長)後才釋放TCP連線。而服務端一旦收到客戶端發出的確認報文就會立馬釋放TCP連線,所以服務端結束TCP連線的時間要比客戶端早一些。
4. 常見面試題
- 為什麼TCP連線的時候是3次?2次不可以嗎?
- 因為需要考慮連線時丟包的問題,如果只握手2次,第二次握手時如果服務端發給客戶端的確認報文段丟失,此時服務端已經準備好了收發數(可以理解服務端已經連線成功)據,而客戶端一直沒收到服務端的確認報文,所以客戶端就不知道服務端是否已經準備好了(可以理解為客戶端未連線成功),這種情況下客戶端不會給服務端發資料,也會忽略服務端發過來的資料。如果是三次握手,即便發生丟包也不會有問題,比如如果第三次握手客戶端發的確認ack報文丟失,服務端在一段時間內沒有收到確認ack報文的話就會重新進行第二次握手,也就是服務端會重發SYN報文段,客戶端收到重發的報文段後會再次給服務端傳送確認ack報文。
- 為什麼TCP連線的時候是3次,關閉的時候卻是4次?
- 因為只有在客戶端和服務端都沒有資料要傳送的時候才能斷開TCP。而客戶端發出FIN報文時只能保證客戶端沒有資料發了,服務端還有沒有資料發客戶端是不知道的。而服務端收到客戶端的FIN報文後只能先回復客戶端一個確認報文來告訴客戶端我服務端已經收到你的FIN報文了,但我服務端還有一些資料沒發完,等這些資料發完了服務端才能給客戶端發FIN報文(所以不能一次性將確認報文和FIN報文發給客戶端,就是這裡多出來了一次)。
- 為什麼客戶端發出第四次揮手的確認報文後要等2MSL的時間才能釋放TCP連線?
- 這裡同樣是要考慮丟包的問題,如果第四次揮手的報文丟失,服務端沒收到確認ack報文就會重發第三次揮手的報文,這樣報文一去一回最長時間就是2MSL,所以需要等這麼長時間來確認服務端確實已經收到了。