• Netty入门系列(3) --使用Netty进行编解码的操作


    前言

    何为编解码,通俗的来说,我们需要将一串文本信息从A发送到B并且将这段文本进行加工处理,如:A将信息文本信息编码为2进制信息进行传输。B接受到的消息是一串2进制信息,需要将其解码为文本信息才能正常进行处理。

    上章我们介绍的Netty如何解决拆包和粘包问题,就是运用了解码的这一功能。

    java默认的序列化机制

    使用Netty大多是java程序猿,我们基于一切都是对象的原则,经常会将对象进行网络传输,那么对于序列化操作肯定大家都是非常熟悉的。

    一个对象是不能直接进行网络I/O传输的,jdk默认是将对象转换为可存储的字节数组来进行网络操作。基于JDK默认的序列化机制可以避免操作底层的字节数组,从而提升开发效率。

    jdk默认的序列化机制虽然能给程序猿带来极大的方便,但是它也带来了许多问题:

    1. 无法跨语言。
    2. 序列化后的码流太大,会给网络传输带来极大的开销。
    3. 序列化的性能太低,对于高性能的网络架构是极其不友好的。

    主流的编解码框架

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

    这几类编解码框架都有各自的特点,有兴趣的童鞋可以自己对其进行研究。

    我们这里主要对MessagePack进行讲解。

    MessagePack简介

    MessagePack是一个高效的二进制序列化框架,它像JSON一样支持不同的语言间的数据交换,并且它的性能更快,序列化之后的码流也更小。

    它的特点如下:

    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,今后在自己的项目中运用上定制化编解码的传输,将会是一件十分简单的活路。

    总结

    无论是之前解决粘包拆包问题,还是这里的使用序列化框架来进行编解码。我相信读者学习到这里,对于Netty的使用都有了较为全面的了解。其实Netty帮我们解决了很多底层棘手问题,如客户端断连、句柄泄漏和消息丢失等等。所以我们才能十分简单开发出一个稳定的网络通讯项目。

  • 相关阅读:
    ASP.NET Core 中间件 中间件(Middleware)和过滤器(Filter)的区别
    ASP.NET Core 中间件详解及项目实战
    开源项目1:某大学校友管理系统
    web安全浅析
    p2p网贷平台设计简析
    一些常见的并且比较难解决的设计问题
    CentOS 新增swap交换空间
    策略模式
    Centos6.4 本地yum源配置
    Linux(CentOs6.4)安装Git
  • 原文地址:https://www.cnblogs.com/zhxiansheng/p/10887604.html
Copyright © 2020-2023  润新知