五、通过Protobuf集成Netty实现对协议消息客户端与服务器通信实战

版权声明:如需转载,请标明转载出处! https://blog.csdn.net/Z0157/article/details/83051995

目录

一、Protocol Buffers 是什么?

二、Protocol Buffers 文件和消息详解

三、项目实战,直接掌握的protobuf应用。


一、Protocol Buffers 是什么?

        1、官网翻译之后如下:

               Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

        2、RPC:Remote Procedure Call ,远程过程调用,很多RPC框架是跨语言的。
             (1)、定义一个接口文件:描述了对象(结构体)、对象成员,接口方法等一系列信息。
             (2)、通过RPC框架所提供的编译器,将接口说明文件编译成具体的语言文件。
             (3)、在客户端与服务器端分别引入RPC编译器所生产的文件,即可像调用本地方法一样调用远程方法。

二、Protocol Buffers 文件和消息详解

     1、安装解压配置环境,就直接省略了,太简单了。

      2、my_example.proto 配置文件详解。 

syntax = "proto2";
//包名
package tutorial; 
//以下面包名为主,可以省略,如果省略以tutoria1为包名
option java_package = "com.example.tutorial";
//生成一个类名字,是public 的外部类的名字,如果不定义则会默认会取文件名字
//以驼峰的形式显示出来MyExample作为类名。
option java_outer_classname = "AddressBookProtos";

//下面的类都是java_outer_classname的内部类
message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

required:
必须提供字段的值,否则消息将被视为“未初始化”。尝试构建一个未初始化的消息将抛
出一个RuntimeException。解析未初始化的消息将抛出IOException。除此之外,
所需的字段与可选字段完全一样。
ps:谷歌的一些工程师已经得出结论,使用required弊大于利;他们喜欢只使用可选的和重复的。
然而,这种观点并不普遍。

optional:
该字段可能也可能不设置。如果未设置可选字段值,则使用缺省值。对于简单类型来说,
您可以指定自己的默认值,就像我们在例子中为电话号码类型所做的那样。否则,就会
使用系统默认值:数值类型为0,字符串为空字符串,bools为false。对于嵌入式消息
来说,默认值始终是消息的“默认实例”或“prototype”,它的字段没有设置。调用
accessor来获取未显式设置的可选(或必需)字段的值总是返回该字段的默认值。

repeated:
该字段可以多次重复(包括零)。重复值的顺序将保留在协议缓冲区中。把重复的字段
看作是动态大小的数组.

编译.proto文件
protoc --java_out= src/main/java(输出路径)  src/protobuf/TransData.proto(目标文件)

三、项目实战,直接掌握的protobuf应用。

项目总览:

syntax = "proto2";

package com.zhurong.protobuf;

option optimize_for = SPEED;
option java_package = "com.zhurong.protobuf";
option java_outer_classname = "TransData";

message MessageIndex{
    enum MessageType{
        MessageBodyPartIndex1 = 1;
        MessageBodyPartIndex2 = 2;
        MessageBodyPartIndex3 = 3;
    }
    required MessageType message_type = 1;
    oneof messageBody{
        MessageBodyPart1 messageBodyPart1 = 2;
        MessageBodyPart2 messageBodyPart2 = 3;
        MessageBodyPart3 messageBodyPart3 = 4;
    }
}
message MessageBodyPart1 {
    required string name = 1;
    required int32 id = 2;
    optional string email = 3;
 }

message MessageBodyPart2 {
    required string name = 1;
    required int32 id = 2;
    optional string email = 3;
}

message MessageBodyPart3 {
    required string name = 1;
    required int32 id = 2;
    optional string email = 3;
}

       

生成java类如下:

// Generated by the protocol buffer compiler.  DO NOT EDIT!
// source: src/protobuf/TransData.proto

package com.zhurong.protobuf.message;

public final class TransData {
  private TransData() {}
  public static void registerAllExtensions(
      com.google.protobuf.ExtensionRegistryLite registry) {
  }

  public static void registerAllExtensions(
      com.google.protobuf.ExtensionRegistry registry) {
    registerAllExtensions(
        (com.google.protobuf.ExtensionRegistryLite) registry);
  }
  public interface MessageIndexOrBuilder extends
      // @@protoc_insertion_point(interface_extends:com.zhurong.protobuf.MessageIndex)
      com.google.protobuf.MessageOrBuilder {

    /**
     * <code>required .com.zhurong.protobuf.MessageIndex.MessageType message_type = 1;</code>
     */
    boolean hasMessageType();
    /**
     * <code>required .com.zhurong.protobuf.MessageIndex.MessageType message_type = 1;</code>
     */
    TransData.MessageIndex.MessageType getMessageType();

    /**
     * <code>optional .com.zhurong.protobuf.MessageBodyPart1 messageBodyPart1 = 2;</code>
     */
    boolean hasMessageBodyPart1();
    /**
     * <code>optional .com.zhurong.protobuf.MessageBodyPart1 messageBodyPart1 = 2;</code>
     */
    TransData.MessageBodyPart1 getMessageBodyPart1();
    /**
     * <code>optional .com.zhurong.protobuf.MessageBodyPart1 messageBodyPart1 = 2;</code>
     */
    TransData.MessageBodyPart1OrBuilder getMessageBodyPart1OrBuilder();

  // @@protoc_insertion_point(outer_class_scope)

    .. .. ..  .  . . . . . .
    .. .. ..  .  . . . . . .
    .. .. ..  .  . . . . . .
    .. .. ..  .  . . . . . .
    .. .. ..  .  . . . . . .
       <省略>
}

服务器端代码:

package com.zhurong.protobuf.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

/**
 * Description:
 * User: zhurong
 * Date: 2018-09-24  23:17
 */
public class NettyServerProtoBuf {

