• Netty5 + Protobuf 使用


    1. 安装开发环境

    1.1 Netty环境
      这里我使用Netty5.0.0版本 到这里下载即可http://netty.io/ 下载netty-all-5.0.0.Alpha2.jar 这个jar包简单配置一下即可使用。
    1.2 Protobuf环境
      这个就比较麻烦了,这里说一下我的做法。 可以在这里下载最新版https://github.com/google/protobuf 或者使用 v2.6.1稳定版 https://github.com/google/protobuf/tree/v2.6.1
      也可以在这里下载http://pkgs.fedoraproject.org/repo/pkgs/protobuf/protobuf-2.6.1.tar.bz2/
      在这里下载对应的Protobuf-java.jar http://central.maven.org/maven2/com/google/protobuf/protobuf-java/
      http://mvnrepository.com/artifact/com.google.protobuf/protobuf-java

    1.3 Protoc 工具
      Linux和Windows都差不多,编译源代码即可。
      以Windows为例,打开protobuf-2.6.1vsprojectsprotobuf.sln

      这样生成解决方案。

      在Debug里面这些文件是有用的

    2. protobuf初始化

    SubscribeReq.proto

     1 package netty;
     2 option java_package = "com.jieli.nettytest.protobuf";
     3 option java_outer_classname = "SubscribeReqProto";
     4 
     5 message SubscribeReq{
     6     required int32 subReqID = 1;
     7     required string userName = 2;
     8     required string productName = 3;
     9     repeated string address = 4;
    10 }

    SubscribeResq.proto

    1 package netty;
    2 option java_package = "com.jieli.nettytest.protobuf";
    3 option java_outer_classname = "SubscribeResqProto";
    4 
    5 message SubscribeResq{
    6     required int32 subReqID = 1;
    7     required int32 respCode = 2;
    8     required string desc = 3;
    9 }

      用protobuf.exe进行编译

    1 protoc.exe --java_out=. --cpp_out=. SubscribeReq.proto
    2 protoc.exe --java_out=. --cpp_out=. SubscribeResq.proto

    3. Protobuf 测试

      TestSubscribeReqProto.java

     1 package com.jieli.nettytest.protobuf;
     2 
     3 import java.util.ArrayList;
     4 import java.util.List;
     5 import com.google.protobuf.InvalidProtocolBufferException;
     6 
     7 public class TestSubscribeReqProto {
     8     
     9     private static byte[] encode(SubscribeReqProto.SubscribeReq req){
    10         return req.toByteArray();
    11     }
    12     
    13     private static SubscribeReqProto.SubscribeReq decode(byte[] body) 
    14             throws InvalidProtocolBufferException {
    15         return SubscribeReqProto.SubscribeReq.parseFrom(body);
    16     }
    17     
    18     private static SubscribeReqProto.SubscribeReq createSubscribeReq(){
    19         SubscribeReqProto.SubscribeReq.Builder builder = 
    20                 SubscribeReqProto.SubscribeReq.newBuilder();
    21         builder.setSubReqID(1);
    22         builder.setUserName("Lilinfeng");
    23         builder.setProductName("netty book");
    24         List<String> address = new ArrayList<>();
    25         address.add("NanJing YuHuaTai");
    26         address.add("beijin lilili");
    27         address.add("asdfasdf");
    28         builder.addAllAddress(address);
    29         return builder.build();
    30     }
    31     
    32     public static void main(String[] args) {
    33         try {
    34             SubscribeReqProto.SubscribeReq req = createSubscribeReq();
    35             System.out.println("befor encode:" + req.toString());
    36             SubscribeReqProto.SubscribeReq req2 = decode(encode(req));
    37             System.out.println("After decode :"+req.toString());
    38             System.out.println("assert equal : ==>" + req2.equals(req));
    39         } catch (Exception e) {
    40             e.printStackTrace();
    41         }
    42     }
    43 }

      运行结果

      项目目录结构

    4. java-java通信例子(跟书本上是差不多一样的)

      SubReqServer.java

     1 package com.jieli.nettytest.protobuf;
     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.ProtobufEncoder;
    13 import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
    14 import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
    15 import io.netty.handler.logging.LogLevel;
    16 import io.netty.handler.logging.LoggingHandler;
    17 
    18 public class SubReqServer {
    19     
    20     public void bind(int port){
    21         EventLoopGroup bossGroup = new NioEventLoopGroup();
    22         EventLoopGroup workerGroup = new NioEventLoopGroup();
    23         try {
    24             ServerBootstrap b = new ServerBootstrap();
    25             b.group(bossGroup, workerGroup)
    26              .channel(NioServerSocketChannel.class)
    27              .option(ChannelOption.SO_BACKLOG, 100)
    28              .handler(new LoggingHandler(LogLevel.INFO))
    29              .childHandler(new ChannelInitializer<SocketChannel>() {
    30                 @Override
    31                 protected void initChannel(SocketChannel ch) throws Exception {
    32                     ch.pipeline().addLast(new ProtobufVarint32FrameDecoder()); 
    33                     //与c++通信时这里的varint32要注释掉,因为默认的protobuf是没有32位对齐的,如果要实现自动分包,那么要在C++客户端进行组装
    34                     ch.pipeline().addLast(new ProtobufDecoder(
    35                             SubscribeReqProto.SubscribeReq.getDefaultInstance()));
    36                     ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
    37                     ch.pipeline().addLast(new ProtobufEncoder());
    38                     ch.pipeline().addLast(new SubReqServerHandler());
    39                 }
    40             });
    41             
    42             ChannelFuture f = b.bind(port).sync();
    43             
    44             f.channel().closeFuture().sync();
    45         } catch (Exception e) {
    46             e.printStackTrace();
    47         } finally {
    48             bossGroup.shutdownGracefully();
    49             workerGroup.shutdownGracefully();
    50         }
    51     }
    52     
    53     public static void main(String[] args) {
    54         new SubReqServer().bind(7777);
    55     }
    56 }
    View Code

      SubReqServerHandler.java

     1 package com.jieli.nettytest.protobuf;
     2 
     3 import io.netty.channel.ChannelHandlerAdapter;
     4 import io.netty.channel.ChannelHandlerContext;
     5 
     6 public class SubReqServerHandler extends ChannelHandlerAdapter{
     7     @Override
     8     public void channelRead(ChannelHandlerContext ctx, Object msg)
     9             throws Exception {
    10         SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq) msg;
    11         if("Lilinfeng".equalsIgnoreCase(req.getUserName())){
    12             System.out.println("Service accept client subscribe req:["+req.toString()+"]");
    13             //ctx.writeAndFlush(resp(req.getSubReqID()));
    14         }
    15     }
    16     
    17     private SubscribeResqProto.SubscribeResq resp(int subReqID){
    18         SubscribeResqProto.SubscribeResq.Builder builder = 
    19                 SubscribeResqProto.SubscribeResq.newBuilder();
    20         builder.setSubReqID(subReqID);
    21         builder.setRespCode(0);
    22         builder.setDesc("Netty book order success..");
    23         return builder.build();
    24     }
    25     
    26     @Override
    27     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    28             throws Exception {
    29         cause.printStackTrace();
    30     }
    31 }
    View Code

      SubReqClient.java

     1 package com.jieli.nettytest.protobuf;
     2 
     3 
     4 import io.netty.bootstrap.Bootstrap;
     5 import io.netty.channel.ChannelFuture;
     6 import io.netty.channel.ChannelInitializer;
     7 import io.netty.channel.ChannelOption;
     8 import io.netty.channel.EventLoopGroup;
     9 import io.netty.channel.nio.NioEventLoopGroup;
    10 import io.netty.channel.socket.SocketChannel;
    11 import io.netty.channel.socket.nio.NioSocketChannel;
    12 import io.netty.handler.codec.protobuf.ProtobufDecoder;
    13 import io.netty.handler.codec.protobuf.ProtobufEncoder;
    14 import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
    15 import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
    16 import io.netty.handler.logging.LogLevel;
    17 import io.netty.handler.logging.LoggingHandler;
    18 
    19 public class SubReqClient {
    20     
    21     public void connect(int port, String host){
    22         EventLoopGroup group = new NioEventLoopGroup();
    23         try {
    24             Bootstrap b = new Bootstrap();
    25             b.group(group)
    26              .channel(NioSocketChannel.class)
    27              .option(ChannelOption.TCP_NODELAY, true)
    28              .handler(new ChannelInitializer<SocketChannel>() {
    29                 @Override
    30                 protected void initChannel(SocketChannel ch) throws Exception {
    31                     ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
    32                     ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
    33                     ch.pipeline().addLast(new ProtobufDecoder(
    34                             SubscribeResqProto.SubscribeResq.getDefaultInstance()));
    35                     ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
    36                     ch.pipeline().addLast(new ProtobufEncoder());
    37                     ch.pipeline().addLast(new SubReqClientHandler());
    38                 }
    39             });
    40             
    41             ChannelFuture f = b.connect(host, port).sync();
    42             
    43             f.channel().closeFuture().sync();
    44         } catch (Exception e) {
    45             e.printStackTrace();
    46         } finally {
    47             group.shutdownGracefully();
    48         }
    49     }
    50     
    51     public static void main(String[] args) {
    52         new SubReqClient().connect(7777, "localhost");
    53     }
    54 }
    View Code

      SubReqClientHandler.java

     1 package com.jieli.nettytest.protobuf;
     2 
     3 
     4 import java.util.ArrayList;
     5 import java.util.List;
     6 
     7 import io.netty.channel.ChannelHandlerAdapter;
     8 import io.netty.channel.ChannelHandlerContext;
     9 
    10 public class SubReqClientHandler extends ChannelHandlerAdapter{
    11     public SubReqClientHandler() {
    12     }
    13     
    14     @Override
    15     public void channelActive(ChannelHandlerContext ctx) throws Exception {
    16         for(int i=0; i<10; i++){
    17             ctx.write(subReq(i));
    18         }
    19         ctx.flush();
    20     }
    21     
    22     private SubscribeReqProto.SubscribeReq subReq(int i){
    23         SubscribeReqProto.SubscribeReq.Builder builder =
    24                 SubscribeReqProto.SubscribeReq.newBuilder();
    25         builder.setSubReqID(i);
    26         builder.setUserName("Lilinfeng");
    27         builder.setProductName("Netty Book..");
    28         List<String> address = new ArrayList<>();
    29         address.add("NanJin LLLLLL");
    30         address.add("beijin lllllll");
    31         address.add("shenzhen jjjjjj");
    32         builder.addAllAddress(address);
    33         return builder.build();
    34     }
    35     
    36     @Override
    37     public void channelRead(ChannelHandlerContext ctx, Object msg)
    38             throws Exception {
    39         System.out.println("Receive server response:["+msg+"]");
    40     }
    41     
    42     @Override
    43     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    44         ctx.flush();
    45     }
    46     
    47 }
    View Code

      服务器运行结果

      客户端运行结果

    5. (C/C++)-java通信例子

      本来想用mingw来实现的但是,试了几次,总是编译不过。就放弃了,使用VS2008来编译了。

      新建一个控制台程序,配置下属性页, 下面这个图配置到protobuf源代码的src目录


      下面这个图配置到用vs编译编译产生的中间文件,包含几个lib包的目录

      将经过protoc.exe产生的*.h和*.cc文件放到对应的项目中

      main.cpp代码

      1 #include <iostream>
      2 #include <windows.h>
      3 #include "SubscribeReq.pb.h"
      4 #include "SubscribeResq.pb.h"
      5 
      6 #pragma comment(lib, "ws2_32.lib")
      7 #pragma comment(lib,"libprotobuf.lib") 
      8 #pragma comment(lib,"libprotobuf-lite.lib")
      9 
     10 using namespace std;
     11 using namespace netty;
     12 
     13 
     14 //打开连接
     15 SOCKET open_msg(char *host, int port)
     16 {
     17     //初始化Socket dll
     18     WSADATA wsaData;
     19     WORD socketVersion = MAKEWORD(2,0);
     20     if(WSAStartup(socketVersion,&wsaData)!=0)
     21     {
     22         printf("Init socket dll error!");
     23         return -1;
     24     }
     25     //创建socket
     26     SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //tcp
     27     if (SOCKET_ERROR == s)
     28     {
     29         printf("Create Socket Error!");
     30         return -1;
     31     }
     32     //指定服务端的地址
     33     sockaddr_in server_addr;
     34     server_addr.sin_family = AF_INET;
     35     server_addr.sin_addr.S_un.S_addr = inet_addr(host);
     36     server_addr.sin_port = htons(port);
     37 
     38     char opt = 1;
     39     int ret = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(char));
     40     if(ret == -1)
     41     {
     42         printf("ERROR
    ");
     43         return -1;
     44     }
     45 
     46     //连接
     47     if (SOCKET_ERROR == connect(s, (LPSOCKADDR)&server_addr, sizeof(server_addr)))
     48     {
     49         printf("Can Not Connect To Server IP!
    ");
     50         return -1;
     51     }
     52     return s;
     53 }
     54 //获取信息
     55 int recv_msg(SOCKET s,char *msg,int size)
     56 {
     57     int ret = recv(s,msg,size,0);
     58     if(ret == SOCKET_ERROR)
     59     {
     60         printf("Recv Error.
    ");
     61         return -1;
     62     }
     63     return ret;
     64 }
     65 //发送信息
     66 int send_msg(SOCKET s,char *msg,int size)
     67 {
     68     int ret = send(s,msg,size,0);
     69     if(ret == SOCKET_ERROR)
     70     {
     71         printf("Send Error.
    ");
     72         return -1;
     73     }
     74     return ret;
     75 }
     76 //关闭连接
     77 int close_msg(SOCKET s)
     78 {
     79     closesocket(s);
     80     return 0;
     81 }
     82 
     83 
     84 int main()
     85 {
     86     SubscribeReq req ;
     87     req.set_username("Lilinfeng");
     88     req.add_address("asdf");
     89     req.set_subreqid(2);
     90     req.set_productname("laskjdfk111");
     91 
     92     
     93     SOCKET s = open_msg("127.0.0.1", 7777);
     94     
     95     char msg[1024] = {0};
     96     req.SerializePartialToArray(msg, 1024);
     97     
     98     cout<<req.GetCachedSize()<<endl;
     99     send_msg(s, msg, req.GetCachedSize());
    100 
    101     close_msg(s);
    102     
    103     system("pause");
    104     return 0;
    105 }
    View Code

      然后编译运行就可以发送protobuf对象到java服务器端,运行后服务器出现这个结果

      找了很久原因,原来是服务器SubReqServer.java中的ProtobufVarint32***解码器对Protobuf包进行处理,导致格式不一致,解决的办法是注释掉这两行,不过这样又会产生书本上说到的问题,会出现粘包。我能想到的办法是1.在C++客户端中进行修改,使之对应到Java中对齐格式,这个要看源代码。 2.发送的包前面增加包头,然后包头信息描述Protobuf大小。

    参考资料

      Netty权威指南 – 第八章 Google Protobuf 编解码
      http://blog.csdn.net/majianfei1023/article/details/45371743
      http://www.cnblogs.com/lidabo/p/3911456.html

    本文地址: http://www.cnblogs.com/wunaozai/p/5236494.html

  • 相关阅读:
    selet 语句详解
    第三章 sql 的约束
    第二章 创建数据库并插入数据
    第一章
    微信小程序(九)
    微信小程序(七)
    微信小程序(八)
    微信小程序(六)
    bzoj4622 [NOI 2003] 智破连环阵
    bzoj3996 [TJOI2015]线性代数
  • 原文地址:https://www.cnblogs.com/wunaozai/p/5236494.html
Copyright © 2020-2023  润新知