Netty学习笔记_13(ProtoBuf在Netty中的简单应用)

15 篇文章 0 订阅

1、Netty编解码器

  1.  数据在网络中是以二进制字节码的形式流动,而我们在端接收或发送的数据形式则各种各样(文本、图片、音视频等),因此需要在发送端对数据进行编码,在接收端对收到的数据解码;
  2. codeC(编解码器)的组成部分——Encoder(编码器)负责将业务数据转换为二进制字节码;Decoder(解码器)负责将二进制字节码转换为业务数据;
  3. Netty编码机制——StringEncoder / StringDecoder负责字符串数据对象的编解码;ObjectEncoder / ObjectDecoder负责java对象的编解码;
  4. Netty自带的ObjectEncoder和ObjectDecoder可以用于实现POJO对象或其他业务对象的编解码,其底层使用的仍是java的序列化技术,存在以下问题:
    1)无法实现客户端与服务器端的跨语言;
    2)序列化体积过大,是二进制字节码的5倍多;
    3)序列化性能相对较低;

2、ProtoBuf概述

2-1 ProtoBuf简单介绍

  • protoBuf是一种平台无关语言无关的、可扩展轻便高效的序列化数据结构的协议,适合用于数据存储RPC(远程过程调用)数据交换格式
  • protoBuf是以Message的方式来管理数据的;
  • 所谓“平台无关、语言无关”,即客户端和服务器可以使用不同的编程语言进行开发;
  • protoBuf具有更高的性能和可靠性;
  • 使用ProtoBuf编译器可以自动生成代码,ProtoBuf是把类的定义使用.proto文件描述出来,在通过proto.exe.proto文件编译为.java文件:

 2-2在IDEA中使用protoBuf的步骤(Linux环境下)

  1. 安装protoc插件
  2. 在maven项目中引入ProtoBuf的坐标,下载相关的jar包:

    Pom.xml

    <dependencies>

            <!--https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->

            <dependency>

                <groupId>com.google.protobuf</groupId>

                <artifactId>protobuf-java</artifactId>

                <version>3.6.1</version>

            </dependency>

    </dependencies>

  3. 在IDEA中安装ProtoBuf Editor插件(用于编写.proto文件),安装ProtoBuf Generator插件(用于编译.proto文件,生成java文件)
  4. 编写.proto文件,并编译生成.java文件

3、ProtoBuf实例

3-1实例一

要求:

  1. 客户端可以发送一个Student PoJo对象到服务器(通过protobuf编码)
  2. 服务器能接收Student PoJo对象,并显示信息

实现:

Student PoJo对象:Student.proto

syntax = "proto3";  //版本
option java_outer_classname = "StudentPOJO"; //生成的外部类名称,同时文件名
//protobuf以message的形式管理数据
message Student{ //会在 studentPOJO 外部类生成一个内部类 Student,它是真正发送的POJO对象
  int32 id = 1; //表示 Student 类中有一个属性 名字为id,类型为 int32(protoType),1表示属性的序号
  string name = 2;
}

服务器端:NettyServer.java

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import jdk.nashorn.internal.runtime.linker.Bootstrap;

public class NettyServer {
    public static void main(String[] args) throws InterruptedException {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {

                            ChannelPipeline pipeline = ch.pipeline();
                            //在pipeline中加入ProtoBufferDecoder
                            //指定对哪一种对象进行解码
                            pipeline.addLast("decoder",new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });
            ChannelFuture cf = serverBootstrap.bind(6668).sync();

            //给 cf 添加监听器,监听我们感兴趣的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()){
                        System.out.println("绑定端口 6668 成功");
                    }else {
                        System.out.println(cf.cause());
                    }
                }
            });

            cf.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

自定义的服务器业务处理器:NettyServerHandler.java

package com.SF.Netty.codeC;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class NettyServerHandler extends SimpleChannelInboundHandler<StudentPOJO.Student> {
//public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//    @Override
//    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//
//        //读取从客户端发送的StudentPOJO.Student
//        StudentPOJO.Student student = (StudentPOJO.Student) msg;
//
//        System.out.println("客户端发送的数据 id = "+student.getId() + " 名字 = "+student.getName());
//    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端~",CharsetUtil.UTF_8));
    }

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

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, StudentPOJO.Student msg) throws Exception {

        System.out.println("*****客户端发送的数据 id = "+msg.getId() + " 名字 = "+msg.getName()+"*******");
    }
}

 客户端:NettyClient.java

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.ProtobufEncoder;


public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup eventExecutors = new NioEventLoopGroup();

        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventExecutors)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //在pipeline中加入ProtoBufferEncoder
                            ChannelPipeline pipeline = ch.pipeline();

                            pipeline.addLast("encoder", new ProtobufEncoder());
                            pipeline.addLast(new NettyClientHandler());

                        }
                    });

            System.out.println("客户端已准备就绪~~~~");

            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
            channelFuture.channel().closeFuture().sync();


        }finally {
            eventExecutors.shutdownGracefully();
        }
    }
}

