﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-每天早晨叫醒你的不是闹钟,而是梦想-随笔分类-开源库</title><link>http://www.cppblog.com/mmdengwo/category/16464.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 05 Apr 2011 10:58:05 GMT</lastBuildDate><pubDate>Tue, 05 Apr 2011 10:58:05 GMT</pubDate><ttl>60</ttl><item><title>protobuf在网络编程中的应用思考</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143466.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Tue, 05 Apr 2011 10:40:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143466.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/143466.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143466.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/143466.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/143466.html</trackback:ping><description><![CDATA[<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left">
<h3 style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-TOP: 0px">protobuf简介</h3>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">protobuf是google提供的一个开源序列化框架，类似于XML，JSON这样的数据表示语言，其最大的特点是基于二进制，因此比传统的XML表示高效短小得多。虽然是二进制数据格式，但并没有因此变得复杂，开发人员通过按照一定的语法定义结构化的消息格式，然后送给命令行工具，工具将自动生成相关的类，可以支持java、c++、python等语言环境。通过将这些类包含在项目中，可以很轻松的调用相关方法来完成业务消息的序列化与反序列化工作。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">protobuf在google中是一个比较核心的基础库，作为分布式运算涉及到大量的不同业务消息的传递，如何高效简洁的表示、操作这些业务消息在google这样的大规模应用中是至关重要的。而protobuf这样的库正好是在效率、数据大小、易用性之间取得了很好的平衡。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">更多信息可参考<a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="http://code.google.com/apis/protocolbuffers/docs/overview.html" target=_blank>官方文档</a><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left"></p>
<h3 style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-TOP: 0px">例子介绍</h3>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">先<a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="http://code.google.com/p/protobuf/downloads/list" target=_blank>下载protobuf-2.3.0.zip</a>源代码库，下载后解压，选择vsprojects目录下的protobuf.sln解决方案打开，编译整个方案顺利成功。其中有一些测试工程，库相关的工程是libprotobuf、libprotobuf-lite、libprotoc和protoc。其中protoc是命令行工具。在example目录下有一个地址薄消息的例子，业务消息的定义文件后缀为.proto，其中的addressbook.proto内容为：</p>
<pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">package tutorial;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">option java_package = "com.example.tutorial";</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">option java_outer_classname = "AddressBookProtos";</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">message Person {</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  required string name = 1;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  required int32 id = 2;        // Unique ID number for this person.</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  optional string email = 3;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  enum PhoneType {</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    MOBILE = 0;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    HOME = 1;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    WORK = 2;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  }</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  message PhoneNumber {</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    required string number = 1;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    optional PhoneType type = 2 [default = HOME];</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  }</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  repeated PhoneNumber phone = 4;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">}</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">// Our address book file is just one of these.</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">message AddressBook {</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  repeated Person person = 1;</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">}</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
</pre>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">该定义文件，定义了地址薄消息的结构，顶层消息为AddressBook，其中包含多个Person消息，Person消息中又包含多个PhoneNumber消息。里面还定义了一个PhoneType的枚举类型。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">类型前面有required表示必须，optional表示可选，repeated表示重复，这些定义都是一目了然的，无须多说。关于消息定义的详细语法可参考官方文档。<br><br><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left"></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">现在用命令行工具来生成业务消息类，切换到protoc.exe所在的debug目录，在命令行敲入：</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><font color=#0000ff>protoc.exe --proto_path=..\..\examples --cpp_out=..\..\examples ..\..\examples\addressbook.proto</font></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">该命令中--proto_path参数表示.proto消息定义文件路径，--cpp_out表示输出c++类的路径，后面接着是addressbook.proto消息定义文件。该命令会读取addressbook.proto文件并生成对应的c++类头文件和实现文件。执行完后在examples目录生存了addressbook.pb.h和addressbook.pb.cpp。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">现在新建两个空控制台工程，第一个不妨叫AddPerson，然后把examples目录下的add_person.cc、addressbook.pb.h和addressbook.pb.cpp加入到该工程，另一个工程不妨叫ListPerson，将examples目录下的list_people.cc、addressbook.pb.h和addressbook.pb.cpp加入到该工程，在两个工程的项目属性中附加头文件路径../src。两个工程的项目依赖都选择libprotobuf工程（库）。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">给AddPerson工程添加一个命令行参数比如叫addressbook.dat用于将地址薄信息序列化写入该文件，然后编译运行AddPerson工程，根据提示输入地址薄信息:<br></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="http://hi.csdn.net/attachment/201007/21/0_12797050492e9Q.gif"></a></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><img src="http://www.cppblog.com/images/cppblog_com/mmdengwo/1.gif" border=0><br>输入完成后，将序列化到addressbook.dat文件中。</p>
<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left">
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">在ListPerson工程的命令行参数中加读取文件参数..\AddPerson\addressbook.dat，然后在运行ListPerson工程，可在 list_people.cc的最后设个断点，避免命令行窗口运行完后关闭看不到结果：<br></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><a style="COLOR: rgb(51,102,153); TEXT-DECORATION: none" href="http://hi.csdn.net/attachment/201007/21/0_1279705112UNkf.gif"></a></p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"><img src="http://www.cppblog.com/images/cppblog_com/mmdengwo/2.gif" border=0><br>写入地址薄的操作，关键操作就是调用address_book.SerializeToOstream进行序列化到文件流。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">而读取操作中就是address_book.ParseFromIstream从文件流反序列化，这都是框架自动生成的类中的方法。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">其他操作都是业务消息的字段set/get之类的对象级操作，很明了。更详细的API参考官方文档有详细说明。</p>
<h3 style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; PADDING-TOP: 0px">在TCP网络编程中的考虑</h3>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">从上面的例子可以看出protobuf这样的库是很方便高效的，那么自然的想到在网络编程中用来做业务消息的序列化、反序列化支持。在基于UDP协议的网络应用中，由于UDP本身是有边界，那么用protobuf来处理业务消息就很方便。但在TCP应用中，由于TCP协议没有消息边界，这就需要有一种机制来确定业务消息边界。在TCP网络编程中这是必须面对的问题。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">注意上面的address_book.ParseFromIstream调用，如果流参数的内容多一个字节或者少一个字节，该方法都会返回失败（虽然某些字段可能正确得到结果了），也就是说送给反序列化的数据参数除了格式正确还必须有正确的大小。因此在tcp网络编程中，要反序列化业务消息，就要先知道业务数据的大小。而且在实际应用中可能在一个发送操作中，发送多个业务消息，而且每个业务消息的大小、类型都不一样。而且可能发送很大的数据流，比如文件。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">显然消息边界的确认问题和protobuf库无关，还得自己搞定。在官方文档中也提到，protobuf并不太适合来作大数据的处理，当业务消息超过1M时，就应该考虑是否应该用另外的替代方案。当然对于大数据，你也可以分割为多个小块用protobuf做小块消息封装进行传递。但对很多应用这样的作法显得比较多余，比如发送一个大的文件，一般是在接收方从协议栈收到多少数据就写多少数据到磁盘，这是一种边接收边处理的流模式，这种模式基本上和每次收到的数据量没有关系。这种模式下再采用分割成小消息进行反序列化就显得多此一举了。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">由于每个业务消息的大小和处理方式都可能不一样，那么就需要独立抽象出一个边界消息来区分不同的业务消息，而且这个边界消息的格式和大小必须固定。对于网络编程熟手，可能早已经想到了这样的消息，我们可以结合protobuf库来定义一个边界消息，不妨叫BoundMsg：</p>
</span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left">
<pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">message BoundMsg
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">{
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  required int32 msg_type = 1;
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  required int32 msg_size = 2;
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">}</pre>
</pre>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">可以根据需要扩充一些字段，但最基本的这两个字段就够用了。我们只需要知道业务消息的类型和大小即可。这个消息大小是固定的8字节，专门用来确定数据流的边界。有了这样的边界消息，在接收端处理任何业务消息就很灵活方便了，下面是接收端处理的简单伪代码示例：</p>
</span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left">
<pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"><span style="COLOR: rgb(0,0,255)">if</span>(net_read(buf,8))
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">{
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  boundMsg.ParseFromIstream(buf);
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  <span style="COLOR: rgb(0,0,255)">switch</span>(boundMsg.msg_type)
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  {
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    <span style="COLOR: rgb(0,0,255)">case</span> BO_1:
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      <span style="COLOR: rgb(0,0,255)">if</span>(net_read(bo1Buf,boundMsg.msg_size))
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      {
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">        bo1.ParseFromIstream(bo1Buf);
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">        ....
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      }
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">     <span style="COLOR: rgb(0,0,255)">break</span>;
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    <span style="COLOR: rgb(0,0,255)">case</span> BO_2:
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      <span style="COLOR: rgb(0,0,255)">if</span>(net_read(bo2Buf,boundMsg.msg_size))
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      {
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">        bo2.ParseFromIstream(bo2Buf);
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">        ....
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      }
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">     <span style="COLOR: rgb(0,0,255)">break</span>;
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)"></pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">    <span style="COLOR: rgb(0,0,255)">case</span> FILE_DATA:
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      count = 0;
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      <span style="COLOR: rgb(0,0,255)">while</span>(count &lt; boundMsg.msg_size)
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      {
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">        piece_size = net_read(fileBuf,1024);
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">        write_file(filename,fileBuf,piece_size);
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">        count = count + piece_size;
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      }
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">      <span style="COLOR: rgb(0,0,255)">break</span>;
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">  }
</pre>
<pre style="FONT-SIZE: 12px; MARGIN: 0em; WIDTH: 844px; FONT-FAMILY: consolas, 'Courier New', courier, monospace; BACKGROUND-COLOR: rgb(255,255,255)">}</pre>
</pre>
</span></span><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: medium Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 14px; LINE-HEIGHT: 21px; FONT-FAMILY: verdana, sans-serif; TEXT-ALIGN: left">
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">注意上面如果FILE_DATA消息后，还紧接其他业务消息的话，需要小心，即count累计出的值可能大于</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">boundMsg.msg_size的值，那么多出来的实际上应该是下一个边界消息数据了。为了避免处理的复杂性，上面所有的循环网络读取操作（上面BO_1，BO_2都可能需要循环读取，为了简化没有写成循环）的缓冲区位置和大小参数应该动态调整，即每次读取时传递的都是还期望读取的数据大小，对于文件的话，可能特殊点，因为边读取边写入，就没有必要事先要分配一个文件大小的缓冲区来存放数据了。对于文件分配一个小缓冲区来读，注意确认下边界即可。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px">上面是我的一点考虑，不妥之处还请大家讨论交流。想想借助于ACE、MINA这样的网络编程框架，然后结合protobuf这样的序列化框架，网络编程中技术基础设施层面的东西就给我们解决得差不多了，我们可以真正只关注于业务的实现。</p>
<p style="PADDING-RIGHT: 0px; PADDING-LEFT: 0px; PADDING-BOTTOM: 0px; MARGIN: 1em 0px 0.5em; PADDING-TOP: 0px"></span></span><br>&nbsp;</p>
</span></span></span></span></span></span>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/143466.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-05 18:40 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/05/143466.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Protocol Buffers Language Guide之proto文件类型格式分析[关键点翻译]</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143465.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Tue, 05 Apr 2011 10:29:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143465.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/143465.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143465.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/143465.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/143465.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;转载自:Protocol Buffers Language Guide之proto文件类型格式分析[关键点翻译] | 漂泊如风1、&nbsp;定义一个消息类型：message SearchRequest {required string query = 1;optional int32 page_number = 2;optional int32 result_per_...&nbsp;&nbsp;<a href='http://www.cppblog.com/mmdengwo/archive/2011/04/05/143465.html'>阅读全文</a><img src ="http://www.cppblog.com/mmdengwo/aggbug/143465.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-05 18:29 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/05/143465.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Protocol Buffers介绍</title><link>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143464.html</link><dc:creator>沛沛</dc:creator><author>沛沛</author><pubDate>Tue, 05 Apr 2011 10:26:00 GMT</pubDate><guid>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143464.html</guid><wfw:comment>http://www.cppblog.com/mmdengwo/comments/143464.html</wfw:comment><comments>http://www.cppblog.com/mmdengwo/archive/2011/04/05/143464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/mmdengwo/comments/commentRss/143464.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/mmdengwo/services/trackbacks/143464.html</trackback:ping><description><![CDATA[<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: 16px Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 12px; COLOR: rgb(57,57,57); LINE-HEIGHT: 20px; FONT-FAMILY: 'Lucida Grande', 'Lucida Sans Unicode', 宋体, Arial, Verdana, sans-serif">
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial"><span class=Apple-style-span style="WORD-SPACING: 0px; FONT: 16px Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 12px; COLOR: rgb(140,140,140); LINE-HEIGHT: 20px; FONT-FAMILY: 'Lucida Grande', 'Lucida Sans Unicode', 宋体, Arial, Verdana, sans-serif"><span class=Apple-converted-space>&nbsp;转载自:</span><a title="本文固定链接 http://www.linux-field.com/?p=78" style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://www.linux-field.com/?p=78" rel=bookmark>Protocol Buffers Language Guide之proto文件类型格式分析[关键点翻译] | 漂泊如风</a></span></span><br><br>今天来介绍一下&#8220;Protocol Buffers&#8221;（以下简称protobuf）这个玩意儿。<span id=more-62 style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial"></span></p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">★<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">protobuf是啥玩意儿？</strong><br>为了照顾从没听说过的同学，照例先来扫盲一把。<br>首先，protobuf是一个开源项目（官方站点在&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://code.google.com/p/protobuf/" target=_blank rel=nofollow>这里</a><span class=Apple-converted-space>&nbsp;</span>&#8221;），而且是后台很硬的开源项目。网上现有的大部分（至少80%）开源项目，要么是某人单干、要么是几个闲杂人等合伙搞。而protobuf则不然，它是 鼎鼎大名的Google公司开发出来，并且在Google内部久经考验的一个东东。由此可见，它的作者绝非一般闲杂人等可比。<br>那这个听起来牛X的东东到底有啥用处捏？简单地说，这个东东干的事儿其实和XML差不多，也就是把某种数据结构的信息，以某种格式保存起来。主要用于数据存储、传输协议格式等 场合。有同学可能心理犯嘀咕了：放着好好的XML不用，干嘛重新发明轮子啊？！先别急，后面俺自然会有说道。<br>话说到了去年（大约是08年7 月），Google突然大发慈悲，把这个好东西贡献给了开源社区。这下，像俺这种喜欢捡现成的家伙可就有福啦！貌似喜欢捡现成的家伙还蛮多滴，再加上 Google的号召力，开源后不到一年，protobuf的人气就已经很旺了。所以俺为了与时俱进，就单独开个帖子来忽悠一把。</p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">★<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">protobuf有啥特色？</strong><br>扫盲完了之后，就该聊一下技术方面的话题了。由于这玩意儿发布的时间较短（未满周岁），所以俺接触的时间也不长。今天在此是先学现卖，列位看官多多包涵<span class=Apple-converted-space>&nbsp;</span><strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial"><img class=wp-smiley style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; DISPLAY: inline; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" alt=:-) src="http://www.linux-field.com/wp-includes/images/smilies/icon_smile.gif" original="http://www.linux-field.com/wp-includes/images/smilies/icon_smile.gif"></strong></p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">◇性能好/效率高<br>现在，俺就来说说Google公司为啥放着好端端的XML不用，非要另起炉灶，重新造轮子。一个根本的原因是XML性能不够好。<br>先说时间开销：XML格式化（序列化）的开销倒还好；但是XML解析（反序列化）的开销就不敢恭维啦。俺之前经常碰到一些时间性能很敏感的场合，由于不堪忍受XML解析的速度，弃之如敝履。<br>再来看空间开销：熟悉XML语法的同学应该知道，XML格式为了有较好的可读性，引入了一些冗余的文本信息。所以空间开销也不是太好（不过这点缺点，俺不常碰到）。<br>由于Google公司赖以吹嘘的就是它的海量数据和海量处理能力。对于几十万、上百万机器的集群，动不动就是PB级的数据量，哪怕性能稍微提高0.1% 也是相当可观滴。所以Google自然无法容忍XML在性能上的明显缺点。再加上Google从来就不缺造轮子的牛人，所以protobuf也就应运而生 了。<br>Google对于性能的偏执，那可是出了名的。所以，俺对于Google搞出来protobuf是非常滴放心，性能上不敢说是最好，但肯定不会太差。</p>
<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: 16px Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 12px; COLOR: rgb(57,57,57); LINE-HEIGHT: 20px; FONT-FAMILY: 'Lucida Grande', 'Lucida Sans Unicode', 宋体, Arial, Verdana, sans-serif">
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">◇代码生成机制<br>除了性能好，代码生成机制是主要吸引俺的地方。为了说明这个代码生成机制，俺举个例子。<br>比如有个电子商务的系统（假设用C++实现），其中的模块A需要发送大量的订单信息给模块B，通讯的方式使用socket。<br>假设订单包括如下属性：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>时间：time（用整数表示）<br>客户id：userid（用整数表示）<br>交易金额：price（用浮点数表示）<br>交易的描述：desc（用字符串表示）<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>如果使用protobuf实现，首先要写一个proto文件（不妨叫Order.proto），在该文件中添加一个名为&#8221;Order&#8221;的message结构，用来描述通讯协议中的结构化数据。该文件的内容大致如下：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>message Order<br>{<br>required int32 time = 1;<br>required int32 userid = 2;<br>required float price = 3;<br>optional string desc = 4;<br>}<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">然后，使用protobuf内置的编译器<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">编译</strong><span class=Apple-converted-space>&nbsp;</span>该proto。由于本例子的模块是C++，你可以通过protobuf编译器的命令行参数（看&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://code.google.com/apis/protocolbuffers/docs/proto.html#generating" target=_blank rel=nofollow>这里</a><span class=Apple-converted-space>&nbsp;</span>&#8221;），指定它生成C++语言的&#8220;订单包装类&#8221;。（一般来说，一个message结构会生成一个包装类）<br>然后你使用类似下面的代码来序列化/解析该订单包装类：<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: 16px Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 12px; COLOR: rgb(57,57,57); LINE-HEIGHT: 20px; FONT-FAMILY: 'Lucida Grande', 'Lucida Sans Unicode', 宋体, Arial, Verdana, sans-serif">
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">// 发送方<br>Order order;<br>order.set_time(XXXX);<br>order.set_userid(123);<br>order.set_price(100.0f);<br>order.set_desc(&#8220;a test order&#8221;);</p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">string sOrder;<br>order.SerailzeToString(&amp;sOrder);<br>// 然后调用某种socket的通讯库把序列化之后的字符串发送出去<br>// &#8230;&#8230;</p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br>// 接收方<br>string sOrder;<br>// 先通过网络通讯库接收到数据，存放到某字符串sOrder<br>// &#8230;&#8230;</p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">Order order;<br>if(order.ParseFromString(sOrder)) // 解析该字符串<br>{<br>cout &lt;&lt; &#8220;userid:&#8221; &lt;&lt; order.userid() &lt;&lt; endl<br>&lt;&lt; &#8220;desc:&#8221; &lt;&lt; order.desc() &lt;&lt; endl;<br>}<br>else<br>{<br>cerr &lt;&lt; &#8220;parse error!&#8221; &lt;&lt; endl;<br>}<br>－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－</p>
<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: 16px Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 12px; COLOR: rgb(57,57,57); LINE-HEIGHT: 20px; FONT-FAMILY: 'Lucida Grande', 'Lucida Sans Unicode', 宋体, Arial, Verdana, sans-serif">
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">有了这种代码生成机制，开发人员再也不用吭哧吭哧地编写那些协议解析的代码了（干这种活是典型的吃力不讨好）。<br>万一将来需求发生变更，要求给订单再增加一个&#8220;状态&#8221;的属性，那只需要在Order.proto文件中增加一行代码。对于发送方（模块A），只要增加一行设置状态的代码；对于接收方（模块B）只要增加一行读取状态的代码。哇塞，简直太轻松了！<br>另外，如果通讯双方使用不同的编程语言来实现，使用这种机制可以有效确保两边的模块对于协议的处理是一致的。<br>顺便跑题一下。<br>从某种意义上讲，可以把proto文件看成是描述通讯协议的规格说明书（或者叫接口规范）。这种伎俩其实老早就有了，搞过微软的COM编程或者接触过CORBA的同学，应该都能从中看到IDL（详细解释看&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://en.wikipedia.org/wiki/IDL_specification_language" target=_blank rel=nofollow>这里</a><span class=Apple-converted-space>&nbsp;</span>&#8221;）的影子。它们的思想是相通滴。</p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">◇支持&#8220;向后兼容&#8221;和&#8220;向前兼容&#8221;<br>还是拿刚才的例子来说事儿。为了叙述方便，俺把增加了&#8220;状态&#8221;属性的订单协议成为&#8220;新版本&#8221;；之前的叫&#8220;老版本&#8221;。<br>所谓的&#8220;向后兼容&#8221;（backward compatible），就是说，当模块B升级了之后，它能够正确识别模块A发出的老版本的协议。由于老版本没有&#8220;状态&#8221;这个属性，在扩充协议时，可以考虑把&#8220;状态&#8221;属性设置成<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">非必填</strong><span class=Apple-converted-space>&nbsp;</span>的，或者给&#8220;状态&#8221;属性设置一个缺省值（如何设置缺省值，参见&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://code.google.com/p/protobuf-c/wiki/Default_Values" target=_blank rel=nofollow>这里</a><span class=Apple-converted-space>&nbsp;</span>&#8221;）。<br>所谓的&#8220;向前兼容&#8221;（forward compatible），就是说，当模块A升级了之后，模块B能够正常识别模块A发出的新版本的协议。这时候，新增加的&#8220;状态&#8221;属性会被忽略。<br>&#8220;向后兼容&#8221;和&#8220;向前兼容&#8221;有啥用捏？俺举个例子：当你维护一个很庞大的分布式系统时，由于你无法<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">同时</strong><span class=Apple-converted-space>&nbsp;</span>升级<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">所有</strong><span class=Apple-converted-space>&nbsp;</span>模块，为了保证在升级过程中，整个系统能够尽可能不受影响，就需要尽量保证通讯协议的&#8220;向后兼容&#8221;或&#8220;向前兼容&#8221;。</p>
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">◇支持多种编程语言<br>俺开博以来点评的几个开源项目（比如&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://program-think.blogspot.com/2009/03/opensource-review-sqlite-database.html" target=_blank>Sqlite</a><span class=Apple-converted-space>&nbsp;</span>&#8221;、&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://program-think.blogspot.com/2009/03/opensource-review-curl-library.html" target=_blank>cURL</a><span class=Apple-converted-space>&nbsp;</span>&#8221;），都是支持<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">很多种</strong><span class=Apple-converted-space>&nbsp;</span>编程语言滴，这次的protobuf也不例外。在Google官方发布的源代码中包含了C++、Java、Python三种语言（正好也是俺最常用的三种，真爽）。如果你平时用的就是这三种语言之一，那就好办了。<br>假如你想把protobuf用于其它语言，咋办捏？由于Google一呼百应的号召力，开源社区对protobuf响应踊跃，近期冒出很多其它编程语言 的版本（比如ActionScript、C#、Lisp、Erlang、Perl、PHP、Ruby等），有些语言还同时搞出了多个开源的项目。具体细节可以参见&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://code.google.com/p/protobuf/wiki/OtherLanguages" target=_blank rel=nofollow>这里</a><span class=Apple-converted-space>&nbsp;</span>&#8221;。<br>不过俺有义务提醒一下在座的各位同学。如果你考虑把protobuf用于上述这些语言，一定认真评估对应的开源库。因为这些开源库不是Google官方提供的、而且出来的时间还不长。所以，它们的质量、性能等方面可能还有欠缺。</p>
<span class=Apple-style-span style="WORD-SPACING: 0px; FONT: 16px Simsun; TEXT-TRANSFORM: none; COLOR: rgb(0,0,0); TEXT-INDENT: 0px; WHITE-SPACE: normal; LETTER-SPACING: normal; BORDER-COLLAPSE: separate; orphans: 2; widows: 2; webkit-border-horizontal-spacing: 0px; webkit-border-vertical-spacing: 0px; webkit-text-decorations-in-effect: none; webkit-text-size-adjust: auto; webkit-text-stroke-width: 0px"><span class=Apple-style-span style="FONT-SIZE: 12px; COLOR: rgb(57,57,57); LINE-HEIGHT: 20px; FONT-FAMILY: 'Lucida Grande', 'Lucida Sans Unicode', 宋体, Arial, Verdana, sans-serif">
<p style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 10px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">★<strong style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; FONT-WEIGHT: bold; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial">protobuf有啥缺陷？</strong><br>前几天刚刚在&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://program-think.blogspot.com/2009/05/halo-effect.html">光环效应</a><span class=Apple-converted-space>&nbsp;</span>&#8221;的帖子里强调了&#8220;要同时评估优点和缺点&#8221;。所以俺最后再来批判一下这玩意儿的缺点。<br>◇应用不够广<br>由于protobuf刚公布没多久，相比XML而言，protobuf还属于初出茅庐。因此，在知名度、应用广度等方面都远不如XML。由于这个原因，假如你设计的系统需要提供若干对外的接口给第三方系统调用，俺奉劝你暂时不要考虑protobuf格式。<br>◇二进制格式导致可读性差<br>为了提高性能，protobuf采用了二进制格式进行编码。这直接导致了可读性差的问题（严格地说，是没有可读性）。虽然protobuf提供了TextFormat这个工具类（文档在&#8220;<a style="BORDER-TOP-WIDTH: 0px; PADDING-RIGHT: 0px; PADDING-LEFT: 0px; BORDER-LEFT-WIDTH: 0px; FONT-SIZE: 12px; BORDER-BOTTOM-WIDTH: 0px; PADDING-BOTTOM: 0px; MARGIN: 0px; VERTICAL-ALIGN: baseline; COLOR: rgb(1,150,227); PADDING-TOP: 0px; BACKGROUND-COLOR: transparent; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; outline-width: 0px; outline-style: initial; outline-color: initial; background-origin: initial; background-clip: initial" href="http://code.google.com/apis/protocolbuffers/docs/reference/cpp/google.protobuf.text_format.html" target=_blank rel=nofollow>这里</a><span class=Apple-converted-space>&nbsp;</span>&#8221;），但终究无法彻底解决此问题。<br>可读性差的危害，俺再来举个例子。比如通讯双方如果出现问题，极易导致扯皮（都不承认自己有问题，都说是对方的错）。俺对付扯皮的一个简单方法就是直接 抓包并dump成log，能比较容易地看出错误在哪一方。但是protobuf的二进制格式，导致你抓包并直接dump出来的log难以看懂。<br>◇缺乏自描述<br>一般来说，XML是自描述的，而protobuf格式则不是。给你一段二进制格式的协议内容，如果不配合相应的proto文件，那简直就像天书一般。<br>由于&#8220;缺乏自描述&#8221;，再加上&#8220;二进制格式导致可读性差&#8221;。所以在配置文件方面，protobuf是肯定无法取代XML的地位滴。</p>
</span></span></span></span></span></span></span></span></span></span>
<img src ="http://www.cppblog.com/mmdengwo/aggbug/143464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/mmdengwo/" target="_blank">沛沛</a> 2011-04-05 18:26 <a href="http://www.cppblog.com/mmdengwo/archive/2011/04/05/143464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>