大话TCP协议
TCP協(xié)議:
1、tcp協(xié)議被定義為可靠的協(xié)議,但是它是屬于傳輸層的協(xié)議,根據(jù)七層協(xié)議的定義,傳輸層的數(shù)據(jù)會先傳網(wǎng)絡(luò)層(ip層),而ip層是盡最大努力做到交付,這里可以理解成ip層也是不可靠的協(xié)議,那么在ip層之上的tcp協(xié)議如何做到可靠交付呢?這里要提到幾個處理方式:
-
停止等待處理方式 停止等待處理方式可以用一下的例子來舉例:a通過tcp協(xié)議將數(shù)據(jù)傳送給b,而在tcp協(xié)議定義里邊,這些數(shù)據(jù)是需要被分組發(fā)送的,當然了這個分組情況是tcp協(xié)議自身決定的,而a每次將數(shù)據(jù)中的一小組發(fā)送給b的時候都需要b發(fā)送數(shù)據(jù)給a確認已經(jīng)成功接收到數(shù)據(jù),這樣a才會繼續(xù)將數(shù)據(jù)發(fā)送給b,那么網(wǎng)絡(luò)有時候會出現(xiàn)異常導致b無法成功接受到數(shù)據(jù)或者數(shù)據(jù)出現(xiàn)錯誤的情況a和b怎么處理呢?b在這里是如果接受到錯誤信息就直接丟棄然后不返回任何數(shù)據(jù)通知a,如果b無法接收到數(shù)據(jù)就不做處理,a在這里的處理方式是給每次發(fā)送數(shù)據(jù)設(shè)置一個計時器,如果超過這個時間了b還沒有發(fā)送確認消息,a就默認這次發(fā)送失敗,然后再次發(fā)送數(shù)據(jù)給b,這里也被稱之為“超時重傳”和"自動重傳請求機制",如果收到了b的確認信息則自動將計時器關(guān)閉。所以這里這個計時器的時間的設(shè)定就顯得非常重要了,因為這個時間要比正常成功傳輸?shù)臅r間稍微長一些,如果短了導致多次重傳,浪費網(wǎng)絡(luò)資源,而長了就會導致效率邊長,而且這個正常成功傳輸?shù)臅r間也不好確定,因為和經(jīng)過哪些網(wǎng)絡(luò)有關(guān)系。那么問題來了,b在接收到a重傳之后如何處理的呢?首先b是先判斷是否已經(jīng)有了這個數(shù)據(jù),如果有了就則直接丟棄這個數(shù)據(jù),如果沒有則繼續(xù)向會話層和應用層交付數(shù)據(jù),而無論如何,都要再次發(fā)送數(shù)據(jù)和a進行確認,因為之所以會導致這個情況就是因為a沒有收到確認信息導致的。 通過這種方式,tcp協(xié)議就能在ip層不可靠的傳輸之上實現(xiàn)可靠的傳輸了。 不過這里如果是每次要等到數(shù)據(jù)1發(fā)送成功后才繼續(xù)發(fā)送數(shù)據(jù)2,數(shù)據(jù)1和數(shù)據(jù)2都在同一個分組里邊,就顯得效率太低了,那么如何做到高效率呢?這里采用了另一種機制,那就是流水線傳輸,就是說發(fā)送方可以并行發(fā)送多個數(shù)據(jù),而不是說串行的發(fā)送,這樣以來效率方面也就提高了。
-
利用tcp協(xié)議如何實現(xiàn)流量控制?這里所謂的流量控制指的是讓發(fā)送方的發(fā)送速率不要那么快,讓接收方及時處理,采用的是滑動窗口的處理方式;以a和b發(fā)送例子為例,a和b在建立連接的時候(三次握手的時候),b會告訴a它的緩存隊列中還可以存儲多少個字節(jié)(rwnd),而a再根據(jù)b告訴它的rwnd的值來傳輸數(shù)據(jù),技術(shù)細節(jié):b每次告訴a的時候首部ACK的值為1,小寫ack的值是確認的序號,而這里a傳輸數(shù)據(jù)的時候會帶上seq,即傳輸數(shù)據(jù)的開始部分,如果b告訴它的rwnd是100,則此刻a傳輸?shù)臄?shù)據(jù)是(1~100)。而等b告訴a的rwnd為0的時候證明b這邊不允許發(fā)送方再發(fā)送數(shù)據(jù)了。而如果b又可以接收數(shù)據(jù)了,就會再次發(fā)送一個rwnd的值告訴a,那么問題來了,如果傳輸?shù)臅r候b的網(wǎng)絡(luò)不好,導致rwnd的值丟失了怎么辦?解決方案是:a在接收到b的零窗口的時候(rwnd=0),a會激發(fā)一個計時器,每隔一段制定的時間發(fā)送一個零窗口探測器,而b在接收到這個零窗口探測信息的時候就會給出目前的窗口值,從而解除了互相等待的狀態(tài)。
tip: 這里保持一個問題,剛剛提到的滑動窗口的傳輸方式其實是針對字節(jié)實現(xiàn)的,那么問題來了?tcp傳輸是數(shù)據(jù)報的形式實現(xiàn)的,那么為何此處是根據(jù)字節(jié)實現(xiàn)的呢?
- 更詳細的三次握手流程:這里以a發(fā)送方和b接收方為例,b作為接收方會先開啟監(jiān)聽狀態(tài),監(jiān)聽是否有發(fā)送方做連接請求,這里稱之為服務器,而a作為發(fā)送方這里稱之為客戶端,第一次握手:a的tcp協(xié)議首部中設(shè)置SYN=1,seq=x之后將數(shù)據(jù)報發(fā)送給b,此處SYN=1的時候規(guī)定不準攜帶數(shù)據(jù),但是序號會自增。完成這一步操作后a的tcp狀態(tài)會變?yōu)镾YN_SEND狀態(tài)。第二次握手:b在接收到a的數(shù)據(jù)的時候會根據(jù)SYN=1判斷是有客戶端連接,b在tcp數(shù)據(jù)包的首部中會將ACK=1,ack=x+1,并且再生成seq=y,將數(shù)據(jù)報發(fā)送給a做確認,此刻b的狀態(tài)會變?yōu)镾YN_RCVD狀態(tài)。第三次握手:a在接收到ACK=1的數(shù)據(jù)后通過ack=x+1明白是b的確認,a就會將ACK=1,ack=y+1,sql=x+1,再次發(fā)送給b,之后進入ESTABLISHED狀態(tài),在這一次的操作中允許攜帶數(shù)據(jù),如果不攜帶則序號不會增加。
tip: 這里將會產(chǎn)生一個問題,如果去掉第三次連接會怎么樣呢?這里給出一個例子來解答這個問題,結(jié)合以上的知識點可以知道,如果在a第一次握手的時候出現(xiàn)了網(wǎng)絡(luò)滯留或者丟失問題,a的協(xié)議機制有個計時器,超時了會再次發(fā)送請求,這也a就發(fā)送了兩次請求連接的操作,而由于網(wǎng)絡(luò)滯留問題,其中有一次操作在該連接結(jié)束后b才收到,那么如果是兩次握手就結(jié)束的話,那么b此刻就會進入established狀態(tài),開始等待a的數(shù)據(jù)傳輸,可是a并沒有打算將數(shù)據(jù)傳輸給b,這也b就會造成等待,導致b的資源浪費。
- 結(jié)合以上知識點來說說看更詳細的四次揮手的流程:這里同樣以a和b進行tcp連接為例,a在要斷開和b的tcp連接的時候,a會先將頭部的FIN信號設(shè)置為1,序號seq=u,即最后一次傳送的序號+1,然后發(fā)送數(shù)據(jù)包,之后進入FIN-WAIT-1狀態(tài),等待b的確認。這也是第一次握手的過程。b在收到a的數(shù)據(jù)包之后,會根據(jù)FIN為1判斷是有客戶端要進行揮手連接,b會將ACK信號設(shè)置為1,ack=u+1,然后發(fā)送數(shù)據(jù)包,之后就會進入CLOSE_WAIT狀態(tài),此刻A到B的這個連接就已經(jīng)斷開了,即a沒有數(shù)據(jù)要發(fā)送了,這時TCP連接就進入了半關(guān)閉狀態(tài),因為tcp是雙全工連接,a到b的連接斷開了,可是b到a的連接還在,而a在接收到b的數(shù)據(jù)包之后便進入了FIN-WAIT-2的狀態(tài),等待b釋放連接,此刻便完成了二次揮手,第三次揮手是b會發(fā)送一個FIN數(shù)據(jù)包,會再次發(fā)送ack為u+1的數(shù)據(jù)包,此刻b便進入LAST_ACK狀態(tài),第三次揮手結(jié)束,而第四次揮手是a在接收到b的數(shù)據(jù)包之后,會根據(jù)FIN和ack=u+1判斷是第三次揮手,之后a會發(fā)送一個ACK數(shù)據(jù)包,然后進入TIME_WAIT 狀態(tài),這里有個地方要注意的,那就是此時TCP連接尚未關(guān)閉,而是要等一個計時器的時間,之后才進入CLOSE狀態(tài),而b在接受到數(shù)據(jù)報紙后也會進入CLOSE狀態(tài)。
tip: 這里將會產(chǎn)生幾個問題:
- 問題一?為什么在第四次揮手a要等待一個計時器的時間才進入CLOSE狀態(tài)?主要原因有:如果a的網(wǎng)絡(luò)不好,在第四次揮手的時候發(fā)送的數(shù)據(jù)包被丟棄掉了,這樣b就會由于沒有收到確認信息而重發(fā)數(shù)據(jù)包,如果a即可就進入CLOSE狀態(tài)了那么肯定無法在此刻接收到b的數(shù)據(jù),而讓a等待一個計時器的時間就可以解決這個問題了。
- 問題二?在a釋放tcp連接處于第二次揮手的時候a關(guān)機了,那么b會處于什么狀態(tài)?并且b怎么處理這種情況?如果a關(guān)機了,那么b會處于LAST-ACK狀態(tài),之后會在發(fā)送數(shù)據(jù)給a的時候無法接受到a的確認數(shù)據(jù)包,為了不讓b無限等待下去,b會啟動一個保活計時器機制,b會在通常兩個小時后還沒有接收到數(shù)據(jù)包的話發(fā)送一個探測報文段,以后會間隔75分鐘發(fā)送一次,如果10次之后還沒有響應,則斷開連接。
- 問題三?服務器tcp連接存在大量close_wait狀態(tài),為什么?這里有個地方上面沒有提到,以為close_wait狀態(tài)遷移到last_ack狀態(tài)是自發(fā)的,其實不是,是要socket.close()才會過度到last_ack,那么大量連接處于close_wait狀態(tài)證明沒有及時close掉socket,可能是io阻塞問題。
2、tcp協(xié)議中使用到的比較高效率的算法:
- Nagle算法:Nagle算法的目的主要是用來解決a和b tcp協(xié)議傳輸?shù)臄?shù)據(jù)傳送的過程,過程是:應用層的應用將要發(fā)送的數(shù)據(jù)逐個字節(jié)傳輸給傳輸層的tcp的發(fā)送緩存的時候,tcp先把第一個字節(jié)發(fā)送給接收方,把之后數(shù)據(jù)繼續(xù)緩存起來,等到接收方回應之后,傳送方再把緩存的數(shù)據(jù)發(fā)送出去,然后等到接收方回應,再繼續(xù)傳送緩存的數(shù)據(jù)給發(fā)送方,這里有點串行的方式。Nagle還規(guī)定:當送達的數(shù)據(jù)已經(jīng)達到發(fā)送窗口大小的一半時就立即發(fā)送數(shù)據(jù)包。 那么問題來了,將滑動窗口機制和這個結(jié)合,可以知道發(fā)送方發(fā)送數(shù)據(jù)是需要接收方告知發(fā)送的rwnd是多少的,而Nagle算法這里,先將第一個字節(jié)發(fā)送給接收方,而如果此時接收方的緩存隊列中接收到這個字節(jié)后就滿了,而交互式的應用進程這里是一個個從緩存中讀取字節(jié)的,如果讀取完接收方就發(fā)送確認并告知rwnd為1,而發(fā)送方收到確認后再次發(fā)送一個字節(jié)過來,接收方再次一個字節(jié)的處理,長此以往,效率肯定是很低的,那么如何解決呢?tcp協(xié)議是這樣處理的,接收方在rwnd少的時候會先積累下來,等到多了再去通知發(fā)送方,而發(fā)送方在數(shù)據(jù)包少的時候也會積累下來,等到足夠量再發(fā)送。
轉(zhuǎn)載于:https://juejin.im/post/5a6458bff265da3e3b7a9f53
總結(jié)
- 上一篇: 实现京东商城地址选择效果(效果还挺一致的
- 下一篇: Jquery实现城市选择(省市联动)