2018-01-20

只是自說自話的學習筆記,各路看官繞路~

TCP協議:

1、tcp協議被定義爲可靠的協議,但是它是屬於傳輸層的協議,根據七層協議的定義,傳輸層的數據會先傳網絡層(ip層),而ip層是盡最大努力做到交付,這裏可以理解成ip層也是不可靠的協議,那麼在ip層之上的tcp協議如何做到可靠交付呢?這裏要提到幾個處理方式:

  • 停止等待處理方式
    停止等待處理方式可以用一下的例子來舉例:a通過tcp協議將數據傳送給b,而在tcp協議定義裏邊,這些數據是需要被分組發送的,當然了這個分組情況是tcp協議自身決定的,而a每次將數據中的一小組發送給b的時候都需要b發送數據給a確認已經成功接收到數據,這樣a纔會繼續將數據發送給b,那麼網絡有時候會出現異常導致b無法成功接受到數據或者數據出現錯誤的情況a和b怎麼處理呢?b在這裏是如果接受到錯誤信息就直接丟棄然後不返回任何數據通知a,如果b無法接收到數據就不做處理,a在這裏的處理方式是給每次發送數據設置一個計時器,如果超過這個時間了b還沒有發送確認消息,a就默認這次發送失敗,然後再次發送數據給b,這裏也被稱之爲“超時重傳”和"自動重傳請求機制",如果收到了b的確認信息則自動將計時器關閉。所以這裏這個計時器的時間的設定就顯得非常重要了,因爲這個時間要比正常成功傳輸的時間稍微長一些,如果短了導致多次重傳,浪費網絡資源,而長了就會導致效率邊長,而且這個正常成功傳輸的時間也不好確定,因爲和經過哪些網絡有關係。那麼問題來了,b在接收到a重傳之後如何處理的呢?首先b是先判斷是否已經有了這個數據,如果有了就則直接丟棄這個數據,如果沒有則繼續向會話層和應用層交付數據,而無論如何,都要再次發送數據和a進行確認,因爲之所以會導致這個情況就是因爲a沒有收到確認信息導致的。
    通過這種方式,tcp協議就能在ip層不可靠的傳輸之上實現可靠的傳輸了。
    不過這裏如果是每次要等到數據1發送成功後才繼續發送數據2,數據1和數據2都在同一個分組裏邊,就顯得效率太低了,那麼如何做到高效率呢?這裏採用了另一種機制,那就是流水線傳輸,就是說發送方可以並行發送多個數據,而不是說串行的發送,這樣以來效率方面也就提高了。

  • 利用tcp協議如何實現流量控制?這裏所謂的流量控制指的是讓發送方的發送速率不要那麼快,讓接收方及時處理,採用的是滑動窗口的處理方式;以a和b發送例子爲例,a和b在建立連接的時候(三次握手的時候),b會告訴a它的緩存隊列中還可以存儲多少個字節(rwnd),而a再根據b告訴它的rwnd的值來傳輸數據,技術細節:b每次告訴a的時候首部ACK的值爲1,小寫ack的值是確認的序號,而這裏a傳輸數據的時候會帶上seq,即傳輸數據的開始部分,如果b告訴它的rwnd是100,則此刻a傳輸的數據是(1~100)。而等b告訴a的rwnd爲0的時候證明b這邊不允許發送方再發送數據了。而如果b又可以接收數據了,就會再次發送一個rwnd的值告訴a,那麼問題來了,如果傳輸的時候b的網絡不好,導致rwnd的值丟失了怎麼辦?解決方案是:a在接收到b的零窗口的時候(rwnd=0),a會激發一個計時器,每隔一段制定的時間發送一個零窗口探測器,而b在接收到這個零窗口探測信息的時候就會給出目前的窗口值,從而解除了互相等待的狀態。


