Netty4自带编解码器详解

前言

本篇文章是Netty专题的第五篇,前面四篇文章如下:

作为一个高性能的异步、NIO通信框架,编解码框架是Netty的重要组成部分。

从网络中读取消息,需要经过解码,将二进制的数据报转换成应用层协议消息,才能够被应用逻辑识别。同样的道理,客户端发送给服务器的消息,也需要经过编码转换成二进制字节数组(Netty就是ByteBuf)才能够发送到网络对端。编码和解码功能是NIO框架必不可少的一部分。

Netty为了降低用户的开发难度,对原始的NIO进行封装,提供了常用的功能和API,屏蔽了底层的实现细节。对于不想了解底层实现的用户,使用Netty自带的编解码器非常容易,都能够快速上手,提高开发效率。

Netty在这方面做得非常好,对编解码功能,提供了通用的编解码框架可以让用户扩展,又提供了常用的一些编解码类让用户直接使用。

Netty自带的编解码功能列表如下:

  • String
  • Protobuf
  • Base64
  • Object
  • 其他等等…..

本篇文章只讲解我列出来的几个,还有一些像粘包的解码器我们后面单独写文章进行讲解。

String编解码

String编解码在Netty中对应的类是io.netty.handler.codec.string.StringEncoderio.netty.handler.codec.string.StringDecoder,提供字符串数据的传输编解码工作。

关于String编解码的使用这边不做过多讲解,可以参考我的《高性能NIO框架Netty入门篇》中的使用。

Protobuf编解码

Protobuf编解码在Netty中对应的类是io.netty.handler.codec.protobuf.ProtobufDecoderio.netty.handler.codec.protobuf.ProtobufEncoder,提供基于Protobuf序列化的数据传输编解码工作。

关于Protobuf编解码的使用这边不做过多讲解,可以参考我的《高性能NIO框架Netty-整合Protobuf高性能数据传输》中的使用。

Base64编解码

base64的使用需要在String的基础上,不然消息是无法直接传递。

服务端

/**
 * Base64编解码示例
 * @author yinjihuan
 *
 */
public class Base64EncoderAndDecoderServer {
    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() { 
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast("decoder", new StringDecoder());
                        ch.pipeline().addLast("encoder", new StringEncoder());
                        ch.pipeline().addLast("base64Decoder", new Base64Decoder());
                        ch.pipeline().addLast("base64Encoder", new Base64Encoder());
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) {
                                System.err.println("server:" + msg.toString());
                                ctx.writeAndFlush(msg.toString() + "你好");
                            }
                        });
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        try {
            ChannelFuture f = bootstrap.bind(2222).sync();
             f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

客户端

/**
 * Base64编解码示例
 * @author yinjihuan
 *
 */
public class Base64EncoderAndDecoderClient {
    public static void main(String[] args) {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        Channel channel = null;
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast("decoder", new StringDecoder());
                    ch.pipeline().addLast("encoder", new StringEncoder());
                    ch.pipeline().addLast("base64Decoder", new Base64Decoder());
                    ch.pipeline().addLast("base64Encoder", new Base64Encoder());
                    ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) {
                            System.err.println("client:" + msg.toString());
                        }
                    });
                }
            });
            ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
            channel = f.channel();
            channel.writeAndFlush("hello yinjihuan");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

Object编解码

Object编解码在Netty中对应的类是io.netty.handler.codec.serialization.ObjectEncoderio.netty.handler.codec.serialization.ObjectDecoder,提供基于对象序列化的数据传输编解码工作。

之前我们在《高性能NIO框架Netty-对象传输》中通过自定义编码器来实现了PO对象的传输,今天就用Netty自带的Object来进行编解码工作。

传输对象

public class ObjectMessage implements Serializable {
    private static final long serialVersionUID = -7543514952950971498L;
    private String id;
    private String content;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}

服务端

/**
 * Object编解码示例
 * @author yinjihuan
 *
 */
public class ObjectEncoderAndDecoderServer {
    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() { 
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(
                                this.getClass().getClassLoader()
                        )));
                        ch.pipeline().addLast("encoder", new ObjectEncoder());
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) {
                                ObjectMessage m = (ObjectMessage) msg;
                                System.err.println("server:" + m.getContent());
                            }
                        });
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)
                .childOption(ChannelOption.SO_KEEPALIVE, true);
        try {
            ChannelFuture f = bootstrap.bind(2222).sync();
             f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

客户端

/**
 * Object编解码示例
 * @author yinjihuan
 *
 */
public class ObjectEncoderAndDecoderClient {
    public static void main(String[] args) {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        Channel channel = null;
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast("decoder", new ObjectDecoder(ClassResolvers.cacheDisabled(
                            this.getClass().getClassLoader()
                    )));
                    ch.pipeline().addLast("encoder", new ObjectEncoder());
                    ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) {
                            ObjectMessage m = (ObjectMessage) msg;
                            System.err.println("client:" + m.getContent());
                        }
                    });
                }
            });
            ChannelFuture f = b.connect("127.0.0.1", 2222).sync();
            channel = f.channel();
            ObjectMessage m = new ObjectMessage();
            m.setContent("hello yinjihuan");
            channel.writeAndFlush(m);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

源码参考:https://github.com/yinjihuan/netty-im

原文发布于微信公众号 - 猿天地(cxytiandi)

原文发表时间:2018-03-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏技术墨客

Java数据校验详解

一个健壮的系统都要对外部提交的数据进行完整性、合法性的校验。即使开发一个不面对最终用户的工具包,也需要对传入的数据进行缜密的校验来防止引发底层难以追踪的问题。各...

33710
来自专栏IT杂记

Slf4j+Logback配置文件变量使用小记

项目中须要根据不同的模块,产生出不同的日志文件名,使用的是同一logback.xml配置文件,这里简单调研,说明两种实现方式,以及两种实现方式的区别。 测试准备...

36480
来自专栏木木玲

Netty 那些事儿 ——— 关于 “Netty 发送大数据包时 触发写空闲超时” 的一些思考

88160
来自专栏技术墨客

Java数据校验详解

一个健壮的系统都要对外部提交的数据进行完整性、合法性的校验。即使开发一个不面对最终用户的工具包,也需要对传入的数据进行缜密的校验来防止引发底层难以追踪的问题。各...

21020
来自专栏Java 技术分享

SpringMVC(一)

13720
来自专栏微服务生态

Flume-NG源码分析-整体结构及配置载入分析

终于开始Flume源码的分析研究工作了,我也是边学边和大家分享,内容上难免有不足之处,望大家见谅。

17240
来自专栏Spring相关

第3章—高级装配—条件化的Bean

通过活动的profile,我们可以获得不同的Bean。Spring 4提供了一个更通用的基于条件的Bean的创建方式,即使用@Conditional注解。

10820
来自专栏代码拾遗

​SpringMVC 教程 - Handler Method

由注解@RequestMapping注解修饰的处理请求的函数的签名非常的灵活,可以使用controller函数支持的一系列参数和返回值。

12310
来自专栏SpringSpace.cn

使用 Postman 与 Kotlin 交互REST API接口数据 顶

在前面2篇文章使用 Kotlin 和Spring Boot 2.0快速开发REST API接口和使用 Kotlin 和Spring Boot 2.0快速开发RE...

19430
来自专栏杨建荣的学习笔记

一些“简单”的linux命令(r2笔记46天)

有些linux命令看起来极其简单,只包含2个字符,但确有很强的功能性。看起来还是有些陌生的命令,不过在工作中别忘记它们的存在。 ab 这条命令式做为性能测试所...

32780

扫码关注云+社区

领取腾讯云代金券