自定义的客户端业务处理器:NettyClientHandler.java

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        //发送一个 student 对象到服务器
        StudentPOJO.Student student = StudentPOJO.Student.newBuilder().setId(1001).setName("张三").build();

        ctx.writeAndFlush(student);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回送消息:"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器端地址:"+ctx.channel().remoteAddress());
    }

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

3-2实例二

要求:

  1. 客户端可以随机发送Student PoJo / Worker PoJo对象到服务器
  2. 服务器能接收Student PoJo / Worker PoJo对象,判断是哪种类型,并显示信息

实现:

POJO对象:Student.proto

syntax = "proto3";
option optimize_for = SPEED; //快速解析
option java_package="com.SF.Netty.codeC2"; //指定生成到那个包下
option java_outer_classname = "MyDataInfo";  //指定外部类名称

//protoBuffer可以使用message管理其他的message
message MyMessage{
  //定义一个枚举类型
  enum DataType{
    StudentType = 0; //proto3中要求 enum 的编号从 0 开始
    WorkerType = 1;
  }
  //用data_type来标识传的是哪一个枚举类型
  DataType data_type = 1;
  //表示每次枚举类型最多只能出现其中的一个,节省空间
  oneof dataBody{
    Student student = 2;
    Worker worker = 3;
  }
}

message Student{
  int32 id = 1;
  string name = 2;
}
message Worker{
  string name = 1;
  int32 age = 2;
}

服务器端:NettyServer.java

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;

public class NettyServer {
    public static void main(String[] args) throws InterruptedException {

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {

                            ChannelPipeline pipeline = ch.pipeline();
                            //在pipeline中加入ProtoBufferDecoder
                            //指定对哪一种对象进行解码
                            pipeline.addLast("decoder",new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));
                            pipeline.addLast(new NettyServerHandler());
                        }
                    });
            ChannelFuture cf = serverBootstrap.bind(6668).sync();

            //给 cf 添加监听器,监听我们感兴趣的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (cf.isSuccess()){
                        System.out.println("绑定端口 6668 成功");
                    }else {
                        System.out.println(cf.cause());
                    }
                }
            });

            cf.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

自定义的服务器业务处理器:NettyServerHandler.java

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class NettyServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> {
//public class NettyServerHandler extends ChannelInboundHandlerAdapter {
//    @Override
//    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//
//        //读取从客户端发送的StudentPOJO.Student
//        StudentPOJO.Student student = (StudentPOJO.Student) msg;
//
//        System.out.println("客户端发送的数据 id = "+student.getId() + " 名字 = "+student.getName());
//    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端~",CharsetUtil.UTF_8));
    }

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

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MyDataInfo.MyMessage msg) throws Exception {

        //根据 DataType 显示不同的信息
        MyDataInfo.MyMessage.DataType dataType = msg.getDataType();
        if (dataType == MyDataInfo.MyMessage.DataType.StudentType){
            MyDataInfo.Student student = msg.getStudent();
            System.out.println("学生ID = "+student.getId() + " 学生名字 = "+student.getName());
        }else if (dataType == MyDataInfo.MyMessage.DataType.WorkerType){
            MyDataInfo.Worker worker = msg.getWorker();
            System.out.println("工人年龄 = "+worker.getAge() + " 工人名字 = "+worker.getName());
        }else {
            System.out.println("传输类型不正确");
        }
    }
}

 客户端:NettyClient.java

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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.ProtobufEncoder;


public class NettyClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup eventExecutors = new NioEventLoopGroup();

        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventExecutors)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //在pipeline中加入ProtoBufferEncoder
                            ChannelPipeline pipeline = ch.pipeline();

                            pipeline.addLast("encoder", new ProtobufEncoder());
                            pipeline.addLast(new NettyClientHandler());

                        }
                    });

            System.out.println("客户端已准备就绪~~~~");

            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
            channelFuture.channel().closeFuture().sync();


        }finally {
            eventExecutors.shutdownGracefully();
        }
    }
}
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

自定义的客户端业务处理器:NettyClientHandler.java

package com.SF.Netty.codeC2;

import com.SF.Netty.codeC.StudentPOJO;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.Random;

public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        //随机发送一个Student 或 worker对象
        int random = new Random().nextInt(3);
        MyDataInfo.MyMessage myMessage = null;

        if (random == 0){   //发送一个Student对象
            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType)
                    .setStudent(MyDataInfo.Student.newBuilder().setId(23).setName("李四")).build();

        }else{
            //发送一个worker对象
            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType)
                    .setWorker(MyDataInfo.Worker.newBuilder().setAge(20).setName("王五")).build();
        }

        ctx.writeAndFlush(myMessage);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("服务器回送消息:"+buf.toString(CharsetUtil.UTF_8));
        System.out.println("服务器端地址:"+ctx.channel().remoteAddress());
    }

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

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值