protobuf是由Google开发的一套对数据结构进行序列化的方法,可用做通信协议,数据存储格式,等等。其特点是不限语言、不限平台、扩展性强

Netty也提供了对Protobuf的天然支持,我们今天就写一个简单的示例,简单地了解一下Netty对Google的protoBuf的支持

我们的示例场景很简单的:客户端发送一个信息,这个信息用Protobuf来做序列化,然后服务器端接收这个信息,解码,读取信息

protobuf与xml,json这样的数据格式一样,都有自己的一套语法,且语法很简单,很容易掌握,xml文件的后缀名是xml,json的后缀名是json,以此类推,那么protobuf的后缀名就是proto

关于proto的基本语法与java的bean很像,详细可以参考官网,可以看下这篇博客:

http://blog.sina.com.cn/s/blog_9b0604b40101qm35.html

现在我们定义一个类似java bean的proto文件,我们定义一个“富人”类,他有多辆车,我们先按照语法,写一个RichMan.proto,如下面的代码清单所示:

  1. package netty;
  2. option java_package = "com.lyncc.netty.codec.protobuf.demo";
  3. option java_outer_classname = "RichManProto";
  4. message RichMan {
  5. required int32 id = 1;
  6. required string name = 2;
  7. optional string email = 3;
  8. enum CarType {
  9. AUDI = 0;
  10. BENZ = 1;
  11. LAMBORGHINI = 2;
  12. DASAUTO = 3;
  13. }
  14. message Car {
  15. required string name = 1;
  16. optional CarType type = 2 [default = BENZ];
  17. }
  18. repeated Car cars = 4;
  19. }

给出上面代码的一些基本解释:

1)java_package值得是该文件生成的java文件的包路径

2)java_outer_classname值的是生成的class的名称

3)message和enum是它的基本类型,很类似于java的class和枚举

4)required表名这个字段是必须的,option表明这个字段可选,default表明这个字段有默认值

5)repeat表明这个字段可以重复,类似于java中的List,该例子中Car的声明中,就相当于java中的List<Car>

6)每个声明的后面的数字,例如1,2,3, 4等等,同级的声明不能重复

总而言之,这个“类”定义了一个富人,该富人有id,名称,邮箱,而且该富人有多个名车,这些名车的类型有奥迪,奔驰,兰博基尼,大众

好了,到目前为止,proto我们已经定义好了,Google提供了一个类似脚本的工具,可以使我们将proto文件转化成java文件

该文件叫做protoc-2.6.1-win32.zip,可以在很多地方下载到,下载地址:

http://download.csdn.net/detail/linuu/9515171

下载好,新建文件夹,且将加载的exe复制到该文件夹,且将我们刚才写的RichMan.proto复制到该文件夹下:

进入命令行,键入:

没有报错的情况下,会在同样的文件夹下生成如下的文件:

进入com的文件夹,你会发现生成的目录是与你proto中定义的java_package一样:

好了,到目前为止,我们已经生成了RichManProto文件了,将其复制到eclipse对应的目录下,整个项目代码的缩略图如下图所示:

添加maven的依赖:

  1. <dependency>
  2. <groupId>com.google.protobuf</groupId>
  3. <artifactId>protobuf-java</artifactId>
  4. <version>2.6.1</version>
  5. </dependency>

注意就是版本必须是2.6.1,因为我们用的是protoc-2.6.1的exe去编译的,所以版本必须保持一致,否则有可能会报错

接下来就是一些大家耳熟能详的server,handler,client,bootstrap,废话多不说,上代码:

ProtoBufServer.java

  1. package com.lyncc.netty.codec.protobuf.demo;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioServerSocketChannel;
  10. import io.netty.handler.codec.protobuf.ProtobufDecoder;
  11. import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
  12. import io.netty.handler.logging.LogLevel;
  13. import io.netty.handler.logging.LoggingHandler;
  14. public class ProtoBufServer {
  15. public void bind(int port) throws Exception {
  16. // 配置服务端的NIO线程组
  17. EventLoopGroup bossGroup = new NioEventLoopGroup();
  18. EventLoopGroup workerGroup = new NioEventLoopGroup();
  19. try {
  20. ServerBootstrap b = new ServerBootstrap();
  21. b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
  22. .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
  23. @Override
  24. public void initChannel(SocketChannel ch) {
  25. ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
  26. ch.pipeline().addLast(new ProtobufDecoder(RichManProto.RichMan.getDefaultInstance()));
  27. ch.pipeline().addLast(new ProtoBufServerHandler());
  28. }
  29. });
  30. // 绑定端口,同步等待成功
  31. ChannelFuture f = b.bind(port).sync();
  32. System.out.println("init start");
  33. // 等待服务端监听端口关闭
  34. f.channel().closeFuture().sync();
  35. } finally {
  36. // 优雅退出,释放线程池资源
  37. bossGroup.shutdownGracefully();
  38. workerGroup.shutdownGracefully();
  39. }
  40. }
  41. public static void main(String[] args) throws Exception {
  42. int port = 8080;
  43. if (args != null && args.length > 0) {
  44. try {
  45. port = Integer.valueOf(args[0]);
  46. } catch (NumberFormatException e) {
  47. // 采用默认值
  48. }
  49. }
  50. new ProtoBufServer().bind(port);
  51. }
  52. }

