第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Netty 教程 Netty 教程 如何自定義編解碼器

如何自定義編解碼器

1. 前言

上一節(jié)我們一節(jié)了解了什么是編碼解碼、序列化和反序列化了,并且留有一道思考題,本節(jié)內(nèi)容主要是深入解析該思考題。

思考題:能否把我們的編碼和解碼封裝成獨立的 Handler 呢?那么應(yīng)該如何去封裝呢?

2. 為什么要封裝獨立 Handler?

即使我們把編碼和解碼封裝成了方法,但是還是需要在 Handler 業(yè)務(wù)邏輯里面進(jìn)行手工調(diào)用,雖然看似不怎么影響,但是業(yè)務(wù) Handler 不夠純粹,應(yīng)該讓 Handler 只是專心的負(fù)責(zé)處理業(yè)務(wù)邏輯就好。

實例:

ch.pipeline().addLast(new MyEncoderHandler());//解碼Handler
ch.pipeline().addLast(new MyDecoderHandler());//編碼Handler
ch.pipeline().addLast(new MyBusiHandler());//業(yè)務(wù)Handler

public class MyBusiHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //1.接受參數(shù),可以直接強(qiáng)轉(zhuǎn)
        UserReq userReq=(UserReq)msg;

        //2.相應(yīng)數(shù)據(jù),直接寫對象
        UserRes res=new UserRes();
        res.setCode(0);
        res.setMsg("接受成功");
        ctx.writeAndFlush(res);
    }
}

通過以上的代碼,我們把編碼和解碼封裝成兩個獨立的 Handler,并且加入到 ChannelPipeline 里面進(jìn)行管理。在我們的業(yè)務(wù) Handler 里面就可以直接操作實體數(shù)據(jù),無需手工轉(zhuǎn)換成字節(jié)數(shù)組了。

思考:那么如何進(jìn)行封裝 Handler 呢?

3. StringDecoder 和 StringEncoder

3.1 簡單使用

StringDecoder 和 StringEncoder 是 Netty 為我們提供的專門針對普通字符串的解碼和編碼器,使用起來非常的簡單。

客戶端直接發(fā)送字符串。

實例:

ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new ClientTestHandler());

public class ClientTestHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
		//客戶端直接寫字符串,沒有任何的數(shù)據(jù)加工
        ctx.channel().writeAndFlush("hello world");
    }
}

服務(wù)端直接強(qiáng)轉(zhuǎn)字符串。

實例:

ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new ServerTestHandler());

public class ServerTestHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		//直接把msg轉(zhuǎn)換成String類型
        String str=msg.toString();
        System.out.println("str="+str);
    }
}

總結(jié),這種模式開發(fā)起來實在太方便了,無需做數(shù)據(jù)的加工,我們還是按照我們熟悉的方式去寫代碼,非常的方便。

但是,它只是支持普通的字符串類型進(jìn)行編碼和解碼而已,對于復(fù)雜的引用類型則無效。

3.2 大體流程

其實原理是非常的簡單的,請看下圖。

圖片描述
執(zhí)行流程說明:

  1. StringDecoder 必須放在業(yè)務(wù) Handler 之前,因為都是 InboundHandler,需要按順序執(zhí)行;
  2. StringEncoder 放在業(yè)務(wù) Handler 之前,則可以使用 ctx.writeAndFlush () 輸出數(shù)據(jù),也可以使用 ctx.channel ().writeAndFlus () 輸出數(shù)據(jù)(ChannelHandler 已經(jīng)講過原理了);
  3. StringEncoder 放在業(yè)務(wù) Handler 之后,則只能使用 ctx.channel().writeAndFlush() 輸出數(shù)據(jù)。

3.3 源碼閱讀

思考:StringDecoder 和 StringEncoder 到底怎么實現(xiàn)的呢?

StringDecoder 源碼:

@Sharable
public class StringDecoder extends MessageToMessageDecoder<ByteBuf> {
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        //直接msg.toString()
        out.add(msg.toString(this.charset));
    }
}

發(fā)現(xiàn) StringDecoder 的源碼非常的簡單,直接.toString() 轉(zhuǎn)換即可。

StringEncoder 源碼:

@Sharable
public class StringEncoder extends MessageToMessageEncoder<CharSequence> {
    protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {
        if (msg.length() != 0) {
            //繼續(xù)跟進(jìn)源碼
            out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), this.charset));
        }
    }
}

public static ByteBuf encodeString(ByteBufAllocator alloc, CharBuffer src, Charset charset) {
    //繼續(xù)跟進(jìn)源碼
    return encodeString0(alloc, false, src, charset, 0);
}

保留核心源碼

