目录
一、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使用,应该是掌握了吧!