可以看见,这个sever的bootstrap与其他的sever很相似,只是channelPipeline中在自定义的handler之前添加了netty对protobuf支持的两个天然的decoder

我没有深究,看名字就知道第一个Decoder是将帧byte数据转化成message,第二步就是将message转化成我们自定义的Rimanproto

自定义的handler很简单,就是打印一下解析的内容:

ProtoBufServerHandler.java

  1. package com.lyncc.netty.codec.protobuf.demo;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.channel.ChannelInboundHandlerAdapter;
  4. import java.util.List;
  5. import com.lyncc.netty.codec.protobuf.demo.RichManProto.RichMan.Car;
  6. public class ProtoBufServerHandler extends ChannelInboundHandlerAdapter {
  7. @Override
  8. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  9. RichManProto.RichMan req = (RichManProto.RichMan) msg;
  10. System.out.println(req.getName()+"他有"+req.getCarsCount()+"量车");
  11. List<Car> lists = req.getCarsList();
  12. if(null != lists) {
  13. for(Car car : lists){
  14. System.out.println(car.getName());
  15. }
  16. }
  17. }
  18. @Override
  19. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  20. cause.printStackTrace();
  21. ctx.close();
  22. }
  23. }

客户端代码

ProtoBufClient.java

  1. package com.lyncc.netty.codec.protobuf.demo;
  2. import io.netty.bootstrap.Bootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioSocketChannel;
  10. import io.netty.handler.codec.protobuf.ProtobufEncoder;
  11. import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
  12. public class ProtoBufClient {
  13. public void connect(int port, String host) throws Exception {
  14. // 配置客户端NIO线程组
  15. EventLoopGroup group = new NioEventLoopGroup();
  16. try {
  17. Bootstrap b = new Bootstrap();
  18. b.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
  19. .handler(new ChannelInitializer<SocketChannel>() {
  20. @Override
  21. public void initChannel(SocketChannel ch) throws Exception {
  22. ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
  23. ch.pipeline().addLast(new ProtobufEncoder());
  24. ch.pipeline().addLast(new ProtoBufClientHandler());
  25. }
  26. });
  27. // 发起异步连接操作
  28. ChannelFuture f = b.connect(host, port).sync();
  29. // 当代客户端链路关闭
  30. f.channel().closeFuture().sync();
  31. } finally {
  32. // 优雅退出,释放NIO线程组
  33. group.shutdownGracefully();
  34. }
  35. }
  36. /**
  37. * @param args
  38. * @throws Exception
  39. */
  40. public static void main(String[] args) throws Exception {
  41. int port = 8080;
  42. if (args != null && args.length > 0) {
  43. try {
  44. port = Integer.valueOf(args[0]);
  45. } catch (NumberFormatException e) {
  46. // 采用默认值
  47. }
  48. }
  49. new ProtoBufClient().connect(port, "127.0.0.1");
  50. }
  51. }

需要注意的是:

在传输之前需要将你的类进行protobuf的序列化,这是两个序列化的编码器

接着看:

ProtoBufClientHandler.java

  1. package com.lyncc.netty.codec.protobuf.demo;
  2. import io.netty.channel.ChannelHandlerContext;
  3. import io.netty.channel.ChannelInboundHandlerAdapter;
  4. import java.util.ArrayList;
  5. import java.util.List;
  6. import com.lyncc.netty.codec.protobuf.demo.RichManProto.RichMan.Car;
  7. import com.lyncc.netty.codec.protobuf.demo.RichManProto.RichMan.CarType;
  8. public class ProtoBufClientHandler extends ChannelInboundHandlerAdapter {
  9. @Override
  10. public void channelActive(ChannelHandlerContext ctx) {
  11. System.out.println("=======================================");
  12. RichManProto.RichMan.Builder builder = RichManProto.RichMan.newBuilder();
  13. builder.setName("王思聪");
  14. builder.setId(1);
  15. builder.setEmail("wsc@163.com");
  16. List<RichManProto.RichMan.Car> cars = new ArrayList<RichManProto.RichMan.Car>();
  17. Car car1 = RichManProto.RichMan.Car.newBuilder().setName("上海大众超跑").setType(CarType.DASAUTO).build();
  18. Car car2 = RichManProto.RichMan.Car.newBuilder().setName("Aventador").setType(CarType.LAMBORGHINI).build();
  19. Car car3 = RichManProto.RichMan.Car.newBuilder().setName("奔驰SLS级AMG").setType(CarType.BENZ).build();
  20. cars.add(car1);
  21. cars.add(car2);
  22. cars.add(car3);
  23. builder.addAllCars(cars);
  24. ctx.writeAndFlush(builder.build());
  25. }
  26. @Override
  27. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  28. cause.printStackTrace();
  29. ctx.close();
  30. }
  31. }

