【轉】TCP3次握手連接協議和4次握手斷開連接協議

轉自:http://wenku.baidu.com/view/46477c8602d276a200292e7e.html



TCP協議三次握手連接協議

        在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包,必須確認客戶ASYN(ACK_NUMBER=j+1),同時自己也發送一個SYN(SEQ_NUMBER=k),即SYN+ACK包,此時服務器B進入SYN_RECV狀態。

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

完成三次握手,客戶端與服務器開始傳送數據。如下圖所示:

 

 

 

 

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採用四次握手關閉連接如下圖所示。

 

 

PS:另一個關閉連接的圖

 

  

 

特別的TIME_WAIT狀態:

 

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

 

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

 

  爲什麼需要2MSL?根據《TCP/IP詳解》和《TheTCP/IP Guide》中的說法,有兩個原因:

  其一,保證發送的ACK會成功發送到對方,如何保證?我覺得可能是通過超時計時器發送。這個就很難用代碼演示了。

  其二,報文可能會被混淆,意思是說,其他時候的連接可能會被當作本次的連接。直接引用《TheTCP/IP Guide》的說法:Thesecond is to provide a “buffering period” between the end of this connectionand any subsequent ones. If not for this period, it is possible that packetsfrom different connections could be mixed, creating confusion.

 

  TIME_WAIT狀態所帶來的影響:(14分鐘)

  當某個連接的一端處於TIME_WAIT狀態時,該連接將不能再被使用。事實上,對於我們比較有現實意義的是,這個端口將不能再被使用。某個端口處於TIME_WAIT狀態(其實應該是這個連接)時,這意味着這個TCP連接並沒有斷開(完全斷開),那麼,如果你bind這個端口,就會失敗。對於服務器而言,如果服務器突然crash掉了,那麼它將無法在2MSL內重新啓動,因爲bind會失敗。解決這個問題的一個方法就是設置socketSO_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選項決定調用closeTCP的行爲。SO_LINGER涉及到linger結構體,如果設置結構體中l_onoff爲非0,l_linger爲0,那麼調用closeTCP連接會立刻斷開,TCP不會將發送緩衝中未發送的數據發送,而是立即發送一個RST報文給對方,這個時候TCP連接(關閉時)就不會進入TIME_WAIT狀態。如你所見,這樣做雖然解決了問題,但是並不安全。通過以上方式設置SO_LINGER狀態,等同於設置SO_DONTLINGER狀態。

 

   斷開連接時的意外:

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

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章