Netty网络编程六:Netty序列化编解码以及Protobuf序列化工具的使用

一:概述

在进行网络传输时,我们传输到网络上的数据,并不一定是字符串,而可能是一个对象,这时候我们就需要,将对象以有效的方式转为字节数组传输到网络中,然后收到数据的一方可以解析出有效的数据。例如在java中,我们需要传输对象,可以直接通过将ObjectOutputStream或ObjectInputStream直接作为可存储的字节数组传输到网络中。
通过java这种序列化对象的方式,存在一些问题:
1、无法跨语言
2、众所众知的java序列化后的数据,相比其他序列化方式产生的数据较大。

进行网络上传输的序列化工具有很多种,常用的有:
1、Protobuf
2、Marshalling
3、MesssagePack

二:Protobuf的使用

1、安装

地址:https://github.com/protocolbuffers/protobuf/releases
下载对应的版本安装
安装后配置下bin目录到环境变量path中,最后调试结果:
在这里插入图片描述
表示可运行…

2、protobuf的使用

安装后通过在所使用的proto文件路径下打开cmd窗口执行以下命令:

protoc -I=源地址 --java_out=目标地址  源地址/xxx.proto

生成出代码

3、netty中protobuf的使用

看一个使用客户端服务端通信的protobuf案例;
定义数据用户传输的proto文件:
在这里插入图片描述
通过proto生成出java代码并copy到项目中.
在这里插入图片描述

服务端:

public class NettyProtobufServer {

    private int port;

    public NettyProtobufServer(int port){
        this.port = port;
    }

    public void start(){
        // boss 是处理客户端连接的线程池
        // worker 是处理从客户端连接转发过来的IO数据的读写线程池
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        try{
            // ServerBootstrap 对外一个便利创建服务端,Builder建造者设计模式
            ServerBootstrap sb = new ServerBootstrap();
            // 绑定线程池
            sb.group(boss,worker)
                    // 绑定channel 服务端绑定NioServerSocketChannel,此实现jdk的ServerSocketChannel
                    .channel(NioServerSocketChannel.class)
                    // 绑定服务端相关参数,可添加绑定多个参数
                    .option(ChannelOption.SO_BACKLOG, 1024) //指定此套接口排队的最大连接个数
                    // IO事件处理类,主要处理IO事件的读写
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 用于半包处理
                            pipeline.addLast(new ProtobufVarint32FrameDecoder());
                            // 解码器,ProtobufDecoder参数是告诉解码器的目标类
                            pipeline.addLast(new ProtobufDecoder(SubscribeReqProto.SubscribeReq.getDefaultInstance()));
                            // 编码,对要发送的数据进行封装处理,头长度,尾部数据
                            pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
                            // protobuf编码
                            pipeline.addLast(new ProtobufEncoder());
                            pipeline.addLast(new SubReqServerHandler());
                        }
                    });
            // 绑定端口,同步等待成功
            ChannelFuture cf = sb.bind(port).sync();
            System.out.println("服务已启动.................监听端口:" + port);
            // 等待服务器监听端口关闭
            cf.channel().closeFuture().sync();
        }catch (Exception e){
            // 优雅关闭线程资源
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        NettyProtobufServer nettyServer = new NettyProtobufServer(9090);
        nettyServer.start();
    }
}

服务端handler处理:

public class SubReqServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq) msg;
        System.out.println("接受的数据: " + req.toString());
        ctx.writeAndFlush(responseMsg(req.getSubReqId()));
    }

    private SubscribeReqProto.SubscribeResp responseMsg(int subReqID){
        SubscribeReqProto.SubscribeResp.Builder builder = SubscribeReqProto.SubscribeResp.newBuilder();
        builder.setSubReqId(subReqID);
        builder.setRespCode("0");
        builder.setDesc("fangyouyun");
        return builder.build();
    }

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

客户端:

public class NettyProtobufClient {

    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        try{
            Bootstrap b = new Bootstrap();
            b.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(SubscribeReqProto.SubscribeResp.getDefaultInstance()));
                            ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
                            ch.pipeline().addLast(new ProtobufEncoder());
                            ch.pipeline().addLast(new SubReqClientHandler());
                        }
                    });
            // 异步连接服务端
            ChannelFuture f = b.connect(new InetSocketAddress(9090)).sync();
            // 等待关闭
            f.channel().closeFuture().sync();
        }finally {
            //优雅退出,是否线程池资源
            group.shutdownGracefully();
        }
    }
}

客户端handler处理:

public class SubReqClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i=0; i<10; i++){
            System.out.println("发送消息......");
            ctx.write(subReq(i));
        }
        ctx.flush();
    }

    private SubscribeReqProto.SubscribeReq subReq(int i) {
        SubscribeReqProto.SubscribeReq.Builder builder = SubscribeReqProto.SubscribeReq.newBuilder();
        builder.setSubReqId(i);
        builder.setUserName("fangyouyun");
        builder.setProductName("netty learning");
        return builder.build();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("收到服务端的消息: " + msg);
    }

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

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

如此运行服务端,客户端即可通信。
注意, 在使用protobuf进行netty的客户端服务端通信时,ProtobufDecoder仅仅只是对通信数据解码,并不处理半包问题,所以netty针对protobuf处理半包问题,可以采用三种方式:
1.使用netty提供的ProtobufVarint32FrameDecoder,它可以处理半包问题
2.使用netty的ByteToMessageDecoder类,自己实现解码器处理半包问题
3.使用netty提供的通用半包处理器LengthFieldBasedFrameDecoder处理半包问题

文章中案例代码:https://download.csdn.net/download/qq_22871607/11072379

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值