1. 程式人生 > >TCP的11種狀態(轉載)

TCP的11種狀態(轉載)

分鐘 方向 time_wait 開始 sin inf 應用程序 返回 疑問

TCP的11種狀態

TCP三次握手建立連接

Tcp頭部

技術分享圖片

六個標誌位中,我們要用到三個:

SYN:SYN= 1 表示這是一個連接請求或連接接受報文。在建立連接時用來進行同步序號(個人理解是,在建立連接的時候,提醒對方記錄本方的起始序號)。當SYN=1而ACK=0時,表明這是一個連接請求報文段。對方若是同意建立連接,則應響應的報文段中使SYN=1、ACK=1。因此SYN=1表示該報文是一個連接請求報文或者是一個連接請求接收報文。

ACK:確認號只有在該位設置為1的時候才生效,當該位為0是表示確認號無效。TCP規定,在TCP連接建立後所有傳送的數據報文段ACK都必須設置為1。

FIN:當 FIN = 1 時,表明此報文段的發送方的數據已經發送完畢,並要求釋放連接。

此外我們還需要用到序號和確認號:

序號:占4個字節,它的範圍在0-2^32-1,序號隨著通信的進行不斷的遞增,當達到最大值的時候重新回到0在開始遞增。TCP是面向字節流的,在一個TCP連接中傳送的字節流中的每一個字節都按照順序編號。整個要傳送的字節流的起始號必須在連接建立時設置。首部中的序列號字段指的是本報文段所發送的數據的第一個字節的序號。例如,一個報文序號是301,而攜帶的數據共有100字節。則表示本次報文中的序號是301,下一個報文的序號是401.重復一下,每一個報文的序號是該報文包含的字節中第一個字節的編號。

確認號:占4個字節,確認號,是對下一個想要接受的字節的期望,這裏隱式確認了對上一個數據包的成功接收。如上例,在成功接收了序號為301的數據包,想要接收下一個數據包因為上個數據包包含100字節,所以此時的確認號應該是401,表示希望接收下一個序號是401的數據包。

三次握手過程:

技術分享圖片

過程描述:

首先由Client發出請求連接即 SYN=1 ACK=0 (請看頭字段的介紹),TCP規定SYN=1時不能攜帶數據,但要消耗一個序號,因此聲明自己的序號是 seq=x。

然後 Server 進行回復確認,即 SYN=1 ACK=1 seq=y,ack=x+1。

再然後 Client 再進行一次確認,但不用SYN 了,這時即為 ACK=1, seq=x+1,ack=y+1。

為什麽要進行三次握手(兩次確認):

為什麽A還要發送一側確認呢?這主要是為了防止已失效的連接請求報文突然又傳送到了B,因而產生錯誤。

所謂“已失效的連接請求報文段”是這樣產生的。考慮一種正常情況。A發出連接請求,但因連接請求丟失而未收到確認。於是A再次重傳一次連接請求。後來收到了確認建立了連接。數據傳輸完畢後,就釋放了連接。A供發送了兩個連接請求的報文段,其中第一個丟失,第二個到達了B。沒有“已失效的連接請求報文段”。

現假定出現一種異常情況,即A發出的第一個連接請求報文段並沒有丟失,而是在某些網絡節點長時間滯留了,以致延誤到連接釋放以後的某個時間才到B。本來這是一個已失效的報文段。但是B收到此失效的連接請求報文段後,就誤認為是A有發出一次新的連接請求。於是就向A發出確認報文段,同意建立連接。假定不采用三次握手,那麽只要B發出確認,新的連接就建立了。

由於現在A並沒有發出建立連接的請求,因此不會理睬B的確認,也不會向B發送數據。但B卻以為新的運輸連接已經建立了,並一直等待A發來數據。B的許多資源就這樣拜拜浪費了。

采用三次握手的辦法可以防止上述現象的發生。例如在剛才的情況下,A不會向B的確認發出確認。B由於收不到確認,就知道A並沒有要求建立連接。

另一種解釋:

這個問題的本質是, 信道不可靠, 但是通信雙發需要就某個問題達成一致. 而要解決這個問題, 無論你在消息中包含什麽信息, 三次通信是理論上的最小值. 所以三次握手不是TCP本身的要求, 而是為了滿足"在不可靠信道上可靠地傳輸信息"這一需求所導致的. 請註意這裏的本質需求,信道不可靠, 數據傳輸要可靠. 三次達到了, 那後面你想接著握手也好, 發數據也好, 跟進行可靠信息傳輸的需求就沒關系了. 因此,如果信道是可靠的, 即無論什麽時候發出消息, 對方一定能收到, 或者你不關心是否要保證對方收到你的消息, 那就能像UDP那樣直接發送消息就可以了”。這可視為對“三次握手”目的的另一種解答思路。

四次揮手關閉連接

技術分享圖片