tip: 這裏保持一個問題,剛剛提到的滑動窗口的傳輸方式其實是針對字節實現的,那麼問題來了?tcp傳輸是數據報的形式實現的,那麼爲何此處是根據字節實現的呢?


  • 更詳細的三次握手流程:這裏以a發送方和b接收方爲例,b作爲接收方會先開啓監聽狀態,監聽是否有發送方做連接請求,這裏稱之爲服務器,而a作爲發送方這裏稱之爲客戶端,第一次握手:a的tcp協議首部中設置SYN=1,seq=x之後將數據報發送給b,此處SYN=1的時候規定不準攜帶數據,但是序號會自增。完成這一步操作後a的tcp狀態會變爲SYN_SEND狀態。第二次握手:b在接收到a的數據的時候會根據SYN=1判斷是有客戶端連接,b在tcp數據包的首部中會將ACK=1,ack=x+1,並且再生成seq=y,將數據報發送給a做確認,此刻b的狀態會變爲SYN_RCVD狀態。第三次握手:a在接收到ACK=1的數據後通過ack=x+1明白是b的確認,a就會將ACK=1,ack=y+1,sql=x+1,再次發送給b,之後進入ESTABLISHED狀態,在這一次的操作中允許攜帶數據,如果不攜帶則序號不會增加。

tip: 這裏將會產生一個問題,如果去掉第三次連接會怎麼樣呢?這裏給出一個例子來解答這個問題,結合以上的知識點可以知道,如果在a第一次握手的時候出現了網絡滯留或者丟失問題,a的協議機制有個計時器,超時了會再次發送請求,這也a就發送了兩次請求連接的操作,而由於網絡滯留問題,其中有一次操作在該連接結束後b才收到,那麼如果是兩次握手就結束的話,那麼b此刻就會進入established狀態,開始等待a的數據傳輸,可是a並沒有打算將數據傳輸給b,這也b就會造成等待,導致b的資源浪費。


  • 結合以上知識點來說說看更詳細的四次揮手的流程:這裏同樣以a和b進行tcp連接爲例,a在要斷開和b的tcp連接的時候,a會先將頭部的FIN信號設置爲1,序號seq=u,即最後一次傳送的序號+1,然後發送數據包,之後進入FIN-WAIT-1狀態,等待b的確認。這也是第一次握手的過程。b在收到a的數據包之後,會根據FIN爲1判斷是有客戶端要進行揮手連接,b會將ACK信號設置爲1,ack=u+1,然後發送數據包,之後就會進入CLOSE_WAIT狀態,此刻A到B的這個連接就已經斷開了,即a沒有數據要發送了,這時TCP連接就進入了半關閉狀態,因爲tcp是雙全工連接,a到b的連接斷開了,可是b到a的連接還在,而a在接收到b的數據包之後便進入了FIN-WAIT-2的狀態,等待b釋放連接,此刻便完成了二次揮手,第三次揮手是b會發送一個FIN數據包,會再次發送ack爲u+1的數據包,此刻b便進入LAST_ACK狀態,第三次揮手結束,而第四次揮手是a在接收到b的數據包之後,會根據FIN和ack=u+1判斷是第三次揮手,之後a會發送一個ACK數據包,然後進入TIME_WAIT 狀態,這裏有個地方要注意的,那就是此時TCP連接尚未關閉,而是要等一個計時器的時間,之後才進入CLOSE狀態,而b在接受到數據報紙後也會進入CLOSE狀態。

tip: 這裏將會產生幾個問題:

  • 問題一?爲什麼在第四次揮手a要等待一個計時器的時間才進入CLOSE狀態?主要原因有:如果a的網絡不好,在第四次揮手的時候發送的數據包被丟棄掉了,這樣b就會由於沒有收到確認信息而重發數據包,如果a即可就進入CLOSE狀態了那麼肯定無法在此刻接收到b的數據,而讓a等待一個計時器的時間就可以解決這個問題了。
  • 問題二?在a釋放tcp連接處於第二次揮手的時候a關機了,那麼b會處於什麼狀態?並且b怎麼處理這種情況?如果a關機了,那麼b會處於LAST-ACK狀態,之後會在發送數據給a的時候無法接受到a的確認數據包,爲了不讓b無限等待下去,b會啓動一個保活計時器機制,b會在通常兩個小時後還沒有接收到數據包的話發送一個探測報文段,以後會間隔75分鐘發送一次,如果10次之後還沒有響應,則斷開連接。
  • 問題三?服務器tcp連接存在大量close_wait狀態,爲什麼?這裏有個地方上面沒有提到,以爲close_wait狀態遷移到last_ack狀態是自發的,其實不是,是要socket.close()纔會過度到last_ack,那麼大量連接處於close_wait狀態證明沒有及時close掉socket,可能是io阻塞問題。

