1. 程式人生 > >TCP3次握手和4次揮手及其為什麽

TCP3次握手和4次揮手及其為什麽

one list 錯誤信息 準備工作 dos攻擊 str recv 協商 ring

TCP 3次握手

客戶端向服務器發送一個SYN(包含了SYN,SEQ)。

當服務器接收到客戶端發過來的SYN時,會向客戶端發送一個SYN+ACK的數據包,其實ACK的ack等於上一次發送SYN數據包的(SYN+SEQ)。

當客戶端接收到服務器發送過來的SYN+ACK數據包時,當接收到後向服務器發送ACK的數據包,此時ACK數據包中的ack值等於上一次SYN中的seq+syn。

當服務器收到了客戶端的發送過來的ACK數據包時,確認無誤後,向客戶端發送數據。

為什麽要3次握手

防止已過期的連接請求報文突然又傳送到服務器,因而產生錯誤。

Client發生一個請求連接報文可能因為網絡延遲等原因,沒有送達到server中。但是當這個client的請求報文送達到server時,如果沒有三次握手的話,server就會直接發數據可client,這樣會導致server資源的浪費。

解決“網絡中存在延遲的重復分組”的問題

在不可靠信道上可靠地傳輸信息

因為TCP是一個可靠的協議,但是IP是一個不可靠的協議,利用TCP使IP傳輸過程變得可靠。這樣的話,如果發生丟包,傳輸順序出錯等問題,TCP協議都可以解決。為了滿足不可靠信息在可靠的傳輸信息。

那就是可以這樣說,確認c/s是不是相應的服務都準備好了,只有通過了3次握手才能直接傳輸數據並且滿足了數據可靠性的傳輸。

http://www.cnblogs.com/xpress/

TCP 4次揮手

先由客戶端向服務器端發送一個FIN,請求關閉數據傳輸。

當服務器接收到客戶端的FIN時,向客戶端發送一個ACK,其中ack的值等於FIN+SEQ

然後服務器向客戶端發送一個FIN,告訴客戶端應用程序關閉。

當客戶端收到服務器端的FIN是,回復一個ACK給服務器端。其中ack的值等於FIN+SEQ

為什麽要4次揮手?

確保數據能夠完成傳輸。

但關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這裏的ACK報文和FIN報文多數情況下都是分開發送的。

___________________________________________________________________________________

TCP三次握手和四次揮手以及缺陷(詳細)

建立TCP需要三次握手才能建立,而斷開連接則需要四次握手。整個過程如下圖所示:

技術分享

1、TCP連接建立——三次握手

幾個概念:
【1】seq:序號,占4個字節,範圍[0,4284967296],由於TCP是面向字節流的,在一個1個TCP連接中傳送字節流中國的每一個字節都按照順序編號,此外序號是循環使用的
【2】ACK: 僅當ACK=1時確認字段才有效,當ACK=0時確認字段無效,並且TCP規定,在連接建立後所有的傳送報文段都必須要把ACK置為1
【3】SYN:同步序列號,用來發起一個連接。當SYN=1而ACK=0時表明這是一個請求報文段;若對方同意連接,則響應報文中SYN=1,ACK=1
【4】FIN :用來釋放一個連接,當FIN=1表示此報文段的發送方已經發送完畢。並要求釋放鏈接

技術分享

1.1、3次握手過程