static ByteBuf encodeString0(ByteBufAllocator alloc, boolean enforceHeap, CharBuffer src, Charset charset, int extraCapacity) {
    CharsetEncoder encoder = CharsetUtil.encoder(charset);
    int length = (int)((double)src.remaining() * (double)encoder.maxBytesPerChar()) + extraCapacity;
    boolean release = true;
    
    //1.創(chuàng)建ByteBuf分配器
    ByteBuf dst;
    if (enforceHeap) {
        dst = alloc.heapBuffer(length);
    } else {
        dst = alloc.buffer(length);
    }

    ByteBuf var12;
    try {
        //2.得到NIO的ByteBuffer【跟進(jìn)Netty的ByteBuf基本上一樣】
        ByteBuffer dstBuf = dst.internalNioBuffer(0, length);
        int pos = dstBuf.position();
        
        //3.把內(nèi)容寫得NIO的ByteBuffer
        CoderResult cr = encoder.encode(src, dstBuf, true);
        cr = encoder.flush(dstBuf);
		
        //4.更新ByteBuf的寫指針writeIndex
        dst.writerIndex(dst.writerIndex() + dstBuf.position() - pos);
        
        //5.給var12賦值
        var12 = dst;
    } catch (CharacterCodingException var16) {
        throw new IllegalStateException(var16);
    }
    return var12;
}

大致流程就是把字符串內(nèi)容轉(zhuǎn)換成 NIO 的 ByteBuffer,這里大致知道整個流程即可,不用深究每行代碼的意思,其實 Netty 的 ByteBuf 底層就是基于 ByteBuffer 進(jìn)行封裝的。

3. 自定義編解碼器

通過上面 Demo 的學(xué)習(xí),以及 StringDecoder 和 StringEncoder 兩個類的學(xué)習(xí),相信大家更加能理解編解碼器了,畢竟 StringDecoder 和 StringEncoder 從字面意思也能理解它們是針對字符串格式的,如果我們想要傳遞一個實體那么怎么辦呢?

主要解決方案有兩種:

方案一: 把實體轉(zhuǎn)換成 json 格式字符串,然后依然使用 StringDecoder 和 StringEncoder 編解碼器,但是每次手工轉(zhuǎn)換和解析,非常的麻煩;
方案二: 自定義針對實體的編解碼器,并且加入到雙向鏈表里面,這樣就可以傳遞自定義實體了。

下面主要講解如何實現(xiàn)針對實體的編解碼器:

3.1 實體

實例:

@Data
public class User {
    private String name;
    private Integer age;
}

3.2 編碼器

核心步驟:

  1. 繼承 MessageToByteEncoder,重寫 encode 方法;
  2. 把 User 對象轉(zhuǎn)換成 byte [];
  3. 把 byte [] 寫到 ByteBuf。

實例:

public class MyEncoder extends MessageToByteEncoder<User> {
    protected void encode(ChannelHandlerContext channelHandlerContext, 
                          User user, 
                          ByteBuf byteBuf) throws Exception {
        
        //1.對象流
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);
        oos.writeObject(user);
        byte[] bytes=os.toByteArray();
        
		//2.關(guān)閉流
        oos.close();
        os.close();
		
        //3.寫到ByteBuf容器
        byteBuf.writeBytes(bytes);
    }
}

3.3 解碼器

核心步驟:

  1. 繼承 ByteToMessageDecoder,重寫 decode 方法;
  2. 自定義一個 byte [] 數(shù)組,長度是 ByteBuf 的可讀長度;
  3. 把 ByteBuf 轉(zhuǎn)換成 User 實體。

實例:

public class MyDecoder extends ByteToMessageDecoder {
    protected void decode(ChannelHandlerContext channelHandlerContext, 
                          ByteBuf byteBuf, 
                          List<Object> list) throws Exception {
        //1.定義byte[],長度為ByteBuf可讀長度
        byte[] bytes=new byte[byteBuf.readableBytes()];
        //2.往byte[]讀取數(shù)據(jù)
        byteBuf.readBytes(bytes);
		//3.對象流
        ByteArrayInputStream is=new ByteArrayInputStream(bytes);
        ObjectInputStream iss=new ObjectInputStream(is);
        User user=(User)iss.readObject();
        //4.關(guān)閉流
        is.close();
        iss.close();
		//5.添加到集合
        list.add(user);
    }
}

3.4 添加到 Pipeline

實例:

.childHandler(new ChannelInitializer<NioSocketChannel>() {
    protected void initChannel(NioSocketChannel ch) {
        //1.解碼器
        ch.pipeline().addLast(new MyDecoder());
        //2.編碼器
        ch.pipeline().addLast(new MyEncoder());
        //3.業(yè)務(wù)Handler
        ch.pipeline().addLast(new ServerTestHandler());
    }
});

4. 小結(jié)

通常情況下,需要把編解碼器分別獨立封裝成 Handler,并且加入到 ChannelPipeline 進(jìn)行管理,主要目的是簡化繁瑣的編碼和解碼的步驟,讓業(yè)務(wù) Handler 更加專注去處理業(yè)務(wù)邏輯,更加的符合開發(fā)人員的習(xí)慣。

本節(jié)主要掌握以下兩點內(nèi)容

  1. 如果針對字符串,那么可以使用 Netty 內(nèi)置的編解碼器,分別是 StringEncoder 和 StringDecoder;
  2. 如果是其它引用類型,主要有兩種方式,①轉(zhuǎn)換成字符串格式;②自定義編解碼器。