TCP的三次握手(創建鏈接)和四次揮手(關閉鏈接)

TCP(Transmission Control Protocol) 傳輸控制協議 三次握手 TCP是主機對主機層的傳輸控制協議,提供可靠的鏈接服務,採用三次握手確認創建一個鏈接: 位碼即tcp標誌位,有6種標示: SYN(synchronous創建聯機) ACK(acknowledgement 確認) PSH(push傳送) FIN(finish結束) RST(reset重置) URG(urgent緊急) Sequence number(順序號碼) Acknowledge number(確認號碼) 客戶端TCP狀態遷移: CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED 服務器TCP狀態遷移: CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED 各個狀態的意義以下: LISTEN - 偵聽來自遠方TCP端口的鏈接請求; SYN-SENT -在發送鏈接請求後等待匹配的鏈接請求; SYN-RECEIVED - 在收到和發送一個鏈接請求後等待對鏈接請求的確認; ESTABLISHED- 表明一個打開的鏈接,數據能夠傳送給用戶; FIN-WAIT-1 - 等待遠程TCP的鏈接中斷請求,或先前的鏈接中斷請求的確認; FIN-WAIT-2 - 從遠程TCP等待鏈接中斷請求; CLOSE-WAIT - 等待從本地用戶發來的鏈接中斷請求; CLOSING -等待遠程TCP對鏈接中斷的確認; LAST-ACK - 等待原來發向遠程TCP的鏈接中斷請求的確認; TIME-WAIT -等待足夠的時間以確保遠程TCP接收到鏈接中斷請求的確認; CLOSED - 沒有任何鏈接狀態; TCP/IP協議中,TCP協議提供可靠的鏈接服務,採用三次握手創建一個鏈接,如圖1所示。 (1)第一次握手:創建鏈接時,客戶端A發送SYN包(SYN=j)到服務器B,並進入SYN_SEND狀態,等待服務器B確認。 (2)第二次握手:服務器B收到SYN包,必須確認客戶A的SYN(ACK=j+1),同時本身也發送一個SYN包(SYN=k),即SYN+ACK包,此時服務器B進入SYN_RECV狀態。 (3)第三次握手:客戶端A收到服務器B的SYN+ACK包,向服務器B發送確認包ACK(ACK=k+1),此包發送完畢,客戶端A和服務器B進入ESTABLISHED狀態,完成三次握手。 完成三次握手,客戶端與服務器開始傳送數據。 第一次握手: 客戶端發送一個TCP的SYN標誌位置1的包指明客戶打算鏈接的服務器的端口,以及初始序號X,保存在包頭的序列號(Sequence Number)字段裏。 第二次握手: 服務器發回確認包(ACK)應答。即SYN標誌位和ACK標誌位均爲1同時,將確認序號(Acknowledgement Number)設置爲客戶的I S N加1以.即X+1。 第三次握手. 客戶端再次發送確認包(ACK) SYN標誌位爲0,ACK標誌位爲1.而且把服務器發來ACK的序號字段+1,放在肯定字段中發送給對方.而且在數據段放寫ISN的+1 關閉鏈接: 因爲TCP鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。這個原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的鏈接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP鏈接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另外一方執行被動關閉。 CP的鏈接的拆除須要發送四個包,所以稱爲四次揮手(four-way handshake)。客戶端或服務器都可主動發起揮手動做,在socket編程中,任何一方執行close()操做便可產生揮手操做。 (1)客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送。 (2)服務器B收到這個FIN,它發回一個ACK,確認序號爲收到的序號加1。和SYN同樣,一個FIN將佔用一個序號。 (3)服務器B關閉與客戶端A的鏈接,發送一個FIN給客戶端A。 (4)客戶端A發回ACK報文確認,並將確認序號設置爲收到序號加1。 TCP採用四次揮手關閉鏈接如圖2所示。 圖2 TCP四次揮手關閉鏈接 參見wireshark抓包,實測的抓包結果並無嚴格按揮手時序。我估計是時間間隔過短形成。 深刻理解TCP鏈接的釋放: 因爲TCP鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的鏈接。收到一個 FIN只意味着這一方向上沒有數據流動,一個TCP鏈接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另外一方執行被動關閉。 TCP協議的鏈接是全雙工鏈接,一個TCP鏈接存在雙向的讀寫通道。 簡單說來是 「先關讀,後關寫」,一共須要四個階段。以客戶機發起關閉鏈接爲例: 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關閉兩個階段,服務器和客戶機均可以發起關閉,徹底對稱。 FIN標識是經過發送最後一塊數據時設置的,標準的例子中,服務器還在發送數據,因此要等到發送完的時候,設置FIN(此時可稱爲TCP鏈接處於半關閉狀態,由於數據仍可從被動關閉一方向主動關閉方傳送)。若是在服務器收到FIN(i)時,已經沒有數據須要發送,能夠在返回ACK(i+1)的時候就設置FIN(j)標識,這樣就至關於能夠合併第二步和第三步。讀《Linux網絡編程》關閉TCP鏈接章節,做如下筆記: TCP的TIME_WAIT和Close_Wait狀態 面試時看到應聘者簡歷中寫精通網絡,TCP編程,我常問一個問題,TCP創建鏈接須要幾回握手?95%以上的應聘者都能答對是3次。問TCP斷開鏈接須要幾回握手,70%的應聘者能答對是4次通信。再問CLOSE_WAIT,TIME_WAIT是什麼狀態,怎麼產生的,對服務有什麼影響,如何消除?有一部分同窗就回答不上來。不是我扣細節,而是在通信爲主的前端服務器上,必須有能力處理各類TCP狀態。好比統計在本廠的一臺前端機上高峯時間TCP鏈接的狀況,統計命令: netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 結果: 除了ESTABLISHED,能夠看到鏈接數比較多的幾個狀態是:FIN_WAIT1, TIME_WAIT, CLOSE_WAIT, SYN_RECV和LAST_ACK;下面的文章就這幾個狀態的產生條件、對系統的影響以及處理方式進行簡單描述。 TCP狀態 TCP狀態以下圖所示: 可能有點眼花繚亂?再看看這個時序圖 下面看下你們通常比較關心的三種TCP狀態 SYN_RECV 服務端收到創建鏈接的SYN沒有收到ACK包的時候處在SYN_RECV狀態。有兩個相關係統配置: 1,net.ipv4.tcp_synack_retries :INTEGER 默認值是5 對於遠端的鏈接請求SYN,內核會發送SYN + ACK數據報,以確認收到上一個 SYN鏈接請求包。這是所謂的三次握手( threeway handshake)機制的第二個步驟。這裏決定內核在放棄鏈接以前所送出的 SYN+ACK 數目。不該該大於255,默認值是5,對應於180秒左右時間。一般咱們不對這個值進行修改,由於咱們但願TCP鏈接不要由於偶爾的丟包而沒法創建。 2,net.ipv4.tcp_syncookies 通常服務器都會設置net.ipv4.tcp_syncookies=1來防止SYN Flood攻擊。假設一個用戶向服務器發送了SYN報文後忽然死機或掉線,那麼服務器在發出SYN+ACK應答報文後是沒法收到客戶端的ACK報文的(第三次握手沒法完成),這種狀況下服務器端通常會重試(再次發送SYN+ACK給客戶端)並等待一段時間後丟棄這個未完成的鏈接,這段時間的長度咱們稱爲SYN Timeout,通常來講這個時間是分鐘的數量級(大約爲30秒-2分鐘)。 這些處在SYNC_RECV的TCP鏈接稱爲半鏈接,並存儲在內核的半鏈接隊列中,在內核收到對端發送的ack包時會查找半鏈接隊列,並將符合的requst_sock信息存儲到完成三次握手的鏈接的隊列中,而後刪除此半鏈接。大量SYNC_RECV的TCP鏈接會致使半鏈接隊列溢出,這樣後續的鏈接創建請求會被內核直接丟棄,這就是SYN Flood攻擊。 可以有效防範SYN Flood攻擊的手段之一,就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk發明。SYN Cookie是對TCP服務器端的三次握手協議做一些修改,專門用來防範SYN Flood攻擊的一種手段。它的原理是,在TCP服務器收到TCP SYN包並返回TCP SYN+ACK包時,不分配一個專門的數據區,而是根據這個SYN包計算出一個cookie值。在收到TCP ACK包時,TCP服務器在根據那個cookie值檢查這個TCP ACK包的合法性。若是合法,再分配專門的數據區進行處理將來的TCP鏈接。 觀測服務上SYN_RECV鏈接個數爲:7314,對於一個高併發鏈接的通信服務器,這個數字比較正常。 CLOSE_WAIT 發起TCP鏈接關閉的一方稱爲client,被動關閉的一方稱爲server。被動關閉的server收到FIN後,但未發出ACK的TCP狀態是CLOSE_WAIT。出現這種情況通常都是因爲server端代碼的問題,若是你的服務器上出現大量CLOSE_WAIT,應該要考慮檢查代碼。 TIME_WAIT 根據TCP協議定義的3次握手斷開鏈接規定,發起socket主動關閉的一方 socket將進入TIME_WAIT狀態。TIME_WAIT狀態將持續2個MSL(Max Segment Lifetime),在Windows下默認爲4分鐘,即240秒。TIME_WAIT狀態下的socket不能被回收使用. 具體現象是對於一個處理大量短鏈接的服務器,若是是由服務器主動關閉客戶端的鏈接,將致使服務器端存在大量的處於TIME_WAIT狀態的socket, 甚至比處於Established狀態下的socket多的多,嚴重影響服務器的處理能力,甚至耗盡可用的socket,中止服務。 爲何須要TIME_WAIT?TIME_WAIT是TCP協議用以保證被從新分配的socket不會受到以前殘留的延遲重發報文影響的機制,是必要的邏輯保證。 和TIME_WAIT狀態有關的系統參數有通常由3個,本廠設置以下: net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_fin_timeout,默認60s,減少fin_timeout,減小TIME_WAIT鏈接數量。 net.ipv4.tcp_tw_reuse = 1表示開啓重用。容許將TIME-WAIT sockets從新用於新的TCP鏈接,默認爲0,表示關閉; net.ipv4.tcp_tw_recycle = 1表示開啓TCP鏈接中TIME-WAIT sockets的快速回收,默認爲0,表示關閉。 爲了方便描述,我給這個TCP鏈接的一端起名爲Client,給另一端起名爲Server。上圖描述的是Client主動關閉的過程,FTP協議中就這樣的。若是要描述Server主動關閉的過程,只要交換描述過程當中的Server和Client就能夠了,HTTP協議就是這樣的。 描述過程: Client調用close()函數,給Server發送FIN,請求關閉鏈接;Server收到FIN以後給Client返回確認ACK,同時關閉讀通道(不清楚就去看一下shutdown和close的差異),也就是說如今不能再從這個鏈接上讀取東西,如今read返回0。此時Server的TCP狀態轉化爲CLOSE_WAIT狀態。 Client收到對本身的FIN確認後,關閉 寫通道,再也不向鏈接中寫入任何數據。 接下來Server調用close()來關閉鏈接,給Client發送FIN,Client收到後給Server回覆ACK確認,同時Client關閉讀通道,進入TIME_WAIT狀態。 Server接收到Client對本身的FIN的確認ACK,關閉寫通道,TCP鏈接轉化爲CLOSED,也就是關閉鏈接。 Client在TIME_WAIT狀態下要等待最大數據段生存期的兩倍,而後才進入CLOSED狀態,TCP協議關閉鏈接過程完全結束。 以上就是TCP協議關閉鏈接的過程,如今說一下TIME_WAIT狀態。 從上面能夠看到,主動發起關閉鏈接的操做的一方將達到TIME_WAIT狀態,並且這個狀態要保持Maximum Segment Lifetime的兩倍時間。爲何要這樣作而不是直接進入CLOSED狀態? 緣由有二: 1、保證TCP協議的全雙工鏈接可以可靠關閉 2、保證此次鏈接的重複數據段從網絡中消失 先說第一點,若是Client直接CLOSED了,那麼因爲IP協議的不可靠性或者是其它網絡緣由,致使Server沒有收到Client最後回覆的ACK。那麼Server就會在超時以後繼續發送FIN,此時因爲Client已經CLOSED了,就找不到與重發的FIN對應的鏈接,最後Server就會收到RST而不是ACK,Server就會覺得是鏈接錯誤把問題報告給高層。這樣的狀況雖然不會形成數據丟失,可是卻致使TCP協議不符合可靠鏈接的要求。因此,Client不是直接進入CLOSED,而是要保持TIME_WAIT,當再次收到FIN的時候,可以保證對方收到ACK,最後正確的關閉鏈接。 再說第二點,若是Client直接CLOSED,而後又再向Server發起一個新鏈接,咱們不能保證這個新鏈接與剛關閉的鏈接的端口號是不一樣的。也就是說有可能新鏈接和老鏈接的端口號是相同的。通常來講不會發生什麼問題,可是仍是有特殊狀況出現:假設新鏈接和已經關閉的老鏈接端口號是同樣的,若是前一次鏈接的某些數據仍然滯留在網絡中,這些延遲數據在創建新鏈接以後纔到達Server,因爲新鏈接和老鏈接的端口號是同樣的,又由於TCP協議判斷不一樣鏈接的依據是socket pair,因而,TCP協議就認爲那個延遲的數據是屬於新鏈接的,這樣就和真正的新鏈接的數據包發生混淆了。因此TCP鏈接還要在TIME_WAIT狀態等待2倍MSL,這樣能夠保證本次鏈接的全部數據都從網絡中消失。 各類協議都是前人千錘百煉後獲得的標準,規範。從細節中都能感覺到精巧和嚴謹。每次深刻都有同一個感受,精妙。
相關文章
相關標籤/搜索