JavaEE初階教程之UDP協(xié)議和TCP協(xié)議

引言
傳輸層是網(wǎng)絡(luò)通信的核心樞紐,TCP(傳輸控制協(xié)議)與 UDP(用戶數(shù)據(jù)報(bào)協(xié)議)作為該層的兩大基石,以差異化設(shè)計(jì)支撐多樣化數(shù)據(jù)傳輸需求。
TCP 秉持 “可靠性優(yōu)先”,通過(guò)面向連接、重傳機(jī)制、流量與擁塞控制,實(shí)現(xiàn)數(shù)據(jù)無(wú)丟失、無(wú)差錯(cuò)、按序交付,成為文件傳輸、網(wǎng)頁(yè)瀏覽等場(chǎng)景的核心支撐。
UDP 堅(jiān)守 “高效輕便” 原則,采用無(wú)連接模式,省去連接建立與釋放開(kāi)銷,以低延遲優(yōu)勢(shì)適配實(shí)時(shí)音視頻、網(wǎng)絡(luò)游戲等對(duì)響應(yīng)速度敏感的場(chǎng)景。
二者優(yōu)勢(shì)互補(bǔ),共同構(gòu)成現(xiàn)代網(wǎng)絡(luò)通信的基礎(chǔ),理解其特性是構(gòu)建高效網(wǎng)絡(luò)應(yīng)用的關(guān)鍵。
一:UDP的協(xié)議
特點(diǎn):UDP傳輸?shù)倪^(guò)程類似于寄信
- 無(wú)連接:知道對(duì)端的IP和端口號(hào)就直接進(jìn)行傳輸,不需要建立連接。
- 不可靠:沒(méi)有確認(rèn)機(jī)制,沒(méi)有重傳機(jī)制;如果因?yàn)榫W(wǎng)絡(luò)故障導(dǎo)致該斷無(wú)法發(fā)送對(duì)方,UDP協(xié)議層也不會(huì)給應(yīng)用層返回任何錯(cuò)誤信息。
- 面向數(shù)據(jù)報(bào):不能夠靈活的控制讀寫(xiě)數(shù)據(jù)的次數(shù)和數(shù)量。
===》:應(yīng)用層交給UDP多長(zhǎng)的報(bào)文,UDP原樣發(fā)送,既不會(huì)拆分,也不會(huì)合并;
例如:用UDP傳輸100個(gè)字節(jié)的數(shù)據(jù):
如果發(fā)送端調(diào)用一次sendto,發(fā)送100個(gè)字節(jié),那么接收端也必須調(diào)用對(duì)應(yīng)的一次recvfrom,接受100個(gè)字節(jié),而不能循環(huán)調(diào)用10次recvfrom,每次接受10個(gè)字節(jié)。
UDP協(xié)議端格式

- 16位UDP長(zhǎng)度,表示整個(gè)數(shù)據(jù)報(bào)(UDP首部+UDP數(shù)據(jù))的最大長(zhǎng)度;
- 如果校驗(yàn)和出錯(cuò),就會(huì)直接丟棄;
UDP使用注意事項(xiàng):
我們注意到, UDP協(xié)議首部中有一個(gè)16位的最大長(zhǎng)度. 也就是說(shuō)一個(gè)UDP能傳輸?shù)臄?shù)據(jù)最大長(zhǎng)度是64K(包含UDP首部),然而64K在當(dāng)今的互聯(lián)網(wǎng)環(huán)境下, 是一個(gè)非常小的數(shù)字。
如果我們需要傳輸?shù)臄?shù)據(jù)超過(guò)64K, 就需要在應(yīng)用層手動(dòng)的分包, 多次發(fā)送, 并在接收端手動(dòng)拼裝;
基于UDP的應(yīng)用層協(xié)議:
- NFS:網(wǎng)絡(luò)文件系統(tǒng)
- TFTP:簡(jiǎn)單文件傳輸協(xié)議
- DHCP:動(dòng)態(tài)主機(jī)配置協(xié)議
- BOOTP:?jiǎn)?dòng)協(xié)議(用于無(wú)盤(pán)設(shè)備啟動(dòng))
- DNS:域名解析協(xié)議
也包括我們自己寫(xiě)UDP程序自定義的應(yīng)用層協(xié)議
二:TCP協(xié)議
?? TCP全稱 傳輸控制協(xié)議(Transmission Control Protocol),人如其名,聚焦數(shù)據(jù)傳輸?shù)娜鞒叹珳?zhǔn)控制,核心在于“可靠”與“有序”。
TCP協(xié)議端格式

??源/目的端口號(hào):表示數(shù)據(jù)是從哪個(gè)進(jìn)程來(lái),到哪個(gè)進(jìn)程去;
??32位序號(hào)/32位確認(rèn)號(hào):32位序號(hào)標(biāo)數(shù)據(jù),確認(rèn)號(hào)應(yīng)答已收;
??4位TCP報(bào)頭長(zhǎng)度:表示該TCP頭部有多少個(gè)32位bit(有多少4個(gè)字節(jié));所以TCP頭部的最大長(zhǎng)度為15*4=60;
??6位標(biāo)志位:
- URG:緊急指針是否有效
- ACK:確認(rèn)序號(hào)是否有效
- PSH:提示接收端應(yīng)用程序盡快從TCP緩沖區(qū)把數(shù)據(jù)讀走
- RST:對(duì)方要求重新建立連接,我們把攜帶RST標(biāo)識(shí)的稱為復(fù)位報(bào)文段
- SYN:請(qǐng)求建立連接,我們把攜帶SYN標(biāo)識(shí)的稱之為同步報(bào)文段
- FIN:通知對(duì)方,本端要關(guān)閉了,我們稱攜帶FIN標(biāo)識(shí)的為結(jié)束報(bào)文段
??16位窗口大小:TCP 流量控制核心,限定接收端緩存容量
??16位校驗(yàn)和:發(fā)送端填充,CRC校驗(yàn),接收端校驗(yàn)不通過(guò),則認(rèn)為數(shù)據(jù)有問(wèn)題,此處的校驗(yàn)和不光包含TCP首部,也包含TCP數(shù)據(jù)部分;
??16位緊急指針:標(biāo)識(shí)哪部分?jǐn)?shù)據(jù)是緊急數(shù)據(jù);
??40字節(jié)頭部選項(xiàng):TCP 頭部可選字段,最大 40 字節(jié)擴(kuò)展功能
下面介紹TCP的核心特性:
2.1:確認(rèn)應(yīng)答

TCP將每個(gè)字節(jié)數(shù)據(jù)都進(jìn)行了編號(hào),即為序列號(hào)。

每一個(gè)ACK都帶有一個(gè)對(duì)應(yīng)的確認(rèn)信號(hào),目的是為了告訴發(fā)送者,我已經(jīng)收到了哪些數(shù)據(jù),下次該從哪里發(fā)送;
2.2:延時(shí)重傳

場(chǎng)景: 主機(jī)A發(fā)送數(shù)據(jù)給主機(jī)B后,可能因?yàn)榫W(wǎng)絡(luò)擁堵等信息,數(shù)據(jù)無(wú)法到達(dá)主機(jī)B;
- 如果主機(jī)A在特定的時(shí)間間隔內(nèi),沒(méi)有收到主機(jī)B發(fā)來(lái)的確認(rèn)應(yīng)答就會(huì)進(jìn)行重發(fā);(但是主機(jī)A未收到主機(jī)B的確認(rèn)應(yīng)答,也可能是因?yàn)锳CK丟包了)

這樣主機(jī)B就會(huì)收到很多重復(fù)的數(shù)據(jù),那么TCP協(xié)議就需要能夠識(shí)別出那些包是重復(fù)的包,并且把重復(fù)的丟棄掉;
那么是如何解決的呢?
這個(gè)時(shí)候就用上了我們的序列號(hào),這樣就可以很容易的實(shí)現(xiàn)去重的效果;
這里還有一個(gè)問(wèn)題:超時(shí)時(shí)間該如何確定?
===》
- 最理想的情況下,找到一個(gè)最小的時(shí)間,保證確認(rèn)應(yīng)答能夠在這個(gè)時(shí)間內(nèi)可以返回。
- 隨著網(wǎng)絡(luò)的的波動(dòng),這個(gè)時(shí)間的長(zhǎng)短是有差異的。
- 如果超時(shí)時(shí)間設(shè)置的太長(zhǎng),可能會(huì)影響整體的重傳效率。
- 如果超時(shí)時(shí)間設(shè)置的太短,可能會(huì)頻繁的發(fā)送重復(fù)的包。
?TCP為了保證無(wú)論在什么環(huán)境下都有比較高性能的通信,因此會(huì)動(dòng)態(tài)的計(jì)算這個(gè)最大的超時(shí)時(shí)間。
- Linux中(BSD Unix和Windows也是如此),超時(shí)以500ms為一個(gè)單位進(jìn)行控制,每次判定超時(shí)重發(fā)的超時(shí)時(shí)間都是500ms的整數(shù)倍。
- 如果重發(fā)一次后,還沒(méi)有收到應(yīng)答,等待2*500ms后再進(jìn)行重傳。
- 如果仍然還沒(méi)有收到應(yīng)答,等待4*500ms進(jìn)行重傳,依次類推,以指數(shù)的形式遞增。
- 累計(jì)到了一定的重傳次數(shù),TCP認(rèn)為網(wǎng)絡(luò)或者對(duì)端主機(jī)出現(xiàn)異常,會(huì)強(qiáng)制關(guān)閉連接。
2.3:連接管理
這里最重要的點(diǎn)三次握手和四次揮手

TCP中的握手,傳輸一個(gè)“打招呼”的數(shù)據(jù)包,這個(gè)數(shù)據(jù)包不攜帶任何的業(yè)務(wù)數(shù)據(jù)(沒(méi)有應(yīng)用層載荷),數(shù)據(jù)包中只有報(bào)頭,沒(méi)有正文。
三次握手,在建立連接的過(guò)程中,客戶端和服務(wù)器要經(jīng)過(guò)三次這樣的“打招呼”,連接才能在雙方這邊建立好。

1):客戶端給服務(wù)器發(fā)起一個(gè)syn(同步報(bào)文段)客戶端告訴服務(wù)器,我要和你進(jìn)行連接,請(qǐng)你保存為的信息。
2):服務(wù)器給客戶端返回一個(gè)ack,服務(wù)器告訴客戶端:收到我會(huì)保存?。?!
3):服務(wù)器也給客戶端發(fā)送一個(gè)syn,告訴客戶端,我也要和你建立連接,請(qǐng)你也保存我的信息
4):客戶端收到之后,也會(huì)返回一個(gè)ack,告訴服務(wù)器:收到 ~我會(huì)保存
從邏輯上是四次交互,但是中間兩次,可以合并成一個(gè)TCP數(shù)據(jù)包,網(wǎng)絡(luò)上實(shí)際只傳輸了三個(gè)數(shù)據(jù)包。 實(shí)際圖如下所示:

??那么三次握手(建立連接)有什么意義?
1.類比于投石問(wèn)路,確認(rèn)當(dāng)前通信路徑是否通暢
2.也是驗(yàn)證通信雙方發(fā)送能力和接受能力是否正常
3.協(xié)商參數(shù),通信雙方共同確認(rèn)一些通信中的必備的參數(shù)數(shù)值
LISTEN狀態(tài):服務(wù)器在
new ServerSocket就會(huì)進(jìn)入到LISTEN表示在監(jiān)聽(tīng)這個(gè)窗口,端口上過(guò)來(lái)的客戶端就可以accept。
ESTABUSHED:客戶端和服務(wù)器之間連接建立好了,可以進(jìn)行通信了。

我們可以使用netstat -ano來(lái)查看狀態(tài)。
TCP斷開(kāi)連接(四次揮手)

1):客戶端告訴服務(wù)器,我要和你斷開(kāi)連接,請(qǐng)你把我刪了;
2):服務(wù)器回應(yīng)收到;
3):服務(wù)器也告訴客戶端我也和你斷開(kāi)連接,請(qǐng)你也把我刪了;
4):客戶端回應(yīng)收到;
客戶端和服務(wù)器都會(huì)把對(duì)方的信息(之前保存對(duì)方的ip和端口)刪除掉,刪除完了就斷開(kāi)連接了;
之所以叫做四次揮手,是因?yàn)橹虚g兩次相互,是不一定觸發(fā)合并的;
而三次握手本質(zhì)區(qū)別在于四次揮手不是完全由操作系統(tǒng)內(nèi)核完成的,而是和應(yīng)用層代碼有關(guān)系。合并場(chǎng)景是特殊情況,優(yōu)化手段,所以還是把TCP的斷開(kāi)連接稱為“四次揮手”;
那么合并的情況是什么?
比如,收到FIN觸發(fā)close之間,間隔的時(shí)間本身就不長(zhǎng),再加上上一個(gè)ack延遲應(yīng)答了,這時(shí)就可以把這兩數(shù)據(jù)合并了;
2.3.1:CLOSE_WAIT:
被動(dòng)一方斷開(kāi)連接進(jìn)入的狀態(tài),收到對(duì)方發(fā)來(lái)的FIN,就會(huì)返回ACK同時(shí)進(jìn)入CLOSE_WAIT狀態(tài)。(可以理解成wait close,等待關(guān)閉,等待應(yīng)用程序代碼,調(diào)用close),通常情況下存在時(shí)間比較短。
一般而言,對(duì)于服務(wù)器上出現(xiàn)大量的 CLOSE_WAIT 狀態(tài), 原因就是服務(wù)器沒(méi)有正確的關(guān)閉 socket, 導(dǎo)致四次揮手沒(méi)有正確完成. 這是一個(gè) BUG. 只需要加上對(duì)應(yīng)的 close 即可解決問(wèn)題.
通過(guò)cmd輸入
netstat -an可以來(lái)查看這個(gè)狀態(tài)。
2.3.2:TIME_WAIT:
(類似于線程中TIME_WAITING狀態(tài),有時(shí)間限制的等待),主動(dòng)發(fā)起斷開(kāi)連接一方進(jìn)入的狀態(tài),我方發(fā)起FIN,對(duì)方返回ACK,對(duì)方發(fā)起FIN,我方返回ACK同時(shí)進(jìn)入TIME_WAIT狀態(tài) 。
想?想, 為什么是TIME_WAIT的時(shí)間是2MSL?
- MSL是TCP報(bào)文的最大生存時(shí)間, 因此TIME_WAIT持續(xù)存在2MSL的話
- 就能保證在兩個(gè)傳輸方向上的尚未被接收或遲到的報(bào)文段都已經(jīng)消失(否則服務(wù)器立刻重啟, 可能會(huì)收到來(lái)自一個(gè)進(jìn)程的遲到的數(shù)據(jù), 但是這種數(shù)據(jù)很可能是錯(cuò)誤的);
- 同時(shí)也是在理論上保證最后一個(gè)報(bào)文可靠到達(dá)(假設(shè)最后一個(gè)ACK丟失, 那么服務(wù)器會(huì)再重新再發(fā)一個(gè)FIN. 這時(shí)雖然客戶端的進(jìn)程不在了, 但是TCP連接還在, 仍然可以重發(fā)LAST_ACK);
其他幾個(gè)關(guān)鍵的狀態(tài):
LISTEN:手機(jī)開(kāi)機(jī),信號(hào)良好,可以隨時(shí)打電話。
ESTABISHED:連接已經(jīng)建立,電話接聽(tīng),可以說(shuō)話了。
TCP的狀態(tài)轉(zhuǎn)換匯總:

2.4:滑動(dòng)窗口
如果我們對(duì)每一個(gè)發(fā)送的數(shù)據(jù)段,都給一個(gè)ACK確認(rèn)應(yīng)答,收到了ACK在發(fā)送下一個(gè)數(shù)據(jù)段,這樣就會(huì)造成問(wèn)題—性能比較差,特別是數(shù)據(jù)返回時(shí)間很長(zhǎng)的時(shí)候。

面對(duì)性能比較低,為我們可以一次發(fā)送多條數(shù)據(jù),這樣就可以大大提升性能(實(shí)際就是將多個(gè)段的等待時(shí)間重疊在一起了)。

當(dāng)我們批量發(fā)送多少數(shù)據(jù)不需要等待的,就稱之為“窗口大小”,當(dāng)收到一個(gè)ACK時(shí)候就在發(fā)送一組數(shù)據(jù),窗口再往后移動(dòng)一格,此時(shí)這個(gè)窗口一直在往右平移,即我們就稱之為“滑動(dòng)窗口”。

操作系統(tǒng)為了維護(hù)這個(gè)滑動(dòng)窗口,需要開(kāi)辟發(fā)送緩沖區(qū)來(lái)記錄當(dāng)前還有哪些數(shù)據(jù)還有應(yīng)答,只有確認(rèn)應(yīng)答過(guò)的數(shù)據(jù),才能從緩沖區(qū)刪掉。
這里的窗口越大,即網(wǎng)絡(luò)的吞吐率越高。
那么如果這里出現(xiàn)了丟包—ACK丟了,怎么進(jìn)行重傳的?

這種情況的丟包沒(méi)啥大要緊關(guān)系,因?yàn)榭梢酝ㄟ^(guò)后面的ACK進(jìn)行確認(rèn)。即當(dāng)1001這個(gè)ACK返回的丟包了,可以通過(guò)收到2001這個(gè)ACK來(lái)確認(rèn)接收方收到了1-1000的數(shù)據(jù)。
但是如果數(shù)據(jù)包丟了。

- 當(dāng)1001-2000報(bào)文段丟失后,發(fā)送端就會(huì)一直收到1001這樣的ACK
- 如果發(fā)送端連續(xù)三次收到了同樣一個(gè)1001這樣的應(yīng)答,就會(huì)將數(shù)據(jù)1001-2000重新發(fā)送;
- 這個(gè)時(shí)候接收端收到1001之后,再次返回的ACK就是7001(這是因?yàn)?001-7000)接收端已經(jīng)就已經(jīng)收到了,被放到了接收端操作系統(tǒng)內(nèi)核的接收緩沖區(qū)里面了;
這種機(jī)制被稱之為“高速重發(fā)控制”(也叫“快重傳”);
2.5:流量控制
接收端處理數(shù)據(jù)的速度是有限的,如果發(fā)送端發(fā)的太快,導(dǎo)致接收端的緩沖區(qū)滿了,這個(gè)時(shí)候發(fā)送端繼續(xù)發(fā)送,就會(huì)造成丟包,繼而引起丟包重傳等等一系列連鎖反應(yīng)。所以TCP支持根據(jù)接收端的處理能力,來(lái)決定發(fā)送端的發(fā)送速度,這個(gè)機(jī)制就叫做流量控制(Flow Control);
- 接受端將自己可以接受的緩沖區(qū)大小放入TCP首部中的“窗口大小”字段,通過(guò)ACK端通知發(fā)送端;
- 窗口大小字段越大,說(shuō)明網(wǎng)絡(luò)吞吐量越高;
- 當(dāng)接收端發(fā)現(xiàn)自己的緩沖區(qū)要滿了,就會(huì)將窗口大小設(shè)置成一個(gè)更小的值通知發(fā)送給發(fā)送端;
- 發(fā)送端接收到這個(gè)窗口后,就會(huì)減慢自己的發(fā)送速度;
- 如果接收端緩沖區(qū)滿了,就會(huì)將窗口大小設(shè)置為0,這時(shí)發(fā)送方不在發(fā)送數(shù)據(jù),但是需要定期發(fā)送一個(gè)窗口探測(cè)數(shù)據(jù)段,把接收端窗口大小告訴給發(fā)送端;

這里TCP首部有一個(gè)16位的窗口字段,就是存放了窗口大小信息;這里有一個(gè)問(wèn)題16位數(shù)字最大表示是65535,那么TCP的窗口大小最大也是65535字節(jié)嗎?實(shí)際上,TCP首部40字節(jié)選項(xiàng)中還包含了一個(gè)窗口擴(kuò)大因子M,實(shí)際窗口的大小是窗口字段的值左移M位;
2.6:擁塞控制
雖然TCP有了滑動(dòng)窗口這個(gè)神器,能夠高效可靠的發(fā)送大量的數(shù)據(jù),但是如果在剛開(kāi)始階段就發(fā)送大量的數(shù)據(jù),可能會(huì)引發(fā)問(wèn)題。因?yàn)榫W(wǎng)絡(luò)上有很多計(jì)算機(jī),看你當(dāng)前的網(wǎng)絡(luò)就是已經(jīng)比較擁堵,在不清楚當(dāng)前網(wǎng)絡(luò)的狀態(tài)下,貿(mào)然發(fā)送大量數(shù)據(jù),會(huì)引起問(wèn)題。
TCP引入慢啟動(dòng)機(jī)制,先發(fā)送少量數(shù)據(jù),先探探路在摸清楚當(dāng)前網(wǎng)絡(luò)的擁堵?tīng)顟B(tài),再?zèng)Q定按照多大的速度傳輸數(shù)據(jù)。

此時(shí)我們的擁塞窗口就起到了作用,剛開(kāi)始發(fā)送,定義擁塞窗口大小為1,每次收到一個(gè)ACK應(yīng)答,擁塞窗口就加1,每次發(fā)送數(shù)據(jù)的時(shí)候,將擁塞窗口和接收端主機(jī)反饋的窗口大小作比較,取較小的值作為實(shí)際發(fā)送端的窗口大??;
擁塞窗口增長(zhǎng)速度是指數(shù)級(jí)別的,慢啟動(dòng)只是初始時(shí)候慢,其增長(zhǎng)速度是非常快的。
為了使增長(zhǎng)的速度不那么快,就不能是擁塞窗口單純的加倍;我們引入一個(gè)叫做慢啟動(dòng)的閾值,當(dāng)擁塞窗口超過(guò)這個(gè)閾值的時(shí)候,就不再按照指數(shù)方式增長(zhǎng),而是按照線性方式增長(zhǎng);當(dāng)TCP啟動(dòng)的時(shí)候慢啟動(dòng)閾值等于窗口最大值,在每次超時(shí)重發(fā)的時(shí)候,慢啟動(dòng)閾值會(huì)變成原來(lái)的一半,同時(shí)擁塞窗口的值置為1;少量的丟包,我們認(rèn)為是出發(fā)超時(shí)重傳,大量的丟包,我們就認(rèn)為網(wǎng)絡(luò)擁塞;
當(dāng)TCP通信開(kāi)始后,網(wǎng)絡(luò)吞吐量會(huì)逐漸上升;隨著網(wǎng)絡(luò)發(fā)生擁堵,吞吐量會(huì)立即下降;

擁塞控制歸根結(jié)底是TCP協(xié)議想盡可能快的把數(shù)據(jù)傳輸給對(duì)方,但又要避免給網(wǎng)絡(luò)造成太大的壓力的折中方案。
2.7:延時(shí)應(yīng)答
承接滑動(dòng)窗口,在可靠性的前提下盡量讓窗口大一些,最終讓傳輸效率提高一些。不立即返回ACK。而是稍微等一等,就相當(dāng)于給接收方留出一些時(shí)間,好能夠多消費(fèi)一些,使接收緩沖區(qū)的剩余空間更大一點(diǎn)!
如果接收數(shù)據(jù)的主機(jī)立刻返回ACK應(yīng)答,此時(shí)返回的窗口可能偏小。舉個(gè)直觀例子:
- 假設(shè)接收端緩沖區(qū)總大小為1M,一次收到500K數(shù)據(jù)后立刻應(yīng)答,返回的窗口僅為500K;
- 但實(shí)際處理端可能速度很快,10ms內(nèi)就把這500K數(shù)據(jù)從緩沖區(qū)消費(fèi)完畢;
- 這種情況下,接收端遠(yuǎn)沒(méi)達(dá)到處理極限,就算窗口再放大也能應(yīng)對(duì)。
如果接收端稍微延遲一點(diǎn)應(yīng)答(比如等待200ms),此時(shí)緩沖區(qū)已空閑,返回的窗口大小就能達(dá)到1M。要知道:窗口越大,網(wǎng)絡(luò)吞吐量越高,傳輸效率也越強(qiáng)。我們的核心目標(biāo),就是在避免網(wǎng)絡(luò)擁塞的前提下,最大化傳輸效率。
不過(guò),并非所有包都能延遲應(yīng)答,需要遵守兩個(gè)關(guān)鍵限制:
- 數(shù)量限制:每接收N個(gè)包,必須應(yīng)答一次;
- 時(shí)間限制:超過(guò)最大延遲時(shí)間,必須應(yīng)答一次。
具體的N值和超時(shí)時(shí)間,不同操作系統(tǒng)會(huì)有差異,通常N取2,超時(shí)時(shí)間默認(rèn)200ms。

