zoukankan      html  css  js  c++  java
  • Netty使用Google的ProtoBuf

    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,如下面的代码清单所示:

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

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

    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的依赖:

    [html] view plain copy
     
    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

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

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

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

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

    ProtoBufServerHandler.java

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

    客户端代码

    ProtoBufClient.java

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

    需要注意的是:

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

    接着看:

    ProtoBufClientHandler.java

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

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

    服务器端启动:

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

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

  • 相关阅读:
    loj 1251(2-sat + 输出一组可行解)
    hdu 4751(dfs染色)
    hdu 2545(并查集求节点到根节点的距离)
    uva 10972(边双连通分量)
    uva 10246(最短路变形)
    uva 11380(最大流+拆点)
    hdu 4640(状压dp)
    hdu 1430+hdu 3567(预处理)
    python基础知识回顾[1]
    基于websocket搭建简易群聊
  • 原文地址:https://www.cnblogs.com/duan2/p/8919351.html
Copyright © 2011-2022 走看看