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

Netty 編碼和解碼

1. 前言

本節(jié)內(nèi)容,主要是講解 Netty 的編碼和解碼,前面我們講解了 ByteBuf,Netty 是面向 ByteBuf 來編程的,發(fā)送的內(nèi)容會被編碼成 ByteBuf,從 Channel 接受的數(shù)據(jù)流則被封裝成了 ByteBuf,需要把它解碼成我們所熟悉的格式。

2. 編碼和解碼的作用

首先,我們先通過一個實例來進行說明。

實例:

ch.pipeline().addLast(new CodecHandler());

public class CodecHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("接受:"+msg.toString());
    }
}

客戶端發(fā)送數(shù)據(jù):
圖片描述

執(zhí)行結(jié)果:

接受:PooledUnsafeDirectByteBuf(ridx: 0, widx: 5, cap: 1024)

通過以上測試,發(fā)現(xiàn)客戶端往服務(wù)端發(fā)送普通的字符串,服務(wù)端接受的時候并不是正常字符串,而是把 ByteBuf 類型打印出來。

主要原因是,Netty 的數(shù)據(jù)類型是 ByteBuf,無法直接強轉(zhuǎn),需要通過解碼的方式去轉(zhuǎn)換才能得到正常的數(shù)據(jù),編碼也是同樣道理。

因此,本節(jié)學編碼和解碼的知識可以了解 Netty 如何去接受和發(fā)送參數(shù)。

3. 解碼示例

實例:

public class ServerLoginHandler extends ChannelInboundHandlerAdapter {
    //1.讀取客戶端發(fā)送過來的數(shù)據(jù)
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //1.轉(zhuǎn)換ByteBuf
        ByteBuf buffer=(ByteBuf)msg;
        //2.定義一個byte數(shù)組,長度是ByteBuf的可讀字節(jié)數(shù)
        byte[] bytes=new byte[buffer.readableBytes()];
        //3.往自定義的byte[]讀取數(shù)據(jù)
        buffer.readBytes(bytes);

        //4.字節(jié)流->字符串
       	String str=new String(bytes);     
    }
}

通過以上代碼,我們發(fā)現(xiàn)能正常接收并且打印客戶端發(fā)送過來的字符串數(shù)據(jù)。但是如果是其它的類型數(shù)據(jù)(比如:Map,實體,List 等)那么還得手工寫另外的轉(zhuǎn)換方法,相對比較麻煩。

4. 編碼解碼流程

4.1 整體流程

無論是使用 Netty 還是原始的 Socket 編程,基于 TCP 通信的數(shù)據(jù)包格式均為二進制,但是我們平時開發(fā)不可能基于二進制去開發(fā),而是封裝一個一個的實體。這樣的話,我們就需要實現(xiàn)實體和二進制之間的編碼和解碼了。

  1. 客戶端往服務(wù)端發(fā)送消息,手寫需要把實體轉(zhuǎn)換成 byte [],并且把 byte [] 寫入到 ByteBuf 容器里面,最終轉(zhuǎn)換二進制。其實,整個過程就是一個編碼的過程;
  2. 服務(wù)端接受到消息,二進制是給機器去識別的,人眼無法快速去識別它,然而實體是我們所熟悉并且一看就能看出有哪些屬性,因此需要把二進制轉(zhuǎn)換我們所熟悉的實體,整個過程就是一個解碼的過程。

圖片描述

4.2 編碼流程

實例:

//封裝編碼方法
public ByteBuf encode(Object obj) {
    // 1. 創(chuàng)建 ByteBuf 對象
    ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
    // 2. 序列化 Java 對象
    byte[] bytes = SerializeUtils.serialize(obj);
    // 3. 實際編碼過程
    byteBuf.writeBytes(bytes);
    return byteBuf;
}

//序列化工具類
public class SerializeUtils{
    //序列化方法
    public static byte[] serialize(Object obj){
        //省略序列化過程
        
        return null;
    }
}