好了,到此为止,所有的代码已经写完毕了,我们运行测试一下:

服务器端启动:

启动客户端后,再看看服务器端的控制台:

好了,到此为止,最简单的demo已经搭建完毕了~

Netty使用Google的ProtoBuf的更多相关文章

  1. Netty游戏服务器之四protobuf编解码和黏包处理

    我们还没讲客户端怎么向服务器发送消息,服务器怎么接受消息. 在讲这个之前我们先要了解一点就是tcp底层存在粘包和拆包的机制,所以我们在进行消息传递的时候要考虑这个问题. 看了netty权威这里处理的办 ...

  2. google的protobuf简单介绍

    google的protobuf是一种轻便高效的结构化数据存储格式,在通信协议和数据存储等领域中使用比较多.protobuf对于结构中的每个成员,会提供set系列函数和get系列函数. 但是,对于使用来 ...

  3. netty 的 Google protobuf 开发

    根据上一篇博文 Google Protobuf 使用 Java 版 netty 集成 protobuf 的方法非常简单.代码如下: server package protobuf.server.imp ...

  4. Netty学习——Google Protobuf使用方式分析和环境搭建

    Google Protobuf使用方式分析 在RPC框架中,Google Protobuf是很常用的一个库,和Apache Thrift 是同款的用于进行序列化的第三方库.原理都是大同小异,无非就是使 ...

  5. Netty学习——Google Protobuf的初步了解

    学习参考的官网: https://developers.google.com/protocol-buffers/docs/javatutorial 简单指南详解:这个文档写的简直是太详细了. 本篇从下 ...

  6. Netty使用Google Protocol Buffer完成服务器高性能数据传输

    一.什么是Google Protocol Buffer(protobuf官方网站) 下面是官网给的解释: Protocol buffers are a language-neutral, platfo ...

  7. Google的Protobuf协议分析

    protobuf和thrift类似,也是一个序列化的协议实现,简称PB(下文出现的PB代表protobuf). Github:https://github.com/google/protobuf 上图 ...

  8. (原)ubuntu16中简单的使用google的protobuf

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5804395.html 参考网址: http://www.cnblogs.com/luosongchao ...

  9. 全图文分析:如何利用Google的protobuf,来思考、设计、实现自己的RPC框架

    目录 一.前言 二.RPC 基础概念 1. RPC 是什么? 2. 需要解决什么问题? 3. 有哪些开源实现? 三.protobuf 基本使用 1. 基本知识 2. 使用步骤 四.libevent 1 ...

随机推荐

  1. vue轮播图

    vue开发中遇到这样一个需求实现导航栏和中间内容相结合实现页面滑动导航跟随改变的效果.看效果: 这里我用的是vue所带的插件:vue-awesome-swiper,传送门:https://www.np ...

  2. 使用阿里云加速docker镜像的安装

    刚接触docker,尝试安装node镜像.docker运行在win7中,安装完Docker Toolbox之后简单敲了docker pull node命令,然后就是漫长的等待了… 等待的结果就是nod ...

  3. slam学习

    学习内容: 数学: 线性代数,概率论, 优化理论,离散数学, 李代数, 凸优化: 算法:   概率机器人, 机器人状态估计, 深度学习,非线性优化: 工程: c/c++ , python, ros, ...

  4. 微信小程序异常解析

    文件解析错误 SyntaxError: Unexpected end of JSON input: 在子目录下添加空白的json配置文件是会出现报错,其原因是因为文件内容空白 pages/index/ ...

  5. BZOJ1208 HNOI2004 宠物收养所 【非旋转Treap】

    BZOJ1208 HNOI2004 宠物收养所 Description 最近,阿Q开了一间宠物收养所.收养所提供两种服务:收养被主人遗弃的宠物和让新的主人领养这些宠物.每个领养者都希望领养到自己满意的 ...

  6. SQl_update,case_when,end

    627.Given a table salary, such as the one below, that has m=male and f=female values. Swap all f and ...

  7. (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切

    if (this == null) Console.WriteLine("this is null"); 这句话一写,大家一定觉得荒谬,然而 if 内代码的执行却是可能的!本文讲介 ...

  8. Java8中计算日期时间差

    一.简述 在Java8中,我们可以使用以下类来计算日期时间差异: 1.Period 2.Duration 3.ChronoUnit 二.Period类 主要是Period类方法getYears(),g ...

  9. flask第十九篇——模板【3】

    请关注微信公众号:自动化测试实战 今天我们继续模板的知识,现在我们增加字典的复杂度,这个时候在render_template第二个参数可以传**title,以后我们会用**context代替原来的ti ...

  10. bat命令2

    echo.@.call.pause.rem(小技巧:用::代替rem)是批处理文件最常用的几个命令,我们就从他们开始学起. echo 表示显示此命令后的字符 echo off 表示在此语句后所有运行的 ...