Netty5 + Protobuf 使用

时间:2021-12-01 10:05:19

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.1\vsprojects\protobuf.sln
Netty5 + Protobuf 使用
  这样生成解决方案。
Netty5 + Protobuf 使用
  在Debug里面这些文件是有用的

Netty5 + Protobuf 使用

2. protobuf初始化

SubscribeReq.proto

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

SubscribeResq.proto

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

  用protobuf.exe进行编译

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

3. Protobuf 测试

  TestSubscribeReqProto.java

 package com.jieli.nettytest.protobuf;

 import java.util.ArrayList;
import java.util.List;
import com.google.protobuf.InvalidProtocolBufferException; public class TestSubscribeReqProto { private static byte[] encode(SubscribeReqProto.SubscribeReq req){
return req.toByteArray();
} private static SubscribeReqProto.SubscribeReq decode(byte[] body)
throws InvalidProtocolBufferException {
return SubscribeReqProto.SubscribeReq.parseFrom(body);
} private static SubscribeReqProto.SubscribeReq createSubscribeReq(){
SubscribeReqProto.SubscribeReq.Builder builder =
SubscribeReqProto.SubscribeReq.newBuilder();
builder.setSubReqID(1);
builder.setUserName("Lilinfeng");
builder.setProductName("netty book");
List<String> address = new ArrayList<>();
address.add("NanJing YuHuaTai");
address.add("beijin lilili");
address.add("asdfasdf");
builder.addAllAddress(address);
return builder.build();
} public static void main(String[] args) {
try {
SubscribeReqProto.SubscribeReq req = createSubscribeReq();
System.out.println("befor encode:" + req.toString());
SubscribeReqProto.SubscribeReq req2 = decode(encode(req));
System.out.println("After decode :"+req.toString());
System.out.println("assert equal : ==>" + req2.equals(req));
} catch (Exception e) {
e.printStackTrace();
}
}
}

  运行结果

Netty5 + Protobuf 使用

  项目目录结构

Netty5 + Protobuf 使用

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

  SubReqServer.java

 package com.jieli.nettytest.protobuf;

 import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; public class SubReqServer { public void bind(int port){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
//与c++通信时这里的varint32要注释掉,因为默认的protobuf是没有32位对齐的,如果要实现自动分包,那么要在C++客户端进行组装
ch.pipeline().addLast(new ProtobufDecoder(
SubscribeReqProto.SubscribeReq.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubReqServerHandler());
}
}); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} public static void main(String[] args) {
new SubReqServer().bind(7777);
}
}

  SubReqServerHandler.java

 package com.jieli.nettytest.protobuf;

 import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; public class SubReqServerHandler extends ChannelHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq) msg;
if("Lilinfeng".equalsIgnoreCase(req.getUserName())){
System.out.println("Service accept client subscribe req:["+req.toString()+"]");
//ctx.writeAndFlush(resp(req.getSubReqID()));
}
} private SubscribeResqProto.SubscribeResq resp(int subReqID){
SubscribeResqProto.SubscribeResq.Builder builder =
SubscribeResqProto.SubscribeResq.newBuilder();
builder.setSubReqID(subReqID);
builder.setRespCode(0);
builder.setDesc("Netty book order success..");
return builder.build();
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
}
}

  SubReqClient.java

 package com.jieli.nettytest.protobuf;

 import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler; public class SubReqClient { public void connect(int port, String host){
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
ch.pipeline().addLast(new ProtobufVarint32FrameDecoder());
ch.pipeline().addLast(new ProtobufDecoder(
SubscribeResqProto.SubscribeResq.getDefaultInstance()));
ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
ch.pipeline().addLast(new ProtobufEncoder());
ch.pipeline().addLast(new SubReqClientHandler());
}
}); ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
} public static void main(String[] args) {
new SubReqClient().connect(7777, "localhost");
}
}

  SubReqClientHandler.java

 package com.jieli.nettytest.protobuf;

 import java.util.ArrayList;
import java.util.List; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; public class SubReqClientHandler extends ChannelHandlerAdapter{
public SubReqClientHandler() {
} @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for(int i=0; i<10; i++){
ctx.write(subReq(i));
}
ctx.flush();
} private SubscribeReqProto.SubscribeReq subReq(int i){
SubscribeReqProto.SubscribeReq.Builder builder =
SubscribeReqProto.SubscribeReq.newBuilder();
builder.setSubReqID(i);
builder.setUserName("Lilinfeng");
builder.setProductName("Netty Book..");
List<String> address = new ArrayList<>();
address.add("NanJin LLLLLL");
address.add("beijin lllllll");
address.add("shenzhen jjjjjj");
builder.addAllAddress(address);
return builder.build();
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
System.out.println("Receive server response:["+msg+"]");
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} }

  服务器运行结果

Netty5 + Protobuf 使用

  客户端运行结果

Netty5 + Protobuf 使用

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

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

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

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

Netty5 + Protobuf 使用

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

Netty5 + Protobuf 使用

  main.cpp代码

 #include <iostream>
