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

原文地址:https://www.cnblogs.com/wunaozai/p/5236494.html