TCP協議屬於可靠的傳輸層協議,提供可靠的字節流服務,採用三次握手確認建立一個連接。
所謂字節流服務(Byte Stream Service)是指,爲了方便傳輸,將大塊數據分割成以報文段(segment)爲單位的數據包進行管理。而可靠的傳輸服務是指,能夠把數據準確可靠的傳送給對方。
爲了準確無誤的將數據送達目標處,TCP協議採用了三次握手(three-way handshaking)和四次揮手(four-way handshake)策略。用TCP協議把數據包送出去後,TCP協議會向對方確認是否成功送達。
三次握手(建立連接)
握手過程中使用了TCP的標誌(flag)——SYN(synchronize,同步)和ACK(acknowledge,確認)。
發送端首先發送一個帶SYN標誌的數據包給對方。接收端收到後,回傳一個帶有SYN/ACK標誌的數據包以示傳達確認信息。最後,發送端再回傳一個帶ACK標誌的數據包,代表“握手”結束。
下面用一個簡圖先建立一下概念:
只是瞭解TCP三次握手的概念,是沒有卵用的,你需要去了解TCP三次握手中的一些細節,來看一個詳細的圖。
位碼即TCP標誌位,有6種標示
SYN
(synchronize建立聯機)
ACK
(acknowledgement確認)
PSH
(push傳送)
FIN
(finish結束)
URG
(urgent緊急)
Sequence number
(順序號碼)
Acknowledge number
(確認號碼)
各個狀態的意義如下:
LISTEN
-偵聽來自遠方TCP端口的連接請求;
SYN_SENT
-在發送連接請求後等待匹配的連接請求;
SYN_RECEVED
-在收到和發送一個連接請求後等待對連接請求的確認;
ESTABLISHED
-代表一個打開的連接,數據可以傳送給用戶;
第一次握手:建立連接。客戶端發送連接請求報文段,將SYN
位置爲1,Sequence Number
爲x;然後,客戶端進入SYN_SEND
狀態,等待服務器的確認;
第二次握手:服務器收到SYN
報文段。服務器收到客戶端的SYN
報文段,需要對這個SYN報文段進行確認,設置Acknowledgement Number
爲x+1
(Sequence Number+1);同時,自己還要發送SYN
請求信息,將SYN
位置爲1,Sequence Number
爲y
;服務器端將上述所有信息放到一個報文段(即SYN+ACK
報文段)中,一併發送給客戶端,此時服務器進入SYN_RECV
狀態;
第三次握手:客戶端收到服務器的SYN+ACK
報文段。然後將Acknowledgment Number
設置爲y+1
,向服務器發送ACK
報文段,這個報文段發送完畢以後,客戶端和服務器端都進入ESTABLISHED
狀態,完成TCP三次握手。
完成了三次握手,客戶端和服務器端就可以開始傳送數據。
確認號:其數值等於發送方的發送序號 +1(即接收方期望接收的下一個序列號)。
四次揮手(斷開連接)
由於TCP連接是全雙工的,一個TCP連接存在雙向的讀寫通道,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
簡單說來是 “先關讀,後關寫”,一共需要四個階段。以客戶機發起關閉連接爲例:
1.服務器讀通道關閉
2.客戶機寫通道關閉
3.客戶機讀通道關閉
4.服務器寫通道關閉
關閉行爲是在發起方數據發送完畢之後,給對方發出一個FIN(finish)數據段。直到接收到對方發送的FIN,且對方收到了接收確認ACK之後,雙方的數據通信完全結束,過程中每次接收都需要返回確認數據段ACK。
詳細過程:
第一階段 客戶機發送完數據之後,向服務器發送一個FIN
數據段,序列號爲i
;
1.服務器收到FIN(i)
後,返回確認段ACK
,序列號爲i+1
,關閉服務器讀通道;
2.客戶機收到ACK(i+1)
後,關閉客戶機寫通道;
(此時,客戶機仍能通過讀通道讀取服務器的數據,服務器仍能通過寫通道寫數據)
第二階段 服務器發送完數據之後,向客戶機發送一個FIN數據段,序列號爲j;
3.客戶機收到FIN(j)
後,返回確認段ACK
,序列號爲j+1
,關閉客戶機讀通道;
4.服務器收到ACK(j+1)
後,關閉服務器寫通道。
這是標準的TCP關閉兩個階段,服務器和客戶機都可以發起關閉,完全對稱。
爲什麼握手是三次
那TCP爲什麼非要進行三次連接呢?在謝希仁的《計算機網絡》中是這樣說的:
爲了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤。
在書中同時舉了一個例子,如下:
“已失效的連接請求報文段”的產生在這樣一種情況下:client發出的第一個連接請求報文段並沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以後的某個時間纔到達server。本來這是一個早已失效的報文段。但server收到此失效的連接請求報文段後,就誤認爲是client再次發出的一個新的連接請求。於是就向client發出確認報文段,同意建立連接。假設不採用“三次握手”,那麼只要server發出確認,新的連接就建立了。由於現在client並沒有發出建立連接的請求,因此不會理睬server的確認,也不會向server發送數據。但server卻以爲新的運輸連接已經建立,並一直等待client發來數據。這樣,server的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。例如剛纔那種情況,client不會向server的確認發出確認。server由於收不到確認,就知道client並沒有要求建立連接。
這就很明白了,防止了服務器端的一直等待而浪費資源。
揮手爲什麼是四次
那四次分手又是爲何呢?TCP協議是一種面向連接的、可靠的、基於字節流的運輸層通信協議。TCP是全雙工模式,這就意味着,當主機1發出FIN報文段時,只是表示主機1已經沒有數據要發送了,主機1告訴主機2,它的數據已經全部發送完畢了;但是,這個時候主機1還是可以接受來自主機2的數據;當主機2返回ACK報文段時,表示它已經知道主機1沒有數據發送了,但是主機2還是可以發送數據到主機1的;當主機2也發送了FIN報文段時,這個時候就表示主機2也沒有數據要發送了,就會告訴主機1,我也沒有數據要發送了,之後彼此就會愉快的中斷這次TCP連接。如果要正確的理解四次分手的原理,就需要了解四次分手過程中的狀態變化。
FIN_WAIT_1
: 這個狀態要好好解釋一下,其實FIN_WAIT_1
和FIN_WAIT_2
狀態的真正含義都是表示等待對方的FIN
報文。而這兩種狀態的區別是:FIN_WAIT_1
狀態實際上是當SOCKET在ESTABLISHED
狀態時,它想主動關閉連接,向對方發送了FIN
報文,此時該SOCKET即進入到FIN_WAIT_1
狀態。而當對方迴應ACK
報文後,則進入到FIN_WAIT_2
狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1
狀態一般是比較難見到的,而FIN_WAIT_2
狀態還有時常常可以用netstat看到。(主動方)FIN_WAIT_2
:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2
狀態下的SOCKET,表示半連接,也即有一方要求close連接,但另外還告訴對方,我暫時還有點數據需要傳送給你(ACK信息),稍後再關閉連接。(主動方)CLOSE_WAIT
:這種狀態的含義其實是表示在等待關閉。怎麼理解呢?當對方close一個SOCKET後發送FIN
報文給自己,你係統毫無疑問地會迴應一個ACK
報文給對方,此時則進入到CLOSE_WAIT
狀態。接下來呢,實際上你真正需要考慮的事情是察看你是否還有數據發送給對方,如果沒有的話,那麼你也就可以 close這個SOCKET,發送FIN
報文給對方,也即關閉連接。所以你在CLOSE_WAIT
狀態下,需要完成的事情是等待你去關閉連接。(被動方)LAST_ACK
: 這個狀態還是比較容易好理解的,它是被動關閉一方在發送FIN
報文後,最後等待對方的ACK
報文。當收到ACK
報文後,也即可以進入到CLOSED
可用狀態了。(被動方)TIME_WAIT
: 表示收到了對方的FIN
報文,併發送出了ACK
報文,就等2MSL後即可回到CLOSED
可用狀態了。如果FINWAIT1
狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT
狀態,而無須經過FIN_WAIT_2
狀態。(主動方)CLOSED
: 表示連接中斷。