服務端的TCP進程先創建傳輸控制塊TCB,準備接受客戶端進程的連接請求,然後服務端進程處於LISTEN狀態,等待客戶端的連接請求,如有,則作出響應。
1、客戶端的TCP進程也首先創建傳輸控制模塊TCB,然後向服務端發出連接請求報文段,該報文段首部中的SYN=1,ACK=0,同時選擇一個初始序號 seq=i。TCP規定,SYN=1的報文段不能攜帶數據,但要消耗掉一個序號。這時,TCP客戶進程進入SYN—SENT(同步已發送)狀態,這是 TCP連接的第一次握手。
2、服務端收到客戶端發來的請求報文後,如果同意建立連接,則向客戶端發送確認。確認報文中的SYN=1,ACK=1,確認號ack=i+1,同時為自己 選擇一個初始序號seq=j。同樣該報文段也是SYN=1的報文段,不能攜帶數據,但同樣要消耗掉一個序號。這時,TCP服務端進入SYN—RCVD(同 步收到)狀態,這是TCP連接的第二次握手。
3、TCP客戶端進程收到服務端進程的確認後,還要向服務端給出確認。確認報文段的ACK=1,確認號ack=j+1,而自己的序號為seq=i+1。 TCP的標準規定,ACK報文段可以攜帶數據,但如果不攜帶數據則不消耗序號,因此,如果不攜帶數據,則下一個報文段的序號仍為seq=i+1。這 時,TCP連接已經建立,客戶端進入ESTABLISHED(已建立連接)狀態。這是TCP連接的第三次握手,可以看出第三次握手客戶端已經可以發送攜帶 數據的報文段了。
當服務端收到確認後,也進入ESTABLISHED(已建立連接)狀態。

1.2、關於第三次握手的解釋

前倆比較容易理解,第三次握手看似多余其實不然,這主要是為了防止已失效的請求報文段突然又傳送到了服務端而產生連接的誤判
比如:客戶端發送了一個連接請求報文段A到服務端,但是在某些網絡節點上長時間滯留了,而後客戶端又超時重發了一個連接請求報文段B該服務端,而後 正常建立連接,數據傳輸完畢,並釋放了連接。但是請求報文段A延遲了一段時間後,又到了服務端,這本是一個早已失效的報文段,但是服務端收到後會誤以為客戶端又發出了一次連接請求,於是向客戶端發出確認報文段,並同意建立連接。那麽問題來了,假如這裏沒有三次握手,這時服務端只要發送了確認,新的連接就建立了,但由於客戶端沒有發出建立連接的請求,因此不會理會服務端的確認,也不會向服務端發送數據,而服務端卻認為新的連接已經建立了,並在 一直等待客戶端發送數據,這樣服務端就會一直等待下去,直到超出保活計數器的設定值,而將客戶端判定為出了問題,才會關閉這個連接。這樣就浪費了很多服務 器的資源。而如果采用三次握手,客戶端就不會向服務端發出確認,服務端由於收不到確認,就知道客戶端沒有要求建立連接,從而不建立該連接。

2、 缺陷引起的SYN Flood

2.1、SYN Flood 攻擊

SYN- Flood攻擊是當前網絡上最為常見的DDoS攻擊,也是最為經典的拒絕服務攻擊,它就是利用了TCP協議實現上的一個缺陷,通過向網絡服務所在端口發送大量 的偽造源地址的攻擊報文,就可能造成目標服務器中的半開連接隊列被占滿,從而阻止其他合法用戶進行訪問。這種攻擊早在1996年就被發現,但至今仍然顯示 出強大的生命力。很多操作系統,甚至防火墻、路由器都無法有效地防禦這種攻擊,而且由於它可以方便地偽造源地址,追查起來非常困難。它的數據包特征通常 是,源發送了大量的SYN包,並且缺少三次握手的最後一步握手ACK回復。
原理:攻擊者首先偽造地址對 服務器發起SYN請求,服務器回應(SYN+ACK)包,而真實的IP會認為,我沒有發送請求,不作回應。服務 器沒有收到回應,這樣的話,服務器不知 道(SYN+ACK)是否發送成功,默認情況下會重試5次(tcp_syn_retries)。這樣的話,對於服務器的內存,帶寬都有很大的消耗。攻擊者 如果處於公網,可以偽造IP的話,對於服務器就很難根據IP來判斷攻擊者,給防護帶來很大的困難。

2.2、SYN Flood 防護措施

