国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

基于定長消息的java nio半包粘包處理

Loong_T / 2268人閱讀

摘要:接收方只需要等待,直到讀到確定數量的字節,然后處理即可。而這個字節流的前個字節用于表示對象的長度,接下來的字節就是傳輸的對象的字節流,最后不夠最大長度的用任意字節進行填充即可。

什么是tcp半包粘包?
簡單來講就是接收到的tcp包并不一定是一個完整的包。
它可能是1個包的一部分,也可能是多個完整包加上1個包的一部分。
為什么?
因為tcp的定義是面向字節流的傳輸協議,所以操作系統實現這個協議的時候,只保證字節的正確傳輸,而至于字節的應用層語義(可能這個字節是個分隔符,也可能這個字節和周圍3個字節組成一個int,代表類的某個字段),操作系統是不管的。
比如下面這個例子(基于java):

public class Account {
    private int accountnum;
    private double balance;
    private int num;
}

要傳輸這個Account對象,實際上就是傳輸它的三個字段accountnum,balance和num,它們的大小分別是4,8,4個字節。當傳輸時,實現tcp協議的系統只負責把這16個字節傳輸到接收方,而不知道這些字節的含義。比如前4個字節是accountnum,然而這是應用層的語義,實現tcp協議的系統并不知道,也不需要知道,因為tcp規范里就沒規定需要知道上層的語義。
那么接收方如何接收呢?
對于這個例子實在是太簡單了,接收方只需要每次都接收16個字節就能保證每一次都能得到一個完整的Account對象,連分隔符都不需要。在已知確定傳輸對象長度(字節數目)的時候:

即使接收到的tcp包并不一定是一個完整的包。
接收方只需要等待,直到讀到確定數量的字節,然后處理即可。
比如現在只傳輸了4個字節,我們知道16個字節才能組成完整的Account對象,那么再讀12個字節后進行處理即可。

這個例子有什么意義?
根據這個例子受到啟發,只要傳輸對象的長度是確定的,那么接收端很容易就能夠對傳輸對象進行解析(就是處理tcp粘包半包)。
然而對象的長度是確定的嗎?往往都不是,比如一個上面的對象現在加一個String類型的成員字段,這個String字段變成字節的時候長度就是未知的,但這并不影響我們把它變成定長的對象。
HOW?
設置最大傳輸長度,每次都接收最大傳輸長度的字節流。而這個字節流的前4個字節用于表示對象的長度,接下來的字節就是傳輸的對象的字節流,最后不夠最大長度的用任意字節進行填充即可。
比如:

public class Account {
private int accountnum;
private double balance;
private int num;
private String extra; 
}

對于增加了String類型字段extra的新Account類來說,它的一個對象長度是不確定的,現在要傳輸它該怎么辦?設置最大的長度為400字節,前4個字節存儲實例對象的長度x,之后的x個字節為對象,最后沒用到的位置用0x0(也就是0)來填充。比如下圖所示:

需要注意的是,前四個字節只是字節,并不是x,需要把這4個字節轉成int類型的變量,然后這個int變量對應的10進制數是x。
這個方式看起來具有很明顯的局限性?
長度是有限制的?比如一次只能傳輸最多400-4=396個字節的對象?
但是可以把超大的對象再次分開,每一次都只傳輸最大包(400)長度,然后再拼接即可解決。
比如現在設計這400個字節的存儲格式是這樣的:
前4個字節存儲這個對象總共被分成幾個最大傳輸的包,接著的4個字節存儲這是第幾個,然后是長度,然后是內容,最后是填充。

這樣看起來最大長度就解決了。
然而。。。基于java nio的傳輸適合傳輸大文件(巨長的字節流)嗎?
nio是什么原理?
是I/O多路復用,簡單來講就是我有一個叫做選擇器(Selector)的類不斷輪詢不同的連接(Socket)的i/o事件,發現了i/o事件就處理,處理時可以用Selector所在的線程,也可以另開啟一個線程。如果要用Selector的線程處理i/o事件,那么i/o的操作時間必須很短,否則可能會丟失消息,而如果開啟一個線程,i/o的時間也應該很短。why?因為如果i/o時間很長,并且線程很多,那么就退化成了bio的模型。。。那么就沒必要用nio了。。。
歸結起來就是nio就不適合多用戶傳輸大文件,否則必然退化為bio模型。
所以實際上不要考慮這種大文件的傳輸,如果要傳輸大文件還是用bio模型比較好,并且在bio的傳輸模式下java提供了對象的序列化和反序列化,這樣都不需要我們定義長度字段了。

