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.1\vsprojects\protobuf.sln
这样生成解决方案。
在Debug里面这些文件是有用的
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();
}
}
}
运行结果
项目目录结构
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();
} }
服务器运行结果
客户端运行结果
5. (C/C++)-java通信例子
本来想用mingw来实现的但是,试了几次,总是编译不过。就放弃了,使用VS2008来编译了。
新建一个控制台程序,配置下属性页, 下面这个图配置到protobuf源代码的src目录
下面这个图配置到用vs编译编译产生的中间文件,包含几个lib包的目录
将经过protoc.exe产生的*.h和*.cc文件放到对应的项目中
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服务器端,运行后服务器出现这个结果
找了很久原因,原来是服务器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 使用的更多相关文章
- netty5 HTTP协议栈浅析与实践
一.说在前面的话 前段时间,工作上需要做一个针对视频质量的统计分析系统,各端(PC端.移动端和 WEB端)将视频质量数据放在一个 HTTP 请求中上报到服务器,服务器对数据进行解析.分拣后从不同的 ...
- python通过protobuf实现rpc
由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行g ...
- Netty5使用自签证书实现SSL安全连接
这次使用的Netty是最新的5.0 Alpha2版本,下载地址是:http://dl.bintray.com/netty/downloads/netty-5.0.0.Alpha2.tar.bz2,发布 ...
- Protobuf使用规范分享
一.Protobuf 的优点 Protobuf 有如 XML,不过它更小.更快.也更简单.它以高效的二进制方式存储,比 XML 小 3 到 10 倍,快 20 到 100 倍.你可以定义自己的数据结构 ...
- java netty socket库和自定义C#socket库利用protobuf进行通信完整实例
之前的文章讲述了socket通信的一些基本知识,已经本人自定义的C#版本的socket.和java netty 库的二次封装,但是没有真正的发表测试用例. 本文只是为了讲解利用protobuf 进行C ...
- 在Wcf中应用ProtoBuf替代默认的序列化器
Google的ProtoBuf序列化器性能的牛逼已经有目共睹了,可以把它应用到Socket通讯,队列,Wcf中,身为dotnet程序员一边期待着不久后Grpc对dotnet core的支持更期待着Wc ...
- protobuf的编译安装
github地址:https://github.com/google/protobuf支持多种语言,有多个语言的版本,本文采用的是在centos7下编译源码进行安装. github上有详细的安装说明: ...
- 编译protobuf的jar文件
1.准备工作 需要到github上下载相应的文件,地址https://github.com/google/protobuf/releases protobuf有很多不同语言的版本,因为我们需要的是ja ...
- protobuf学习(2)-相关学习资料
protobuf官方git地址 protobuf官方英文文档 (你懂的需要FQ) protobuf中文翻译文档 protobuf概述 (官方翻译 推荐阅读) protobuf入门 ...
随机推荐
- crossplatform---bower解决js的依赖管理
从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎.chrome浏 ...
- 生成月初月末便于拼接sql
for ($i=1; $i < 13; $i++) { $date = strtotime(date("2015-$i-01")); $firstday = date(&qu ...
- Javascript实现计数器,定时警告和停止
<html> <head> <meta charset="utf-8"> <title>定时警告</title> < ...
- 每天一个linux命令(5):rm 命令
昨天学习了创建文件和目录的命令mkdir ,今天学习一下linux中删除文件和目录的命令: rm命令.rm是常用的命令,该命令的功能为删除一个目录中的一个或多个文件或目录,它也可以将某个目录及其下的所 ...
- c#之第一课入门
这几天看到微软的build大会,感觉微软不甘落后他人,曾经的巨头难道又要重新崛起,不管了,为了以后的饭碗,还是简单学习一些c#吧,有时这种紧张感不错的,现在由于这种紧张感,我已经掌握的java(主要弄 ...
- 转:RTMPDump源代码分析
0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...
- ASP.NET Web API中的参数绑定总结
ASP.NET Web API中的action参数类型可以分为简单类型和复杂类型. HttpResponseMessage Put(int id, Product item) id是int类型,是简单 ...
- linux C++ 获取文件绝对路径
提供ftp服务时需要获取文件绝对路径,这里记录一下. #include <stdlib.h> #include <stdio.h> #include <limits.h& ...
- Redis安装及HA(High Availability)配置
Redis是一种内存数据库,以KEY-VALUE(即键值对)的形式存储数据.这篇文章主要介绍的是Redis安装及配置,所以不对Redis本身作详细介绍了. 下载: http://redis.io/do ...
- 【转】webstorm 注册码 / phpstorm 注册码
WebStorm注册码 User Name: EMBRACE License Key: ===== LICENSE BEGIN ===== 24718-12042010 00001h6wzKLpfo3 ...