2.2.1. 無效連接監視釋放
這種方法不停的監視系統中半開連接和不活動連接,當達到一定閾值時拆除這些連接,釋放系統資源。這種絕對公平的方法往往也會將正常的連接的請求也會被釋放掉,”傷敵一千,自損八百“
2.2.2. 延緩TCB分配方法
SYN Flood關鍵是利用了,SYN數據報文一到,系統立即分配TCB資源,從而占用了系統資源,因此有倆種技術來解決這一問題
Syn Cache技術
這種技術在收到SYN時不急著去分配TCB,而是先回應一個ACK報文,並在一個專用的HASH表中(Cache)中保存這種半開連接,直到收到正確的ACK報文再去分配TCB
Syn Cookie技術
Syn Cookie技術則完全不使用任何存儲資源,它使用一種特殊的算法生成Sequence Number,這種算法考慮到了對方的IP、端口、己方IP、端口的固定信息,以及對方無法知道而己方比較固定的一些信息,如MSS、時間等,在收到對方 的ACK報文後,重新計算一遍,看其是否與對方回應報文中的(Sequence Number-1)相同,從而決定是否分配TCB資源
2.2.3. 使用SYN Proxy防火墻
原理:對試圖穿越的SYN請求進行驗證之後才放行

3、經歷狀態

3.1 Client端所經歷的狀態

技術分享

3.2 Server端所經歷的狀態

技術分享

4、常見問題:

4.1、為什麽連接的時候是三次握手,關閉的時候卻是四次握手?

答:因為當Server端收到Client端的SYN連接請求報文後,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,”你發的FIN報文我收到了”。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。

4.2、為什麽存在TIME_WAIT狀態?

答:1、可靠的終止TCP連接

技術分享

2、保證讓遲來的TCP報文段有足夠的時間被識別並丟棄

技術分享
技術分享

4.3、TCP第三次握手失敗怎麽辦?

在此,將《TCP/IP協議族》中每一個狀態的轉換偽代碼整理下:

技術分享

第58行指明了當第三次握手失敗時的處理操作,可以看出當失敗時服務器並不會重傳ack報文,而是直接發送RTS報文段,進入CLOSED狀態。這樣做的目的是為了防止SYN洪泛攻擊。

4.4、為什麽不能用兩次握手進行連接

我們知道,3次握手完成兩個重要的功能,既要雙方做好發送數據的準備工作(雙方都知道彼此已準備好),也要允許雙方就初始序列號進行協商,這個序列號在握手過程中被發送和確認。 現在把三次握手改成僅需要兩次握手,死鎖是可能發生的。作為例子,考慮計算機S和C之間的通信,假定C給S發送一個連接請求分組,S收到了這個分組,並發 送了確認應答分組。按照兩次握手的協定,S認為連接已經成功地建立了,可以開始發送數據分組。可是,C在S的應答分組在傳輸中被丟失的情況下,將不知道S 是否已準備好,不知道S建立什麽樣的序列號,C甚至懷疑S是否收到自己的連接請求分組。在這種情況下,C認為連接還未建立成功,將忽略S發來的任何數據分 組,只等待連接確認應答分組。而S在發出的分組超時後,重復發送同樣的分組。這樣就形成了死鎖。

5、小知識

a.默認情況下(不改變socket選項),當你調用close( or closesocket,以下說close不再重復)時,如果發送緩沖中還有數據,TCP會繼續把數據發送完。
b. 發送了FIN只是表示這端不能繼續發送數據(應用層不能再調用send發送),但是還可以接收數據。
c. 應用層如何知道對端關閉?通常,在最簡單的阻塞模型中,當你調用recv時,如果返回0,則表示對端關閉。在這個時候通常的做法就是也調用close,那麽TCP層就發送FIN,繼續完成四次握手。如果你不調用close,那麽對端就會處於FIN_WAIT_2狀態,而本端則會處於CLOSE_WAIT狀態。這個可以寫代碼試試。
d. 在很多時候,TCP連接的斷開都會由TCP層自動進行,例如你CTRL+C終止你的程序,TCP連接依然會正常關閉,你可以寫代碼試試

6、TIME_WAIT狀態

a.從以上TCP連接關閉的狀態轉換圖可以看出,主動關閉的一方在發送完對對方FIN報文的確認(ACK)報文後,會進入TIME_WAIT狀態。TIME_WAIT狀態也稱為2MSL狀態。

