寫到最後發現我描述的挺水的,這個老哥的用語比較專業一點http://www.javashuo.com/article/p-zqmaiqar-kb.html (老哥這篇有些許錯別字服務器
你們能夠看看它的. 看個人這篇可能會比較好理解一點,畢竟都是大白話網絡
三次握手! 併發
三次握手其實不許確。在官方文檔中對於TCP鏈接,是用握手 handshake 來描述整個TCP鏈接 創建的過程。spa
只握一次手就能夠創建TCP鏈接。那麼爲何說是三次握手創建鏈接呢? 其實它的三次是發送 三個報文,咱們能夠說是三報文握手.net
①一開始客戶機和服務器都處於關閉狀態,客戶機主動打開,向服務器發送一個報文。這個報文攜帶着 SYN = 1 (同步)和seq = x (一個序列號,有各自的序列號,會不斷的增長)blog
此時客戶端處於 :SYN-SENT狀態資源
PS:大概看一下文檔
同步SYN:鏈接創建時用於同步序號。當SYN=1,ACK=0時表示:這是一個鏈接請求報文段。若贊成鏈接,則在響應報文段中使得SYN=1,ACK=1。所以,SYN=1表示這是一個鏈接請求,或鏈接接受報文。SYN這個標誌位只有在TCP建產鏈接時纔會被置1,握手完成後SYN標誌位被置0。get
序列號seq:佔4個字節,用來標記數據段的順序,TCP把鏈接中發送的全部數據字節都編上一個序號,第一個字節的編號由本地隨機產生;給字節編上序號後,就給每個報文段指派一個序號;序列號seq就是這個報文段中的第一個字節的數據編號。同步
② 而後咱們的服務器就 被動打開,處於監聽狀態,等待客戶機發送第一個握手的報文。
收到客戶機發送的報文後,就會馬上向客戶機發送一個確認報文 --------------- ACK =1 ,SYN = 1,seq = y, (小寫)ack = x + 1
(大寫)ACK是確認位,若是爲1,代碼確認報文有效 .若是爲0,則能夠直接忽略,是沒有意義的
此時服務端處於SYN-RECV狀態
③此次的報文也有ACK = 1,確認位有效,ack = y + 1 ,是確認報文
就是' 我 確認 我 收到了你的 確認報文 '
就是咱們客戶端,確認,服務端發送的確認報文 咱們客戶端收到了
就是 服務器端,用來確認客戶端發送的用來創建鏈接的報文,我服務端收到了,那我服務端發給你客戶端的這個確認報文,你客戶端收到了沒有呢?
over 此時客戶端服務器處於 ESTABLISHED(TCP鏈接成功狀態)
細節: ①seq = x 到 ③seq = x + 1 說明客戶機在發送③報文以前,期間是沒有再發送其餘報文
而後服務器收到確認報文後,就創建了TCP鏈接
有個疑問?明明執行①②的時候,TCP鏈接就能夠創建了,那爲何客戶端還有再次發送一個確認報文過去呢?
是由於①這樣一個請求報文,可能會在發送途中就失效了。這個失效不是說它 沒有做用了。
而是說 因爲網絡中某種緣由,致使這個請求報文在網絡中發生了 阻塞滯留, 可是TCP鏈接有一個超時重傳機制
那麼①請求報文沒有到達的話,服務端就沒有收到。收不到這一條 ,服務端就會認爲剛剛的①請求報文沒有發出去,那客戶端就會再次發送一遍請求報文。
而剛剛那條請求報文就滯留在網絡中。 客戶端再次發送一條一樣的創建TCP鏈接的請求,此次服務端就收到了,而後 進行②確認 ③確認,這樣就創建TCP鏈接了
就在咱們美滋滋創建TCP鏈接成功,用完了鏈接後,就銷燬掉了
然而剛剛滯留在網絡中的那條 鏈接,可能咱們的網絡忽然又通暢了,那剛剛那條鏈接又生效了,又到達了服務器
服務器收到了,就會再次給客戶端發送確認報文 。 若是沒有③這一次確認報文,那麼TCP鏈接就創建了。
可是這條鏈接 咱們不須要此次創建。由於咱們以前已經從新 傳了一次請求報文,創建了鏈接,並釋放了。
因此沒有③就會從新創建一次鏈接,咱們不須要此次鏈接。
有了③以後,服務器發送過來的確認報文,我客戶端這邊 收到後會說:我沒有給你發送啊。 那麼就不會處理剛剛那條 '鏈接'
四次揮手
關閉以前都是創建狀態
①客戶端發送 ‘鏈接釋放報文’,頭部攜帶的 FIN =1,seq = u 序列值爲u 終止FIN:用來釋放一個鏈接。FIN=1表示:此報文段的發送方的數據已經發送完畢,並要求釋放運輸鏈接
②服務端收到報文後,知道客戶端須要斷開鏈接。 那我給你發送一個確認報文,ACK = 1,表明確認有效 seq 爲 v , ack = u +1
客戶端收到報文, 客戶端 ---》服務端 這個方向的TCP鏈接已經斷開,已經失效了
而 客戶端 《--- 服務端 這個方向仍是能夠鏈接的
③ 可是咱們服務端可能還要數據沒有傳遞過去,就須要從新發送報文, FIN = 1,ACK = 1,seq = w, ack = u+1 細節:①的seq = v ③的seq = w 正常都是+1,v到w說明中間不連續
此次是 服務器要斷開 對客戶端的鏈接
④ 服務器端可能沒有收到咱們的④確認報文, 服務器就會認識是本身發送的③咱們沒有收到,那麼服務端就會再次發送③,我要關閉了。這就是爲啥要設置2MSL 最大報文生成時段
要否則客戶端都已經關閉了,那你服務端還發送個錘子?
客戶端收到服務器的鏈接釋放報文後,必須發出確認,ACK=1,ack=w+1,而本身的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP鏈接尚未釋放,必須通過2MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。
【問題1】爲何鏈接的時候是三次握手,關閉的時候倒是四次握手?
答:由於當Server端收到Client端的SYN鏈接請求報文後,能夠直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。可是關閉鏈接時,當Server端收到FIN報文時,極可能並不會當即關閉SOCKET,因此只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端全部的報文都發送完了,我才能發送FIN報文,所以不能一塊兒發送。故須要四步握手。
【問題2】爲何TIME_WAIT狀態須要通過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
答:雖然按道理,四個報文都發送完畢,咱們能夠直接進入CLOSE狀態了,可是咱們必須假象網絡是不可靠的,有能夠最後一個ACK丟失。因此TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在Client發送出最後的ACK回覆,但該ACK可能丟失。Server若是沒有收到ACK,將不斷重複發送FIN片斷。因此Client不能當即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK以後進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。若是在該時間內再次收到FIN,那麼Client會重發ACK並再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片斷在網絡中最大的存活時間,2MSL就是一個發送和一個回覆所需的最大時間。若是直到2MSL,Client都沒有再次收到FIN,那麼Client推斷ACK已經被成功接收,則結束TCP鏈接。
【問題3】爲何不能用兩次握手進行鏈接?
答:3次握手完成兩個重要的功能,既要雙方作好發送數據的準備工做(雙方都知道彼此已準備好),也要容許雙方就初始序列號進行協商,這個序列號在握手過程當中被髮送和確認。
如今把三次握手改爲僅須要兩次握手,死鎖是可能發生的。做爲例子,考慮計算機S和C之間的通訊,假定C給S發送一個鏈接請求分組,S收到了這個分組,併發 送了確認應答分組。按照兩次握手的協定,S認爲鏈接已經成功地創建了,能夠開始發送數據分組。但是,C在S的應答分組在傳輸中被丟失的狀況下,將不知道S 是否已準備好,不知道S創建什麼樣的序列號,C甚至懷疑S是否收到本身的鏈接請求分組。在這種狀況下,C認爲鏈接還未創建成功,將忽略S發來的任何數據分 組,只等待鏈接確認應答分組。而S在發出的分組超時後,重複發送一樣的分組。這樣就造成了死鎖。
【問題4】若是已經創建了鏈接,可是客戶端忽然出現故障了怎麼辦?
TCP還設有一個保活計時器,顯然,客戶端若是出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會從新復位這個計時器,時間一般是設置爲2小時,若兩小時尚未收到客戶端的任何數據,服務器就會發送一個探測報文段,之後每隔75秒鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉鏈接