1. 程式人生 > >netty學習筆記:從jdk NIO到netty作用

netty學習筆記:從jdk NIO到netty作用

之前一篇Java網路I/O  介紹了Java I/O,從NIO引出了netty.

netty作用:

1 封裝了I/O: 

底層的I/O 實現複雜,netty遮蔽了底層。更面向業務層的實現。

2 對資料格式的封裝

NIO只是封裝了I/O模型,並不關心資料格式。而netty對資料格式的封裝,更專注於業務。

支援常見的如pb,集成了HTTP 協議的request,response.

3 修復了JDK NIO層的已知bug.

4.解決了半包,粘包的問題

背景:

先說TCP:由於TCP協議本身的機制(面向連線的可靠地協議-三次握手機制)客戶端與伺服器會維持一個連線(Channel),資料在連線不斷開的情況下,可以持續不斷地將多個數據包發往伺服器,但是如果傳送的網路資料包太小,那麼他本身會啟用Nagle演算法(可配置是否啟用)對較小的資料包進行合併(基於此,TCP的網路延遲要UDP的高些)然後再發送(超時或者包大小足夠)。那麼這樣的話,伺服器在接收到訊息(資料流)的時候就無法區分哪些資料包是客戶端自己分開發送的,這樣產生了粘包;伺服器在接收到資料庫後,放到緩衝區中,如果訊息沒有被及時從快取區取走,下次在取資料的時候可能就會出現一次取出多個數據包的情況,造成粘包現象(本質的原因,對於TCP協議是基於資料

流(就是沒有界限沒有分割的一串資料))

UDP為啥沒有這個問題。UDP本身作為無連線的不可靠的傳輸協議(適合頻繁傳送較小的資料包),他不會對資料包進行合併傳送,他直接是一端傳送什麼資料,直接就發出去了,既然他不會對資料合併,每一個數據包都是完整的。即使分包後如下case,網路資料經過路由器,如果資料很小,沒有超過路由器的封包大小,就會直接直接經過路由器到達下一個路由器,一層一層最終到達目的地如果資料很大,這裡指一個傳送,超過了路由器的封包大小,那麼路由器就會把這個資料包進行拆分,比如拆分成A B C三個包,這三個包都沒有超過路由器的封包大小,到達下一個路由器的時候,TCP與UDP的區別就來了:
TCP收到A的時候,會resp通知源路由器,A到達,B C包依然如此,如果由於網路的各種原因,目的路由收到了A C,B沒有收到,TCP會要求源路由把B包重新發一次,直到ABC包目的路由都接受到了,那麼目的路由把ABC包重新組成起始包,繼續往下一個路由傳送,這就是TCP安全連線的由來,只要傳送,我就能保證目的一定能收到。
UDP則不是這樣,如果ABC包拆分之後,目的路由只收到AC,經過檢測,B沒有被收到,那麼此包就會被當作不完整,直接被丟棄。由於UDP沒有resp的通知過程,所以,UDP的傳輸效率要高一些,當然安全性也低一些。所以UDP不存在粘包問題。

當然TCP短指令也不存在粘包問題,因為傳送完一個指令連結就會斷開,不會繼續傳送。

常見的解決措施是:

1 加入特殊分隔符。這樣我們接收到資料後,如果出現結尾標識,即人為的將粘包分開,如果一個包中沒有出現結尾符,認為出現了分包,則等待下個包中出現後 組合成一個完整的資料包,這種方式適合於文字傳輸的數如:"\r\n"等特殊字元。

2 資料包中新增長度的方式,即在資料包中的固定位置封裝資料包的長度資訊;例如:第一位代表封包頭,第二位代表封型別,第三、四位代表封包的資料長度。然後後面是實際的資料內容。我們先把接收回來的資料寫入一個流中。然後分析其中是否有完整的資料包,如果有,將其從流中取出,並將這部分資料從流中清除。直到流中沒有完整的資料為止,以後接收回來的資料就將其寫入流的結尾處,並從頭繼續分析。直到結束。

不管怎麼辦,在Java NIO裡面,是需要自己動手去處理的。Netty 框架提供了許多解碼器的封裝,幫助我們解決半包粘包的問題。

FixedLengthFrameDecoder

定長解碼器,需要客戶端,服務端設定相同的frameLength:幀的固定長度

DelimiterBasedFrameDecoder

 特殊字元解碼器 需要約定特殊字元

等等。

參考:

https://blog.csdn.net/cherish_2012/article/details/41681853