编码解码介绍

编码和解码的基本介绍

编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码
codec(编解码器)的组成部分有两个:decoder(解码器)和encoder(编码器),encoder负责把业务数据转换成字节码数据,decoder负责把字节码数据转换成业务数据

示意图

netty本身的编码解码的机制和问题分析

Protobuf

Protobuf基本介绍和使用示意图

 

SimpleChannelInboundHandler可以指定传输信息的类型

ChannelInboundHandlerAdapter传输信息的类型为object

Netty的handler的调用机制

实例要求:

  • 使用自定义的编码器和解码器来说明Netty的handler调用机制
  • 客户端发送long->服务端
  • 服务端发送long->客户端

客户端到服务端编码解码

服务端代码

public class MyServer {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            ServerBootstrap group = bootstrap.group(bossGroup, workerGroup);
            group.channel(NioServerSocketChannel.class).childHandler(new MyServerInitializer());
            ChannelFuture channelFuture = bootstrap.bind(7000).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
  • 设置处理连接group,处理业务group
  • 新建ServerBootstrap,设置parent和child
  • 设置channel类型
  • 设置处理业务数据的handler类型
  • 绑定端口异步处理

自定义处理器

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        // 入站的handler进行解码 MyByteLongDecoder
        pipeline.addLast(new MyByteLongDecoder());
        pipeline.addLast(new MyServerHandler());
    }
}
  • 现在是客户端到服务端传输数据,编写的是服务端的代码
  • 客户端进行编码生成二进制数据,然后进行解码生成对应数据
  • 所以服务端编写解码处理器以及处理自己业务的处理器

解码处理器

public class MyByteLongDecoder extends ByteToMessageDecoder {

    /*
    ctx 是上下文
    in 入站的buf
    list 解码后的数据放到集合里面之后,可以传到下一个handler进行处理(channelinboundhandler处理) ,
    decode 会根据接收的数据,被调用多次,直到确定没有新的元素被添加到list,或者byteBuf没有更多可读字节为止,同样也会被下一个处理器执行多次
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        //超过8个字节会调用2次这个方法,因为超过的部分交给了下一个处理器 又会调用一次这个方法
        // 因为long为8个字节
        if (in.readableBytes()>=8){
            out.add(in.readLong());//readLong按long进行处理
        }
    }
}
  • 这里解码,能看到里面源码,又好像跟源码没有关系,in里面数据没有被读完还会调用decode这个方法
  • 所以如果超过8个字节,会调用多次这个方法
@Override
    public long readLong() {
        checkReadableBytes0(8);
        long v = _getLong(readerIndex);
        readerIndex += 8;
        return v;
    }

自己的业务处理器

public class MyServerHandler extends SimpleChannelInboundHandler<Long> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
        System.out.println("从客户端" + ctx.channel().remoteAddress() + "读取到long" + msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
  • 解码之后获取到long数据进行打印

客户端代码

public class MyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class).handler(new MyClientInitializer());

            ChannelFuture localhost = bootstrap.connect("localhost", 7000).sync();
            localhost.channel().closeFuture().sync();
        }finally {
            group.shutdownGracefully();
        }
    }
}
  • 一样设置通道,绑定端口,设置线程组处理以及异步处理

设置处理器

public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //加入一个出站的handler,对数据进行一个编码
        pipeline.addLast(new MyLongToByteEncoder());
        //加入一个自定义的handler,处理业务
        pipeline.addLast(new MyClientHandler());
    }
}
  • 与之对应的编码处理器和业务处理器

编码处理器

public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
    // 编码的方法
    @Override
    protected void encode(ChannelHandlerContext ctx, Long msg, ByteBuf out) throws Exception {
        System.out.println("MyLongToByteEncoder encode 被调用");
        System.out.println("msg=" + msg);
        out.writeLong(msg);
    }
}
  • 往ByteBuf里面写入数据

自己业务数据

public class MyClientHandler extends SimpleChannelInboundHandler<Long> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("MyClientHandler 发送数据");
        ctx.writeAndFlush(12456L);//发送的是Long数据
    }
}
  • 这个是最先被调用的,往通道里面写入数据
  • 然后通过那个编码器
  • 这么理解吧,就是客户端和服务端数据传输主要是靠writeAndFlush,写到channel里面,channel里面的数据的编码以及解码主要是靠自定义了
  • 简而言之,就是客户端往管道里放个long值,然后通过编码器以二进制的形式写入到ByteBuf中
  • 然后到服务器那边时,通过解码,8个字节8个字节进行读取到list集合中,然后再通过自定义的处理器进行输出

这块能看到啥也没读,但是解码了一个数据,对2222L进行了解析,这里面也调用了两次

注意

注意点就是如果传送的数据,和泛型中的数据不相同的话,有可能encode方法在源码中不会执行,也就是我们重写的方法不会被调用的

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值