具體的代碼
參考:https://github.com/ItCrazyer/...

說明一點的是,這個例子里傳輸的對象是不定長的字符串,不是一個定義的類(不是像上面的Account這種),并且使用了SelectionKey對象的attachment方法,來暫存數據,暫存數據存儲在TempData這個類的對象里,為什么?
因為雖然我們知道確定的長度(比如是600),并且據此處理定長的數據,但是一次傳來的數據很可能是好幾個定長的數據包,而且每一次我們都必須讀完,比如傳來了1300字節的數據,就必須1300字節都讀完,不能這一次i/o事件我只讀600,然后下一次i/o事件在讀接下來的700個字節,那是沒辦法做到的,因為i/o響應的是這一次可讀,并不響應你還有數據,所以這次不讀完下次就沒有了。。。

不過經過我測試。。。沒有讀完的數據再下一次仍然可以讀取到,并不會因為這次沒讀完下一次就沒了。。所以不加緩存處理也沒問題!!!

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/72139.html

相關文章

  • 徹底理解Netty,這一篇文章就夠了

    摘要:如果什么事都沒得做,它也不會死循環,它會將線程休眠起來,直到下一個事件來了再繼續干活,這樣的一個線程稱之為線程。而請求處理邏輯既可以使用單獨的線程池進行處理,也可以跟放在讀寫線程一塊處理。 Netty到底是什么 從HTTP說起 有了Netty,你可以實現自己的HTTP服務器,FTP服務器,UDP服務器,RPC服務器,WebSocket服務器,Redis的Proxy服務器,MySQL的P...

    yy13818512006 評論0 收藏0
  • 使用 LineBasedFrameDecoder 和 StringDecoder 解決半包粘包問題

    摘要:修改之前的服務端開發代碼修改為下面代碼綁定端口同步等待成功等待服務端監聽端口關閉主要修改了和方法和原理分析的工作原理是它依次遍歷中的可讀字節判斷看是否有或如果有就以此位置為結束位置從可讀索引到結束位置區間的字節就組成了一行它是以換行符為結束 修改之前的 Netty 服務端開發 代碼, 修改為下面代碼 public class TimeServer { public void ...

    HollisChuang 評論0 收藏0
  • 簡易RPC框架:基于 netty 協議編解碼

    摘要:概述在簡易框架需求與設計這篇文章中已經給出了協議的具體細節,協議類型為二進制協議,如下協議的解碼我們稱為,編碼我們成為,下文我們將直接使用和術語。直接貼代碼,參考前文提到的協議格式閱讀以下代碼協議編碼器 概述 在《簡易RPC框架:需求與設計》這篇文章中已經給出了協議的具體細節,協議類型為二進制協議,如下: ---------------------------------------...

    Loong_T 評論0 收藏0
  • netty

    摘要:設置每個數據包的大小如個字節,如果某個數據包不足個字節可能會出現丟包的情況,即該數據包未從一個端到另一個端,此時需要用空格或者既定的符號補充在數據包之間使用一些字符進行分割如號之類的,解析的時候先處理掉分隔符再拿到各個數據包就好了。 netty 概念: Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠...

    cfanr 評論0 收藏0
  • Java】幾道讓你拿offer面試題

    摘要:的方法,的默認實現會判斷是否是類型注意自動拆箱,自動裝箱問題。適應自旋鎖鎖競爭是下的,會經過用戶態到內核態的切換,是比較花時間的。在中引入了自適應的自旋鎖,說明自旋的時間不固定,要不要自旋變得越來越聰明。 前言 只有光頭才能變強 之前在刷博客的時候,發現一些寫得比較好的博客都會默默收藏起來。最近在查閱補漏,有的知識點比較重要的,但是在之前的博客中還沒有寫到,于是趁著閑整理一下。 文本的...

    張春雷 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<