精通并发与Netty(Google Protobuf 多协议支持)

在netty数据传输里,客户端和服务端同一时间仅一种协议生效,但proto文件可能定义多个message。需通过枚举方式实现多协议传递,文中给出了proto文件定义方式,还列举了服务端和客户端相关代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在netty的数据传输中,客户端和服务端只能同时有一种协议生效,而在proto文件中却可能会存在定义多个message,那么就需要我们通过枚举的方式去实现多协议的传递方式。

如下是proto文件的定义方式

Person.proto

syntax="proto2";

//文件特性
package com.test.protobuf;
option optimize_for = SPEED; //加快解析速度 默认是SPEED
option java_package = "com.netty.sixth";
option java_outer_classname = "MyDateInto";

message MyMessage{

    enum DataType{
        PersonType = 1;
        DogType = 2;
        CatType = 3;
    }
    required DataType data_type = 1;
    //oneof 数据体
    oneof dataBody{
        Person person = 2;
        Dog dog = 3;
        Cat cat = 4;
    }
}

message Person{
    optional string name = 1;
    optional int32 age = 2;
    optional string address = 3;
}

message Dog{
    optional string name=1;
    optional int32 age=2;
}
message Cat{
    optional string name=1;
    optional string city=2;
}

执行protoc操作会生成对应的class MyDataInfo

服务端代码:

TestServer.java

public class TestServer {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new TestServerInitializer());
            ChannelFuture channelFuture = bootstrap.bind(8899).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

     TestServerInitializer.java

public class TestServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //netty 提供了四个处理器进行protobuf的编解码处理
        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        //ProtobufDecoder解码器:将字节数组转换成对象 参数就是要转换的类的实例
        pipeline.addLast(new ProtobufDecoder(MyDateInto.MyMessage.getDefaultInstance()));
        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        pipeline.addLast(new ProtobufEncoder());
        pipeline.addLast(new TestServerHandler());
    }
}

TestServerHandler.java

//数据传递类型为协议最顶层的message
public class TestServerHandler extends SimpleChannelInboundHandler<MyDateInto.MyMessage> {
    @Override
    //此时过来的时候就已经被netty的handler处理了
    protected void channelRead0(ChannelHandlerContext ctx, MyDateInto.MyMessage msg) throws Exception {
        //对接收到的对象进行处理
        MyDateInto.MyMessage.DataType dataType = msg.getDataType();
        if(dataType == MyDateInto.MyMessage.DataType.PersonType){
            MyDateInto.Person person = msg.getPerson();
            System.out.println(person.getName());
            System.out.println(person.getAge());
            System.out.println(person.getAddress());
        }else if (dataType == MyDateInto.MyMessage.DataType.DogType){
            MyDateInto.Dog dog = msg.getDog();
            System.out.println(dog.getName());
            System.out.println(dog.getAge());
        }else{
            MyDateInto.Cat cat = msg.getCat();
            System.out.println(cat.getName());
            System.out.println(cat.getCity());
        }
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

 

客户端代码

TestClient.java

public class TestClient {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try{
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
                    .handler(new TestClientInitializer());
            Channel channel = bootstrap.connect("localhost",8899).sync().channel();
            channel.closeFuture().sync();
        }finally {
            eventLoopGroup.shutdownGracefully();
        }
    }
}

TestClientInitializer.java

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

        pipeline.addLast(new ProtobufVarint32FrameDecoder());
        pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        pipeline.addLast(new ProtobufDecoder(MyDateInto.MyMessage.getDefaultInstance()));
        pipeline.addLast(new ProtobufEncoder());
        pipeline.addLast(new TestClientHandler());
    }
}

TestClientHandler.java

public class TestClientHandler extends SimpleChannelInboundHandler<MyDateInto.MyMessage> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MyDateInto.MyMessage msg) throws Exception {
        System.out.println(msg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        int randomInt = new Random().nextInt(3); //0,1,2
        MyDateInto.MyMessage myMessage = null;
        //构造对象
        if (0 == randomInt){
             myMessage = MyDateInto.MyMessage.newBuilder().
                    setDataType(MyDateInto.MyMessage.DataType.PersonType).
                    setPerson(MyDateInto.Person.newBuilder().setName("张三")
                    .setAge(25).setAddress("湖南").build()).build();
        }else if (1== randomInt){
            myMessage = MyDateInto.MyMessage.newBuilder().
                    setDataType(MyDateInto.MyMessage.DataType.DogType).
                    setDog(MyDateInto.Dog.newBuilder().setName("德牧")
                            .setAge(25).build()).build();
        }else{
            myMessage = MyDateInto.MyMessage.newBuilder().
                    setDataType(MyDateInto.MyMessage.DataType.CatType).
                    setCat(MyDateInto.Cat.newBuilder().setName("龙猫")
                            .setCity("西班牙").build()).build();
        }
        ctx.writeAndFlush(myMessage);
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值