關於TCP的三次握手和四次分手 專題

 

 

客戶端TCP狀態遷移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服務器TCP狀態遷移:
CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSEDhtml


各個狀態的意義以下:
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 - 沒有任何鏈接狀態;服務器

 

 

 

【注意】
在TIME_WAIT狀態中,若是TCP client端最後一次發送的ACK丟失了,它將從新發送。TIME_WAIT狀態中所須要的時間是依賴於實現方法的。
典型的值爲30秒、1分鐘和2分鐘。等待以後鏈接正式關閉,而且全部的資源(包括端口號)都被釋放。

【問題1】爲何鏈接的時候是三次握手,關閉的時候倒是四次握手?
答:
由於當Server端收到Client端的SYN鏈接請求報文後,能夠直接發送SYN+ACK報文。

可是關閉鏈接時,當Server端收到FIN報文時,極可能並不會當即關閉SOCKET,
因此只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了",(報文1,ACK,應答)
只有等到我Server端全部的報文都發送完了,我才能發送FIN報文(報文2,FIN,服務器已經結束),
所以不能一塊兒發送。
故須要四步握手。

【問題2】爲何TIME_WAIT狀態須要通過2MSL(Maximum Segment Lifetime,最大報文段生存時間)才能返回到CLOSE狀態?cookie

答:雖然按道理,四個報文都發送完畢,咱們能夠直接進入CLOSE狀態了,可是咱們必須假象網絡是不可靠的,有能夠最後一個ACK丟失。
因此TIME_WAIT狀態就是用來重發可能丟失的ACK報文。
詳細解析:網絡

先說第一點(要知足可靠鏈接的須要),
若是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,這樣能夠保證本次鏈接的全部數據都從網絡中消失。socket

各類協議都是前人千錘百煉後獲得的標準,規範。
從細節中都能感覺到精巧和嚴謹。
每次深刻體會都有同一個感受:精妙。tcp

 

在TCP層,有個FLAGS字段,這個字段有如下幾個標識:SYN, FIN, ACK, PSH, RST, URG.
其中,對於咱們平常的分析有用的就是前面的五個字段。ide

它們的含義是:
SYN表示創建鏈接,
FIN表示關閉鏈接,
ACK表示響應,
PSH表示有DATA數據傳輸,
RST表示鏈接重置。高併發

其中,
ACK是可能與SYN,FIN等同時使用的,好比SYN和ACK可能同時爲1,它表示的就是創建鏈接以後的響應,
若是隻是單個的一個SYN,它表示的只是創建鏈接。idea

TCP的幾回握手就是經過這樣的ACK表現出來的。
但SYN與FIN是不會同時爲1的,由於前者表示的是創建鏈接,然後者表示的是斷開鏈接。

RST通常是在FIN以後纔會出現爲1的狀況,表示的是鏈接重置。
通常地,當出現FIN包或RST包時,咱們便認爲客戶端與服務器端斷開了鏈接;而當出現SYN和SYN+ACK包時,咱們認爲客戶端與服務器創建了一個鏈接。
PSH爲1的狀況,通常只出如今 DATA內容不爲0的包中,也就是說PSH爲1表示的是有真正的TCP數據包內容被傳遞。

TCP的鏈接創建和鏈接關閉,都是經過請求-響應的模式完成的。

概念補充-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/IP協議中,TCP協議提供可靠的鏈接服務,採用三次握手創建一個鏈接。
第一次握手:創建鏈接時,客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時本身也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。

完成三次握手,客戶端與服務器開始傳送數據.


示例:
第一次握手:主機A發送位碼爲syn=1,隨機產生seq number=1234567的數據包到服務器,主機B由SYN=1知道,A要求創建聯機;
第二次握手:主機B收到請求後要確認聯機信息,向A發送ack number=(主機A的seq+1),syn=1,ack=1,隨機產生seq=7654321的包;
第三次握手:主機A收到後檢查ack number是否正確,即第一次發送的seq number+1,以及位碼ack是否爲1,
若正確,主機A會再發送ack number=(主機B的seq+1),ack=1,主機B收到後確認seq值與ack=1則鏈接創建成功。

完成三次握手,主機A與主機B開始傳送數據。

 

 

 

這個協議很是重要,這裏把它的連接和釋放整理一下

首先是三次握手:

一、  客戶端發起,像服務器發送的報文SYN=1,ACK=0,而後選擇了一個初始序號:seq=x。

SYN是幹什麼用的?

在連接的時候建立一個同步序號,當SYN=1同時ACK=0的時候,代表這是一個鏈接請求的報文段。若是對方有意連接,返回的報文裏面SYN=1,ACK=1,。從這個意義上來講,SYN=1的時候,就代表這是一個‘請求’或者‘接受請求’的報文。

SYN=1的報文段不能攜帶數據。可是要消耗掉一個序號,

ACK是幹什麼用的?

僅當ACK=1的時候,確認字號(指望收到對方下一個報文段的第一個數據字節的編號)纔有效。所以,TCP規定,當連接創建以後,全部往來的報文裏面的ACK都應該是1(事實上,也只有客戶端發起的連接請求報文的ACK沒有置1)。

如今的狀態:客戶端進入SYN-SEND狀態;

