Netty入門系列(3) --使用Netty進行編解碼的操做

前言

何爲編解碼,通俗的來講,咱們須要將一串文本信息從A發送到B而且將這段文本進行加工處理,如:A將信息文本信息編碼爲2進制信息進行傳輸。B接受到的消息是一串2進制信息,須要將其解碼爲文本信息才能正常進行處理。java

上章咱們介紹的Netty如何解決拆包和粘包問題,就是運用瞭解碼的這一功能。bootstrap

java默認的序列化機制

使用Netty大可能是java程序猿,咱們基於一切都是對象的原則,常常會將對象進行網絡傳輸,那麼對於序列化操做確定你們都是很是熟悉的。api

一個對象是不能直接進行網絡I/O傳輸的,jdk默認是將對象轉換爲可存儲的字節數組來進行網絡操做。基於JDK默認的序列化機制能夠避免操做底層的字節數組,從而提高開發效率。數組

jdk默認的序列化機制雖然能給程序猿帶來極大的方便,可是它也帶來了許多問題:網絡

  1. 沒法跨語言。
  2. 序列化後的碼流太大,會給網絡傳輸帶來極大的開銷。
  3. 序列化的性能過低,對於高性能的網絡架構是極其不友好的。

主流的編解碼框架

  1. Google的Protobuf。
  2. Facebok的Thrift。
  3. Jboss Marshalling
  4. MessagePack

這幾類編解碼框架都有各自的特色,有興趣的童鞋能夠本身對其進行研究。架構

咱們這裏主要對MessagePack進行講解。框架

MessagePack簡介

MessagePack是一個高效的二進制序列化框架,它像JSON同樣支持不一樣的語言間的數據交換,而且它的性能更快,序列化以後的碼流也更小。socket

它的特色以下:ide

  1. 編解碼高效,性能高
  2. 序列化以後的碼流小,利於網絡傳輸或存儲
  3. 支持跨語言

MessagePack Java Api的使用

  1. 首先導包
<!-- https://mvnrepository.com/artifact/org.msgpack/msgpack -->
<dependency>
    <groupId>org.msgpack</groupId>
    <artifactId>msgpack</artifactId>
    <version>0.6.12</version>
</dependency>
  1. 使用API進行編碼和解碼
List<String> nameList = new ArrayList<String>();
nameList.add("Tom");
nameList.add("Jack");
MessagePack messagePack = new MessagePack();
//開始序列化
byte[] raw = messagePack.write(nameList);
//使用MessagePack的模版,來接序列化後的字節數組轉換爲List
List<String> deNameList = messagePack.read(raw,Templates.tList(Templates.TString));
System.out.println(deNameList.get(0));
System.out.println(deNameList.get(1));
System.out.println(deNameList.get(2));

Netty中如何使用MessagePack

編碼器的實現

public class MsgpackEncoder extends MessageToByteEncoder {

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        MessagePack msgpack = new MessagePack();
        //使用MessagePack對要發送的數據進行序列化
        byte[] raw = msgpack.write(msg);
        out.writeBytes(raw);
        
    }

}

解碼器的實現

public class MsgpackDecoder extends MessageToMessageDecoder<ByteBuf> {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        //從msg中獲取須要解碼的byte數組
        final int length = msg.readableBytes();
        byte[] b = new byte[length];
        msg.getBytes(msg.readerIndex(), b,0,length);
        //使用MessagePack的read方法將其反序列化成Object對象,並加入到解碼列表out中
        MessagePack msgpack = new MessagePack();
        out.add(msgpack.read(b));
    }

}

實現該編碼器和解碼器的Netty服務端

public class NettyServer {
    public void bind(int port) throws Exception {
        EventLoopGroup bossGruop = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGruop, workGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // TODO Auto-generated method stub
                        socketChannel.pipeline()
                         //添加支持粘包、拆包解碼器,意義:從頭兩個字節解析出數據的長度,而且長度不超過1024個字節
                        .addLast("frameDecoder",new LengthFieldBasedFrameDecoder(1024, 0, 2,0,2))
                         //反序列化解碼器
                        .addLast("msgpack decoder",new MsgpackDecoder())
                         //添加支持粘包、拆包編碼器,發送的每一個數據都在頭部增長兩個字節表消息長度
                        .addLast("frameEncoder",new LengthFieldPrepender(2))
                         //序列化編碼器
                        .addLast("msgpack encoder",new MsgpackEncoder()
                         //後續本身的業務邏輯
                        .addLast(new ServerHandler());
                    }
                });
        try {
            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGruop.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

實現該編碼器和解碼器的Netty客戶端

public class NettyClient {
    private void bind(int port, String host) {
        EventLoopGroup group = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(new ChannelInitializer<SocketChannel>(){
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // TODO Auto-generated method stub
                        socketChannel.pipeline()
                        .addLast("frameDecoder", new LengthFieldBasedFrameDecoder(1024, 0, 2, 0, 2))
                        .addLast("msgpack decoder", new MsgpackDecoder())
                        .addLast("frameEncoder", new LengthFieldPrepender(2))
                        .addLast("msgpack encoder", new MsgpackEncoder())
                        .addLast(new ClientHandler());
                    }
                });
        try {
            ChannelFuture f = b.connect(host, port).sync();
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

能夠看出客戶端的代碼與服務端基本相同,因此啊,若是能熟練掌握Netty,從此在本身的項目中運用上定製化編解碼的傳輸,將會是一件十分簡單的活路。oop

總結

不管是以前解決粘包拆包問題,仍是這裏的使用序列化框架來進行編解碼。我相信讀者學習到這裏,對於Netty的使用都有了較爲全面的瞭解。其實Netty幫咱們解決了不少底層棘手問題,如客戶端斷連、句柄泄漏和消息丟失等等。因此咱們才能十分簡單開發出一個穩定的網絡通信項目。

相關文章
相關標籤/搜索