b.什麽是2MSL?MSL即Maximum Segment Lifetime,也就是報文最大生存時間,引用《TCP/IP詳解》中的話:“它(MSL)是任何報文段被丟棄前在網絡內的最長時間。”那麽,2MSL也就是這個時間的2倍。其實我覺得沒必要把這個MSL的確切含義搞明白,你所需要明白的是,當TCP連接完成四個報文段的交換時,主動關閉的一方將繼續等待一定時間(2-4分鐘),即使兩端的應用程序結束。你可以寫代碼試試,然後用setstat查看下。

c.為什麽需要2MSL?根據《TCP/IP詳解》和《The TCP/IP Guide》中的說法,有兩個原因:
其一,保證發送的ACK會成功發送到對方,如何保證?我覺得可能是通過超時計時器發送。這個就很難用代碼演示了。
其二,報文可能會被混淆,意思是說,其他時候的連接可能會被當作本次的連接。直接引用《The TCP/IP Guide》的說法:The second is to provide a“buffering period” between the end of this connection and any subsequent ones. If not for this period, it is possible that packets from different connections could be mixed, creating confusion.

d.TIME_WAIT狀態所帶來的影響:

當某個連接的一端處於TIME_WAIT狀態時,該連接將不能再被使用。事實上,對於我們比較有現實意義的是,這個端口將不能再被使用。某個端口處於TIME_WAIT狀態(其實應該是這個連接)時,這意味著這個TCP連接並沒有斷開(完全斷開),那麽,如果你bind這個端口,就會失敗。對於服務器而言,如果服務器突然crash掉了,那麽它將無法再2MSL內重新啟動,因為bind會失敗。解決這個問題的一個方法就是設置socket的SO_REUSEADDR選項。這個選項意味著你可以重用一個地址。

e.特別註意

當建立一個TCP連接時,服務器端會繼續用原有端口監聽,同時用這個端口與客戶端通信。而客戶端默認情況下會使用一個隨機端口與服務器端的監聽端口通信。有時候,為了服務器端的安全性,我們需要對客戶端進行驗證,即限定某個IP某個特定端口的客戶端。客戶端可以使用bind來使用特定的端口。對於服務器端,當設置了SO_REUSEADDR選項時,它可以在2MSL內啟動並listen成功。
但是對於客戶端,當使用bind並設置SO_REUSEADDR時,如果在2MSL內啟動,雖然bind會成功,但是在windows平臺上connect會失敗。而在linux上則不存在這個問題。(我的實驗平臺:winxp, ubuntu7.10)

要解決windows平臺的這個問題,可以設置SO_LINGER選項。SO_LINGER選項決定調用close時TCP的行為。SO_LINGER涉及到linger結構體,如果設置結構體中l_onoff為非0,l_linger為0,那麽調用close時TCP連接會立刻斷開,TCP不會將發送緩沖中未發送的數據發送,而是立即發送一個RST報文給對方,這個時候TCP連接就不會進入TIME_WAIT狀態。如你所見,這樣做雖然解決了問題,但是並不安全。通過以上方式設置SO_LINGER狀態,等同於設置SO_DONTLINGER狀態。

斷開連接時的意外:
這個算不上斷開連接時的意外,當TCP連接發生一些物理上的意外情況時,例如網線斷開,linux上的TCP實現會依然認為該連接有效,而windows則會在一定時間後返回錯誤信息。這似乎可以通過設置SO_KEEPALIVE選項來解決,不過不知道這個選項是否對於所有平臺都有效。

7、各種狀態解析

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信息),稍後再關閉連接。(主動方)

TIME_WAIT: 表示收到了對方的FIN報文,並發送出了ACK報文,就等2MSL後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。(主動方)

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

CLOSE_WAIT: 這種狀態的含義其實是表示在等待關閉。怎麽理解呢?當對方close一個SOCKET後發送FIN報文給自己,你系統毫無疑問地會回應一個ACK報文給對方,此時則進入到CLOSE_WAIT狀態。接下來呢,實際上你真正需要考慮的事情是查看你是否還有數據發送給對方,如果沒有的話,那麽你也就可以close這個SOCKET,發送FIN報文給對方,也即關閉連接。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連接。(被動方)

LAST_ACK: 這個狀態還是比較容易好理解的,它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。(被動方)

CLOSED: 表示連接中斷。

TCP3次握手和4次揮手及其為什麽