二、  服務器接收到了SYN=1,ACK=0的請求報文以後,返回一個SYN=1,ACK=1的確認報文。

同時,確認號ack=x+1,同時也爲本身選擇一個初始序號seq=y

如今的狀態:服務器進入SYN-REVD狀態;

三、  客戶端接收到了服務器的返回信息以後,還要給服務器返回最後一條確認,ACK=1,確認號ack=y+1;

如今的狀態:客戶端進入ESTABLISHED狀態。

 

下面說一下爲何兩次握手不行,非得三次:

 

首先說明一種正常的狀況,就是客戶端發送了一條請求連接的報文,可是因爲網絡緣由丟失了,因此,不可能接收到服務器端的確認。這個時候,客戶端就就只有再一次發送原來的請求報文,此次服務器收到以後返回確認,客戶端再確認一次,連接確立。

 

而後考慮一種不正常的狀況,客戶端發了兩次請求連接的報文,第二條被服務器捕捉到,返回數據,完成了兩次握手。數據傳送完成以後,連接關閉。可是這時候,第一條擁塞的請求報文如今到達了服務器端,服務器還覺得客戶端要又一次創建鏈接,因而發送確認,而後把本身敞開,等着客戶端發送過來數據。因而,不少的網絡資源就是這樣浪費掉了。

 

要是實行三次握手,服務器收到了一條過時的請求報文,返回確認信息,客戶端接收到了服務器的信息以後感到莫名其妙,心想:我他媽又沒要連接,你返回這個是否是瘋了。因而不置一詞。服務器過一段時間尚未收到第三次握手的數據,知道客戶端並無要求創建連接的請求,含淚離開。

 

 

而後是四次揮手(four-way handshake):

如今雙方的狀態都是ESTABLISHED狀態。

一、  客戶端發起請求,請求斷開連接。FIN=1,seq=u。u是以前傳送過來的最後一個字節的序號+1。

FIN:用來釋放一個連接,當FIN=1的時候,代表此報文的發送方已經完成了數據的發送,沒有新的數據要傳送,並要求釋放連接。

客戶端進入FIN-WAIT-1狀態,等着服務器返回確認;

二、  服務器收到客戶端的請求斷開連接的報文以後,返回確認信息。ACK=1,seq=v,ack=u+1。

服務器進入CLOSE-WAIT狀態。

這個時候,客戶端不能給服務器發送信息報文,只能接收。可是服務器要是還有信息要傳給服務器,仍然能傳送。

三、  當服務器也沒有了能夠傳的信息以後,給客戶端發送請求結束的報文。FIN=1,ACK=1,

ack=u+1,seq=w。

這個時候的狀態:服務器進入LAST-ACK狀態。

四、  客戶端接收到FIN=1的報文以後,返回確認報文,ACK=1,seq=u+1,ack=w+1。

發送完畢以後,客戶端進入等待狀態,等待兩個時間週期。關閉。

 

爲何最後還要等待兩個時間週期呢?

一、  客戶端的最後一個ACK報文在傳輸的時候丟失,服務器並無接收到這個報文。
這個時候服務器就會超時重傳這個FIN消息,而後客戶端就會從新返回最後一個ACK報文,等待兩個時間週期,完成關閉。
若是不等待這兩個時間週期,服務器重傳的那條消息就不會收到。服務器就由於接收不到客戶端的信息而沒法正常關閉。

二、  預防上一次在三次握手中提到的失效的報文干擾。兩個時間週期過去以後,全部的報文都會在網絡中消失,保證下一次從新鏈接的時候有亂七八糟的報文影響。

 

下面看下你們通常比較關心的三種TCP狀態

SYN_RECV
服務端收到創建鏈接的SYN沒有收到ACK包的時候處在SYN_RECV狀態。有兩個相關係統配置:
一、net.ipv4.tcp_synack_retries :INTEGER
默認值是5
對於遠端的鏈接請求SYN,內核會發送SYN + ACK數據報,以確認收到上一個 SYN鏈接請求包。這是所謂的三次握手( threeway handshake)機制的第二個步驟。這裏決定內核在放棄鏈接以前所送出的 SYN+ACK 數目。不該該大於255,默認值是5,對應於180秒左右時間。一般咱們不對這個值進行修改,由於咱們但願TCP鏈接不要由於偶爾的丟包而沒法創建。

二、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,中止服務。

 

創建鏈接:

理解:窗口和滑動窗口
TCP的流量控制,TCP使用窗口機制進行流量控制
什麼是窗口?
鏈接創建時,各端分配一塊緩衝區用來存儲接收的數據,並將緩衝區的尺寸發送給另外一端,接收方發送的確認信息中包含了本身剩餘的緩衝區尺寸

剩餘緩衝區空間的數量叫作窗口

2. TCP的流控過程(滑動窗口)

參考文章:

http://www.cnblogs.com/lamian/p/3983497.html

http://kb.cnblogs.com/page/209100/

http://blog.csdn.net/whuslei/article/details/6667471/

http://www.cnblogs.com/Jessy/p/3535612.html

http://www.cnblogs.com/softidea/p/6062147.html(TIME_WAIT)

相關文章
相關標籤/搜索