tcp爲什要三次握手

準備知識: windows

單工:信息只能單向傳遞。發送-->接收,單向,不能返回響應。服務器

雙工:指的是信息可雙向發送。網絡

全雙工:信息可同時雙向傳遞。socket

半雙工:不能同時,單行道,一邊傳輸完了,另外一邊才能發起傳輸。tcp

 

因爲IP協議是不可靠的,爲了在不可靠信道上傳輸可靠數據,就要進行三次握手,準確的說是發送三次預備信息,這樣就完成了信息傳遞的雙工準備。(HTTP協議是須要雙向傳遞的)。spa

雙方都須要確認本身的發信和收信功能正常,收信功能經過接收對方信息獲得確認,發信功能須要發出信息—>對方回覆信息獲得確認。操作系統

第一次發信息: CLIENT --> SERVER blog

第二次發信息:  SERVER --> CLIENT     客戶端接收到了,確信本身能夠收,而且收到的信息裏有上一步發過去信息裏的內容,說明也是能夠發送的。io

第三次發信息: CLIENT --> SERVER     同理,服務端也確信了能夠收和發的能力。sed

 

若是沒有第三次發信息,只是保證了一個單向的鏈接暢通。客戶端能夠大膽地單向給服務端發信息。服務端因爲沒收到過「迴音」,不能肯定對方能不能收到,tcp協議是保證了傳輸可靠性的,不容許這種不肯定性存在,就不容許服務器給客戶端發送響應了。

固然UDP協議就不保證輸出的可靠性了,就算沒有收到過「迴音」也敢發出信息。

 

-------------------------------鏈接斷開的時候爲何要四次呢-----------------

因爲TCP鏈接是全雙工的,所以每一個方向都必須單獨進行關閉。

由於能夠同時雙向傳輸,A B 兩個終端上有兩個通道四個端口。分別是A端的讀端口,A端的寫端口和B端的讀端,B端的寫端口。 

TCP採用四次揮手關閉鏈接如圖2所示。

以客戶機發起關閉鏈接爲例:
1.Client發送FIN給Server,Server收到Fin信息後知道已經沒有要再接收的東西了,就關閉本身的讀端口,而後發送ACK給CLient   --Server 讀端關閉 
2.Client收到ACK後,知道對面已經關閉讀通道了,如今本身能夠把寫端口關閉了                                                                    --Client  寫端關閉
3.Server發送FIN給Client表示本身沒有要發送的了,Client收到FIN後,關閉本身的讀端口,而後發送ACK給Server                  --Client  讀端關閉
4.Server接受到ACK後,關閉本身的寫端口,而且Server的Conection狀態變爲Closed,完全關閉能夠重用了。                        --Server 寫端關閉 
5.Client在最後發給Server的ACK報文後等待兩個MSL時間,而後Client端的onection狀態變爲Closed。

* 能夠看出,讀端口的關閉,是收到對方發來的FIN信號便可。而寫端口的關閉是,發送一個FIN給對方,收到對方的ACK回覆才能夠關閉。這是確保數據包都發送過去的重發機制要求的。

第2步和第3步不少時候均可以合併,就是收到對方的FIN的時候,本身沒有要發過去的東西,就把ACK和FIN一塊兒返回去了。

* 主動發起關閉鏈接的一方發出最後的ACK報文後達到TIME_WAIT狀態,並且這個狀態要保持Maximum Segment Lifetime的兩倍時間。這是由於兩點:

保證TCP協議的全雙工鏈接可以可靠關閉
2 保證此次鏈接的重複數據段從網絡中消失

先說第一點,若是Client直接CLOSED了,那麼因爲IP協議的不可靠性或者是其它網絡緣由,致使Server沒有收到Client最後回覆的ACK。那麼Server就會在超時以後繼續發送FIN,此時因爲Client已經CLOSED了,就找不到與重發的FIN對應的鏈接,最後Server就會收到RST而不是ACK,Server就會覺得是鏈接錯誤把問題報告給高層。這種狀況雖然不會形成數據丟失,可是卻致使TCP協議不符合可靠鏈接的要求。因此,Client不是直接進入CLOSED,而是要保持TIME_WAIT,當再次收到FIN的時候再發一個ACK給對方。保證對方收到,最後正確的關閉鏈接。(MSL指一個片斷在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間,爲操做系統決定大小,windows默認MSL值爲2分鐘。若是直到2MSL,Client都沒有再次收到FIN,那麼Client推斷ACK已經被成功接收,則結束TCP鏈接。)

再說第二點,若是Client直接CLOSED,而後又再向Server發起一個新鏈接,咱們不能保證這個新鏈接與剛關閉的鏈接的端口號是不一樣的。也就是說有可能新鏈接和老鏈接的端口號是相同的。通常來講不會發生什麼問題,可是仍是有特殊狀況出現:假設新鏈接和已經關閉的老鏈接端口號是同樣的,若是前一次鏈接的某些數據仍然滯留在網絡中,這些延遲數據在創建新鏈接以後纔到達Server,因爲新鏈接和老鏈接的端口號是同樣的,又由於TCP協議判斷不一樣鏈接的依據是socket pair,因而,TCP協議就認爲那個延遲的數據是屬於新鏈接的,這樣就和真正的新鏈接的數據包發生混淆了。因此TCP鏈接還要在TIME_WAIT狀態等待2倍MSL,這樣能夠保證本次鏈接的全部數據都從網絡中消失。

------------------TCP的粗暴關閉鏈接方式--------------------

除了如上可靠的關閉方式以外,TCP還提供了另一種不可靠的關閉方式RST(Reset)
(CLOSED)  A    ---RST-->  B (CLOSED)
  A端發送RST狀態以後,TCP進入CLOSED狀態,B端接收到RST後,也便可進入CLOSED狀態。
在上面第一關閉方式上(可靠的),很是遺憾,A端在最後發送一個ACK請求後,並不能立刻將該Socket回收,由於A並不能肯定B必定可以接收到這個ACK報文,所以A端必須對這個Socket維持TIME_WAIT狀態2MSL。

若是A端是Client,這並不會成爲問題,但若是A端是Server,那就很危險了,若是鏈接的Socket很是多,而又維持如此多的TIME_WAIT狀態的話,那麼有可能會將Socket耗盡(報Too Many Open File)。

服務端爲了解決這個問題,可選擇的方式有三種:    Ø  保證由客戶端主動發起關閉(即作爲B端)    Ø  關閉的時候使用RST的方式    Ø  對處於TIME_WAIT狀態的TCP容許重用通常咱們固然最好是選擇第一種方式,實在沒有辦法的時候,咱們可使用SOCKET參數【SO_LINGER】開啓第二種方式,使用SOCKET參數【SO_REUSEADDR】開啓第三種方式

相關文章
相關標籤/搜索