2.8:捎帶應(yīng)答
網(wǎng)絡(luò)通信中,經(jīng)常是“一問(wèn)一答的模型”,客戶端發(fā)起request,服務(wù)器返回response

由于TCP會(huì)延時(shí)應(yīng)答,當(dāng)推遲后就正好趕上服務(wù)器返回的響應(yīng)了,這樣就可以直接把ACK報(bào)文和響應(yīng)數(shù)據(jù),做成一個(gè)tcp數(shù)據(jù)報(bào)返回給客戶端了。此時(shí)基于延時(shí)應(yīng)答的基礎(chǔ)上,提高傳輸效率的方案==》就叫做捎帶應(yīng)答

捎帶應(yīng)答,既取決于延時(shí)應(yīng)答,有和應(yīng)用程序的處理邏輯有關(guān),捎帶應(yīng)答不是100%會(huì)觸發(fā)的,但TCP會(huì)盡可能的進(jìn)行捎帶應(yīng)答。
2.9:面向字節(jié)流
創(chuàng)建TCP socket時(shí),內(nèi)核會(huì)同步生成發(fā)送緩沖區(qū)與接收緩沖區(qū),二者是TCP數(shù)據(jù)傳輸?shù)暮诵闹虚g載體,支撐起連接的全雙工特性。
數(shù)據(jù)寫(xiě)入流程:調(diào)用write時(shí),數(shù)據(jù)并非直接發(fā)送至網(wǎng)絡(luò),而是先寫(xiě)入發(fā)送緩沖區(qū)。若數(shù)據(jù)量過(guò)大,內(nèi)核會(huì)自動(dòng)拆分為多個(gè)TCP數(shù)據(jù)包分批發(fā)送;若數(shù)據(jù)量過(guò)小,則會(huì)在緩沖區(qū)暫存,等待達(dá)到合適大小或滿足觸發(fā)條件后再批量發(fā)送,以此提升傳輸效率。
數(shù)據(jù)讀取流程:接收方的數(shù)據(jù)包先由網(wǎng)卡驅(qū)動(dòng)接收,再傳入內(nèi)核的接收緩沖區(qū),應(yīng)用程序通過(guò)調(diào)用read,即可從該緩沖區(qū)中獲取數(shù)據(jù)。
緩沖區(qū)的存在讓TCP的讀寫(xiě)操作完全解耦,無(wú)需一一匹配:
- 寫(xiě)入100字節(jié)時(shí),可一次調(diào)用
write寫(xiě)入全部,也可分100次調(diào)用、每次寫(xiě)1字節(jié); - 讀取100字節(jié)時(shí),無(wú)需關(guān)注發(fā)送方的寫(xiě)入方式,既可一次
read讀取全部,也可分100次、每次讀1字節(jié)。
正是這兩個(gè)獨(dú)立緩沖區(qū)的存在,使得單個(gè)TCP連接能同時(shí)進(jìn)行讀、寫(xiě)操作,這一特性被稱為全雙工。
2.10:粘包問(wèn)題
以下面例子為例:

去掉報(bào)頭后,把載荷內(nèi)容梵高一個(gè)接收緩沖區(qū)里面;

則read的可能性:
1):a a a b b b c c c
2):aa ab bb ccc
3):aaa bbb ccc
4):aaab bbcc c
5):aa abb bcc c
6):aaa bbbc cc
··········等等
粘包,粘的是應(yīng)用層的數(shù)據(jù)包
由于TCP字節(jié)流的特性,收到多個(gè)TCP數(shù)據(jù)報(bào)的時(shí)候把所有的載荷都混到了一起放到接收緩沖區(qū)里面。
那么如何解決粘包問(wèn)題:
- 通過(guò)特殊的分隔符,來(lái)作為包邊界的區(qū)分。(比如:約定每個(gè)應(yīng)用層數(shù)據(jù)包都以 ;結(jié)尾)但是這里要注意的是包里面內(nèi)容不能包括 ;這個(gè)字符,所以需要找到合適的分隔符,確保這個(gè)符號(hào)不會(huì)再正文中重復(fù)出現(xiàn);
- 在應(yīng)用層數(shù)據(jù)包開(kāi)頭的地方,通過(guò)固定長(zhǎng)度,約定整個(gè)應(yīng)用層數(shù)據(jù)包的長(zhǎng)度。