#include <windows.h>
#include "SubscribeReq.pb.h"
#include "SubscribeResq.pb.h" #pragma comment(lib, "ws2_32.lib")
#pragma comment(lib,"libprotobuf.lib")
#pragma comment(lib,"libprotobuf-lite.lib") using namespace std;
using namespace netty; //打开连接
SOCKET open_msg(char *host, int port)
{
//初始化Socket dll
WSADATA wsaData;
WORD socketVersion = MAKEWORD(,);
if(WSAStartup(socketVersion,&wsaData)!=)
{
printf("Init socket dll error!");
return -;
}
//创建socket
SOCKET s = socket(AF_INET, SOCK_STREAM, ); //tcp
if (SOCKET_ERROR == s)
{
printf("Create Socket Error!");
return -;
}
//指定服务端的地址
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr(host);
server_addr.sin_port = htons(port); char opt = ;
int ret = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(char));
if(ret == -)
{
printf("ERROR\n");
return -;
} //连接
if (SOCKET_ERROR == connect(s, (LPSOCKADDR)&server_addr, sizeof(server_addr)))
{
printf("Can Not Connect To Server IP!\n");
return -;
}
return s;
}
//获取信息
int recv_msg(SOCKET s,char *msg,int size)
{
int ret = recv(s,msg,size,);
if(ret == SOCKET_ERROR)
{
printf("Recv Error.\n");
return -;
}
return ret;
}
//发送信息
int send_msg(SOCKET s,char *msg,int size)
{
int ret = send(s,msg,size,);
if(ret == SOCKET_ERROR)
{
printf("Send Error.\n");
return -;
}
return ret;
}
//关闭连接
int close_msg(SOCKET s)
{
closesocket(s);
return ;
} int main()
{
SubscribeReq req ;
req.set_username("Lilinfeng");
req.add_address("asdf");
req.set_subreqid();
req.set_productname("laskjdfk111"); SOCKET s = open_msg("127.0.0.1", ); char msg[] = {};
req.SerializePartialToArray(msg, ); cout<<req.GetCachedSize()<<endl;
send_msg(s, msg, req.GetCachedSize()); close_msg(s); system("pause");
return ;
}

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

Netty5 + Protobuf 使用

  找了很久原因,原来是服务器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

Netty5 + Protobuf 使用的更多相关文章

  1. netty5 HTTP协议栈浅析与实践

      一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...

  2. python通过protobuf实现rpc

    由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行g ...

  3. Netty5使用自签证书实现SSL安全连接

    这次使用的Netty是最新的5.0 Alpha2版本,下载地址是:http://dl.bintray.com/netty/downloads/netty-5.0.0.Alpha2.tar.bz2,发布 ...

  4. Protobuf使用规范分享

    一.Protobuf 的优点 Protobuf 有如 XML,不过它更小.更快.也更简单.它以高效的二进制方式存储,比 XML 小 3 到 10 倍,快 20 到 100 倍.你可以定义自己的数据结构 ...

  5. java netty socket库和自定义C&num;socket库利用protobuf进行通信完整实例

    之前的文章讲述了socket通信的一些基本知识,已经本人自定义的C#版本的socket.和java netty 库的二次封装,但是没有真正的发表测试用例. 本文只是为了讲解利用protobuf 进行C ...

  6. 在Wcf中应用ProtoBuf替代默认的序列化器

    Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wc ...

  7. protobuf的编译安装

    github地址:https://github.com/google/protobuf支持多种语言,有多个语言的版本,本文采用的是在centos7下编译源码进行安装. github上有详细的安装说明: ...

  8. 编译protobuf的jar文件

    1.准备工作 需要到github上下载相应的文件,地址https://github.com/google/protobuf/releases protobuf有很多不同语言的版本,因为我们需要的是ja ...

  9. protobuf学习&lpar;2&rpar;-相关学习资料

    protobuf官方git地址 protobuf官方英文文档   (你懂的需要FQ) protobuf中文翻译文档 protobuf概述          (官方翻译 推荐阅读) protobuf入门 ...

随机推荐

  1. installshield使用教程

    从Visual Studio 2012开始,微软就把自家原来的安装与部署工具彻底废掉了,转而让大家去安装使用第三方的打包工具“InstallShield Limited Edition for Vis ...

  2. 用GUI完成了斗地主发牌

    JAVA真的很强大,简单的步骤他自己就可以帮助我们解决了,简单,方便,并且还有着非常大的开发潜力

  3. Android FragmentActivity&plus;viewpager的使用

    使用场景,打算设计一个“底部菜单栏+其余可滑动的页面”的简单的功能. package com.lanyuweng.mibaby; import android.content.Intent; impo ...

  4. MySQL 二进制日志过滤

    binlog_do_db; binlog_ignore_db; 这个两个参数是用来控制对哪个数据库的更改要记录日志:下面以binlog_ignore_db为例子. 假如binlog_ignore_db ...

  5. Oracle表空间和表的常用操作指令

    查看端口号指令 netstat –a 设置: set pagesize 100; //设置每页显示的行数set linesize 200; //设置每页显示的字符数 空格也算col 列名A for a ...

  6. 计蒜客 2019 蓝桥杯省赛 B 组模拟赛(一)

    D题:马的管辖 二进制枚举方案.判断该方案是否全部能被覆盖,将最优方案存下来并进行剪枝. #include<iostream> #include<cstring> #inclu ...

  7. python冒泡排序法

  8. 深入理解Java对象序列化

    关于Java序列化的文章早已是汗牛充栋了,本文是对我个人过往学习,理解及应用Java序列化的一个总结.此文内容涉及Java序列化的基本原理,以及多种方法对序列化形式进行定制.在撰写本文时,既参考了Th ...

  9. JavaScript中&OpenCurlyDoubleQuote;&amp&semi;&amp&semi;”和&OpenCurlyDoubleQuote;&vert;&vert;”操作符的意义,深入理解和使用场景

     一.概念 与其他语言不同,在js中,逻辑运算符可以返回任何类型的数据,不仅仅是true和false. &&和||的返回值是两个操作数的其中一个.即a&&b或者a||b ...

  10. App保持登录状态的常用方法(转)

    我们在使用App时,一次登录后App如果不主动退出登录或者清除数据,App会在很长一段时间内保持登录状态,或者让用户感觉到登录一次就不用每次都输入用户密码才能进行登录.银行.金融涉及到支付类的App一 ...