• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            每天早晨叫醒你的不是鬧鐘,而是夢(mèng)想

              C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
              62 Posts :: 0 Stories :: 5 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(1)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            protobuf簡(jiǎn)介

            protobuf是google提供的一個(gè)開(kāi)源序列化框架,類似于XML,JSON這樣的數(shù)據(jù)表示語(yǔ)言,其最大的特點(diǎn)是基于二進(jìn)制,因此比傳統(tǒng)的XML表示高效短小得多。雖然是二進(jìn)制數(shù)據(jù)格式,但并沒(méi)有因此變得復(fù)雜,開(kāi)發(fā)人員通過(guò)按照一定的語(yǔ)法定義結(jié)構(gòu)化的消息格式,然后送給命令行工具,工具將自動(dòng)生成相關(guān)的類,可以支持java、c++、python等語(yǔ)言環(huán)境。通過(guò)將這些類包含在項(xiàng)目中,可以很輕松的調(diào)用相關(guān)方法來(lái)完成業(yè)務(wù)消息的序列化與反序列化工作。

            protobuf在google中是一個(gè)比較核心的基礎(chǔ)庫(kù),作為分布式運(yùn)算涉及到大量的不同業(yè)務(wù)消息的傳遞,如何高效簡(jiǎn)潔的表示、操作這些業(yè)務(wù)消息在google這樣的大規(guī)模應(yīng)用中是至關(guān)重要的。而protobuf這樣的庫(kù)正好是在效率、數(shù)據(jù)大小、易用性之間取得了很好的平衡。

            更多信息可參考官方文檔

            例子介紹

            下載protobuf-2.3.0.zip源代碼庫(kù),下載后解壓,選擇vsprojects目錄下的protobuf.sln解決方案打開(kāi),編譯整個(gè)方案順利成功。其中有一些測(cè)試工程,庫(kù)相關(guān)的工程是libprotobuf、libprotobuf-lite、libprotoc和protoc。其中protoc是命令行工具。在example目錄下有一個(gè)地址薄消息的例子,業(yè)務(wù)消息的定義文件后綴為.proto,其中的addressbook.proto內(nèi)容為:

            package tutorial;
            
            
            option java_package = "com.example.tutorial";
            option java_outer_classname = "AddressBookProtos";
            
            
            message Person {
              required string name = 1;
              required int32 id = 2;        // Unique ID number for this person.
              optional string email = 3;
            
            
              enum PhoneType {
                MOBILE = 0;
                HOME = 1;
                WORK = 2;
              }
            
            
              message PhoneNumber {
                required string number = 1;
                optional PhoneType type = 2 [default = HOME];
              }
            
            
              repeated PhoneNumber phone = 4;
            }
            
            
            // Our address book file is just one of these.
            message AddressBook {
              repeated Person person = 1;
            }
            
            

            該定義文件,定義了地址薄消息的結(jié)構(gòu),頂層消息為AddressBook,其中包含多個(gè)Person消息,Person消息中又包含多個(gè)PhoneNumber消息。里面還定義了一個(gè)PhoneType的枚舉類型。

            類型前面有required表示必須,optional表示可選,repeated表示重復(fù),這些定義都是一目了然的,無(wú)須多說(shuō)。關(guān)于消息定義的詳細(xì)語(yǔ)法可參考官方文檔。

            現(xiàn)在用命令行工具來(lái)生成業(yè)務(wù)消息類,切換到protoc.exe所在的debug目錄,在命令行敲入:

            protoc.exe --proto_path=..\..\examples --cpp_out=..\..\examples ..\..\examples\addressbook.proto

            該命令中--proto_path參數(shù)表示.proto消息定義文件路徑,--cpp_out表示輸出c++類的路徑,后面接著是addressbook.proto消息定義文件。該命令會(huì)讀取addressbook.proto文件并生成對(duì)應(yīng)的c++類頭文件和實(shí)現(xiàn)文件。執(zhí)行完后在examples目錄生存了addressbook.pb.h和addressbook.pb.cpp。

            現(xiàn)在新建兩個(gè)空控制臺(tái)工程,第一個(gè)不妨叫AddPerson,然后把examples目錄下的add_person.cc、addressbook.pb.h和addressbook.pb.cpp加入到該工程,另一個(gè)工程不妨叫ListPerson,將examples目錄下的list_people.cc、addressbook.pb.h和addressbook.pb.cpp加入到該工程,在兩個(gè)工程的項(xiàng)目屬性中附加頭文件路徑../src。兩個(gè)工程的項(xiàng)目依賴都選擇libprotobuf工程(庫(kù))。

            給AddPerson工程添加一個(gè)命令行參數(shù)比如叫addressbook.dat用于將地址薄信息序列化寫入該文件,然后編譯運(yùn)行AddPerson工程,根據(jù)提示輸入地址薄信息:


            輸入完成后,將序列化到addressbook.dat文件中。

            在ListPerson工程的命令行參數(shù)中加讀取文件參數(shù)..\AddPerson\addressbook.dat,然后在運(yùn)行ListPerson工程,可在 list_people.cc的最后設(shè)個(gè)斷點(diǎn),避免命令行窗口運(yùn)行完后關(guān)閉看不到結(jié)果:


            寫入地址薄的操作,關(guān)鍵操作就是調(diào)用address_book.SerializeToOstream進(jìn)行序列化到文件流。

            而讀取操作中就是address_book.ParseFromIstream從文件流反序列化,這都是框架自動(dòng)生成的類中的方法。

            其他操作都是業(yè)務(wù)消息的字段set/get之類的對(duì)象級(jí)操作,很明了。更詳細(xì)的API參考官方文檔有詳細(xì)說(shuō)明。

            在TCP網(wǎng)絡(luò)編程中的考慮

            從上面的例子可以看出protobuf這樣的庫(kù)是很方便高效的,那么自然的想到在網(wǎng)絡(luò)編程中用來(lái)做業(yè)務(wù)消息的序列化、反序列化支持。在基于UDP協(xié)議的網(wǎng)絡(luò)應(yīng)用中,由于UDP本身是有邊界,那么用protobuf來(lái)處理業(yè)務(wù)消息就很方便。但在TCP應(yīng)用中,由于TCP協(xié)議沒(méi)有消息邊界,這就需要有一種機(jī)制來(lái)確定業(yè)務(wù)消息邊界。在TCP網(wǎng)絡(luò)編程中這是必須面對(duì)的問(wèn)題。

            注意上面的address_book.ParseFromIstream調(diào)用,如果流參數(shù)的內(nèi)容多一個(gè)字節(jié)或者少一個(gè)字節(jié),該方法都會(huì)返回失敗(雖然某些字段可能正確得到結(jié)果了),也就是說(shuō)送給反序列化的數(shù)據(jù)參數(shù)除了格式正確還必須有正確的大小。因此在tcp網(wǎng)絡(luò)編程中,要反序列化業(yè)務(wù)消息,就要先知道業(yè)務(wù)數(shù)據(jù)的大小。而且在實(shí)際應(yīng)用中可能在一個(gè)發(fā)送操作中,發(fā)送多個(gè)業(yè)務(wù)消息,而且每個(gè)業(yè)務(wù)消息的大小、類型都不一樣。而且可能發(fā)送很大的數(shù)據(jù)流,比如文件。

            顯然消息邊界的確認(rèn)問(wèn)題和protobuf庫(kù)無(wú)關(guān),還得自己搞定。在官方文檔中也提到,protobuf并不太適合來(lái)作大數(shù)據(jù)的處理,當(dāng)業(yè)務(wù)消息超過(guò)1M時(shí),就應(yīng)該考慮是否應(yīng)該用另外的替代方案。當(dāng)然對(duì)于大數(shù)據(jù),你也可以分割為多個(gè)小塊用protobuf做小塊消息封裝進(jìn)行傳遞。但對(duì)很多應(yīng)用這樣的作法顯得比較多余,比如發(fā)送一個(gè)大的文件,一般是在接收方從協(xié)議棧收到多少數(shù)據(jù)就寫多少數(shù)據(jù)到磁盤,這是一種邊接收邊處理的流模式,這種模式基本上和每次收到的數(shù)據(jù)量沒(méi)有關(guān)系。這種模式下再采用分割成小消息進(jìn)行反序列化就顯得多此一舉了。

            由于每個(gè)業(yè)務(wù)消息的大小和處理方式都可能不一樣,那么就需要獨(dú)立抽象出一個(gè)邊界消息來(lái)區(qū)分不同的業(yè)務(wù)消息,而且這個(gè)邊界消息的格式和大小必須固定。對(duì)于網(wǎng)絡(luò)編程熟手,可能早已經(jīng)想到了這樣的消息,我們可以結(jié)合protobuf庫(kù)來(lái)定義一個(gè)邊界消息,不妨叫BoundMsg:

            message BoundMsg
            
            {
            
              required int32 msg_type = 1;
            
              required int32 msg_size = 2;
            
            }

            可以根據(jù)需要擴(kuò)充一些字段,但最基本的這兩個(gè)字段就夠用了。我們只需要知道業(yè)務(wù)消息的類型和大小即可。這個(gè)消息大小是固定的8字節(jié),專門用來(lái)確定數(shù)據(jù)流的邊界。有了這樣的邊界消息,在接收端處理任何業(yè)務(wù)消息就很靈活方便了,下面是接收端處理的簡(jiǎn)單偽代碼示例:

            if(net_read(buf,8))
            
            {
            
              boundMsg.ParseFromIstream(buf);
            
              switch(boundMsg.msg_type)
            
              {
            
                case BO_1:
            
                  if(net_read(bo1Buf,boundMsg.msg_size))
            
                  {
            
                    bo1.ParseFromIstream(bo1Buf);
            
                    ....
            
                  }
            
                 break;
            
                case BO_2:
            
                  if(net_read(bo2Buf,boundMsg.msg_size))
            
                  {
            
                    bo2.ParseFromIstream(bo2Buf);
            
                    ....
            
                  }
            
                 break;
            
            
            
                case FILE_DATA:
            
                  count = 0;
            
                  while(count < boundMsg.msg_size)
            
                  {
            
                    piece_size = net_read(fileBuf,1024);
            
                    write_file(filename,fileBuf,piece_size);
            
                    count = count + piece_size;
            
                  }
            
                  break;
            
              }
            
            }

            注意上面如果FILE_DATA消息后,還緊接其他業(yè)務(wù)消息的話,需要小心,即count累計(jì)出的值可能大于

            boundMsg.msg_size的值,那么多出來(lái)的實(shí)際上應(yīng)該是下一個(gè)邊界消息數(shù)據(jù)了。為了避免處理的復(fù)雜性,上面所有的循環(huán)網(wǎng)絡(luò)讀取操作(上面BO_1,BO_2都可能需要循環(huán)讀取,為了簡(jiǎn)化沒(méi)有寫成循環(huán))的緩沖區(qū)位置和大小參數(shù)應(yīng)該動(dòng)態(tài)調(diào)整,即每次讀取時(shí)傳遞的都是還期望讀取的數(shù)據(jù)大小,對(duì)于文件的話,可能特殊點(diǎn),因?yàn)檫呑x取邊寫入,就沒(méi)有必要事先要分配一個(gè)文件大小的緩沖區(qū)來(lái)存放數(shù)據(jù)了。對(duì)于文件分配一個(gè)小緩沖區(qū)來(lái)讀,注意確認(rèn)下邊界即可。

            上面是我的一點(diǎn)考慮,不妥之處還請(qǐng)大家討論交流。想想借助于ACE、MINA這樣的網(wǎng)絡(luò)編程框架,然后結(jié)合protobuf這樣的序列化框架,網(wǎng)絡(luò)編程中技術(shù)基礎(chǔ)設(shè)施層面的東西就給我們解決得差不多了,我們可以真正只關(guān)注于業(yè)務(wù)的實(shí)現(xiàn)。


             

            posted on 2011-04-05 18:40 沛沛 閱讀(1190) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 開(kāi)源庫(kù)
            性高朝久久久久久久久久| 日韩精品无码久久一区二区三 | 777久久精品一区二区三区无码| 久久婷婷五月综合97色直播| 2020久久精品亚洲热综合一本| 久久久网中文字幕| 久久精品一本到99热免费| 无码人妻精品一区二区三区久久| 久久久久亚洲AV无码网站| 亚洲国产精品无码久久| 69SEX久久精品国产麻豆| 欧美伊香蕉久久综合类网站| 亚洲精品国产综合久久一线| 97精品依人久久久大香线蕉97| 亚洲精品国产字幕久久不卡| 久久国产高清字幕中文| 国产精品99久久久精品无码| 青青青国产成人久久111网站| 久久这里的只有是精品23| 韩国免费A级毛片久久| 久久精品无码专区免费 | 久久天堂AV综合合色蜜桃网 | 久久午夜免费视频| 久久97精品久久久久久久不卡| 亚洲欧美成人久久综合中文网 | 久久www免费人成精品香蕉| 久久99这里只有精品国产| 国产精品免费久久久久电影网| 久久国产欧美日韩精品| 午夜精品久久久久久久无码| 久久综合综合久久97色| 国产精品一区二区久久国产| 久久亚洲精品无码VA大香大香| 久久久精品波多野结衣| 91久久九九无码成人网站| 99久久免费国产精品热| 无码伊人66久久大杳蕉网站谷歌| 亚洲中文字幕伊人久久无码| 亚洲色欲久久久久综合网| 久久久久99精品成人片| 久久99精品久久久久久9蜜桃 |