1. 程式人生 > >關於TCP三次握手鍊接,四次握手關閉

關於TCP三次握手鍊接,四次握手關閉

轉自:

http://blog.csdn.net/lostyears/article/details/7104349

TCP/IP 狀態機,如下圖所示:

 

    在TCP/IP協議中,TCP協議提供可靠的連線服務,採用三次握手建立一個連線,如圖1所示。 (SYN包表示標誌位syn=1,ACK包表示標誌位ack=1,SYN+ACK包表示標誌位syn=1,ack=1)

 (1) 第一次握手:建立連線時,客戶端A傳送SYN包(SEQ_NUMBER=j)到伺服器B,並進入SYN_SEND狀態,等待伺服器B確認。

 (2) 第二次握手:伺服器B收到SYN包,必須確認客戶A的SYN(ACK_NUMBER=j+1),同時自己也傳送一個SYN包(SEQ_NUMBER=k),即SYN+ACK包,此時伺服器B進入SYN_RECV狀態。

 (3) 第三次握手:客戶端A收到伺服器B的SYN+ACK包,向伺服器B傳送確認包ACK(ACK_NUMBER=k+1),此包傳送完畢,客戶端A和伺服器B進入ESTABLISHED狀態,完成三次握手。

完成三次握手,客戶端與伺服器開始傳送資料。

                               圖1 TCP三次握手建立連線

由於TCP連線是全雙工的,因此每個方向都必須單獨進行關閉。這個原則是當一方完成它的資料傳送任務後就能傳送一個FIN來終止這個方向的連線。收到一個 FIN只意味著這一方向上沒有資料流動,一個TCP連線在收到一個FIN後仍能傳送資料。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。

(1)客戶端A傳送一個FIN,用來關閉客戶A到伺服器B的資料傳送(報文段4)。

(2)伺服器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將佔用一個序號。

(3)伺服器B關閉與客戶端A的連線,傳送一個FIN給客戶端A(報文段6)。

(4)客戶端A發回ACK報文確認,並將確認序號設定為收到序號加1(報文段7)。

TCP採用四次揮手關閉連線如圖2所示。

                               圖2  TCP四次揮手關閉連線

PS:另一個關閉連線的圖

1.為什麼建立連線協議是三次握手,而關閉連線卻是四次握手呢?

這是因為服務端的LISTEN狀態下的SOCKET當收到SYN報文的連線請求後,它可以把ACK和SYN(ACK起應答作用,而SYN起同步作用)放在一個報文裡來發送。但關閉連線時,當收到對方的FIN報文通知時,它僅僅表示對方沒有資料傳送給你了;但未必你所有的資料都全部發送給對方了,所以你可能未必會馬上會關閉SOCKET,也即你可能還需要傳送一些資料給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連線了,所以它這裡的ACK報文和FIN報文多數情況下都是分開發送的。

2.為什麼TIME_WAIT狀態還需要等2MSL後才能返回到CLOSED狀態?

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

3. 為什麼不能用兩次握手進行連線?

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

補充:

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連線依然會正常關閉,你可以寫程式碼試試。

插曲:

   特別的TIME_WAIT狀態:

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

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

   為什麼需要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.

   TIME_WAIT狀態所帶來的影響:(1到4分鐘)

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

   對於TIME_WAIT的插曲:

   當建立一個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選項來解決,不過不知道這個選項是否對於所有平臺都有效。

轉載來源:

http://blog.pfan.cn/xman/44384.html

http://blog.csdn.net/uestc_huan/archive/2009/03/07/3965923.aspx