Netty編解碼之ProtoBuf案例二

  前面我們介紹了Protobuf的基本使用,但是我們是一個POJO對象創建一個proto文件,那麼在實際環境中將要創建多個文件,會非常的不方便,本文我們來看看怎麼根據類型來動態處理

Protobuf案例二

proto文件

  在proto文件中我們通過message來管理類型,具體如下

syntax = "proto3";
option optimize_for = SPEED; // 加快解析
option java_package="com.dpb.netty.codec2";   //指定生成到哪個包下
option java_outer_classname="MyDataInfo"; // 外部類名, 文件名

//protobuf 可以使用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;//Student類的屬性
    string name = 2; //
}
message Worker {
    string name=1;
    int32 age=2;
}

對應的POJO文件

  通過proto.exe文件來動態生成pojo文件。

在這裏插入圖片描述

服務端代碼

  將生成的文件拷貝進項目中,然後編寫服務端代碼。

package com.dpb.netty.codec2;

import com.dpb.netty.codec.StudentPojo;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
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;

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:15
 */
public class NettyServerDemo {
    public static void main(String[] args) {
        // 創建對應的 線程池
        // 創建Boss group
        EventLoopGroup boosGroup = new NioEventLoopGroup(1);
        // 創建 workgroup
        EventLoopGroup workGroup = new NioEventLoopGroup();
        // 創建對應的啓動類
        ServerBootstrap bootstrap = new ServerBootstrap();
        try{
            // 設置相關的配置信息
            bootstrap.group(boosGroup,workGroup) // 設置對應的線程組
                    .channel(NioServerSocketChannel.class) // 設置對應的通道
                    .option(ChannelOption.SO_BACKLOG,1024) // 設置線程的連接個數
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 設置
                        /**
                         * 給pipeline 設置處理器
                         * @param socketChannel
                         * @throws Exception
                         */
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 指定Protobuf解碼
                            socketChannel.pipeline().addLast("decoder"
                                    ,new ProtobufDecoder(MyDataInfo.MyMessage.getDefaultInstance()));
                            socketChannel.pipeline().addLast(new NettyServerHandler());
                        }
                    });
            System.out.println("服務啓動了....");
            // 綁定端口  啓動服務
            ChannelFuture channelFuture = bootstrap.bind(6668).sync();
            // 對關閉通道進行監聽
            channelFuture.channel().closeFuture().sync();
        }catch (Exception e){

        }finally {
            // 優雅停服
            boosGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }

    }
}

package com.dpb.netty.codec2;

import com.dpb.netty.codec.StudentPojo;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:24
 */
public class NettyServerHandler extends SimpleChannelInboundHandler<MyDataInfo.MyMessage> {



    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyDataInfo.MyMessage data) throws Exception {
        // 得根據不同的類型來獲取對應的數據
        MyDataInfo.MyMessage.DataType type = data.getDataType();
        if(type == MyDataInfo.MyMessage.DataType.StudentType){
            // 表示傳遞過來的是 Student類型
            System.out.println("學生信息:" + data.getStudent().getId() + " " + data.getStudent().getName());
        }else if(type == MyDataInfo.MyMessage.DataType.WorkerType){
            // 表示傳遞的是 worker類型
            System.out.println("worker信息:" + data.getWorker().getName() + " " + data.getWorker().getAge());
        }else{
            System.out.println("類型不匹配.... ");
        }
    }

    /**
     * 讀取客戶端發送數據完成後的方法
     *    在本方法中可以發送返回的數據
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // writeAndFlush 是組合方法
        ctx.writeAndFlush(Unpooled.copiedBuffer("你好啊,客戶端....^_^",CharsetUtil.UTF_8));
    }
}

注意解碼器的位置

客戶端代碼

package com.dpb.netty.codec2;

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

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:31
 */
public class NettyClientDemo {

    public static void main(String[] args) {
        // 客戶端就只需要創建一個 線程組了
        EventLoopGroup loopGroup = new NioEventLoopGroup();
        // 創建 啓動器
        Bootstrap bootstrap = new Bootstrap();
        try{
            // 設置相關的參數
            bootstrap.group(loopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            // 指定protobu編碼
                            socketChannel.pipeline().addLast("encoder",new ProtobufEncoder());
                            socketChannel.pipeline().addLast(new NettyClientHandler());
                        }
                    });
            // 連接服務
            ChannelFuture future = bootstrap.connect("localhost",6668).sync();
            // 對服務關閉 監聽
            future.channel().closeFuture().sync();
        }catch (Exception e){

        }finally {
            loopGroup.shutdownGracefully();
        }

    }
}

package com.dpb.netty.codec2;

import com.dpb.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;

/**
 * @program: netty4demo
 * @description:
 * @author: 波波烤鴨
 * @create: 2019-12-23 11:36
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 連接上服務的回調方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 發送數據
        //隨機的發送Student 或者 Workder 對象
        int random = new Random().nextInt(3);
        MyDataInfo.MyMessage myMessage = null;

        if(0 == random) { //發送Student 對象

            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.StudentType).setStudent(MyDataInfo.Student.newBuilder().setId(666).setName("波波烤鴨").build()).build();
        } else { // 發送一個Worker 對象

            myMessage = MyDataInfo.MyMessage.newBuilder().setDataType(MyDataInfo.MyMessage.DataType.WorkerType).setWorker(MyDataInfo.Worker.newBuilder().setAge(20).setName("鄧師傅").build()).build();
        }

        ctx.writeAndFlush(myMessage);
    }

    /**
     * 讀取服務端返回的信息
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf  = (ByteBuf) msg;
        System.out.println("服務端返回的信息:" + buf.toString(CharsetUtil.UTF_8));
    }
}

測試

  先啓動服務器,然後啓動多個客戶端。
在這裏插入圖片描述

通過輸出結果我們可以看到,服務器可以根據不同的類型獲取到對應的POJO對象中的數據,會比原來單一的處理要更加的靈活些!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章