有大神用protobuf java 使用做网络通信协议的吗

网络游戏的数据包通信协议定义有哪些资料可以参考? - 知乎323被浏览9443分享邀请回答531 条评论分享收藏感谢收起461 条评论分享收藏感谢收起查看更多回答1 个回答被折叠()本帖子已过去太久远了,不再提供回复功能。筒子们,使用Protobuf优化你的协议
来源:博客园
Protocol buffers是google提供的一种将结构化数据进行序列化和反序列化的方法,其优点是语言中立,平台中立,可扩展性好,目前在google内部大量用于数据存储,通讯协议等方面。PB在功能上类似XML,但是序列化后的数据更小,解析更快,使用上更简单。用户只要按照proto语法在.proto文件中定义好数据的结构,就可以使用PB提供的工具(protoc)自动生成处理数据的代码,使用这些代码就能在程序中方便的通过各种数据流读写数据。PB目前支持Java, C++和Python3种语言。另外,PB还提供了很好的向后兼容,即旧版本的程序可以正常处理新版本的数据,新版本的程序也能正常处理旧版本的数据。我们主要研究PB在C++语言的使用,PB的编译安装比较简单,C++的库使用make完成,java使用maven完成,python直接使用setup命令完成。
Protocol Buffers要如何用在socket通信的通信协议中?可以大概地说一下:你需要根据你的协议编写一个.proto文件,此文件的格式是按Protocol Buffers的要求书写的。然后用Protocol Buffers编译器生成这个文件对应的类文件(包括一个.h文件和一个.cc文件),然后在你的程序中include生成的头文件,当需要发送socket消息的时候,先用这个类的对象的SerializeToString()方法,生成一个字符串,这个字符串也就相当于我们传统意义上的编码过的消息,然后在socket消息的接收方,使用ParseFromString()方法,就可以将消息中包含的数据解析到生成的类的成员变量中,就可以直接取出来用了。整个过程不需要你去考虑编码、解码,就算更改了协议,修改工作也非常方便。
零、为什么要研究ProtoBuff
需求是最大的驱动,加入百度Hi团队一个多月了,这一个多月的时间里做了许多以前没有做过的事。针对Hi用户的反馈,移动端Hi对流量的消耗太大。Hi消息传输的格式目前主要有2种,一种是公司定义的一种nshead+mcbody的形式,这种方式类似于我们今天所要讲解的ProtoBuff,统一使用这种方式也能够减小传输流量,而且数据安全性也更高;另一中方式就是比较古老的文本协议格式了。公司决定对现有的文本协议进行优化。怎么优化呢?Hi目前使用文本协议在客户端和服务端进行信息等传输,这里所说的消息包括CS之间Request包、ACK包,或者服务端的Notify包等等,这些包里面的包括的内容不仅仅是聊天信息,也包括联系人状态变更等各种通知信息。
总得来说,Hi消息传输的报文格式可以概括为:header + body,header主要由一些键值对构成,而body则更多的是使用xml的格式。最终决定使用高大上的ProtoBuff对当前的文本协议进行改进,在这里先列出具体的实现思路:
现有的消息包括header和body两个部分,我们需要一个转换工具,分析现有报文的定义要求,生成一个消息格式xml文件:即包括了header和body的一个xml文件;
根据该xml格式生成proto的描述文件,然后通过Proto编译生成对应的.h和.cc文件;
最后就是要考虑和现有协议的转换了,庞大的系统兼容性是不可忽略的,即服务端添加一个专门协议转换的模块,分别针对上行和下行进行原有报文到pb报文的相互转换。
一、ProtoBuff开发三部曲
所谓开发三部曲,即:
定义描述文件,即.proto文件;
使用proto编译器生成该描述文件对应的定义文件,C++包括.h和.cc;
使用定义文件中提供的ProtoBuff的API读写消息。
举个简单的实例,百度Hi在拉取群列表时的协议定义为group:get_list,我们在描述文件中定义如下:

我们如何去按照这个定义要求去封包呢?直接看代码:

上图中88行中的日志就是将我们的PB报文以字符串形式打印出来,便于我们调试,即DebugString方法。在日志中我们可到PB结构如下:

二、序列化与反序列化
继续看代码,我们已经准备好了PB的封包,序列化之后就可以通过连接发出去了

百度Hi-Server在接收到PB包之后通过一系列的处理,会返回一个group:get_list的ACK包回来,我们怎么把回包拿出来并转化成我们现有的文本协议形式呢?继续看代码:

红色框图中的部分就是反序列化的过程,通过反序列化之后,我们就可以调用ProtoBuff定义的API去解析该PB报文了。
三、完成协议的测试
我们的测试目的就是保证PB与原协议的兼容性,换句话说就是转换后的数据一致性,我们在ImpPacket中重载了==运算符,完成比较的过程,即分别对ImpPacket中的header和body做比较,header是由一些map构成的,body则是xml构成的。我们的比较就是分别对header和body中的map和xml进行比较。

xml的比较代码如下(部分):

 
这就是该博文的所有内容,6个不同的协议,耗时2天时间完成,期间还踩到老代码中的一个坑,gdb调试才定位到,累觉不爱。感谢阅读,Published by Windows LiveWriter.
免责声明:本站部分内容、图片、文字、视频等来自于互联网,仅供大家学习与交流。相关内容如涉嫌侵犯您的知识产权或其他合法权益,请向本站发送有效通知,我们会及时处理。反馈邮箱&&&&。
学生服务号
在线咨询,奖学金返现,名师点评,等你来互动protobuf厂内通讯模块的设计的思考 - 移动互联网后端技术 - ITeye博客
博客分类:
一直以来比较关心高性能通讯模块的设计,最近看到两篇好文,由此想到曾经参与的几个通讯模块的设计和实现,跟大家分享思路。
首先来分享好文:
这一篇讲得是google protobuf协议和其他序列化的性能测试。朋友们看了一定会砰然心动,protobuf如此之高的通讯效率当然是求之不得。新浪微博IM各模块之间也采用protobuf作为通讯协议,TimYang写过文章比较google protobuf和facebook Thrift之间的性能。也证实了protobuf还是好东东。加之MINA可以很好的和此类通讯协议结合,无非是编解码嘛,一行伪代码搞定:
mySocketAcceptor.getFilterChain().addFirst("protobuf", new ProtocolCodecFilter(ProtobufCodecFactory.newInstance(MyProBufPacket.getDefaultInstance())));
第二篇是讲解为什么protobuf有如此效率,大家慢慢品味吧。
使用protobuf二进制协议高效的传输,在通讯压力大得场合非常合适。
私有协议,如果是基于公共协议的中间件。想推广使用,有一定阻力。
还是那句话:场景和应用决定构架,技术只是手段,客户的需求才是根本。
引用TimYang的话作为结尾
一个构架师的经验主要在于经历了多少场景,即解决了特定场景下的要求的经验
以及试错过程中积累的经验和对各种主流技术的掌握
背靠Google这棵大树,不用太担心了。protobuf自08年开源以来得到了业内积极的评价,且Google开源产品的质量向来是可信的。参考protobuf官网:/protocol-buffers/docs/overview,protobuf相比XML,速度快20到100倍,大小缩小3-10倍;因此protobuf不仅仅可以用来做数据传输,还可以用来压测存储。也可以考虑Facebook的thrift,网上有朋友测过,两者性能差不多。但因Facebook风头正劲,根据程序猿的兴趣,使用thrift也无不可。国内的案例,新浪微博大量使用了protobuf。谢谢lz的补充介绍,十分感谢!
浏览: 231477 次
来自: 北京
RingBuffer不存在生产覆盖未消费的数据,或者消费已经消 ...
完全没看懂诶
楼主 为什么是1.27F 求解释
有个疑问,请教下:扩容问题之一:如果不降低命中率?如果使用为垂 ...
&div class=&quote_title ...socket&netty(21)
netty(21)
下载protoc代码生成器和源码包:/p/protobuf/downloads/list,
protobuf-2.4.1.tar.bz2:主要是生成protobuf-2.4.1.jar包,操作需要安装maven,可以到网上下载这个Jar包,所以这里略。
protoc-2.4.1-win32.zip:生成XXX.Proto执行文件。
&参考http://blog.csdn.net/dreamdoc/article/details/6577180相关代码
&制作.Proto文件
格式参考如下:
注:可以按如上内容编写文本文件,然后修改后缀为.Proto。
proto文件结构
从上面的代码可以看出,proto文件的结构非常简单。
package java_package 定义生成的java类的package名称。
message定义一种类型,类型以Java中的一个class,Book是类名,在生成Java代码时,就是使用这个类名。
option optimize_for = LITE_RUNTIME:表示生成的java代码继承GeneratedMessageLite ,继承GeneratedMessageLite的好处是可张念以用jboss提供的编解码器,可以认为是必写项。
&该类型中包含的字段格式: [限定符 类型 字段名称 = tag [default = 默认值]]
&支持的默认类型
.proto TypeNotesC++ TypeJava Typedouble&doubledoublefloat&floatfloatint32Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead.int32intint64Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead.int64longuint32Uses variable-length encoding.uint32intuint64Uses variable-length encoding.uint64longsint32Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s.int32intsint64Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.int64longfixed32Always four bytes. More efficient than uint32 if values are often greater than 228.uint32intfixed64Always eight bytes. More efficient than uint64 if values are often greater than 256.uint64longsfixed32Always four bytes.int32intsfixed64Always eight bytes.int64longbool&boolbooleanstringA string must always contain UTF-8 encoded or 7-bit ASCII text.stringStringbytesMay contain any arbitrary sequence of bytes.stringByteString&&&&除此之外,还支持枚举,自定义类型等。
protobuf的限定符
required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;如果该项不设值,在序列化时会抛出RuntimeException,推荐不使用该字段,该字段一旦使用,无法更改。
&由于一些历史原因,基本数值类型的repeated的字段并没有被尽可能地高效编码。在新的代码中,用户应该使用特殊选项[packed=true]来保证更高效的编码。如:repeated int32 samples = 4 [packed=true];
&required是永久性的:在将一个字段标识为required的时候,应该特别小心。如果在某些情况下不想写入或者发送一个required的字段,将原始该字段修饰符更改为optional可能会遇到问题——旧版本的使用者会认为不含该字段的消息是不完整的,从而可能会无目的的拒绝解析。在这种情况下,你应该考虑编写特别针对于应用程序的、自定义的消息校验函数。Google的一些工程师得出了一个结论:使用required弊多于利;他们更愿意使用optional和repeated而不是required。当然,这个观点并不具有普遍性。
&optional:消息格式中该字段可以有0个或1个值(不超过1个)。不存在时使用默认值,默认值可自定义如[default = 10]。系统默认值如下:int = 0,bool = false,string=""
&repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java中的List。
&guide style
Protocol Buffers官方提供了一个Guide Style。其中介绍了一些定义proto的优秀实践。一句话总结就是名称时,message名称以驼峰方式定义,字段名则需要以如author_name方式定义。字段名在生成Java代码时,会自动转换为符合Java风格的命名方式。
生成. Java文件
将编写的.Proto文件与下载的protoc.exe文件放在同一指定的目录,进入dos并系统编译命令:protoc
--java_out=C:/,这时会在你所指令的目录生成相应的java文件。
它的一些缺点
不支持大数据集的处理,少于1M
不支持Date,Map这些Java内建的对象
&客户端示例
如果Message类中用到repeated 定义的类型,则在客户端中按如下代码操作: builder.addBody(Message.Body.newBuilder().setContent(bytes).build());
也可用下面默认的方式传输二进制,这时在节点7中就要使用默认的protobuf解码方式,
&&&&byte[] test = "asdfa".getBytes();&&&&&&&&
&&&&ChannelBuffer channelBuffer = ChannelBuffers.buffer(test.length);
&&&&// 将 获得到的数组写入 channelBuffer中
&&&&channelBuffer.writeBytes(test);
&&&&// 发送到服务器端
&&&&ChannelFuture lastWriteFuture = channel.write(channelBuffer);
服务器端示例
ServerHandler示例
如果使用默认的Protobuf解码方式传输二进制,则接收时按以下代码接收:
if((e.getMessage() instanceof ChannelBuffer)){
&&&&&&&&ChannelBuffer channelBuffer =(ChannelBuffer)e.getMessage();
&&&&&&&&byte[] bytes = channelBuffer.array();
PipelineFactory示例
其中,以下两行代码是默认的Protobuf解码类型,如果到自定义解码,这两行可以注释掉,反之把自定义的加码解码注释掉.
pipeline.addLast("frameDecoder", new ProtobufVarint32FrameDecoder());
pipeline.addLast("frameEncoder", new ProtobufVarint32LengthFieldPrepender());
org.jboss.netty.handler.codec.frame ,这个包里面有三种封包方法(主要用在传输字符串):
1.DelimiterBasedFrameDecoder 是利用分隔符来进行包的界定;2.FixedLengthFrameDecoder 是利用固定的长度来进行包的界定;3.LengthFieldBasedFrameDecoder 和 LengthFieldPrepender 是利用在发送数据的时候在里面加上头字段,头字段里面包含了包的长度。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:113138次
积分:3939
积分:3939
排名:第7997名
原创:322篇
(3)(8)(26)(285)}

我要回帖

更多关于 python protobuf 使用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信