代碼說明:

  1. 創(chuàng)建一個 ByteBuf(前面章節(jié)詳細講解過);
  2. 把內(nèi)容序列化成字節(jié)數(shù)組;
  3. 把字節(jié)數(shù)組寫入到 ByteBuf。

4.3 解碼流程

實例:

//解碼
public <T> T decode(ByteBuf byteBuf,Class clazz) {
    // 數(shù)據(jù)包長度
    int length = byteBuf.readableBytes();

    byte[] bytes = new byte[length];
    byteBuf.readBytes(bytes);

    return SerializeUtils.desrialize(bytes,clazz);
}

//序列化工具類
public class SerializeUtils{
    //序列化方法
    public static <T> T desrialize(byte[] bytes,Class clazz){
        //省略反序列化過程
        
        return null;
    }
}

代碼說明:

  1. 根據(jù) ByteBuf 獲取可讀的數(shù)據(jù)長度;
  2. 根據(jù)數(shù)據(jù)長度創(chuàng)建相應(yīng)的字節(jié)數(shù)組;
  3. 把 ByteBuf 里面的內(nèi)容讀取到自定義的字節(jié)數(shù)組里面;
  4. 通過反序列化的手段,把字節(jié)數(shù)組反序列化成對象。

5. 序列化和反序列化

上面講編碼和解碼的時候,涉及兩個空方法沒有實現(xiàn),分別是 serialize() 序列化和 desrialize() 反序列化,其實序列化和反序列化技術(shù)選擇很多,常見的解決方案大概如下:

  1. 通過對象流來手工實現(xiàn)序列化,但是實體必須實現(xiàn) Serializeable 序列化接口,否則無法被正常序列化和反序列化;
  2. 對象 -> 轉(zhuǎn)換 json 格式的字符串,Java 里面 String 類型字符串可以自動轉(zhuǎn)換字節(jié)數(shù)組,常見的開源框架分別有 Fastjson、Jackjson 等;
  3. 對象 - 轉(zhuǎn)存 xml 格式的字符串,常見框架有 XStream 等;
  4. 其他技術(shù),如:Hessian 序列化、Kryo 序列化等。

這里就不詳細展開展示序列化和反序列化的說明,如果有興趣,可以參考我寫的另外一篇文章:

接下來,主要說明的是,為了靈活擴展,我們最好不要寫死某種序列化技術(shù),為了方便后期更改技術(shù)框架,因為每種序列化技術(shù)的差距比較大,主要體現(xiàn)兩點:

  1. 消耗時間: 序列化和反序列化的消耗時間長度;
  2. 數(shù)據(jù)長度: 序列化過后的字節(jié)數(shù)組長度,這個是會影響網(wǎng)絡(luò)傳輸性能的。

一般情況下,通過面向接口 + 策略模式的方式去解耦,底層可以靈活的切換序列化技術(shù)。

實例:

//定義一個序列化接口
public interface SerializeService<T>{
    //序列化方法
    public byte[] serialize(T t);
    //反序列化方法
    public T deserialize(byte[] bytes,Class<T> clazz);
}

//具體序列化實現(xiàn)列
public class JsonSerializeService<T> implements SerializeService<T>{
    //序列化方法
    public byte[] serialize(T t){
        return null;
    }
    //反序列化方法
    public T deserialize(byte[] bytes,Class<T> clazz){
        return null;
    }
}
//序列化使用
@Component
public class Test{
    @Autowired
    private SerializeService serializeService;
    
    public ByteBuf encode(Object obj) {
        // 1. 創(chuàng)建 ByteBuf 對象
        ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
        // 2. 序列化 Java 對象
        byte[] bytes = serializeService.serialize(obj);
        // 3. 實際編碼過程
        byteBuf.writeBytes(bytes);
        return byteBuf;
    }
}

6. 小結(jié)

本節(jié)內(nèi)容大家掌握好以下內(nèi)容:

  1. 編碼和解碼的概念是什么?為什么需要編碼和解碼?
  2. Netty 如何去進行編碼和解碼,以及大體流程是什么?
  3. 編碼和解碼需要依賴序列化和反序列化技術(shù),要了解序列化方面的技術(shù)有哪些。

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