亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

Netty 編碼和解碼

1. 前言

本節內容,主要是講解 Netty 的編碼和解碼,前面我們講解了 ByteBuf,Netty 是面向 ByteBuf 來編程的,發送的內容會被編碼成 ByteBuf,從 Channel 接受的數據流則被封裝成了 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());
    }
}

客戶端發送數據:
圖片描述

執行結果:

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

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

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

因此,本節學編碼和解碼的知識可以了解 Netty 如何去接受和發送參數。

3. 解碼示例

實例:

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

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

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

4. 編碼解碼流程

4.1 整體流程

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

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

圖片描述

4.2 編碼流程

實例:

//封裝編碼方法
public ByteBuf encode(Object obj) {
    // 1. 創建 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. 創建一個 ByteBuf(前面章節詳細講解過);
  2. 把內容序列化成字節數組;
  3. 把字節數組寫入到 ByteBuf。

4.3 解碼流程

實例:

//解碼
public <T> T decode(ByteBuf byteBuf,Class clazz) {
    // 數據包長度
    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. 根據 ByteBuf 獲取可讀的數據長度;
  2. 根據數據長度創建相應的字節數組;
  3. 把 ByteBuf 里面的內容讀取到自定義的字節數組里面;
  4. 通過反序列化的手段,把字節數組反序列化成對象。

5. 序列化和反序列化

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

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

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

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

  1. 消耗時間: 序列化和反序列化的消耗時間長度;
  2. 數據長度: 序列化過后的字節數組長度,這個是會影響網絡傳輸性能的。

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

實例:

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

//具體序列化實現列
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. 創建 ByteBuf 對象
        ByteBuf byteBuf = ByteBufAllocator.DEFAULT.ioBuffer();
        // 2. 序列化 Java 對象
        byte[] bytes = serializeService.serialize(obj);
        // 3. 實際編碼過程
        byteBuf.writeBytes(bytes);
        return byteBuf;
    }
}

6. 小結

本節內容大家掌握好以下內容:

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

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