當客戶A 沒有東西要發送時就要釋放 A 這邊的連接,A會發送一個報文(沒有數據),其中 FIN 設置為1, 服務器B收到後會給應用程序一個信,這時A那邊的連接已經關閉,即A不再發送信息(但仍可接收信息)。 A收到B的確認後進入等待狀態,等待B請求釋放連接, B數據發送完成後就向A請求連接釋放,也是用FIN=1 表示, 並且用 ack = u+1(如圖), A收到後回復一個確認信息,並進入 TIME_WAIT 狀態, 等待 2MSL 時間。

l 為什麽要等待呢?

l 為了防止這種情況:A接到B的釋放連接請求後會發送一個確認信息,但是如果這個確認信息丟了,也就是B沒有收到確認釋放連接,那麽B就會重發一個釋放連接請求,這時候A還處於TIME_WAIT狀態,所以會再次發送一個確認信息。

l Q2為什麽TIME_WAIT 狀態還需要等2*MSL秒之後才能返回到CLOSED 狀態呢?

l A2因為雖然雙方都同意關閉連接了,而且握手的4個報文也都發送完畢,按理可以直接回到CLOSED 狀態(就好比從SYN_SENT 狀態到ESTABLISH 狀態那樣),但是我們必須假想網絡是不可靠的,你無法保證你最後發送的ACK報文一定會被對方收到,就是說對方處於LAST_ACK 狀態下的SOCKET可能會因為超時未收到ACK報文,而重發FIN報文,所以這個TIME_WAIT 狀態的作用就是用來重發可能丟失的ACK報文。

11種狀態

技術分享圖片

簡單解釋:

l CLOSED:初始狀態,表示TCP連接是“關閉著的”或“未打開的”。

l LISTEN :表示服務器端的某個SOCKET處於監聽狀態,可以接受客戶端的連接。

l SYN_RCVD :表示服務器接收到了來自客戶端請求連接的SYN報文。在正常情況下,這個狀態是服務器端的SOCKET在建立TCP連接時的三次握手會話過程中的一個中間狀態,很短暫,基本上用netstat很難看到這種狀態,除非故意寫一個監測程序,將三次TCP握手過程中最後一個ACK報文不予發送。當TCP連接處於此狀態時,再收到客戶端的ACK報文,它就會進入到ESTABLISHED 狀態。

l SYN_SENT :這個狀態與SYN_RCVD 狀態相呼應,當客戶端SOCKET執行connect()進行連接時,它首先發送SYN報文,然後隨即進入到SYN_SENT 狀態,並等待服務端的發送三次握手中的第2個報文。SYN_SENT 狀態表示客戶端已發送SYN報文。

l ESTABLISHED :表示TCP連接已經成功建立。

l 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看到。

l FIN_WAIT_2 :上面已經解釋了這種狀態的由來,實際上FIN_WAIT_2狀態下的SOCKET表示半連接,即有一方調用close()主動要求關閉連接。註意:FIN_WAIT_2 是沒有超時的(不像TIME_WAIT 狀態),這種狀態下如果對方不關閉(不配合完成4次揮手過程),那這個 FIN_WAIT_2 狀態將一直保持到系統重啟,越來越多的FIN_WAIT_2 狀態會導致內核crash。

l TIME_WAIT :表示收到了對方的FIN報文,並發送出了ACK報文。 TIME_WAIT狀態下的TCP連接會等待2*MSL(Max Segment Lifetime,最大分段生存期,指一個TCP報文在Internet上的最長生存時間。每個具體的TCP協議實現都必須選擇一個確定的MSL值,RFC 1122建議是2分鐘,但BSD傳統實現采用了30秒,Linux可以cat /proc/sys/net/ipv4/tcp_fin_timeout看到本機的這個值),然後即可回到CLOSED 可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。(這種情況應該就是四次揮手變成三次揮手的那種情況)

l CLOSING :這種狀態在實際情況中應該很少見,屬於一種比較罕見的例外狀態。正常情況下,當一方發送FIN報文後,按理來說是應該先收到(或同時收到)對方的ACK報文,再收到對方的FIN報文。但是CLOSING 狀態表示一方發送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麽情況下會出現此種情況呢?那就是當雙方幾乎在同時close()一個SOCKET的話,就出現了雙方同時發送FIN報文的情況,這是就會出現CLOSING 狀態,表示雙方都正在關閉SOCKET連接。

l CLOSE_WAIT :表示正在等待關閉。怎麽理解呢?當對方close()一個SOCKET後發送FIN報文給自己,你的系統毫無疑問地將會回應一個ACK報文給對方,此時TCP連接則進入到CLOSE_WAIT狀態。接下來呢,你需要檢查自己是否還有數據要發送給對方,如果沒有的話,那你也就可以close()這個SOCKET並發送FIN報文給對方,即關閉自己到對方這個方向的連接。有數據的話則看程序的策略,繼續發送或丟棄。簡單地說,當你處於CLOSE_WAIT 狀態下,需要完成的事情是等待你去關閉連接。

l LAST_ACK :當被動關閉的一方在發送FIN報文後,等待對方的ACK報文的時候,就處於LAST_ACK 狀態。當收到對方的ACK報文後,也就可以進入到CLOSED 可用狀態了。

CLOSING狀態:

技術分享圖片

TCP的11種狀態(轉載)