一. UDP協(xié)議
UDP協(xié)議端格式
由上圖可以看出,一個(gè)UDP報(bào)文最大長(zhǎng)度就是65535.?
? 16位長(zhǎng)度,表示整個(gè)數(shù)據(jù)報(bào)(UDP首部+UDP數(shù)據(jù))的最大長(zhǎng)度(注意,這里的16位UDP長(zhǎng)度只是一個(gè)標(biāo)識(shí)這個(gè)數(shù)據(jù)報(bào)長(zhǎng)度的字段,并不是這個(gè)數(shù)據(jù)報(bào)傳輸?shù)臄?shù)據(jù))
? 如果校驗(yàn)和出錯(cuò),就會(huì)直接丟棄。
?校驗(yàn)和:通過(guò)網(wǎng)線傳輸時(shí),電信號(hào)使用高低電平來(lái)表示0和1.。但是,如果外部環(huán)境干擾,就有可能導(dǎo)致低電平->高電平,高電平->低電平,造成比特翻轉(zhuǎn)=>數(shù)據(jù)就傳輸錯(cuò)了。校驗(yàn)和就是通過(guò)數(shù)據(jù)報(bào)中的數(shù)據(jù)內(nèi)容通過(guò)計(jì)算得到的。值得注意的是:如果校驗(yàn)和不對(duì),此時(shí)你的數(shù)據(jù)一定不對(duì),如果校驗(yàn)和對(duì),但是數(shù)據(jù)也有一定概率是錯(cuò)誤的。
面向數(shù)據(jù)報(bào):應(yīng)用層交給UDP長(zhǎng)的報(bào)文,UDP原樣發(fā)送,既不會(huì)拆分,也不會(huì)合并。
用UDP傳輸100個(gè)字節(jié)的數(shù)據(jù)報(bào):
如果一次發(fā)送端發(fā)送100個(gè)字節(jié),那么接收端也必須一次接收100個(gè)字節(jié);而不能循環(huán)10次接收,每次接收10個(gè)字節(jié)。
緩沖區(qū):UDP只有接收緩沖區(qū),沒(méi)有發(fā)送緩沖區(qū)
UDP沒(méi)有真正意義上的發(fā)送緩沖區(qū),發(fā)送的數(shù)據(jù)會(huì)直接交給內(nèi)核,由內(nèi)核將數(shù)據(jù)傳給網(wǎng)絡(luò)層協(xié)議進(jìn)行后續(xù)的傳輸動(dòng)作;
DUP具有接收緩沖區(qū),但是這個(gè)接收緩沖區(qū)不能保證收到的DUP報(bào)的順序和發(fā)送DUP報(bào)的順序一致;如果緩沖區(qū)滿了,再到達(dá)的DUP數(shù)據(jù)就會(huì)被丟棄;
二. TCP協(xié)議
TCP協(xié)議段格式:
2.1 TCP原理?
2.1.1 確認(rèn)應(yīng)答機(jī)制
TCP 將每個(gè)字節(jié)的數(shù)據(jù)都進(jìn)行了編號(hào)。即位序列號(hào)。
發(fā)送方的序號(hào)為最后一個(gè)字節(jié)的編號(hào),確認(rèn)序號(hào)為無(wú)意義的數(shù)據(jù);
接收方的序號(hào)和發(fā)送方的序號(hào)無(wú)關(guān),確認(rèn)序號(hào)為接收數(shù)據(jù)的序號(hào)+1。
(在接收緩沖區(qū)中,優(yōu)先級(jí)隊(duì)列通過(guò)序號(hào)來(lái)確定數(shù)據(jù)的發(fā)送先后順序 )
接收方就可以通過(guò)ack的確認(rèn)序號(hào),告訴發(fā)送方哪些數(shù)據(jù)已經(jīng)收到了。
2.1.2 超時(shí)重傳機(jī)制
主機(jī)A發(fā)送數(shù)據(jù)給B之后,可能因?yàn)榫W(wǎng)絡(luò)擁堵等原因,數(shù)據(jù)無(wú)法到達(dá)主機(jī)B;
如果主機(jī)A在一個(gè)特定時(shí)間間隔內(nèi)沒(méi)有收到B發(fā)來(lái)的確認(rèn)應(yīng)答,就會(huì)重發(fā)。
但是,主機(jī)A未收到B發(fā)來(lái)的確認(rèn)應(yīng)答,也可能是因?yàn)橹鳈C(jī)B收到了數(shù)據(jù),但是ACK丟失了;
因此若ACK丟失了,主機(jī)B會(huì)收到很多重復(fù)數(shù)據(jù)。那么TCP協(xié)議需要能夠識(shí)別哪些包是重復(fù)的包,并且把重復(fù)的丟棄掉。這時(shí)候我們就可以利用前面提到的序號(hào)來(lái)達(dá)到去重的效果。
超時(shí)重傳后,重復(fù)發(fā)送的數(shù)據(jù)報(bào)仍可能會(huì)丟失,TCP為了保證無(wú)論在任何環(huán)境下都能比較高性能的通信,因此會(huì)動(dòng)態(tài)的計(jì)算這個(gè)最大超時(shí)時(shí)間。
如果重發(fā)一次,仍得不到應(yīng)答,TCP就會(huì)將這個(gè)超時(shí)時(shí)間延長(zhǎng)后再重發(fā),在不停的延長(zhǎng)超時(shí)時(shí)間后,當(dāng)累積到一定的重傳次數(shù)后,TCP就會(huì)重置連接,如果重置連接失效,TCP就會(huì)關(guān)閉連接,放棄網(wǎng)絡(luò)通信。
2.1.3 連接管理機(jī)制(三次握手,四次揮手)
建立連接:三次握手
握手指的是通信雙發(fā),進(jìn)行一次網(wǎng)絡(luò)交互,相當(dāng)于客戶(hù)端和服務(wù)器之間,通過(guò)三次交互,建立了連接關(guān)系。?
syn稱(chēng)為同步報(bào)文段。意思就是一方要向另一方,申請(qǐng)連接。
在報(bào)文頭部中有6個(gè)特殊的比特位,如果設(shè)為1,則表示特定含義。
其中第二位,是ACK,如果這一位為1,表示當(dāng)前TCP數(shù)據(jù)報(bào)是一個(gè)應(yīng)答報(bào)文;
其中第五位,是SYN,如果這一位為1,表示當(dāng)前TCP數(shù)據(jù)報(bào)是一個(gè)同步報(bào)文;
如果一個(gè)TCP數(shù)據(jù)報(bào),第二位和第五位都是1,則當(dāng)前這個(gè)報(bào)文時(shí)SYN+ACK
三次握手這個(gè)過(guò)程,本質(zhì)上時(shí)投石問(wèn)路,驗(yàn)證了客戶(hù)端和服務(wù)器,各自的發(fā)送能力和接收能力是否正常?
斷開(kāi)連接:四次揮手
FIN:結(jié)束報(bào)文段
四次揮手中的ack和fin是否可以合并?
在三次握手中,ack和syn時(shí)同一時(shí)刻觸發(fā)的(都是內(nèi)核來(lái)完成的)
四次揮手,ack和fin則是在不同時(shí)機(jī)觸發(fā)的。
ack是內(nèi)核完成的,會(huì)在收到fin的時(shí)候第一時(shí)間返回
fin則是應(yīng)用程序代碼控制的。在調(diào)用到socket的close方法時(shí)候才會(huì)出發(fā)fin
finally{ //thread.sleep(1000); socket.close(); }
這個(gè)close的執(zhí)行時(shí)機(jī),可能是立即,也可能是隔很久,卻決于你的代碼怎么寫(xiě)。
如果立即 close,趁著剛才ack還沒(méi)發(fā)呢,這里就可以合并,如果是隔很久再close,此時(shí)fin就只能單獨(dú)發(fā)了。
注:這里的close只是進(jìn)程關(guān)閉了,但是連接(連接是內(nèi)核維護(hù)的)還在,客戶(hù)端發(fā)送的ack,服務(wù)器仍然可以收到。
2.1.4 滑動(dòng)窗口(效率機(jī)制)
對(duì)于每一個(gè)發(fā)送的數(shù)據(jù)段,都要給一個(gè)ACK確認(rèn)應(yīng)答,收到ACK后再發(fā)送下一個(gè)數(shù)據(jù)段。.由于這樣的一收一發(fā)的方式性能較低,那么我們一次發(fā)送多條數(shù)據(jù),就可以大大的提高性能。(其實(shí)是將多個(gè)段的等待時(shí)間重疊在一起了。
? 窗口大小指的是無(wú)需等待應(yīng)答而可以繼續(xù)發(fā)送數(shù)據(jù)的最大值.上圖的窗口大小就是4000個(gè)字節(jié)(4個(gè)段)。
? 發(fā)送前四個(gè)段的時(shí)候,不需要等待任何ACK,直接發(fā)送。
? 操作系統(tǒng)內(nèi)核為了維護(hù)這個(gè)滑動(dòng)窗口,需要開(kāi)辟發(fā)送緩沖區(qū)來(lái)記錄當(dāng)前還有哪些數(shù)據(jù)沒(méi)有應(yīng)答;只有確認(rèn)應(yīng)答過(guò)的數(shù)據(jù),才能從緩沖區(qū)刪掉。
??窗口越大,則網(wǎng)絡(luò)的吞吐率就越高。
如果出現(xiàn)了丟包的情況,如何重傳?分兩種情況:
情況一:數(shù)據(jù)包已經(jīng)抵達(dá),ACK被丟了。
這種情況下,部分ACK丟了并不要緊,因?yàn)榭梢酝ㄟ^(guò)后續(xù)的ACK進(jìn)行確認(rèn)。如果1001的 ACK沒(méi)有接收到,收到了2001的ACK,那么就說(shuō)明已經(jīng)接收到了2001以前的數(shù)據(jù)。
情況二:數(shù)據(jù)包直接丟了。?
? 當(dāng)某一段報(bào)文丟失之后,發(fā)送端會(huì)一直收到1001這樣的ACK,就像是在提醒發(fā)送端“我想要的是"1001"一樣;
? 當(dāng)發(fā)送端主機(jī)連續(xù)收到三次同樣的“1001”這樣的應(yīng)答,就會(huì)將對(duì)應(yīng)的數(shù)據(jù)1001~2000重新發(fā)送
? 這個(gè)時(shí)候接收端收到了1001之后,再次返回的ACK就是7001了,因?yàn)?001~7000接收端其實(shí)之前就已經(jīng)收到了,被放到了接收端操作系統(tǒng)內(nèi)核的接收緩沖區(qū)中。
2.1.5 流量控制(安全機(jī)制)
接收端處理數(shù)據(jù)的速度是有限的。如果發(fā)送端發(fā)送的太快,導(dǎo)致接收端的緩沖區(qū)被打滿,這個(gè)時(shí)候如果發(fā)送端繼續(xù)發(fā)送,就會(huì)造成丟包,繼而引起丟包重傳等一些列連鎖反應(yīng)。?
因此TCP支持根據(jù)接收端的處理能力,來(lái)決定發(fā)送端的發(fā)送速度。這個(gè)機(jī)制就叫做流量控制
? 接收端將自己可以接收的緩沖區(qū)大小放入TCP首部中的“窗口大小”字段,通過(guò)ACK端通知發(fā)送端,此時(shí)發(fā)送端就可以根據(jù)這個(gè)窗口大小來(lái)批量發(fā)送這些數(shù)據(jù)了。
? 接收端一旦發(fā)現(xiàn)自己的緩沖區(qū)快滿了,就會(huì)將窗口大小設(shè)置成一個(gè)更小的值通知給發(fā)送端;
? 發(fā)送端接收到了這個(gè)窗口之后,就會(huì)減慢自己的發(fā)送速度
? 如果接收端緩沖區(qū)滿了,就會(huì)將窗口設(shè)置為0;這時(shí)發(fā)送方就不再發(fā)送數(shù)據(jù),但是仍需要定期發(fā)送一個(gè)窗口探測(cè)數(shù)據(jù)段,使接收端把窗口大小告訴發(fā)送端。
上述過(guò)程是把返回的窗口大小,當(dāng)作實(shí)際的窗口,實(shí)踐中可能會(huì)有出入。
發(fā)送方的窗口大小=min(流量控制,擁塞控制)。
2.1.6 擁塞控制(安全機(jī)制)?
雖然TCP有了滑動(dòng)窗口這個(gè)大殺器,能夠高效可靠的發(fā)送大量的數(shù)據(jù).但是如果在剛開(kāi)始階段就發(fā)送大量的數(shù)據(jù),仍然可能引發(fā)問(wèn)題.
因?yàn)榫W(wǎng)絡(luò)上有很多的計(jì)算機(jī),在傳輸?shù)倪^(guò)程中,需要經(jīng)歷許多的節(jié)點(diǎn),可能當(dāng)前的網(wǎng)絡(luò)狀態(tài)就已經(jīng)比較擁堵了,但是此時(shí)在不清楚當(dāng)前網(wǎng)絡(luò)狀態(tài)下,貿(mào)然發(fā)送大量的數(shù)據(jù),是很有可能引起雪上加霜的.于是,不管在客戶(hù)端與服務(wù)器之間是經(jīng)歷怎樣的路徑,把這個(gè)路徑當(dāng)做黑盒一樣的東西,每次發(fā)送不同數(shù)量的請(qǐng)求,來(lái)試驗(yàn)出最佳的發(fā)送窗口.
TCP引入 慢啟動(dòng) 機(jī)制,先發(fā)送少量的數(shù)據(jù),探探路,摸清當(dāng)前的網(wǎng)絡(luò)擁堵?tīng)顟B(tài),再?zèng)Q定按照多大的速度傳輸數(shù)據(jù)
? 發(fā)送開(kāi)始的時(shí)候,定義擁塞窗口大小為1
? 每次收到一個(gè)ACK應(yīng)答,擁塞窗口加1
? 每次發(fā)送數(shù)據(jù)包的時(shí)候,將擁塞窗口和接收端主機(jī)反饋的窗口大小作比較,取較小的值作為實(shí)際發(fā)送的窗口
?為了讓擁塞窗口不增長(zhǎng)的那么快,變引入了一個(gè)叫做慢啟動(dòng)的閾值,當(dāng)擁塞窗口超過(guò)這個(gè)閾值的時(shí)候,不再按照指數(shù)的方式增長(zhǎng),而是按照線性方式增長(zhǎng)
? 當(dāng)TCP開(kāi)始啟動(dòng)的時(shí)候,慢啟動(dòng)閾值等于窗口最大值;
? 在每次超時(shí)重發(fā)的時(shí)候,慢啟動(dòng)閾值會(huì)變成原來(lái)的一般,同時(shí)擁塞窗口重置回1.
2.1.7 延遲應(yīng)答
如果接受數(shù)據(jù)的主機(jī)立刻返回ACK應(yīng)答,這時(shí)候返回的窗口肯能比較小.
? 假設(shè)接收緩沖區(qū)為1M,一次收到了500k的數(shù)據(jù),如果立刻應(yīng)答,返回的窗口就是500k
? 但實(shí)際上可能處理端處理的速度很快,10ms之內(nèi)就把500k的數(shù)據(jù)從緩沖區(qū)消費(fèi)掉了
? 在這種情況下,接收端處理還遠(yuǎn)沒(méi)有達(dá)到自己的極限,即使窗口再放大一些,也能處理過(guò)來(lái)
? 如果接受端稍微等一會(huì)再應(yīng)答,比如等待200ms再應(yīng)答,那么這個(gè)時(shí)候返回的窗口大小就是1M
窗口越大,網(wǎng)絡(luò)吞吐量就越大,傳輸效率就越高.我們的目標(biāo)是在保證網(wǎng)絡(luò)不擁塞的情況下盡量提高傳輸效率.
但是并不是所有的包都可以延遲應(yīng)答
? 數(shù)量限制:每隔N個(gè)包就應(yīng)答一次;
? 時(shí)間限制:超過(guò)最大延遲時(shí)間就應(yīng)答一次
具體的數(shù)量和超時(shí)時(shí)間,操作系統(tǒng)不同也有差異;一般N取2,超時(shí)時(shí)間取200ms(根據(jù)業(yè)務(wù)需求可自定義)
2.1.8 捎帶應(yīng)答
在延遲應(yīng)答的基礎(chǔ)上,我們可以發(fā)現(xiàn),很多情況下,客戶(hù)端服務(wù)器在應(yīng)用層也是一發(fā)一收的.意味著客戶(hù)端給服務(wù)器說(shuō)了"How are you" , 服務(wù)器中程序處理過(guò)請(qǐng)求后也會(huì)給客戶(hù)端返回一個(gè)"Fine,thank you" , 那么這個(gè)時(shí)候ACK就可以搭順風(fēng)車(chē),和服務(wù)器回應(yīng)的"Fine, thank you" 一起返回給客戶(hù)端?
?上述ACK是內(nèi)核收到數(shù)據(jù)報(bào)后直接返回的,Fine....是應(yīng)用程序,通過(guò)write寫(xiě)的數(shù)據(jù),通過(guò)一系列代碼執(zhí)行到才返回,這倆時(shí)機(jī)本來(lái)是不同的.但是延時(shí)應(yīng)答,使此時(shí)的ACK 就可能稍等一會(huì)再發(fā)送就很有可能和response 合并成一個(gè)數(shù)據(jù)報(bào).四次揮手,有可能是三次揮手,就是捎帶應(yīng)答起到的效果.
2.2 粘包問(wèn)題
在TCP協(xié)議頭中,沒(méi)有如同UDP一樣的“報(bào)文長(zhǎng)度”這樣的字段,但是有一個(gè)序號(hào)這樣的字段,站在傳輸層的角度,TCP是一個(gè)一個(gè)報(bào)文過(guò)來(lái)的。按照序號(hào)放在緩沖區(qū)中。站在應(yīng)用層的角度,看到的只是一串連續(xù)的字節(jié)數(shù)據(jù)。那么應(yīng)用程序看到了這么一連串的字節(jié)數(shù)據(jù),就不知道從哪個(gè)部分開(kāi)始到哪個(gè)部分結(jié)束,是一個(gè)完整的數(shù)據(jù)包。
如何解決粘包問(wèn)題?歸根結(jié)底就是一句話,明確兩個(gè)包之間的邊界。
??對(duì)于定長(zhǎng)的包,保證每次都按照固定大小讀取即可;例如上面的Request結(jié)構(gòu),是固定大小的,那么就從緩沖區(qū)從頭開(kāi)始按sizeof(Request) 依次讀取即可
? 對(duì)于變長(zhǎng)的包,可以定義分隔符,來(lái)區(qū)分包(應(yīng)用層協(xié)議,是程序員自己來(lái)定的,只要定義保證分隔符不和正文沖突即可)
? 對(duì)于變長(zhǎng)的包,還可以在包頭的位置,約定一個(gè)包總長(zhǎng)讀的字段,從而知道了包的結(jié)束位置
對(duì)于UDP協(xié)議來(lái)說(shuō),是否也存在“粘包問(wèn)題”?
? 對(duì)于UDP,如果還沒(méi)有上層交付數(shù)據(jù),UDP的報(bào)文長(zhǎng)度仍然在。同時(shí),UDP是一個(gè)一個(gè)把數(shù)據(jù)交付給應(yīng)用層。就有很明確的數(shù)據(jù)邊界。
?? 站在應(yīng)用層的角度,使用DUP的時(shí)候,要么收到完整的UDP報(bào)文,要么不收,不會(huì)出現(xiàn)“半個(gè)”的情況。
2.3 TCP異常情況
2.3.1 進(jìn)程關(guān)閉/進(jìn)程崩潰
進(jìn)程沒(méi)了,socket 是文件,隨之被關(guān)閉,雖然進(jìn)程沒(méi)了,但是連接還在,仍然可以繼續(xù)四次揮手
2.3.2 主機(jī)關(guān)機(jī)(正常流程關(guān)機(jī))
先殺死所有的用戶(hù)進(jìn)程,然后就和進(jìn)程關(guān)閉一樣。
雖然可以出發(fā)四次揮手,如果能夠在關(guān)閉之前完成更好。
如果沒(méi)有發(fā)完,比如,對(duì)方發(fā)的 fin 過(guò)來(lái)了,咱們沒(méi)來(lái)得及ack就關(guān)機(jī)了,此時(shí)對(duì)端就會(huì)重傳fin,重傳幾次之后,發(fā)現(xiàn)都沒(méi)有ack,嘗試重置連接,如果還不行,就直接釋放連接。
2.3.3 主機(jī)掉電(把電源)
瞬間機(jī)器就關(guān)了,來(lái)不及進(jìn)行任何揮手操作。此時(shí)分兩種情況:
1)對(duì)端是發(fā)送端
對(duì)端就會(huì)收不到ack=>超時(shí)重傳=>重置連接=>釋放連接
2)對(duì)端是接收端
對(duì)端是沒(méi)法立即知道,你這邊是還沒(méi)來(lái)得及發(fā)新的數(shù)據(jù),還是直接沒(méi)了。即使發(fā)送端沒(méi)有寫(xiě)入操作,TCP自己也內(nèi)置了一個(gè)保活機(jī)制“心跳包”。雖然是接收端,但是接收端會(huì)定期給發(fā)送端發(fā)送一個(gè)心跳包(ping),正常情況下就會(huì)返回一個(gè)(pong),如果每個(gè) ping 都有及時(shí)的 pong,這個(gè)時(shí)候就說(shuō)明當(dāng)前對(duì)端的狀態(tài)良好,如果 ping 過(guò)去之后,沒(méi)用 pong ,說(shuō)明心跳沒(méi)了,這邊怕是打概率掛了(pong也有概率丟的,因此會(huì)連續(xù)幾次都丟了才會(huì)判定連接斷開(kāi))。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-855323.html
注:發(fā)送方也是有心跳包的,但是通過(guò)對(duì)方返回的ack來(lái)判定會(huì)更快一些。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-855323.html
到了這里,關(guān)于網(wǎng)絡(luò)原理-UDP/TCP詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!