應(yīng)用程序在read的時(shí)候,先固定read2個(gè)字節(jié),看看2個(gè)字節(jié)里面的內(nèi)容是啥?(發(fā)現(xiàn)是3接下來(lái)在read3個(gè)字節(jié),就讀到了aaa這樣就是完整的應(yīng)用層數(shù)據(jù)保包了)。
粘包問(wèn)題只針對(duì)于字節(jié)流的傳輸,對(duì)于文件操作(使用文件存儲(chǔ)多個(gè)結(jié)構(gòu)化數(shù)據(jù),也是可能涉及到粘包問(wèn)題的)例如:
通過(guò)文件,保存若干學(xué)生信息
//簡(jiǎn)寫(xiě)代碼
class Student{
name;
id;
age;
classId;
···
}
此時(shí)約定成每個(gè)學(xué)生信息占一行使用 \n 作為結(jié)束標(biāo)記即分隔符;
對(duì)于UDP來(lái)說(shuō),就不存在粘包問(wèn)題;


這里應(yīng)用層每次調(diào)用recv得到的就是一個(gè)完整的DatagramPacket ,也就對(duì)應(yīng)一個(gè)完整應(yīng)用層數(shù)據(jù)包;
2.11:異常情況
- 1.進(jìn)程崩潰
意味著對(duì)應(yīng)的文件描述符就被關(guān)閉了(調(diào)用close,關(guān)閉進(jìn)程)只要進(jìn)程退出了,都會(huì)釋放PCB,釋放文件描述表。
- 2.主機(jī)關(guān)機(jī)(正常流程)
正常流程下主機(jī)關(guān)機(jī),就會(huì)先殺死所有的進(jìn)程,此時(shí)也會(huì)觸發(fā)FIN,進(jìn)而進(jìn)入四次揮手;如果關(guān)機(jī)速度比較慢,有很大的可能四次揮手揮完了;如果關(guān)機(jī)速度表較快,四次揮手就會(huì)沒(méi)有揮完剛發(fā)FIN,機(jī)器就關(guān)閉了; 對(duì)端可以正常返回ACK,也會(huì)繼續(xù)正常發(fā)送FIN,這里的FIN就沒(méi)有收到ACK,嘗試重傳幾次FIN;還是沒(méi)有收到ACK,對(duì)端就會(huì)直接斷開(kāi)連接即刪除之前保存的對(duì)端信息。
- 3.主機(jī)掉電(直接拔掉電源)
這里就會(huì)來(lái)不及發(fā)起FIN
1):如果掉電的一方是接收方,對(duì)方是發(fā)送方。對(duì)方會(huì)繼續(xù)發(fā)送數(shù)據(jù),沒(méi)有ack就會(huì)超時(shí)重傳在還沒(méi)有ack繼續(xù)超時(shí)重傳;到了一定的程度后,發(fā)送方就會(huì)發(fā)送一個(gè)復(fù)位報(bào)文 表示放棄鏈接。
2):如果掉電一方是發(fā)送方,對(duì)方是接收方;這里就引入了一個(gè)心跳包 :接收方會(huì)周期性的和發(fā)送方交換“心跳包”;
A給B發(fā)送一個(gè)無(wú)業(yè)務(wù)數(shù)據(jù)的報(bào)文,B就給A返回一個(gè)ACK。如果對(duì)方有應(yīng)答就表示對(duì)方是正常工作的;如果心跳包沒(méi)有應(yīng)答,就可以認(rèn)為對(duì)方掛了,這時(shí)候就可以單方面的釋放連接了;
- 網(wǎng)線斷開(kāi)
對(duì)于是發(fā)送方來(lái)說(shuō),沒(méi)有ack=》超時(shí)重傳=》仍然沒(méi)有ack=》超時(shí)重傳=》達(dá)到一定程度,放棄連接;
對(duì)于是接收方來(lái)說(shuō),周期性出發(fā)心跳包=》發(fā)現(xiàn)對(duì)端下線=》放棄連接;
三:UDP和TCP的區(qū)別
雖然TCP是可靠連接,但不能簡(jiǎn)單判定其絕對(duì)優(yōu)于UDP——二者作為T(mén)CP/IP協(xié)議族的核心傳輸層協(xié)議,優(yōu)缺點(diǎn)各有側(cè)重,需結(jié)合具體場(chǎng)景選擇,而非絕對(duì)化比較。
TCP的核心優(yōu)勢(shì)是可靠傳輸:通過(guò)三次握手建立連接、四次揮手關(guān)閉連接,搭配重傳、排序、流量控制、擁塞控制等機(jī)制,能確保數(shù)據(jù)無(wú)丟失、無(wú)重復(fù)、按序到達(dá)。其適用場(chǎng)景集中在對(duì)數(shù)據(jù)完整性要求高的場(chǎng)景,比如文件傳輸、重要數(shù)據(jù)同步、支付轉(zhuǎn)賬、狀態(tài)更新等,缺點(diǎn)是協(xié)議開(kāi)銷大、傳輸延遲較高,且不支持廣播/組播。
UDP的核心優(yōu)勢(shì)是高效實(shí)時(shí):無(wú)需建立連接,頭部開(kāi)銷極小,數(shù)據(jù)發(fā)送直接高效,延遲低、吞吐量高,且支持廣播和組播。它適用于對(duì)實(shí)時(shí)性、傳輸速度要求優(yōu)先于可靠性的場(chǎng)景,比如早期QQ聊天、視頻/語(yǔ)音通話、直播、游戲數(shù)據(jù)傳輸?shù)?,缺點(diǎn)是無(wú)可靠傳輸保障,數(shù)據(jù)可能丟失、亂序,需應(yīng)用層自行處理可靠性需求。
歸根結(jié)底,TCP和UDP是程序員應(yīng)對(duì)不同需求的工具,選擇的核心是匹配場(chǎng)景訴求:追求可靠選TCP,追求高效實(shí)時(shí)選UDP。
最后再分享一道經(jīng)典面試題給大家哈!----》用UDP實(shí)現(xiàn)可靠傳輸!
我們可以參考TCP的可靠性機(jī)制,在應(yīng)用層實(shí)現(xiàn)類似的邏輯!
如:引入序列號(hào),保證數(shù)據(jù)順序;
引入確認(rèn)應(yīng)答,確保對(duì)端收到了數(shù)據(jù);
引入超時(shí)重傳,如果隔一段時(shí)間沒(méi)有收到應(yīng)答,就重新發(fā)送數(shù)據(jù)
引入滑動(dòng)窗口,設(shè)定發(fā)送窗口大小,無(wú)需等待前一個(gè)包的 ACK 即可發(fā)送后續(xù)包,避免 “發(fā)一個(gè)等一個(gè)” 的低效問(wèn)題,平衡可靠性與傳輸效率。
以及流量控制和擁塞控制;
總結(jié)
到此這篇關(guān)于JavaEE初階教程之UDP協(xié)議和TCP協(xié)議的文章就介紹到這了,更多相關(guān)Java UDP協(xié)議和TCP協(xié)議內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot框架阿里開(kāi)源低代碼工具LowCodeEngine
這篇文章主要為大家介紹了springboot框架阿里開(kāi)源低代碼LowCodeEngine工具使用詳解有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Java使用Validation自定義Double類型屬性校驗(yàn)
這篇文章主要為大家詳細(xì)介紹了Java如何使用Validation自定義Double類型屬性校驗(yàn),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2024-11-11
IDEA開(kāi)發(fā)并部署運(yùn)行WEB項(xiàng)目全過(guò)程
文章介紹了WEB項(xiàng)目標(biāo)準(zhǔn)結(jié)構(gòu)及部署方法,涵蓋目錄劃分(如WEB-INF、classes、lib)、核心文件(web.xml、index.html),以及三種部署方式:直接放置webapps、war包部署、自定義路徑配置,同時(shí)說(shuō)明了IDEA中如何關(guān)聯(lián)Tomcat、配置項(xiàng)目結(jié)構(gòu)及部署原理2025-07-07
基于mybatis查詢結(jié)果映射不到對(duì)象的處理
這篇文章主要介紹了mybatis查詢結(jié)果映射不到對(duì)象的處理方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
SpringBoot中Jar包沖突在線檢測(cè)的方法詳解
在 Spring Boot 項(xiàng)目開(kāi)發(fā)和運(yùn)維中,Jar 包沖突是讓開(kāi)發(fā)者最頭疼的問(wèn)題之一,本文主要為大家詳細(xì)介紹了如何使用SpringBoot在線檢測(cè)Jar包沖突,需要的小伙伴可以了解下2025-09-09
Java Web實(shí)現(xiàn)自動(dòng)登陸功能
這篇文章主要為大家詳細(xì)介紹了Java Web實(shí)現(xiàn)自動(dòng)登陸功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Java中toString方法的深度解析與應(yīng)用場(chǎng)景詳解
這篇文章主要介紹了Java中的toString方法及其重寫(xiě)的重要性和注意事項(xiàng),包括信息的完整性、簡(jiǎn)潔性、格式的統(tǒng)一性、避免性能問(wèn)題和遞歸循環(huán)等問(wèn)題,文中將解決的辦法介紹的非常詳細(xì),需要的朋友可以參考下2025-04-04
java實(shí)現(xiàn)計(jì)算器加法小程序(圖形化界面)
這篇文章主要介紹了Java實(shí)現(xiàn)圖形化界面的計(jì)算器加法小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
Trie樹(shù)(字典樹(shù))的介紹及Java實(shí)現(xiàn)
Trie樹(shù),又稱字典樹(shù)或前綴樹(shù),關(guān)于它的結(jié)構(gòu)就不詳細(xì)介紹了。Trie樹(shù)在單詞統(tǒng)計(jì)、前綴匹配等很多方面有很大用處。下面這篇文章主要介紹了Trie樹(shù),以及Java實(shí)現(xiàn)如何Trie樹(shù),有需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-02-02

