netty系列之:netty中常用的字符串編碼解碼器

簡介

字符串是我們程序中最常用到的消息格式,也是最簡單的消息格式,但是正因為字符串string太過簡單,不能附加更多的信息,所以在netty中選擇的是使用byteBuf作為最底層的消息傳遞載體。

雖然底層使用的ByteBuf,但是對于程序員來說,還是希望能夠使用這種最簡單的字符串格式,那么有什么簡單的方法嗎?

netty中的字符串編碼解碼器

為了解決在netty的channel中傳遞字符串的問題,netty提供了針對于字符串的編碼和解碼器,分別是StringEncoder和StringDecoder。

我們來看下他們是怎么在程序中使用的,首先是將StringDecoder和StringEncoder加入channelPipeline中:

   ChannelPipeline pipeline = ...;
  
   // Decoders
   pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(80));
   pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
  
   // Encoder
   pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));

注意,這里我們在使用StringDecoder之前還調(diào)用了LineBasedFrameDecoder,先把數(shù)據(jù)按行進行分割,然后再進行字符串的讀取。

那么有人要問了,decoder加入了LineBasedFrameDecoder預(yù)處理,為什么寫入的時候沒有添加行的分割符呢?

事實上這里有兩種處理方式,第一種就是在向channel中寫入字符串的時候,手動加上行分隔符,如下所示:

   void channelRead(ChannelHandlerContext ctx, String msg) {
       ch.write("Did you say '" + msg + "'?\n");
   }

如果不想每次都在msg后面加上換行符,那么可以將StringEncoder替換成為LineEncoder,上面的pipeline就變成下面這樣:

   ChannelPipeline pipeline = ...;
  
   // Decoders
   pipeline.addLast("frameDecoder", new LineBasedFrameDecoder(80));
   pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
  
   // Encoder
   pipeline.addLast("lineEncoder", new LineEncoder(LineSeparator.UNIX, CharsetUtil.UTF_8));

這樣,我們在handler中就不需要手動添加換行符了,如下所示:

   void channelRead(ChannelHandlerContext ctx, String msg) {
       ch.write("Did you say '" + msg + "'?");
   }

不同平臺的換行符

在unix和windows平臺傳遞過文本文件的朋友可能會遇到一個問題,就是windows創(chuàng)建的文本文件,如果在unix下面打開的話,會發(fā)現(xiàn)每行后面多出了一個特殊字符,這是因為unix和windows平臺定義的換行符是不同的。

在unix平臺通常使用"\n"來換行,而在windows平臺則使用""\r\n"來換行。

java程序因為是跨平臺的,寫出的程序可能運行在unix平臺,也可能運行在windows平臺,所以我們需要有一個辦法來獲取平臺的換行符,netty提供了一個LineSeparator的類來完成這個工作。

LineSeparator中有三個換行符的定義,分別是:

   public static final LineSeparator DEFAULT = new LineSeparator(StringUtil.NEWLINE);

    public static final LineSeparator UNIX = new LineSeparator("\n");

    public static final LineSeparator WINDOWS = new LineSeparator("\r\n");

UNIX和WINDOWS很好理解,他們就是我們剛剛講到的不同的平臺。

那么什么是DEFAULT呢?DEFAULT中傳入的NEWLINE,實際上是從系統(tǒng)屬性中獲取到的,如果沒有獲取到,則使用默認(rèn)的"\n"。

public static final String NEWLINE = SystemPropertyUtil.get("line.separator", "\n");

字符串編碼的實現(xiàn)

上面我們講到了和字符串編碼解碼相關(guān)的類分別是StringEncoder,LineEncoder和StringDecoder,我們來詳細看下這三個類的實現(xiàn)。

首先是StringEncoder,StringEncoder繼承了MessageToMessageEncoder:

public class StringEncoder extends MessageToMessageEncoder<CharSequence> 

泛型中的CharSequence表示StringEncoder要encode的對象是CharSequence,也就是字符序列。

雖然大家常用String這個類,但是不一定大家都知道String其實是CharSequence的子類,所以StringEncoder也可以編碼字符串。

StringEncoder的編碼邏輯很簡單,將傳入的字符串msg轉(zhuǎn)換成為CharBuffer,然后調(diào)用ByteBufUtil的encodeString方法就可以轉(zhuǎn)換成為ByteBuf,并加入out中去:

    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {
        if (msg.length() == 0) {
            return;
        }
        out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset));
    }

LineEncoder和StringEncoder很類似,它也是繼承自MessageToMessageEncoder:

public class LineEncoder extends MessageToMessageEncoder<CharSequence> 

不同之處在于encoder方法:

    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {
        ByteBuf buffer = ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset, lineSeparator.length);
        buffer.writeBytes(lineSeparator);
        out.add(buffer);
    }

ByteBufUtil的encodeString多了一個lineSeparator.length參數(shù),用來預(yù)留lineSeparator的位置,然后在返回的ByteBuf后面加上lineSeparator作為最終的輸出。

StringDecoder是和StringEncoder相反的過程:

public class StringDecoder extends MessageToMessageDecoder<ByteBuf> 

這里的ByteBuf表示的是要解碼的對象是ByteBuf,我們看下他的解碼方法:

    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        out.add(msg.toString(charset));
    }

直接調(diào)用msg.toString方法即可將ByteBuf轉(zhuǎn)換成為字符串。

總結(jié)

以上就是netty中對字符串的編碼解碼器,通過使用這幾個編碼解碼器可以大大簡化我們的工作。

本文已收錄于 http://www.flydean.com/14-6-netty-codec-string/

最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!

歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù),更懂你!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 字符串是我們程序中最常用到的消息格式,也是最簡單的消息格式,但是正因為字符串string太過簡單,不能附加更多的信...
    Java編程日記閱讀 921評論 0 0
  • 1 Netty編解碼框架 在上一節(jié)中,我們提到TCP的粘包、拆包問題,可以通過自定義通信協(xié)議的方式來解決。通信協(xié)議...
    沉淪2014閱讀 3,214評論 0 8
  • 簡介 netty中的數(shù)據(jù)是通過ByteBuf來進行傳輸?shù)?,一個ByteBuf中可能包含多個有意義的數(shù)據(jù),這些數(shù)據(jù)可...
    flydean程序那些事閱讀 437評論 0 1
  • 每個網(wǎng)絡(luò)應(yīng)用程序都必須定義如何解析在兩個節(jié)點之間來回傳輸?shù)脑甲止?jié),以及如何將其和目標(biāo)應(yīng)用程序的數(shù)據(jù)格式做相互轉(zhuǎn)換...
    wo883721閱讀 652評論 0 0
  • 前言 在了解Netty編解碼之前,先了解Java的編解碼: 編碼(Encode)稱為序列化, 它將對象序列化為字節(jié)...
    小波同學(xué)閱讀 3,352評論 0 1

友情鏈接更多精彩內(nèi)容