1. 程式人生 > >計算機網路——TCP協議中的三次握手四次揮手以及11種狀態轉換

計算機網路——TCP協議中的三次握手四次揮手以及11種狀態轉換

TCP的傳輸連線分為3個階段:連線建立(三次握手)、資料傳送和連線釋放(四次揮手)。TCP傳輸連線的管理就是使傳輸連線的建立和釋放都能正常地進行。
一、TCP的三次握手
準備工作:伺服器必須準備好接受外來的連線。這通常通過呼叫socket,bindlisten這三個函式來完成。我們稱之為被動開啟。 第一次握手:客戶端通過呼叫connect發起主動開啟。客戶端向伺服器發出連線請求的TCP報文段,其TCP首部中的同步位元SYN置為1,並TCP首部中序號seq設定為x(TCP規定SYN報文段不能攜帶資料,但是要消耗一個序號),表明要轉送資料時初始序列號是x。通常SYN分節不攜帶資料,其所在IP資料報只含有一個IP首部,一個TCP首部。 第二次握手:伺服器收到資料報後,從TCP資料報首部的同步位元SYN位為1就知道這是一個建立連線的請求。伺服器如果同意,會發回確認。在確認報文段中把同步位元位SYN設定為1,確認位元位ACK設定為1,由於TCP請求報文段中的序號是x,所以伺服器在傳送確認報文段中的確認號ack是x+1,同時把確認報文段中的序號seq設定為y,表明伺服器傳送資料的初始序列號為y。該報文段也不能攜帶資料(因為SYN=1,所以不攜帶任何資料)。 第三次握手:客戶端收到伺服器端的報文段後,要對伺服器端中的SYN進行確認。在確認報文段中把確認位元位ACK設定為1,然後把確認號ack設定為y+1,自身的序號seq設定x+1。 注:客戶的初始序列號為x,伺服器的初始序列號為y,那麼確認報文段中的確認號ack就是所期待的對方要傳送的下一個序列號。 客戶端呼叫connect將激發TCP的三次握手,僅在連線建立成功或者出錯時才返回,上面介紹了建立成功的情況,下面列出了出錯返回可能有的幾種情況。 (1)客戶端傳送的建立連線的SYN報文丟失或者伺服器回覆的ACK報文丟失。客戶端因為沒有收到伺服器的回覆,會等待6s再發一次,若無響應則等待24s再發一次,總共等75s還未收到響應就返回本錯誤。 (2)伺服器回覆的TCP報文中復位標誌RST置位1。表示伺服器主機沒有在指定的埠上監聽或接受該連線或伺服器程式根本沒有執行。 (3)客戶端傳送的SYN報文不可達。還記IP資料報中有一個生存時間(TTL)的標誌嗎?它每進過一個路由器都會減1,當它減到0時還沒到達目的地,會發送一個ICMP錯誤。然後客戶端會和第一種情況一樣每隔一段時間重發一次。 可以把TCP連線時的三次握手換成兩次握手嗎?(假設客戶端主動,伺服器端被動)
假如客戶端發出的第一個連線請求報文段並沒有丟失,而是在某個網路結點長時間的滯留了,以致延誤到連線釋放以後的某個時間才到達伺服器。也就是說這是一個早已失效的報文段。但伺服器端收到此失效的連線請求報文段後,就誤認為是客戶端再次發出的一個新的連線請求。於是就向客戶端發出確認報文段,同意建立連線。假設不採用“三次握手”,那麼只要伺服器端發出確認,新的連線就建立了。由於現在客戶端並沒有發出建立連線的請求,因此不會理會伺服器端的確認。經過三次握手,客戶端和伺服器都有應有答可以確保TCP正確連線。 二、TCP的四次揮手
資料傳輸完畢後,通訊雙方都可以釋放連線。對於首先呼叫close的一端我們稱該端為主動關閉
,另外一端執行被動關閉。 第一次揮手:假設客戶端執行主動關閉,那麼它會向伺服器端發出釋放連線的報文段,這個TCP報文段中終止位元FIN置為1,序號seq設定為u(假設上一個發的資料序號是u-1)。並停止傳送資料。主動關閉TCP連線。等待伺服器的確認,這裡需要注意,因為TCP是全雙工的,所以TCP連線上有兩條資料通路,傳送FIN的一端就不能傳送資料,也就是關閉了其中一條資料通路,對方還是可以繼續傳送資料。 第二次揮手:伺服器端收到客戶端的釋放連線的報文段後會執行被動關閉,它要對客戶端的資料報進行確認,伺服器端會發送一個確認的資料報,確認位元ACK設定為1,確認號為u+1,自身的序號seq為v(假設上一個發的資料序號是v-1)。這個時候TCP處於半關閉狀態,伺服器依然可以向客戶端傳送資料(資料的序號為v+1 ~ w-1),客戶端任要接受。 第三次揮手:伺服器端已經沒有要傳送給客戶端的資料,那麼伺服器端也會呼叫close關閉套接字,這樣伺服器端也會發送一個FIN的TCP報文段,序號是w(假設上一個發的資料序號是w-1)。這個時候伺服器端不會再向客戶端傳送資料了。 第四次揮手:客戶端接受到這個最終的FIN的釋放連線報文段後必須對報文段進行確認。在確認的報文段中,ACK=1,確認序號ack=w+1,自己的序號seq=u+1(他的上一個序號的資料報就是申請釋放連線的資料報,序號是seq=u)。 為什麼TCP握手是三次,揮手卻是四次?(假設客戶端主動,伺服器端被動)
在TCP三次握手中,伺服器端的SYN和ACK是放在一個TCP報文段中向客戶端傳送的,而在斷開連線的過程中,伺服器端向客戶單端傳送的ACK和FIN是是分別在兩個不同的TCP報文段中。這是因為在伺服器端接收到客戶端的FIN後,伺服器端可能還有資料要傳輸,所以先發送ACK,伺服器端把資料發完之後就可以傳送FIN斷開連線了。 三、TCP的11種狀態轉換 CLOSED:起始點,不在連線狀態。可以主動開啟連線,或者等待對端的連線。 -->收到“被動開啟”報文,進入LISTEN狀態。 -->收到“主動開啟”報文,進入SYN_SENT狀態。 -->收到任何報文段,傳送RST報文段。 -->收到其它任何報文段,發出差錯報文。 LISTEN:被動開啟,TCP正在等待對端的連線請求。 -->收到“傳送資料”報文,傳送SYN報文段,進入SYN_SENT狀態。 -->收到任何SYN報文段,傳送SYN+ACK報文段,進入SYN_RECEIVED狀態。 -->收到任何其它報文段或者報文,傳送差錯報文。 SYN_SENT:主動開啟,傳送完一個連線請求後等待回覆。 -->超時,進入CLOSED狀態。 -->收到SYN報文段,傳送SYN+ACK報文段,進入SYN_RECEIVED狀態。 -->收到SYN+ACK報文段,傳送ACK報文段,進入ESTABLISHED狀態。
-->收到任何其它報文段或者報文,傳送差錯報文。
SYN_RECEIVED:被動開啟,接受連線請求以後進行確認同時也向對端傳送連線請求傳送,等待對方的回覆。 -->超時,傳送RST報文段,進入CLOSED狀態。
-->收到ACK報文段,進入ESTABLISHED狀態。
-->收到"關閉"報文,傳送FIN報文段,進入FIN_WAIT_1狀態。 -->收到RST報文段,進入LISTEN狀態。
-->收到任何其它報文段或者報文,傳送差錯報文。 ESTABLISHED:三次握手完畢,TCP連線建立完成,可以傳輸資料。 -->收到FIN報文段,進入CLOSED_WAIT狀態。 -->收到“關閉”報文,傳送FIN報文段,進入FIN_WAIT_1狀態。 -->收到RST或SYN報文段,發出差錯報文。 -->收到資料或ACK報文段,呼叫輸入模組。 -->收到“傳送”報文,呼叫輸出模組。 FIN_WAIT_1:四次揮手開始,主動關閉,傳送斷開連線請求,等待對端確認。 -->收到FIN報文段,傳送ACK報文段,進入CLOSING狀態(同時關閉)。 -->收到FIN+ACK報文段,傳送ACK報文段,進入FIN_WAIT狀態(?)。 -->收到ACK報文段,進入FIN_WAIT_2狀態。 -->收到任何其它報文段或者報文,傳送差錯報文。
FIN_WAIT_2:接收對方確認,但未接受對方的斷開連線請求。
-->收到FIN報文段,傳送ACK報文段,進入TIME_WAIT狀態。 CLOSING:主動關閉的一方本希望收到對方的ACK卻收到了對方的斷開連線請求。 -->收到ACK報文段,進入TIME_WAIT狀態。
-->收到任何其它報文段或者報文,傳送差錯報文。
TIME_WAIT:對方確認後發起斷開連線請求,需要等待2MSL保證正常關閉。 -->超時,進入CLOSED狀態。
-->收到任何其它報文段或者報文,傳送差錯報文。 CLOSE_WAIT:被動關閉,確認對端的連線終止請求,但是未向對端傳送連線終止請求(可能資料沒傳完)。 -->收到"關閉"報文,傳送FIN報文段,進入LAST_ACK狀態。
-->收到任何其它報文段或者報文,傳送差錯報文。 LAST_ACK:資料傳完,向對端發起斷開連線請求後等待確認。 -->收到ACK報文段,進入CLOSED狀態。
-->收到任何其它報文段或者報文,傳送差錯報文。 CLOSED:終點,不在連線狀態。可以主動開啟連線,或者等待對端的連線。 (1)TIME_WAIT狀態(假設客戶端主動,伺服器端被動) 從狀態圖中我們可以發現,執行主動關閉的那端(最終重傳ACK的那端)會經歷這個狀態。這個狀態停留是的時間是2MSL(MSL:最長報文段生存時間,1~4分鐘)。 TIME_WAIT狀態的作用: 1、可靠地實現TCP的連線終止。 在終止TCP連線時有4個報文需要交換,其中最後一個ACK報文是由客戶端發往伺服器。假設這個ACK報文在網路中被丟棄了,那麼伺服器端收不到這個確認ACK,伺服器端會向客戶端再次傳送FIN。這就是為什麼TIME_WAIT狀態持續2倍的最長報文段生存時間:1MSL時間留給最後的ACK確認報文段到達伺服器端,1MSL時間留給伺服器端再次傳送的FIN(這一段摘抄自《unix系統程式設計手冊》P1046,我不明白,超過2MSL連線就超時關閉了,再次傳送FIN後,即客戶端剛收到,2MSL時間也就到了,連線就關閉了,再次傳送FIN的意義何在?,希望有知道的同學告訴我)。 2、確保老的重複的報文段在網路中過期失效,這樣建立新的連線時將不再接受它們。 TCP協議採用的是出錯重傳,也就是會生成重複的報文,並且根據路由器的選擇,這些重複的報文可能在連線終止後才到達,如果客戶端/伺服器端收到這個老的報文會把它誤認為一個同一連線的新的報文,然後對這個報文進行處理,這樣就會出現錯誤。從狀態轉換圖我們可以看到從TIME_WAIT到連線終止,中間有2MSL,這個時間足以讓老的重複的報文段過期失效。 TIME_WAIT會出現在伺服器嗎? 會的,TIME_WATI存在首先執行主動關閉的那端,比如爬蟲伺服器他本身其實就是客戶端,完成爬取任務後,執行關閉,那麼所有的TCP連線都會處於TIME_WAIT狀態。
TIME_WAIT的危害:Linux分配給一個使用者的檔案控制代碼是有限的,如果系統中存在大量的TIME_WAIT狀態,一旦達到控制代碼數上限,新的請求就無法被處理了,而且大量TIME_WAIT連線佔用資源影響效能。

