大话TCP协议
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少的時候會先積累下來,等到多了再去通知發送方,而發送方在數據包少的時候也會積累下來,等到足夠量再發送。
轉載于:https://juejin.im/post/5a6458bff265da3e3b7a9f53
總結
- 上一篇: 实现京东商城地址选择效果(效果还挺一致的
- 下一篇: Jquery实现城市选择(省市联动)