2、tcp協議中使用到的比較高效率的算法:

  • Nagle算法:Nagle算法的目的主要是用來解決a和b tcp協議傳輸的數據傳送的過程,過程是:應用層的應用將要發送的數據逐個字節傳輸給傳輸層的tcp的發送緩存的時候,tcp先把第一個字節發送給接收方,把之後數據繼續緩存起來,等到接收方迴應之後,傳送方再把緩存的數據發送出去,然後等到接收方迴應,再繼續傳送緩存的數據給發送方,這裏有點串行的方式。Nagle還規定:當送達的數據已經達到發送窗口大小的一半時就立即發送數據包。
    那麼問題來了,將滑動窗口機制和這個結合,可以知道發送方發送數據是需要接收方告知發送的rwnd是多少的,而Nagle算法這裏,先將第一個字節發送給接收方,而如果此時接收方的緩存隊列中接收到這個字節後就滿了,而交互式的應用進程這裏是一個個從緩存中讀取字節的,如果讀取完接收方就發送確認並告知rwnd爲1,而發送方收到確認後再次發送一個字節過來,接收方再次一個字節的處理,長此以往,效率肯定是很低的,那麼如何解決呢?tcp協議是這樣處理的,接收方在rwnd少的時候會先積累下來,等到多了再去通知發送方,而發送方在數據包少的時候也會積累下來,等到足夠量再發送。

下面記錄的算法是在擁塞控制的時候使用到的算法:

  • 慢開始算法:發送方會先維持一個擁塞窗口, 擁塞窗口的大小取決於網絡的情況,只要網絡沒有出現問題,那麼擁塞窗口就會變大,慢開始算法是成倍變大,那麼tcp協議如何識別網絡出現問題呢?從上面應該可以看到,發送方在發送出數據包的時候,如果沒有收到確認數據包,那麼便會當做網絡出現問題處理。如果收到了確認數據包,則擁塞窗口就會變大,之所以會成倍變大,這裏的意思是 如果擁塞窗口是4,則發送方會發送4個數據包出去,都收到應答之後,擁塞窗口變回成倍變大,擁塞窗口默認值爲1,並且會設置一個ssthresh慢開始門限,那麼如果遇見了網絡阻塞,發出去的數據包沒有應答的話怎麼處理?如果遇見了ssthresh會減半處理,並且擁塞窗口的值會降爲1,而如果一直擁塞窗口的值一直增加到超過ssthresh之後便會採取擁塞算法,在這個階段和慢開始階段不同的地方在於,發送方發送的數據包串行化的逐個發送的,也即是說擁塞窗口是逐一增加的。
  • 快重傳算法:發送方在發送了數據包之後會設置一個重發計時器,而快重傳的作用就在此處使用。當接收方接收了數據包m1、m2,之後沒有接收到m3,而是接收到了m4,快重傳算法要求接收方立即發送對m2的重複確認,沒錯,這裏就是m2的重複確認,可以看出來之前已經確認過了一次了,這樣可以讓發送方及早知道接收方沒有接收到m3的數據包,發送方還會繼續發送m5和m6,接收方接收到m5和m6後還要繼續發送對m2的重複確認,當三次以後接收方會立即發送m3的數據包,而不用等待重發計時器的時間到來。
  • 快恢復算法:與快重傳算法搭配使用的一個算法,當發生了快重傳現象的時候tcp會認爲網絡可能沒有阻塞,不然不可能連續有三個數據到達接收方,所以會將ssthresh設置爲一半,而擁塞窗口設置爲1,可是這裏並不會進行慢開始算法,而是採用擁塞算法,緩慢增加。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章