作者:rickiyang
出处:www.cnblogs.com/rickiyang/p/11074232.html
咱们来应用 Protobuf 进行序列化,它和 XML,json 一样都有本人的语法,xml 的后缀是.xml,json 文件的后缀是.json,天然 Protobuf 文件的后缀就是.proto(哈哈,当然不是全称)。
上面咱们应用 Protobuf 来封装一段音讯,通过一个案例简略介绍一下它的应用。
首先咱们用 Protobuf 的语法格局来写一段须要序列化的对象,命名格局为:Msg.proto
option java_package = "cn.edu.hust.netty.demo10";
option java_outer_classname = "MessageProto";
message RequestMsg{
required bytes msgType = 1;
required string receiveOne = 2;
required string msg = 3;
}
message ResponseMsg{
required bytes msgType = 1;
required string receiveOne = 2;
required string msg = 3;
}
对于 Message.proto 中的语法格局,详情大家 google 一下相干的阐明,网上很多介绍,再次简略就下面的语法阐明一下:
- option java_package:示意生成的.java 文件的包名
- option java_outer_classname:生成的 java 文件的文件名
- message:为他的根本类型,如同 java 中的 class 一样
字段修饰符:
- required:一个格局良好的音讯肯定要含有 1 个这种字段。示意该值是必须要设置的;
- optional:音讯格局中该字段能够有 0 个或 1 个值(不超过 1 个)。
- repeated:在一个格局良好的音讯中,这种字段能够反复任意屡次(包含 0 次)。反复的值的程序会被保留。示意该值能够反复,相当于 java 中的 List。
字符类型略微有些不同:double,float,int32,int64,bool(boolean),string,bytes。略微有些不同,String,boolean,int 有差异。
另外咱们看到下面 3 个字段别离赋值了,这个值是什么意思呢?音讯定义中,每个字段都有惟一的一个 数字标识符 。这些标识符是用来在音讯的二进制格局中辨认各个字段的,一旦开始应用就不可能再扭转。注:[1,15] 之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用 2 个字节。所以应该为那些频繁呈现的音讯元素保留 [1,15]之内的标识号。
对于 Protobuf 的语法咱们就简略的介绍这么多,更多细节大家本人去查阅文档吧。上面咱们开始应用 Protobuf 来进行序列化。
首先咱们的在工程中引入 protobuf 的 jar 包,目前官网版本最高 3.2,咱们用 3.0 的吧:
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.0.2</version>
</dependency>
Protobuf 的文件曾经定义好了,下就须要把它编译成 java 代码,这里咱们的借助到 google 为咱们提供的脚本工具 protoc,链接在这里,点击下载这里提供的是 protoc-3.0.2。要留神 protoc 的版本须要和 Protobuf 的版本对应上,不然不同的版本之间会有一些差别解析可能会有问题。当初晓得咱们为啥非得选用 protobuf3.0.2 版本吧,因为我没有找到别的版本的 protoc。。。
下载好了咱们解压缩而后把方才写好的 Msg.proto 文件复制进去。
接着咱们进 cmd 输出如下命令:
次要是第三句命令。如果你输出没有报错的话你的 proto 文件夹应该会生成一个子文件夹:
进去该文件夹你会看到曾经生成了 MessageProto.java 文件,祝贺你,这时候你曾经实现了 protobuf 序列化文件的生成。而后你把该文件拷贝至工程目录下。
接下来咱们用生成的文件去发消息吧。还是老套路服务端和客户端。
服务端:
public class ProtoBufServer {
private int port;
public ProtoBufServer(int port) {this.port = port;}
public void start(){EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap server = new ServerBootstrap().group(bossGroup,workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerChannelInitializer());
try {ChannelFuture future = server.bind(port).sync();
future.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();
}finally {bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();}
}
public static void main(String[] args) {ProtoBufServer server = new ProtoBufServer(7788);
server.start();}
}
服务端 Initializer:
public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new ProtobufVarint32FrameDecoder());
pipeline.addLast(new ProtobufDecoder(MessageProto.RequestMsg.getDefaultInstance()));
pipeline.addLast(new ProtoBufServerHandler());
}
}
服务端 handler:
public class ProtoBufServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {MessageProto.ResponseMsg.Builder builder = MessageProto.ResponseMsg.newBuilder();
builder.setMsgType(ByteString.copyFromUtf8("CBSP"));
builder.setReceiveOne("小红");
builder.setMsg("你好,你有啥事");
ctx.writeAndFlush(builder.build());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {MessageProto.RequestMsg m = (MessageProto.RequestMsg)msg;
System.out.println("Client say:"+m.getReceiveOne()+","+m.getMsg());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {super.exceptionCaught(ctx, cause);
ctx.close();}
}
客户端:
public class ProtoBufClient {
private int port;
private String address;
public ProtoBufClient(int port, String address) {
this.port = port;
this.address = address;
}
public void start(){EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ClientChannelInitializer());
try {ChannelFuture future = bootstrap.connect(address,port).sync();
future.channel().closeFuture().sync();} catch (Exception e) {e.printStackTrace();
}finally {group.shutdownGracefully();
}
}
public static void main(String[] args) {ProtoBufClient client = new ProtoBufClient(7788,"127.0.0.1");
client.start();}
}
客户端 Initializer:
public class ClientChannelInitializer extends ChannelInitializer<SocketChannel> {protected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new ProtobufVarint32LengthFieldPrepender());
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new ProtoBufClientHandler());
}
}
客户端 handler:public class ProtoBufClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {MessageProto.ResponseMsg m = (MessageProto.ResponseMsg)msg;
System.out.println("Server say:"+m.getReceiveOne()+","+m.getMsg());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {MessageProto.RequestMsg.Builder builder = MessageProto.RequestMsg.newBuilder();
builder.setMsgType(ByteString.copyFromUtf8("CBSP"));
builder.setReceiveOne("小明");
builder.setMsg("你好,我找你有事");
ctx.writeAndFlush(builder.build());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {System.out.println("Client is close");
}
}
启动服务端和客户端,输入如下:
最简略的 protoBuf 利用案例咱们就写完了,实在的应用场景大同小异,随机应变即可。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿(2021 最新版)
2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!
3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!