第3講 編解碼

本章要點(diǎn):

  • java序列化缺點(diǎn)
  • 業(yè)界流行的集中編解碼框架介紹

3.1 java序列化缺點(diǎn)

  • 無(wú)法跨越語(yǔ)言,是java序列化最致命的缺點(diǎn);
  • 序列化后的碼流太大
  • 序列化性能太低

3.2 主流編輯碼框架

3.2.1 Google Protobuf編解碼

Protobuf在業(yè)界非常流行,Protobuf具有以下有點(diǎn):

  • 產(chǎn)品成熟
  • 跨語(yǔ)言、支持多種語(yǔ)言
  • 編碼后消息更小,便與存儲(chǔ)和傳輸
  • 編解碼性能高、
  • 支持定義可選和必選字段
    Protobuf是一個(gè)靈活、高效、結(jié)構(gòu)化的數(shù)據(jù)序列化框架,相比于XML等傳統(tǒng)的序列化工具,它更小、更快、更簡(jiǎn)單。
    https://github.com/google/protobuf/releases
    下載protof工具,protoc-3.5.1-win32.zip,并解壓,會(huì)看到protoc.exe工具。

User.proto的文件內(nèi)容如下:

package com.bj58.wuxian.protobuf;

option java_outer_classname = "UserProto"; 
message User{
    required int32 id=1;
    required string username=2;
    required string password=3;
    enum Sex{
     nan=1;
     nv=2;
    }
    required Sex sex=4;
}

執(zhí)行如下命令:

D:/develop/protoc-3.5.1-win32/bin/protoc.exe -I=./proto --java_out=D:/develop/protoc-3.5.1-win32/bin/proto/ ./proto/User.proto

生成了一個(gè)UserProto.java文件。
protoc.exe -I=proto的輸入目錄 --java_out=java類輸出目錄 proto的輸入目錄包括包括proto文件
此時(shí)將UserProto.java文件拷貝到IDE中會(huì)報(bào)錯(cuò),那是因?yàn)槿鄙侔蕾嚕?/p>

        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.1</version>
        </dependency>

protobuf序列化傳輸代碼實(shí)例:
UserServer代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import com.bj58.wuxian.protobuf.UserProto;

public class UserServer {
    
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup=new NioEventLoopGroup(); 
        EventLoopGroup workGroup=new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap=new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 1024)
            .handler(new LoggingHandler(LogLevel.INFO))
            .childHandler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());//用于半包處理
                    ch.pipeline().addLast(new ProtobufDecoder(UserProto.User.getDefaultInstance()));//ProtobufDecoder參數(shù)是所要解碼的目標(biāo)類
                    ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                    ch.pipeline().addLast(new ProtobufEncoder());
                    ch.pipeline().addLast(new UserServerHandler());
                }
            });
            
            ChannelFuture f=bootstrap.bind(8888).sync();
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

UserServerHandler 代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import com.bj58.wuxian.protobuf.UserProto;
import com.bj58.wuxian.protobuf.UserProto.User.Sex;

public class UserServerHandler  extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("**********server channelActive*************");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        
        UserProto.User user =(UserProto.User)msg;
        System.out.println("request:"+msg);
        UserProto.User.Builder builder = UserProto.User.newBuilder();
        builder.setId(123);
        builder.setSex(Sex.nv);
        builder.setPassword("45678");
        if("zhaoshichao".equalsIgnoreCase(user.getUsername())){
            builder.setUsername("shangjing");
        }else{
            builder.setUsername("guanggunhan");
        }
        UserProto.User wife = builder.build();
        ctx.writeAndFlush(wife);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

UserClient 代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import com.bj58.wuxian.protobuf.UserProto;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

public class UserClient {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group=new NioEventLoopGroup();
        