    public static void main(String[] args) {
        //接收连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //连接发送给work
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            System.out.println("服务器启动成功!");
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).
                    handler(new LoggingHandler(LogLevel.INFO)).
                    childHandler(new NettyServerProtobufInitializer());
            ChannelFuture channelFuture = serverBootstrap.bind(8000).sync();
            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}

package com.zhurong.protobuf.server;

import com.zhurong.protobuf.message.TransData;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

/**
 * Description:
 * User: zhurong
 * Date: 2018-10-14  9:53
 */
public class NettyServerProtobufInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline channelPipeline = ch.pipeline();

        channelPipeline.addLast(new ProtobufVarint32FrameDecoder());
        channelPipeline.addLast(new ProtobufDecoder(TransData.MessageIndex.getDefaultInstance()));
        channelPipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        channelPipeline.addLast(new ProtobufEncoder());
        channelPipeline.addLast(new NettyServerProtoBufHandler());
    }
}

package com.zhurong.protobuf.server;

import com.zhurong.protobuf.message.TransData;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.concurrent.EventExecutorGroup;

/**
 * Description:
 * User: zhurong
 * Date: 2018-10-14  9:59
 */
public class NettyServerProtoBufHandler extends SimpleChannelInboundHandler<TransData.MessageIndex> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TransData.MessageIndex msg) throws Exception {
        if((TransData.MessageIndex.MessageType)msg.getMessageType()
                == TransData.MessageIndex.MessageType.MessageBodyPartIndex1){
            System.out.println(msg.getMessageBodyPart1().getId());
            System.out.println(msg.getMessageBodyPart1().getName());
            System.out.println(msg.getMessageBodyPart1().getEmail());
        }

    }
}

客户端代码:

package com.zhurong.protobuf.server;

import com.zhurong.protobuf.message.TransData;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.concurrent.EventExecutorGroup;

/**
 * Description:
 * User: zhurong
 * Date: 2018-10-14  9:59
 */
public class NettyServerProtoBufHandler extends SimpleChannelInboundHandler<TransData.MessageIndex> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TransData.MessageIndex msg) throws Exception {
        if((TransData.MessageIndex.MessageType)msg.getMessageType()
                == TransData.MessageIndex.MessageType.MessageBodyPartIndex1){
            System.out.println(msg.getMessageBodyPart1().getId());
            System.out.println(msg.getMessageBodyPart1().getName());
            System.out.println(msg.getMessageBodyPart1().getEmail());
        }

    }
}


package com.zhurong.protobuf.client;

import com.zhurong.protobuf.message.TransData;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
 * Description:
 * User: zhurong
 * Date: 2018-10-14  20:53
 */
public class NettyClientProtoBufHandler extends SimpleChannelInboundHandler<TransData.MessageIndex>{

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TransData.MessageIndex msg) throws Exception {

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {

        TransData.MessageIndex messageIndex = TransData.MessageIndex.newBuilder()
                .setMessageType(TransData.MessageIndex.MessageType.MessageBodyPartIndex1)
                .setMessageBodyPart1(TransData.MessageBodyPart1.newBuilder()
                .setId(11111).setEmail("23242343255").setName("jim").build())
                .build();

        ctx.channel().writeAndFlush(messageIndex);
    }
}

package com.zhurong.protobuf.client;

import com.zhurong.protobuf.message.TransData;
import com.zhurong.protobuf.server.NettyServerProtoBufHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;

/**
 *  Netstat –ano|findstr
 * Description: 客户端与服务器端连接一旦创建,这个类中方法就会被回调
 * User: zhurong
 * Date: 2018-09-24  21:29
 */
public class NettyClientProtoBufInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline channelPipeline = ch.pipeline();

        channelPipeline.addLast(new ProtobufVarint32FrameDecoder());
        channelPipeline.addLast(new ProtobufDecoder(TransData.MessageIndex.getDefaultInstance()));
        channelPipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
        channelPipeline.addLast(new ProtobufEncoder());
        channelPipeline.addLast(new NettyClientProtoBufHandler());
    }
}

多协议消息解决方法就是在消息最前面加一个标识,告诉服务器这个哪一个消息体,当然这个是属于自定义编解码格式。

上面我只是写了一个分支,完整的是后面2个body消息体,都应该加上去放在else if里面。此处就不过多讲解了。到这里protobuf使用,应该是掌握了吧!

猜你喜欢

转载自blog.csdn.net/Z0157/article/details/83051995