如何關閉TIME_WAIT狀態或者讓TIME_WAIT狀態過早終止? 對/etc/sysctl.conf檔案內容進行修改:
#表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉  
net.ipv4.tcp_tw_reuse = 1  
#表示開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉  
net.ipv4.tcp_tw_recycle = 1  
雖然我們可以這麼做,但是我們應該避免這樣做,因為這樣會阻礙TIME_WAIT狀態提供的可靠性保證。 (2)CLOSE_WAIT狀態(假設客戶端主動,伺服器端被動) 從上面的狀態圖可以看出,被動關閉的那端會經歷這個狀態。如果一直保持在CLOSE_WAIT狀態,那麼只有一種情況,就是在客戶端關閉連線之後伺服器程式自己沒有進一步發出FIN報文,一般原因都是TCP連線沒有呼叫關閉方法,或者對方連線關閉之後程式裡沒有檢測到,或者程式壓根就忘記了這個時候需要關閉連線,於是這個資源就一直被程式佔著。這種情況,通過伺服器核心引數也沒辦法解決,伺服器對於程式搶佔的資源沒有主動回收的權利,除非終止程式執行,一定程度上,可以使用TCP的keepalive功能,讓作業系統替我們自動清理掉CLOSE_WAIT連線。
#表示當keepalive起用的時候,TCP傳送keepalive訊息的頻度。預設是2小時,改為300秒  
net.ipv4.tcp_keepalive_time=1200 

參考: 《UNIX網路程式設計》 《UNIX系統程式設計手冊 下》 TCP/IP協議族