        try {
            Bootstrap bootstrap=new Bootstrap();
            bootstrap.group(group)
            .channel(NioSocketChannel.class)
            .option(ChannelOption.TCP_NODELAY, true)
            .handler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
                    ch.pipeline().addLast(new ProtobufDecoder(UserProto.User.getDefaultInstance()));
                    ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                    ch.pipeline().addLast(new ProtobufEncoder());
                    ch.pipeline().addLast(new UserClientHandler());
                }
            });
            
            ChannelFuture f=bootstrap.connect("127.0.0.1",8888).sync();
            f.channel().closeFuture().sync();
            
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            group.shutdownGracefully();
        }
    }
}

UserClientHandler 代碼:

package com.bj58.wuxian.netty.codec.protobuf;

import com.bj58.wuxian.protobuf.UserProto;
import com.bj58.wuxian.protobuf.UserProto.User.Sex;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class UserClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i=0;i<=5;i++){
            UserProto.User.Builder builder = UserProto.User.newBuilder();
            builder.setId(123);
            builder.setSex(Sex.nan);
            builder.setPassword("45678");
            if(i%2==0){
                builder.setUsername("zhaoshichao");
            }else{
                builder.setUsername("zhaoshichao"+i);
            }
            
            UserProto.User wife = builder.build();
            ctx.writeAndFlush(wife);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("response:"+(UserProto.User)msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}

3.2.1 Message Pack編解碼

MessagePack特點(diǎn)如下:

  • 編解碼高效,性能高
  • 序列化之后的碼流小
  • 支持跨語(yǔ)言

添加pom依賴:

        <dependency>
            <groupId>org.msgpack</groupId>
            <artifactId>msgpack</artifactId>
            <version>0.6.12</version>
        </dependency>

自定義編碼器:

package com.bj58.wuxian.netty.codec.msgpack;

import org.msgpack.MessagePack;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

public class MsgPackEncoder extends MessageToByteEncoder<Object> {

    @Override
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out)
            throws Exception {
        MessagePack msfPack=new MessagePack();
        byte[] bytes=msfPack.write(msg);
        out.writeBytes(bytes);
    }
}

自定義解碼器:

package com.bj58.wuxian.netty.codec.msgpack;

import java.util.List;
import org.msgpack.MessagePack;
import com.bj58.wuxian.msgpack.model.User;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;

public class MsgPackDecoder extends MessageToMessageDecoder<ByteBuf> {

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf msg,
            List<Object> out) throws Exception {
        byte[] bytes=new byte[msg.readableBytes()];
        msg.readBytes(bytes);
        MessagePack messagePack=new MessagePack();
        out.add(messagePack.read(bytes,User.class));
    }
}

UserServer代碼:

package com.bj58.wuxian.netty.codec.msgpack;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

public class UserServer {
    public static void main(String[] argsStrings) throws Exception {
        // 配置服務(wù)端NIO線程組(boss線程、worker線程)
        EventLoopGroup bGroup = new NioEventLoopGroup();
        EventLoopGroup wGroup = new NioEventLoopGroup();
        // 創(chuàng)建啟動(dòng)輔助類
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bGroup, wGroup).channel(NioServerSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        // 添加對(duì)象系列化編解碼器,同時(shí)提供粘包拆包支持
                        channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                        channel.pipeline().addLast("解碼器", new MsgPackDecoder());
                        channel.pipeline().addLast(new LengthFieldPrepender(2));
                        channel.pipeline().addLast("編碼器", new MsgPackEncoder());
                        channel.pipeline().addLast(new UserServerHandler());
                    }

                });

        try {
            // 監(jiān)聽本地端口,同步等待監(jiān)聽結(jié)果
            ChannelFuture future = bootstrap.bind(8888).sync();
            // 等待服務(wù)端監(jiān)聽端口關(guān)閉,優(yōu)雅退出
            future.channel().closeFuture().sync();
        } finally {
            bGroup.shutdownGracefully();
            wGroup.shutdownGracefully();
        }

    }
}

ServerHanler代碼:

package com.bj58.wuxian.netty.codec.msgpack;

import com.bj58.wuxian.msgpack.model.User;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class UserServerHandler extends ChannelInboundHandlerAdapter  {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("**********server channelActive**********");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        User user=(User)msg;
        User wife=new User();
        if("zhaoshichao".equals(user.getUsername())){
            wife.setPassword("1234");
            wife.setUsername("shangjing");;
        }else{
            wife.setPassword("1234");
            wife.setUsername("others");
        }
        
        ctx.writeAndFlush(wife);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

User客戶端:

package com.bj58.wuxian.netty.codec.msgpack;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;

public class UserClient {
    public static void  main(String [] argsStrings) throws Exception {  
        //配置客戶端端NIO線程組  
        EventLoopGroup bGroup = new NioEventLoopGroup();  
        //創(chuàng)建客戶端啟動(dòng)輔助類  
        Bootstrap bootstrap = new Bootstrap();  
        bootstrap.group(bGroup).  
                  channel(NioSocketChannel.class).  
                  option(ChannelOption.TCP_NODELAY, true).  
                  option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000).  
                  handler(new ChannelInitializer<SocketChannel>() {  
                        @Override  
                        protected void initChannel(SocketChannel channel) throws Exception {  
                            //添加對(duì)象系列化編解碼器,同時(shí)提供粘包拆包支持  
                            channel.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                            channel.pipeline().addLast("解碼器", new MsgPackDecoder());
                            channel.pipeline().addLast(new LengthFieldPrepender(2));
                            channel.pipeline().addLast("編碼器", new MsgPackEncoder());
                            channel.pipeline().addLast(new UserClientHandler());
                        }  
                           
                    });  
          
        //發(fā)起異步連接  
        ChannelFuture future = bootstrap.connect("127.0.0.1", 8888).sync();  
        try {  
            //等待客戶端鏈路關(guān)閉  
            future.channel().closeFuture().sync();  
            } finally {  
            //優(yōu)雅退出,釋放資源  
            bGroup.shutdownGracefully();  
        }  
    }  
}

ClientHandler代碼:

package com.bj58.wuxian.netty.codec.msgpack;

import com.bj58.wuxian.msgpack.model.User;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class UserClientHandler extends ChannelInboundHandlerAdapter{
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i=0;i<=5;i++){
            User user=new User();
            user.setId(123);
            user.setPassword("45678");
            if(i%2==0){
                user.setUsername("zhaoshichao");
            }else{
                user.setUsername("zhaoshichao"+i);
            }
            
            ctx.writeAndFlush(user);
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("response:"+(User)msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

}

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

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,109評(píng)論 25 709
  • 很少曬娃,今天有感而發(fā)曬一記。 姆二長(zhǎng)大了,上了幼兒園。作為父母,自然都希望孩子天資聰穎,這跟取名“逸生”無(wú)關(guān)。然...
    realMeathill閱讀 562評(píng)論 1 0
  • 最近小熊中招流感,請(qǐng)假在家休息。堅(jiān)持到第三天不再發(fā)燒,我就有點(diǎn)急著送他去幼兒園。熊爸挺不可思議的看著我:他還在咳嗽...
    熊媽侯蕾閱讀 799評(píng)論 1 3
  • 張藝興1991年10月7日出生在湖南省長(zhǎng)沙市(標(biāo)準(zhǔn)的90后,一口湖南話說(shuō)的相當(dāng)標(biāo)準(zhǔn)),歌手、演員、EXO-M中國(guó)籍...
    小梁超閱讀 1,314評(píng)論 0 0
  • 我若為林,便欣欣以向榮;我若為草,便萋萋而搖綠。 儒家說(shuō):勿我勿必;道家言:無(wú)為忘機(jī);佛家云:驅(qū)除心魔,天神合一。...
    心夢(mèng)飛翔h閱讀 266評(píng)論 0 1

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