国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

Protocol Buffer技術(shù)詳解(語(yǔ)言規(guī)范)

2019-11-11 05:21:01
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

該系列Blog的內(nèi)容主體主要源自于PRotocol Buffer的官方文檔,而代碼示例則抽取于當(dāng)前正在開(kāi)發(fā)的一個(gè)公司內(nèi)部項(xiàng)目的Demo。這樣做的目的主要在于不僅可以保持Google文檔的良好風(fēng)格和系統(tǒng)性,同時(shí)再結(jié)合一些比較實(shí)用和通用的用例,這樣就更加便于公司內(nèi)部的培訓(xùn),以及和廣大網(wǎng)友的技術(shù)交流。需要說(shuō)明的是,Blog的內(nèi)容并非line by line的翻譯,其中包含一些經(jīng)驗(yàn)性總結(jié),與此同時(shí),對(duì)于一些不是非常常用的功能并未予以說(shuō)明,有興趣的開(kāi)發(fā)者可以直接查閱Google的官方文檔。      一、為什么使用Protocol Buffer?      在回答這個(gè)問(wèn)題之前,我們還是先給出一個(gè)在實(shí)際開(kāi)發(fā)中經(jīng)常會(huì)遇到的系統(tǒng)場(chǎng)景。比如:我們的客戶端程序是使用java開(kāi)發(fā)的,可能運(yùn)行自不同的平臺(tái),如:linux、Windows或者是Android,而我們的服務(wù)器程序通常是基于Linux平臺(tái)并使用C++開(kāi)發(fā)完成的。在這兩種程序之間進(jìn)行數(shù)據(jù)通訊時(shí)存在多種方式用于設(shè)計(jì)消息格式,如:      1. 直接傳遞C/C++語(yǔ)言中一字節(jié)對(duì)齊的結(jié)構(gòu)體數(shù)據(jù),只要結(jié)構(gòu)體的聲明為定長(zhǎng)格式,那么該方式對(duì)于C/C++程序而言就非常方便了,僅需將接收到的數(shù)據(jù)按照結(jié)構(gòu)體類(lèi)型強(qiáng)行轉(zhuǎn)換即可。事實(shí)上對(duì)于變長(zhǎng)結(jié)構(gòu)體也不會(huì)非常麻煩。在發(fā)送數(shù)據(jù)時(shí),也只需定義一個(gè)結(jié)構(gòu)體變量并設(shè)置各個(gè)成員變量的值之后,再以char*的方式將該二進(jìn)制數(shù)據(jù)發(fā)送到遠(yuǎn)端。反之,該方式對(duì)于Java開(kāi)發(fā)者而言就會(huì)非常繁瑣,首先需要將接收到的數(shù)據(jù)存于ByteBuffer之中,再根據(jù)約定的字節(jié)序逐個(gè)讀取每個(gè)字段,并將讀取后的值再賦值給另外一個(gè)值對(duì)象中的域變量,以便于程序中其他代碼邏輯的編寫(xiě)。對(duì)于該類(lèi)型程序而言,聯(lián)調(diào)的基準(zhǔn)是必須客戶端和服務(wù)器雙方均完成了消息報(bào)文構(gòu)建程序的編寫(xiě)后才能展開(kāi),而該設(shè)計(jì)方式將會(huì)直接導(dǎo)致Java程序開(kāi)發(fā)的進(jìn)度過(guò)慢。即便是Debug階段,也會(huì)經(jīng)常遇到Java程序中出現(xiàn)各種域字段拼接的小錯(cuò)誤。      2. 使用SOAP協(xié)議(WebService)作為消息報(bào)文的格式載體,由該方式生成的報(bào)文是基于文本格式的,同時(shí)還存在大量的xml描述信息,因此將會(huì)大大增加網(wǎng)絡(luò)IO的負(fù)擔(dān)。又由于XML解析的復(fù)雜性,這也會(huì)大幅降低報(bào)文解析的性能。總之,使用該設(shè)計(jì)方式將會(huì)使系統(tǒng)的整體運(yùn)行性能明顯下降。      對(duì)于以上兩種方式所產(chǎn)生的問(wèn)題,Protocol Buffer均可以很好的解決,不僅如此,Protocol Buffer還有一個(gè)非常重要的優(yōu)點(diǎn)就是可以保證同一消息報(bào)文新舊版本之間的兼容性。至于具體的方式我們將會(huì)在后續(xù)的博客中給出。      二、定義第一個(gè)Protocol Buffer消息。      創(chuàng)建擴(kuò)展名為.proto的文件,如:MyMessage.proto,并將以下內(nèi)容存入該文件中。      message LogonReqMessage {          required int64 acctID = 1;          required string passwd = 2;      }      這里將給出以上消息定義的關(guān)鍵性說(shuō)明。      1. message是消息定義的關(guān)鍵字,等同于C++中的struct/class,或是Java中的class。      2. LogonReqMessage為消息的名字,等同于結(jié)構(gòu)體名或類(lèi)名。      3. required前綴表示該字段為必要字段,既在序列化和反序列化之前該字段必須已經(jīng)被賦值。與此同時(shí),在Protocol Buffer中還存在另外兩個(gè)類(lèi)似的關(guān)鍵字,optional和repeated,帶有這兩種限定符的消息字段則沒(méi)有required字段這樣的限制。相比于optional,repeated主要用于表示數(shù)組字段。具體的使用方式在后面的用例中均會(huì)一一列出。      4. int64和string分別表示長(zhǎng)整型和字符串型的消息字段,在Protocol Buffer中存在一張類(lèi)型對(duì)照表,既Protocol Buffer中的數(shù)據(jù)類(lèi)型與其他編程語(yǔ)言(C++/Java)中所用類(lèi)型的對(duì)照。該對(duì)照表中還將給出在不同的數(shù)據(jù)場(chǎng)景下,哪種類(lèi)型更為高效。該對(duì)照表將在后面給出。      5. acctID和passwd分別表示消息字段名,等同于Java中的域變量名,或是C++中的成員變量名。      6. 標(biāo)簽數(shù)字12則表示不同的字段在序列化后的二進(jìn)制數(shù)據(jù)中的布局位置。在該例中,passwd字段編碼后的數(shù)據(jù)一定位于acctID之后。需要注意的是該值在同一message中不能重復(fù)。另外,對(duì)于Protocol Buffer而言,標(biāo)簽值為1到15的字段在編碼時(shí)可以得到優(yōu)化,既標(biāo)簽值和類(lèi)型信息僅占有一個(gè)byte,標(biāo)簽范圍是16到2047的將占有兩個(gè)bytes,而Protocol Buffer可以支持的字段數(shù)量則為2的29次方減一。有鑒于此,我們?cè)谠O(shè)計(jì)消息結(jié)構(gòu)時(shí),可以盡可能考慮讓repeated類(lèi)型的字段標(biāo)簽位于1到15之間,這樣便可以有效的節(jié)省編碼后的字節(jié)數(shù)量。      三、定義第二個(gè)(含有枚舉字段)Protocol Buffer消息。      //在定義Protocol Buffer的消息時(shí),可以使用和C++/Java代碼同樣的方式添加注釋。      enum UserStatus {          OFFLINE = 0;  //表示處于離線狀態(tài)的用戶          ONLINE = 1;   //表示處于在線狀態(tài)的用戶      }      message UserInfo {          required int64 acctID = 1;          required string name = 2;          required UserStatus status = 3;      }      這里將給出以上消息定義的關(guān)鍵性說(shuō)明(僅包括上一小節(jié)中沒(méi)有描述的)。      1. enum是枚舉類(lèi)型定義的關(guān)鍵字,等同于C++/Java中的enum。      2. UserStatus為枚舉的名字。      3. 和C++/Java中的枚舉不同的是,枚舉值之間的分隔符是分號(hào),而不是逗號(hào)。      4. OFFLINE/ONLINE為枚舉值。      5. 0和1表示枚舉值所對(duì)應(yīng)的實(shí)際整型值,和C/C++一樣,可以為枚舉值指定任意整型值,而無(wú)需總是從0開(kāi)始定義。如:      enum OperationCode {          LOGON_REQ_CODE = 101;          LOGOUT_REQ_CODE = 102;          RETRIEVE_BUDDIES_REQ_CODE = 103;              LOGON_RESP_CODE = 1001;          LOGOUT_RESP_CODE = 1002;          RETRIEVE_BUDDIES_RESP_CODE = 1003;      }      四、定義第三個(gè)(含有嵌套消息字段)Protocol Buffer消息。      我們可以在同一個(gè).proto文件中定義多個(gè)message,這樣便可以很容易的實(shí)現(xiàn)嵌套消息的定義。如:      enum UserStatus {          OFFLINE = 0;          ONLINE = 1;      }      message UserInfo {          required int64 acctID = 1;          required string name = 2;          required UserStatus status = 3;      }      message LogonRespMessage {          required LoginResult logonResult = 1;          required UserInfo userInfo = 2;      }      這里將給出以上消息定義的關(guān)鍵性說(shuō)明(僅包括上兩小節(jié)中沒(méi)有描述的)。      1. LogonRespMessage消息的定義中包含另外一個(gè)消息類(lèi)型作為其字段,如UserInfo userInfo。      2. 上例中的UserInfo和LogonRespMessage被定義在同一個(gè).proto文件中,那么我們是否可以包含在其他.proto文件中定義的message呢?Protocol Buffer提供了另外一個(gè)關(guān)鍵字import,這樣我們便可以將很多通用的message定義在同一個(gè).proto文件中,而其他消息定義文件可以通過(guò)import的方式將該文件中定義的消息包含進(jìn)來(lái),如:      import "myproject/CommonMessages.proto"     五、限定符(required/optional/repeated)的基本規(guī)則。      1. 在每個(gè)消息中必須至少留有一個(gè)required類(lèi)型的字段。       2. 每個(gè)消息中可以包含0個(gè)或多個(gè)optional類(lèi)型的字段。      3. repeated表示的字段可以包含0個(gè)或多個(gè)數(shù)據(jù)。需要說(shuō)明的是,這一點(diǎn)有別于C++/Java中的數(shù)組,因?yàn)楹髢烧咧械臄?shù)組必須包含至少一個(gè)元素。      4. 如果打算在原有消息協(xié)議中添加新的字段,同時(shí)還要保證老版本的程序能夠正常讀取或?qū)懭耄敲磳?duì)于新添加的字段必須是optional或repeated。道理非常簡(jiǎn)單,老版本程序無(wú)法讀取或?qū)懭胄略龅膔equired限定符的字段。      六、類(lèi)型對(duì)照表。

.proto TypeNotesC++ TypeJava Type
double  double double
float  float float
int32Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. int32 int
int64Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. int64 long
uint32Uses variable-length encoding. uint32 int
uint64Uses variable-length encoding. uint64 long
sint32Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. int32 int
sint64Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s.  int64 long
fixed32Always four bytes. More efficient than uint32 if values are often greater than 228 uint32 int
fixed64Always eight bytes. More efficient than uint64 if values are often greater than 256. uint64 long
sfixed32Always four bytes. int32 int
sfixed64Always eight bytes. int64 long
bool  bool boolean
stringA string must always contain UTF-8 encoded or 7-bit ASCII text. string String
bytesMay contain any arbitrary sequence of bytes.stringByteString

      七、Protocol Buffer消息升級(jí)原則。      在實(shí)際的開(kāi)發(fā)中會(huì)存在這樣一種應(yīng)用場(chǎng)景,既消息格式因?yàn)槟承┬枨蟮淖兓坏貌贿M(jìn)行必要的升級(jí),但是有些使用原有消息格式的應(yīng)用程序暫時(shí)又不能被立刻升級(jí),這便要求我們?cè)谏?jí)消息格式時(shí)要遵守一定的規(guī)則,從而可以保證基于新老消息格式的新老程序同時(shí)運(yùn)行。規(guī)則如下:      1. 不要修改已經(jīng)存在字段的標(biāo)簽號(hào)。      2. 任何新添加的字段必須是optional和repeated限定符,否則無(wú)法保證新老程序在互相傳遞消息時(shí)的消息兼容性。      3. 在原有的消息中,不能移除已經(jīng)存在的required字段,optional和repeated類(lèi)型的字段可以被移除,但是他們之前使用的標(biāo)簽號(hào)必須被保留,不能被新的字段重用。      4. int32、uint32、int64、uint64和bool等類(lèi)型之間是兼容的,sint32和sint64是兼容的,string和bytes是兼容的,fixed32和sfixed32,以及fixed64和sfixed64之間是兼容的,這意味著如果想修改原有字段的類(lèi)型時(shí),為了保證兼容性,只能將其修改為與其原有類(lèi)型兼容的類(lèi)型,否則就將打破新老消息格式的兼容性。      5. optional和repeated限定符也是相互兼容的。      八、Packages。      我們可以在.proto文件中定義包名,如:      package ourproject.lyphone;      該包名在生成對(duì)應(yīng)的C++文件時(shí),將被替換為名字空間名稱(chēng),既namespace ourproject { namespace lyphone。而在生成的Java代碼文件中將成為包名。      九、Options。      Protocol Buffer允許我們?cè)?proto文件中定義一些常用的選項(xiàng),這樣可以指示Protocol Buffer編譯器幫助我們生成更為匹配的目標(biāo)語(yǔ)言代碼。Protocol Buffer內(nèi)置的選項(xiàng)被分為以下三個(gè)級(jí)別:      1. 文件級(jí)別,這樣的選項(xiàng)將影響當(dāng)前文件中定義的所有消息和枚舉。      2. 消息級(jí)別,這樣的選項(xiàng)僅影響某個(gè)消息及其包含的所有字段。      3. 字段級(jí)別,這樣的選項(xiàng)僅僅響應(yīng)與其相關(guān)的字段。      下面將給出一些常用的Protocol Buffer選項(xiàng)。      1. option java_package = "com.companyname.projectname";      java_package是文件級(jí)別的選項(xiàng),通過(guò)指定該選項(xiàng)可以讓生成Java代碼的包名為該選項(xiàng)值,如上例中的Java代碼包名為com.companyname.projectname。與此同時(shí),生成的Java文件也將會(huì)自動(dòng)存放到指定輸出目錄下的com/companyname/projectname子目錄中。如果沒(méi)有指定該選項(xiàng),Java的包名則為package關(guān)鍵字指定的名稱(chēng)。該選項(xiàng)對(duì)于生成C++代碼毫無(wú)影響。      2. option java_outer_classname = "LYPhoneMessage";      java_outer_classname是文件級(jí)別的選項(xiàng),主要功能是顯示的指定生成Java代碼的外部類(lèi)名稱(chēng)。如果沒(méi)有指定該選項(xiàng),Java代碼的外部類(lèi)名稱(chēng)為當(dāng)前文件的文件名部分,同時(shí)還要將文件名轉(zhuǎn)換為駝峰格式,如:my_project.proto,那么該文件的默認(rèn)外部類(lèi)名稱(chēng)將為MyProject。該選項(xiàng)對(duì)于生成C++代碼毫無(wú)影響。      注:主要是因?yàn)镴ava中要求同一個(gè).java文件中只能包含一個(gè)Java外部類(lèi)或外部接口,而C++則不存在此限制。因此在.proto文件中定義的消息均為指定外部類(lèi)的內(nèi)部類(lèi),這樣才能將這些消息生成到同一個(gè)Java文件中。在實(shí)際的使用中,為了避免總是輸入該外部類(lèi)限定符,可以將該外部類(lèi)靜態(tài)引入到當(dāng)前Java文件中,如:import static com.company.project.LYPhoneMessage.*。      3. option optimize_for = LITE_RUNTIME;      optimize_for是文件級(jí)別的選項(xiàng),Protocol Buffer定義三種優(yōu)化級(jí)別SPEED/CODE_SIZE/LITE_RUNTIME。缺省情況下是SPEED。      SPEED: 表示生成的代碼運(yùn)行效率高,但是由此生成的代碼編譯后會(huì)占用更多的空間。      CODE_SIZE: 和SPEED恰恰相反,代碼運(yùn)行效率較低,但是由此生成的代碼編譯后會(huì)占用更少的空間,通常用于資源有限的平臺(tái),如Mobile。      LITE_RUNTIME: 生成的代碼執(zhí)行效率高,同時(shí)生成代碼編譯后的所占用的空間也是非常少。這是以犧牲Protocol Buffer提供的反射功能為代價(jià)的。因此我們?cè)贑++中鏈接Protocol Buffer庫(kù)時(shí)僅需鏈接libprotobuf-lite,而非libprotobuf。在Java中僅需包含protobuf-java-2.4.1-lite.jar,而非protobuf-java-2.4.1.jar。      注:對(duì)于LITE_MESSAGE選項(xiàng)而言,其生成的代碼均將繼承自MessageLite,而非Message。          4. [pack = true]: 因?yàn)闅v史原因,對(duì)于數(shù)值型的repeated字段,如int32、int64等,在編碼時(shí)并沒(méi)有得到很好的優(yōu)化,然而在新近版本的Protocol Buffer中,可通過(guò)添加[pack=true]的字段選項(xiàng),以通知Protocol Buffer在為該類(lèi)型的消息對(duì)象編碼時(shí)更加高效。如:      repeated int32 samples = 4 [packed=true]。      注:該選項(xiàng)僅適用于2.3.0以上的Protocol Buffer。      5. [default = default_value]: optional類(lèi)型的字段,如果在序列化時(shí)沒(méi)有被設(shè)置,或者是老版本的消息中根本不存在該字段,那么在反序列化該類(lèi)型的消息是,optional的字段將被賦予類(lèi)型相關(guān)的缺省值,如bool被設(shè)置為false,int32被設(shè)置為0。Protocol Buffer也支持自定義的缺省值,如:      optional int32 result_per_page = 3 [default = 10]。     十、命令行編譯工具。      protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR path/to/file.proto      這里將給出上述命令的參數(shù)解釋。      1. protoc為Protocol Buffer提供的命令行編譯工具。      2. --proto_path等同于-I選項(xiàng),主要用于指定待編譯的.proto消息定義文件所在的目錄,該選項(xiàng)可以被同時(shí)指定多個(gè)。      3. --cpp_out選項(xiàng)表示生成C++代碼,--java_out表示生成Java代碼,--python_out則表示生成Python代碼,其后的目錄為生成后的代碼所存放的目錄。      4. path/to/file.proto表示待編譯的消息定義文件。

      注:對(duì)于C++而言,通過(guò)Protocol Buffer編譯工具,可以將每個(gè).proto文件生成出一對(duì).h和.cc的C++代碼文件。生成后的文件可以直接加載到應(yīng)用程序所在的工程項(xiàng)目中。如:MyMessage.proto生成的文件為MyMessage.pb.h和MyMessage.pb.cc。

http://www.cnblogs.com/stephen-liu74/archive/2013/01/02/2841485.html

Protocol Buffer技術(shù)詳解(C++實(shí)例)

這篇Blog仍然是以Google的官方文檔為主線,代碼實(shí)例則完全取自于我們正在開(kāi)發(fā)的一個(gè)Demo項(xiàng)目,通過(guò)前一段時(shí)間的嘗試,感覺(jué)這種結(jié)合的方式比較有利于培訓(xùn)和內(nèi)部的技術(shù)交流。還是那句話,沒(méi)有最好的,只有最適合的。我想寫(xiě)B(tài)log也是這一道理吧,不同的技術(shù)主題可能需要采用不同的風(fēng)格。好了,還是讓我們盡早切入主題吧。          一、生成目標(biāo)語(yǔ)言代碼。      下面的命令幫助我們將MyMessage.proto文件中定義的一組Protocol Buffer格式的消息編譯成目標(biāo)語(yǔ)言(C++)的代碼。至于消息的內(nèi)容,我們會(huì)在后面以分段的形式逐一列出,同時(shí)也會(huì)在附件中給出所有源代碼。      protoc -I=./message --cpp_out=./src ./MyMessage.proto      從上面的命令行參數(shù)中可以看出,待編譯的文件為MyMessage.proto,他存放在當(dāng)前目錄的message子目錄下。--cpp_out參數(shù)則指示編譯工具我們需要生成目標(biāo)語(yǔ)言是C++,輸出目錄是當(dāng)前目錄的src子目錄。在本例中,生成的目標(biāo)代碼文件名是MyMessage.pb.h和MyMessage.pb.cc。          二、簡(jiǎn)單message生成的C++代碼。      這里先定義一個(gè)最簡(jiǎn)單的message,其中只是包含原始類(lèi)型的字段。      option optimize_for = LITE_RUNTIME;      message LogonReqMessage {          required int64 acctID = 1;          required string passwd = 2;      }      由于我們?cè)贛yMessage文件中定義選項(xiàng)optimize_for的值為L(zhǎng)ITE_RUNTIME,因此由該.proto文件生成的所有C++類(lèi)的父類(lèi)均為::google::protobuf::MessageLite,而非::google::protobuf::Message。在上一篇博客中已經(jīng)給出了一些簡(jiǎn)要的說(shuō)明,MessageLite類(lèi)是Message的父類(lèi),在MessageLite中將缺少Protocol Buffer對(duì)反射的支持,而此類(lèi)功能均在Message類(lèi)中提供了具體的實(shí)現(xiàn)。對(duì)于我們的項(xiàng)目而言,整個(gè)系統(tǒng)相對(duì)比較封閉,不會(huì)和更多的外部程序進(jìn)行交互,與此同時(shí),我們的客戶端部分又是運(yùn)行在Android平臺(tái),有鑒于此,我們考慮使用LITE版本的Protocol Buffer。這樣不僅可以得到更高編碼效率,而且生成代碼編譯后所占用的資源也會(huì)更少,至于反射所能帶來(lái)的靈活性和極易擴(kuò)展性,對(duì)于該項(xiàng)目而言完全可以忽略。下面我們來(lái)看一下由message LogonReqMessage生成的C++類(lèi)的部分聲明,以及常用方法的說(shuō)明性注釋。

復(fù)制代碼
 1     class LogonReqMessage : public ::google::protobuf::MessageLite { 2     public: 3         LogonReqMessage(); 4         virtual ~LogonReqMessage(); 5  6         // implements Message ---------------------------------------------- 7         //下面的成員函數(shù)均實(shí)現(xiàn)自MessageLite中的虛函數(shù)。 8         //創(chuàng)建一個(gè)新的LogonReqMessage對(duì)象,等同于clone。 9         LogonReqMessage* New() const;10         //用另外一個(gè)LogonReqMessage對(duì)象初始化當(dāng)前對(duì)象,等同于賦值操作符重載(operator=)11         void CopyFrom(const LogonReqMessage& from);12         //清空當(dāng)前對(duì)象中的所有數(shù)據(jù),既將所有成員變量置為未初始化狀態(tài)。13         void Clear();14         //判斷當(dāng)前狀態(tài)是否已經(jīng)初始化。15         bool IsInitialized() const;16         //在給當(dāng)前對(duì)象的所有變量賦值之后,獲取該對(duì)象序列化后所需要的字節(jié)數(shù)。17         int ByteSize() const;18         //獲取當(dāng)前對(duì)象的類(lèi)型名稱(chēng)。19         ::std::string GetTypeName() const;20 21         // required int64 acctID = 1;22         //下面的成員函數(shù)都是因message中定義的acctID字段而生成。23         //這個(gè)靜態(tài)成員表示AcctID的標(biāo)簽值。命名規(guī)則是k + FieldName(駝峰規(guī)則) + FieldNumber。24         static const int kAcctIDFieldNumber = 1;25         //如果acctID字段已經(jīng)被設(shè)置返回true,否則false。26         inline bool has_acctid() const;27         //執(zhí)行該函數(shù)后has_acctid函數(shù)將返回false,而下面的acctid函數(shù)則返回acctID的缺省值。28         inline void clear_acctid();29         //返回acctid字段的當(dāng)前值,如果沒(méi)有設(shè)置則返回int64類(lèi)型的缺省值。30         inline ::google::protobuf::int64 acctid() const;31         //為acctid字段設(shè)置新值,調(diào)用該函數(shù)后has_acctid函數(shù)將返回true。32         inline void set_acctid(::google::protobuf::int64 value);33     34         // required string passwd = 2;35         //下面的成員函數(shù)都是因message中定義的passwd字段而生成。這里生成的函數(shù)和上面acctid36         //生成的那組函數(shù)基本相似。因此這里只是列出差異部分。37         static const int kPasswdFieldNumber = 2;38         inline bool has_passwd() const;39         inline void clear_passwd();40         inline const ::std::string& passwd() const;41         inline void set_passwd(const ::std::string& value);42         //對(duì)于字符串類(lèi)型字段設(shè)置const char*類(lèi)型的變量值。43         inline void set_passwd(const char* value);44         inline void set_passwd(const char* value, size_t size);45         //可以通過(guò)返回值直接給passwd對(duì)象賦值。在調(diào)用該函數(shù)之后has_passwd將返回true。46         inline ::std::string* mutable_passwd();47         //釋放當(dāng)前對(duì)象對(duì)passwd字段的所有權(quán),同時(shí)返回passwd字段對(duì)象指針。調(diào)用此函數(shù)之后,passwd字段對(duì)象48         //的所有權(quán)將移交給調(diào)用者。此后再調(diào)用has_passwd函數(shù)時(shí)將返回false。49         inline ::std::string* release_passwd();50     private:51         ... ... 52     };復(fù)制代碼

      下面是讀寫(xiě)LogonReqMessage對(duì)象的C++測(cè)試代碼和說(shuō)明性注釋。

復(fù)制代碼
 1     void testSimpleMessage() 2     { 3         printf("==================This is simple message.================/n"); 4         //序列化LogonReqMessage對(duì)象到指定的內(nèi)存區(qū)域。 5         LogonReqMessage logonReq; 6         logonReq.set_acctid(20); 7         logonReq.set_passwd("Hello World"); 8         //提前獲取對(duì)象序列化所占用的空間并進(jìn)行一次性分配,從而避免多次分配 9         //而造成的性能開(kāi)銷(xiāo)。通過(guò)該種方式,還可以將序列化后的數(shù)據(jù)進(jìn)行加密。10         //之后再進(jìn)行持久化,或是發(fā)送到遠(yuǎn)端。11         int length = logonReq.ByteSize();12         char* buf = new char[length];13         logonReq.SerializeToArray(buf,length);14         //從內(nèi)存中讀取并反序列化LogonReqMessage對(duì)象,同時(shí)將結(jié)果打印出來(lái)。15         LogonReqMessage logonReq2;16         logonReq2.ParseFromArray(buf,length);17         printf("acctID = %I64d, passWord = %s/n",logonReq2.acctid(),logonReq2.passwd().c_str());18         delete [] buf;19     }復(fù)制代碼

      三、嵌套message生成的C++代碼。      enum UserStatus {          OFFLINE = 0;          ONLINE = 1;      }      enum LoginResult {          LOGON_RESULT_SUCCESS = 0;          LOGON_RESULT_NOTEXIST = 1;          LOGON_RESULT_ERROR_PASSWD = 2;          LOGON_RESULT_ALREADY_LOGON = 3;          LOGON_RESULT_SERVER_ERROR = 4;      }      message UserInfo {          required int64 acctID = 1;          required string name = 2;          required UserStatus status = 3;      }      message LogonRespMessage {          required LoginResult logonResult = 1;          required UserInfo userInfo = 2; //這里嵌套了UserInfo消息。      }      對(duì)于上述消息生成的C++代碼,UserInfo因?yàn)橹皇前嗽碱?lèi)型字段,因此和上例中的LogonReqMessage沒(méi)有太多的差別,這里也就不在重復(fù)列出了。由于LogonRespMessage消息中嵌套了UserInfo類(lèi)型的字段,在這里我們將僅僅給出該消息生成的C++代碼和關(guān)鍵性注釋。

復(fù)制代碼
 1     class LogonRespMessage : public ::google::protobuf::MessageLite { 2     public: 3         LogonRespMessage(); 4         virtual ~LogonRespMessage(); 5      6         // implements Message ---------------------------------------------- 7         ... ... //這部分函數(shù)和之前的例子一樣。 8          9         // required .LoginResult logonResult = 1;10         //下面的成員函數(shù)都是因message中定義的logonResult字段而生成。11         //這一點(diǎn)和前面的例子基本相同,只是類(lèi)型換做了枚舉類(lèi)型LoginResult。    12         static const int kLogonResultFieldNumber = 1;13         inline bool has_logonresult() const;14         inline void clear_logonresult();15         inline LoginResult logonresult() const;16         inline void set_logonresult(LoginResult value);17         18         // required .UserInfo userInfo = 2;19         //下面的成員函數(shù)都是因message中定義的UserInfo字段而生成。20         //這里只是列出和非消息類(lèi)型字段差異的部分。21         static const int kUserInfoFieldNumber = 2;22         inline bool has_userinfo() const;23         inline void clear_userinfo();24         inline const ::UserInfo& userinfo() const;25         //可以看到該類(lèi)并沒(méi)有生成用于設(shè)置和修改userInfo字段set_userinfo函數(shù),而是將該工作26         //交給了下面的mutable_userinfo函數(shù)。因此每當(dāng)調(diào)用函數(shù)之后,Protocol Buffer都會(huì)認(rèn)為27         //該字段的值已經(jīng)被設(shè)置了,同時(shí)has_userinfo函數(shù)亦將返回true。在實(shí)際編碼中,我們可以28         //通過(guò)該函數(shù)返回userInfo字段的內(nèi)部指針,并基于該指針完成userInfo成員變量的初始化工作。29         inline ::UserInfo* mutable_userinfo();30         inline ::UserInfo* release_userinfo();31     private:32         ... ...33     };                    復(fù)制代碼

      下面是讀寫(xiě)LogonRespMessage對(duì)象的C++測(cè)試代碼和說(shuō)明性注釋。

復(fù)制代碼
 1     void testNestedMessage() 2     { 3         printf("==================This is nested message.================/n"); 4         LogonRespMessage logonResp; 5         logonResp.set_logonresult(LOGON_RESULT_SUCCESS); 6         //如上所述,通過(guò)mutable_userinfo函數(shù)返回userInfo字段的指針,之后再初始化該對(duì)象指針。 7         UserInfo* userInfo = logonResp.mutable_userinfo(); 8         userInfo->set_acctid(200); 9         userInfo->set_name("Tester");10         userInfo->set_status(OFFLINE);11         int length = logonResp.ByteSize();12         char* buf = new char[length];13         logonResp.SerializeToArray(buf,length);14     15         LogonRespMessage logonResp2;16         logonResp2.ParseFromArray(buf,length);17         printf("LogonResult = %d, UserInfo->acctID = %I64d, UserInfo->name = %s, UserInfo->status = %d/n"18             ,logonResp2.logonresult(),logonResp2.userinfo().acctid(),logonResp2.userinfo().name().c_str(),logonResp2.userinfo().status());19         delete [] buf;20     }    復(fù)制代碼

      四、repeated嵌套message生成的C++代碼。      message BuddyInfo {          required UserInfo userInfo = 1;          required int32 groupID = 2;      }      message RetrieveBuddiesResp {          required int32 buddiesCnt = 1;          repeated BuddyInfo buddiesInfo = 2;      }      對(duì)于上述消息生成的代碼,我們將只是針對(duì)RetrieveBuddiesResp消息所對(duì)應(yīng)的C++代碼進(jìn)行詳細(xì)說(shuō)明,其余部分和前面小節(jié)的例子基本相同,可直接參照。而對(duì)于RetrieveBuddiesResp類(lèi)中的代碼,我們也僅僅是對(duì)buddiesInfo字段生成的代碼進(jìn)行更為詳細(xì)的解釋。

復(fù)制代碼
 1     class RetrieveBuddiesResp : public ::google::protobuf::MessageLite { 2     public: 3         RetrieveBuddiesResp(); 4         virtual ~RetrieveBuddiesResp(); 5  6         ... ... //其余代碼的功能性注釋均可參照前面的例子。 7              8         // repeated .BuddyInfo buddiesInfo = 2; 9         static const int kBuddiesInfoFieldNumber = 2;10         //返回?cái)?shù)組中成員的數(shù)量。11         inline int buddiesinfo_size() const;12         //清空數(shù)組中的所有已初始化成員,調(diào)用該函數(shù)后,buddiesinfo_size函數(shù)將返回0。13         inline void clear_buddiesinfo();14         //返回?cái)?shù)組中指定下標(biāo)所包含元素的引用。15         inline const ::BuddyInfo& buddiesinfo(int index) const;16         //返回?cái)?shù)組中指定下標(biāo)所包含元素的指針,通過(guò)該方式可直接修改元素的值信息。17         inline ::BuddyInfo* mutable_buddiesinfo(int index);18         //像數(shù)組中添加一個(gè)新元素。返回值即為新增的元素,可直接對(duì)其進(jìn)行初始化。19         inline ::BuddyInfo* add_buddiesinfo();20         //獲取buddiesInfo字段所表示的容器,該函數(shù)返回的容器僅用于遍歷并讀取,不能直接修改。21         inline const ::google::protobuf::RepeatedPtrField< ::BuddyInfo >&22           buddiesinfo() const;23         //獲取buddiesInfo字段所表示的容器指針,該函數(shù)返回的容器指針可用于遍歷和直接修改。24         inline ::google::protobuf::RepeatedPtrField< ::BuddyInfo >*25           mutable_buddiesinfo();26     private:27         ... ...28     };復(fù)制代碼

      下面是讀寫(xiě)RetrieveBuddiesResp對(duì)象的C++測(cè)試代碼和說(shuō)明性注釋。

復(fù)制代碼
 1     void testRepeatedMessage() 2     { 3         printf("==================This is repeated message.================/n"); 4         RetrieveBuddiesResp retrieveResp; 5         retrieveResp.set_buddiescnt(2); 6         BuddyInfo* buddyInfo = retrieveResp.add_buddiesinfo(); 7         buddyInfo->set_groupid(20); 8         UserInfo* userInfo = buddyInfo->mutable_userinfo(); 9         userInfo->set_acctid(200);10         userInfo->set_name("user1");11         userInfo->set_status(OFFLINE);12     13         buddyInfo = retrieveResp.add_buddiesinfo();14         buddyInfo->set_groupid(21);15         userInfo = buddyInfo->mutable_userinfo();16         userInfo->set_acctid(201);17         userInfo->set_name("user2");18         userInfo->set_status(ONLINE);19     20         int length = retrieveResp.ByteSize();21         char* buf = new char[length];22         retrieveResp.SerializeToArray(buf,length);23     24         RetrieveBuddiesResp retrieveResp2;25         retrieveResp2.ParseFromArray(buf,length);26         printf("BuddiesCount = %d/n",retrieveResp2.buddiescnt());27         printf("Repeated Size = %d/n",retrieveResp2.buddiesinfo_size());28         //這里僅提供了通過(guò)容器迭代器的方式遍歷數(shù)組元素的測(cè)試代碼。29         //事實(shí)上,通過(guò)buddiesinfo_size和buddiesinfo函數(shù)亦可循環(huán)遍歷。30         RepeatedPtrField<BuddyInfo>* buddiesInfo = retrieveResp2.mutable_buddiesinfo();31         RepeatedPtrField<BuddyInfo>::iterator it = buddiesInfo->begin();32         for (; it != buddiesInfo->end(); ++it) {33             printf("BuddyInfo->groupID = %d/n", it->groupid());34             printf("UserInfo->acctID = %I64d, UserInfo->name = %s, UserInfo->status = %d/n"35                 , it->userinfo().acctid(), it->userinfo().name().c_str(),it->userinfo().status());36         }37         delete [] buf;38     }復(fù)制代碼

      最后需要說(shuō)明的是,Protocol Buffer仍然提供了很多其它非常有用的功能,特別是針對(duì)序列化的目的地,比如文件流和網(wǎng)絡(luò)流等。與此同時(shí),也提供了完整的官方文檔和規(guī)范的命名規(guī)則,在很多情況下,可以直接通過(guò)函數(shù)的名字便可獲悉函數(shù)所完成的工作。      本打算將該Blog中使用的示例代碼以附件的方式上傳,但是沒(méi)有發(fā)現(xiàn)此功能,望諒解。

Protocol Buffer技術(shù)詳解(Java實(shí)例)

該篇Blog和上一篇(C++實(shí)例)基本相同,只是面向于我們團(tuán)隊(duì)中的Java工程師,畢竟我們項(xiàng)目的前端部分是基于Android開(kāi)發(fā)的,而且我們研發(fā)團(tuán)隊(duì)中目前主要使用的開(kāi)發(fā)語(yǔ)言就是C++、Java和Python,其中Python主要用于編寫(xiě)各種工具程序。然而為了保證該篇Blog的完整性和獨(dú)立性,我仍然會(huì)將上一篇Blog中已經(jīng)出現(xiàn)的內(nèi)容再一次贅述,同時(shí)對(duì)于Java中特有的部分也會(huì)著重介紹。          一、生成目標(biāo)語(yǔ)言代碼。      下面的命令幫助我們將MyMessage.proto文件中定義的一組Protocol Buffer格式的消息編譯成目標(biāo)語(yǔ)言(Java)的代碼。至于消息的內(nèi)容,我們會(huì)在后面以分段的形式逐一列出,同時(shí)也會(huì)在附件中給出所有源代碼。      protoc -I=./message --java_out=./src ./MyMessage.proto      從上面的命令行參數(shù)中可以看出,待編譯的文件為MyMessage.proto,他存放在當(dāng)前目錄的message子目錄下。--java_out參數(shù)則指示編譯工具我們需要生成目標(biāo)語(yǔ)言是java,輸出目錄是當(dāng)前目錄的src子目錄。這里需要補(bǔ)充說(shuō)明的是,因?yàn)樵贛yMessage.proto文件中定義了option java_package = "com.lsk.lyphone"的文件級(jí)選項(xiàng),所以輸出的目前是src/com/lsk/lyphone,生成的目標(biāo)代碼文件名是MyMessage.java。          二、簡(jiǎn)單message生成的Java代碼。      這里先定義一個(gè)最簡(jiǎn)單的message,其中只是包含原始類(lèi)型的字段。      option java_package = "com.lsk.lyphone";      option java_outer_classname = "LYPhoneMessage";      option optimize_for = LITE_RUNTIME;      message LogonReqMessage {          required int64 acctID = 1;          required string passwd = 2;      }      對(duì)于選項(xiàng)java_packagejava_outer_classname的功能,我們已經(jīng)在之前的一篇Blog(語(yǔ)言規(guī)范)中進(jìn)行了清晰的闡述,這里就不在另行介紹了。然而對(duì)于選項(xiàng)optimize_for,這里將結(jié)合本例給出一些實(shí)用性描述。      當(dāng)前.proto文件中該選項(xiàng)的值為LITE_RUNTIME,因此由該.proto文件生成的所有Java類(lèi)的父類(lèi)均為com.google.protobuf.GeneratedMessageLite,而非com.google.protobuf.GeneratedMessage,同時(shí)與之對(duì)應(yīng)的Builder類(lèi)則均繼承自com.google.protobuf.MessageLiteOrBuilder,而非com.google.protobuf.MessageOrBuilder。在之前的博客中已經(jīng)給出了一些簡(jiǎn)要的說(shuō)明,MessageLite接口是Message的父接口,在MessageLite中將缺少Protocol Buffer對(duì)反射的支持,而此功能均在Message接口中提供了接口規(guī)范,同時(shí)又在其實(shí)現(xiàn)類(lèi)GeneratedMessage中給予了最小功能的實(shí)現(xiàn)。對(duì)于我們的項(xiàng)目而言,整個(gè)系統(tǒng)相對(duì)比較封閉,不會(huì)和更多的外部程序進(jìn)行交互,與此同時(shí),我們的客戶端部分又是運(yùn)行在Android平臺(tái),有鑒于此,我們考慮使用LITE版本的Protocol Buffer。這樣不僅可以得到更高編碼效率,而且生成代碼編譯后所占用的資源也會(huì)更少,至于反射所能帶來(lái)的靈活性和極易擴(kuò)展性,對(duì)于該項(xiàng)目而言完全可以忽略。下面我們來(lái)看一下由message LogonReqMessage生成的Java類(lèi)的部分聲明,以及常用方法的說(shuō)明性注釋。      在做各種case的對(duì)比性分析之前必須要事先聲明的是,Protocol Buffer針對(duì)Java語(yǔ)言所生成的代碼和C++相比存在一個(gè)非常重要的差別,即為每個(gè)消息均會(huì)生成一個(gè)Builder接口和一個(gè)與消息對(duì)應(yīng)的實(shí)現(xiàn)類(lèi),該實(shí)現(xiàn)類(lèi)又將同時(shí)實(shí)現(xiàn)生成的Builder接口和擴(kuò)展Protocol Buffer內(nèi)置的GeneratedMessageLite(或GeneratedMessage)類(lèi)。這一點(diǎn)對(duì)于Protocol Buffer而言,是巧妙的使用了設(shè)計(jì)模式中的Builder模式。換言之,對(duì)于所有消息字段的修改操作均需要通過(guò)與其對(duì)應(yīng)的Builder接口輔助完成。相信我們會(huì)通過(guò)對(duì)下面用例的學(xué)習(xí)可以得到更為清楚的認(rèn)識(shí)。

復(fù)制代碼
  1     //用于修改LogonReqMessage消息字段的輔助Builder接口。  2     //該接口會(huì)為消息中的每個(gè)字段均提供getter和setter方法。  3     public interface LogonReqMessageOrBuilder  4         extends com.google.protobuf.MessageLiteOrBuilder {  5       6         // required int64 acctID = 1;  7         boolean hasAcctID();  8         long getAcctID();  9          10         // required string passwd = 2; 11         boolean haspasswd(); 12         String getPasswd(); 13     } 14     //該類(lèi)為final類(lèi),即不可以在被子類(lèi)化了。這一點(diǎn)在Protocol Buffer的官方文檔中給予了明確 15     //的說(shuō)明,因?yàn)樽宇?lèi)化將會(huì)破壞序列化和反序列化的過(guò)程。 16     public static final class LogonReqMessage extends 17         com.google.protobuf.GeneratedMessageLite 18         implements LogonReqMessageOrBuilder { 19          20         // Use LogonReqMessage.newBuilder() to construct. 21         // 由于所有構(gòu)造函數(shù)均為私有方法,由此可見(jiàn),我們不能直接new LogonReqMessage的對(duì)象 22         // 實(shí)例,而是只能通過(guò)與其對(duì)應(yīng)Builder來(lái)構(gòu)造,或是直接通過(guò)反序列化的方式生成。 23         private LogonReqMessage(Builder builder) { 24             super(builder); 25         } 26         //該靜態(tài)方法為該類(lèi)Builder接口的工廠方法。返回的Builder實(shí)現(xiàn)類(lèi)在完成各個(gè)字段的 27         //初始化后,通過(guò)build()方法返回與其對(duì)應(yīng)的消息實(shí)現(xiàn)類(lèi),即LogonReqMessage。 28         public static Builder newBuilder() { return Builder.create(); } 29         //通過(guò)該類(lèi)的對(duì)象獲取與其對(duì)應(yīng)的Builder類(lèi)對(duì)象,一般用于通過(guò)Builder類(lèi)完成消息字段的修改。 30         public Builder toBuilder() { return newBuilder(this); } 31  32         private LogonReqMessage(boolean noInit) {} 33         //判斷當(dāng)前對(duì)象的所有字段是否都已經(jīng)被初始化。 34         public final boolean isInitialized() { 35             ... ... 36         } 37         //獲取已經(jīng)被初始化后的對(duì)象序列化時(shí)所占用的字節(jié)空間。 38         public int getSerializedSize() { 39             ... ... 40         } 41         //從內(nèi)存中飯序列化LogonReqMessage對(duì)象。 42         //Protocol Buffer中還提供其他一些接口方法,用于從不同的數(shù)據(jù)源反序列化對(duì)象。 43         public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(byte[] data) 44             throws com.google.protobuf.InvalidProtocolBufferException { 45             return newBuilder().mergeFrom(data).buildParsed(); 46         } 47         //功能和上一個(gè)函數(shù)相同,只是輸入源改為InputStream接口。 48         public static com.lsk.lyphone.LYPhoneMessage.LogonReqMessage parseFrom(java.io.InputStream input) 49             throws java.io.IOException { 50             return newBuilder().mergeFrom(input).buildParsed(); 51         } 52          53         // required int64 acctID = 1; 54         // 下面的靜態(tài)變量對(duì)應(yīng)于該字段在.proto中定義的標(biāo)簽號(hào)。該變量的命名規(guī)則為:字段(全部大寫(xiě)) + _FIELD_NUMBER。 55         public static final int ACCTID_FIELD_NUMBER = 1; 56         public boolean hasAcctID() { 57             return ((bitField0_ & 0x00000001) == 0x00000001); 58         } 59         public long getAcctID() { 60             return acctID_; 61         } 62  63         // required string passwd = 2; 64         public static final int PASSWD_FIELD_NUMBER = 2; 65         public boolean hasPasswd() { 66             return ((bitField0_ & 0x00000002) == 0x00000002); 67         } 68         public String getPasswd() { 69             ... ...  70         } 71         //每一個(gè)Message類(lèi)都會(huì)包含一個(gè)靜態(tài)內(nèi)部類(lèi),即與之對(duì)應(yīng)的Builder類(lèi)。上面代碼中所涉及的Builder類(lèi)均為該內(nèi)部類(lèi)。 72         public static final class Builder extends 73             com.google.protobuf.GeneratedMessageLite.Builder< 74             com.lsk.lyphone.LYPhoneMessage.LogonReqMessage, Builder> 75             implements com.lsk.lyphone.LYPhoneMessage.LogonReqMessageOrBuilder { 76             //清空當(dāng)前對(duì)象中的所有設(shè)置。調(diào)用該函數(shù)之后,本例中的hasAcctID和hasPasswd都會(huì)返回false。     77             public Builder clear() { 78                 super.clear(); 79                 acctID_ = 0L; 80                 bitField0_ = (bitField0_ & ~0x00000001); 81                 passwd_ = ""; 82                 bitField0_ = (bitField0_ & ~0x00000002); 83                 return this; 84             } 85             //克隆出一個(gè)Builder對(duì)象。 86             public Builder clone() { 87                 return create().mergeFrom(buildPartial()); 88             } 89             public com.lsk.lyphone.LYPhoneMessage.LogonReqMessage build() { 90                 com.lsk.lyphone.LYPhoneMessage.LogonReqMessage result = buildPartial(); 91                 if (!result.isInitialized()) { 92                     throw newUninitializedMessageException(result); 93                 } 94                 return result; 95             } 96             // Builder類(lèi)中修改外部消息類(lèi)的方法。 97             // required int64 acctID = 1; 98             public boolean hasAcctID() { 99                 return ((bitField0_ & 0x00000001) == 0x00000001);100             }101             public long getAcctID() {102                 return acctID_;103             }104             //設(shè)置AcctID字段,該函數(shù)調(diào)用后hasAcctID函數(shù)將返回true。105             //這里之所以讓返回值為Builder對(duì)象,就是可以讓調(diào)用者在一條代碼中方便的連續(xù)修改多個(gè)字段,106             //如:myMessage.setAcctID(100).setPasswd("MyName");107             public Builder setAcctID(long value) {108                 bitField0_ |= 0x00000001;109                 acctID_ = value;110                 return this;111             }112             //清空AcctID字段,該函數(shù)調(diào)用后hasAcctID函數(shù)返回false。113             //這里之所以讓返回值為Builder對(duì)象,就是可以讓調(diào)用者在一條代碼中方便的連續(xù)清空多個(gè)字段,114             //如:myMessage.clearAcctID().clearPasswd();115             public Builder clearAcctID() {116                 bitField0_ = (bitField0_ & ~0x00000001);117                 acctID_ = 0L;118                 return this;119             }120       121             // required string passwd = 2;122             public boolean hasPasswd() {123                 return ((bitField0_ & 0x00000002) == 0x00000002);124             }125             public String getPasswd() {126                 ... ...        127             }128             public Builder setPasswd(String value) {129                 ... ...130             }131             public Builder clearPasswd() {132                 bitField0_ = (bitField0_ & ~0x00000002);133                 passwd_ = getDefaultInstance().getPasswd();134                 return this;135             }136             void setPasswd(com.google.protobuf.ByteString value) {137                 bitField0_ |= 0x00000002;138                 passwd_ = value;139             }140         }141     }            復(fù)制代碼

      在上面生成的代碼中并沒(méi)有列出與序列化相關(guān)的函數(shù),這部分代碼基本都是在父類(lèi)中實(shí)現(xiàn)的,我們將在下面的例子中給出一些最基本的用法,有興趣的開(kāi)發(fā)者可以直接看Protocol Buffer中的源碼,這部分代碼比較通俗易懂。      下面是讀寫(xiě)LogonReqMessage對(duì)象的Java測(cè)試代碼和說(shuō)明性注釋。

復(fù)制代碼
 1     private static void testSimpleMessage() { 2         System.out.println("==================This is simple message.================"); 3         //如前所述,不能直接構(gòu)造該消息類(lèi)對(duì)象,只能通過(guò)他的內(nèi)部Builder類(lèi)構(gòu)造并完成所有字段的初始化。 4         LogonReqMessage.Builder logonReqBuilder = LogonReqMessage.newBuilder(); 5         logonReqBuilder.setAcctID(20); 6         logonReqBuilder.setPasswd("Hello World"); 7         //builder對(duì)象初始化完畢后,再通過(guò)build方法生成與之對(duì)應(yīng)的消息類(lèi)對(duì)象。 8         LogonReqMessage logonReq = logonReqBuilder.build(); 9         int length = logonReq.getSerializedSize();10         System.out.println("The result length is " + length);11         //直接序列化到內(nèi)存中,之后可對(duì)該內(nèi)存進(jìn)行二次加工后再寫(xiě)到本地文件或發(fā)送到遠(yuǎn)端,如加密。12         byte[] buf = logonReq.toByteArray();13 14         try {15             LogonReqMessage logonReq2 = LogonReqMessage.parseFrom(buf);16             System.out.println("acctID = " + logonReq2.getAcctID() + "/tpassword = " + logonReq2.getPasswd());17         } catch (InvalidProtocolBufferException e) {18             e.printStackTrace();19         }20         //需要說(shuō)明的是,文件中的內(nèi)容是由之前C++實(shí)例代碼寫(xiě)入的,這里這樣寫(xiě)主要是一種驗(yàn)證。21         System.out.println("Reading data from local file generated by C++");22         try {23             LogonReqMessage logonReq3 = LogonReqMessage.parseFrom(new FileInputStream("C:/Mine/LogonReq.dat"));24             System.out.println("acctID = " + logonReq3.getAcctID() + "/tpassword = " + logonReq3.getPasswd());25         } catch (FileNotFoundException e) {26             e.printStackTrace();27         } catch (IOException e) {28             e.printStackTrace();29         }30     }復(fù)制代碼

      三、嵌套message生成的Java代碼。      enum UserStatus {          OFFLINE = 0;          ONLINE = 1;      }      enum LoginResult {          LOGON_RESULT_SUCCESS = 0;          LOGON_RESULT_NOTEXIST = 1;          LOGON_RESULT_ERROR_PASSWD = 2;          LOGON_RESULT_ALREADY_LOGON = 3;          LOGON_RESULT_SERVER_ERROR = 4;      }      message UserInfo {          required int64 acctID = 1;          required string name = 2;          required UserStatus status = 3;      }      message LogonRespMessage {          required LoginResult logonResult = 1;          required UserInfo userInfo = 2; //這里嵌套了UserInfo消息。      }      對(duì)于上述消息生成的Java代碼,UserInfo因?yàn)橹皇前嗽碱?lèi)型字段,因此和上例中的LogonReqMessage沒(méi)有太多的差別,這里也就不在重復(fù)列出了。由于LogonRespMessage消息中嵌套了UserInfo類(lèi)型的字段,在這里我們將僅僅給出該消息生成的Java代碼和關(guān)鍵性注釋。

復(fù)制代碼
 1     public static final class LogonRespMessage extends 2         com.google.protobuf.GeneratedMessageLite 3         implements LogonRespMessageOrBuilder { 4          5         //Message類(lèi)的通用性函數(shù)定義。 6         ... ... 7          8         // required .LoginResult logonResult = 1; 9         public static final int LOGONRESULT_FIELD_NUMBER = 1;10         public boolean hasLogonResult() {11             return ((bitField0_ & 0x00000001) == 0x00000001);12         }13         public com.lsk.lyphone.LYPhoneMessage.LoginResult getLogonResult() {14             return logonResult_;15         }16         17         // required .UserInfo userInfo = 2;18         public static final int USERINFO_FIELD_NUMBER = 2;19         public boolean hasUserInfo() {20             return ((bitField0_ & 0x00000002) == 0x00000002);21         }22         public com.lsk.lyphone.LYPhoneMessage.UserInfo getUserInfo() {23             return userInfo_;24         }25         //Message類(lèi)的通用性函數(shù)定義。可參照上一小節(jié)中的代碼和注釋。26         ... ...27         28         public static final class Builder extends29             com.google.protobuf.GeneratedMessageLite.Builder<30             com.lsk.lyphone.LYPhoneMessage.LogonRespMessage, Builder>31             implements com.lsk.lyphone.LYPhoneMessage.LogonRespMessageOrBuilder {32 33             //一些適用于絕大多數(shù)Builder對(duì)象的通用性方法。34             ... ...35             36             //當(dāng)前示例中Builder生成的代碼和上一小節(jié)中生成的代碼非常類(lèi)似,這里就不一一贅述了。37             //和前面的例子相比一個(gè)重要的差別是setUserInfo函數(shù)多提供了一種函數(shù)簽名,其參數(shù)為38             //UserInfo類(lèi)的Builder對(duì)象。這樣調(diào)用者在使用時(shí)可以直接將Builder對(duì)象作為參數(shù)傳入。39             public Builder setUserInfo(com.lsk.lyphone.LYPhoneMessage.UserInfo.Builder builderForValue) {40                 userInfo_ = builderForValue.build();41                 bitField0_ |= 0x00000002;42                 return this;43             }44         }45     }復(fù)制代碼

      下面是讀寫(xiě)LogonRespMessage對(duì)象的Java測(cè)試代碼和說(shuō)明性注釋。

復(fù)制代碼
 1     private static void testNestedMessage() { 2         System.out.println("==================This is nested message.================"); 3         LogonRespMessage.Builder logonRespBuilder = LogonRespMessage.newBuilder(); 4         logonRespBuilder.setLogonResult(LoginResult.LOGON_RESULT_SUCCESS); 5         UserInfo.Builder userInfo = UserInfo.newBuilder(); 6         userInfo.setAcctID(200); 7         userInfo.setName("Tester"); 8         userInfo.setStatus(UserStatus.OFFLINE); 9         //這里也可以直接傳遞userInfo對(duì)象作為參數(shù)。因?yàn)長(zhǎng)ogonRespBuilder類(lèi)提供了setUserInfo的方法重載。10         logonRespBuilder.setUserInfo(userInfo.build());11         LogonRespMessage logonResp = logonRespBuilder.build();12         int length = logonResp.getSerializedSize();13         System.out.println("The result length is " + length);14         byte[] buf = logonResp.toByteArray();15 16         try {17             LogonRespMessage logonResp2 = LogonRespMessage.parseFrom(buf);18             UserInfo userInfo2 = logonResp2.getUserInfo();19             System.out.println("LogonResult = " + logonResp2.getLogonResult().toString() + " acctID = " 20                     + userInfo2.getAcctID() + " name = " + userInfo2.getName() + " status = " + userInfo2.getStatus().toString());21         } catch (InvalidProtocolBufferException e) {22             e.printStackTrace();23         }24         System.out.println("Reading data from local file generated by C++");25         try {26             LogonRespMessage logonResp3 = LogonRespMessage.parseFrom(new FileInputStream("C:/Mine/LogonResp.dat"));27             UserInfo userInfo3 = logonResp3.getUserInfo();28             System.out.println("LogonResult = " + logonResp3.getLogonResult().toString() + " acctID = " 29                     + userInfo3.getAcctID() + " name = " + userInfo3.getName() + " status = " + userInfo3.getStatus().toString());30         } catch (FileNotFoundException e) {31             e.printStackTrace();32         } catch (IOException e) {33             e.printStackTrace();34         }35     }復(fù)制代碼

      四、repeated嵌套message生成的Java代碼。      message BuddyInfo {          required UserInfo userInfo = 1;          required int32 groupID = 2;      }      message RetrieveBuddiesResp {          required int32 buddiesCnt = 1;          repeated BuddyInfo buddiesInfo = 2;      }      對(duì)于上述消息生成的代碼,我們將只是針對(duì)RetrieveBuddiesResp消息所對(duì)應(yīng)的Java代碼進(jìn)行詳細(xì)說(shuō)明,其余部分和前面小節(jié)的例子基本相同,可直接參照。而對(duì)于RetrieveBuddiesResp類(lèi)中的代碼,我們也僅僅是對(duì)buddiesInfo字段生成的代碼進(jìn)行更為詳細(xì)的解釋。

復(fù)制代碼
 1     public static final class RetrieveBuddiesResp extends 2         com.google.protobuf.GeneratedMessageLite 3         implements RetrieveBuddiesRespOrBuilder { 4         //這里均為Protocol Buffer生成的通用性代碼。 5         ... ... 6         // repeated .BuddyInfo buddiesInfo = 2; 7         public static final int BUDDIESINFO_FIELD_NUMBER = 2; 8         //對(duì)于repeated類(lèi)型的字段,均返回類(lèi)型參數(shù)為字段類(lèi)型的泛型容器對(duì)象。 9         public java.util.List<com.lsk.lyphone.LYPhoneMessage.BuddyInfo> getBuddiesInfoList() {10             return buddiesInfo_;11         }12         public java.util.List<? extends com.lsk.lyphone.LYPhoneMessage.BuddyInfoOrBuilder> getBuddiesInfoOrBuilderList() {13             return buddiesInfo_;14         }15         public int getBuddiesInfoCount() {16             return buddiesInfo_.size();17         }18         public com.lsk.lyphone.LYPhoneMessage.BuddyInfo getBuddiesInfo(int index) {19             return buddiesInfo_.get(index);20         }21         public com.lsk.lyphone.LYPhoneMessage.BuddyInfoOrBuilder getBuddiesInfoOrBuilder(int index) {22             return buddiesInfo_.get(index);23         }24         25         //這里仍有一些Protocol Buffer生成的通用性代碼。26         ... ...27         28         public static final class Builder extends29             com.google.protobuf.GeneratedMessageLite.Builder<30             com.lsk.lyphone.LYPhoneMessage.RetrieveBuddiesResp, Builder>31             implements com.lsk.lyphone.LYPhoneMessage.RetrieveBuddiesRespOrBuilder {32             33             //這里僅列出和操作repeated字段相關(guān)的方法,其他的方法和前面的例子基本一致。34             // repeated .BuddyInfo buddiesInfo = 2;35             //本來(lái)打算給出比較詳細(xì)的說(shuō)明,但是看到Google為每個(gè)函數(shù)的命名之后就放棄這個(gè)想法,36             //這樣一來(lái)不僅可以避免畫(huà)蛇添足,而且也節(jié)省了時(shí)間。:)            37             public java.util.List<com.lsk.lyphone.LYPhoneMessage.BuddyInfo> getBuddiesInfoList() {38                 return java.util.Collections.unmodifiableList(buddiesInfo_);39             }40             public int getBuddiesInfoCount() {41                 return buddiesInfo_.size();42             }43             public com.lsk.lyphone.LYPhoneMessage.BuddyInfo getBuddiesInfo(int index) {44                 return buddiesInfo_.get(index);45             }46             public Builder setBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) {47                 ... ...48             }49             public Builder setBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) {50                 ... ...51             }52             public Builder addBuddiesInfo(com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) {53                 ... ...54             }55             public Builder addBuddiesInfo(int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo value) {56                 ... ...57             }58             public Builder addBuddiesInfo(com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) {59                 ... ...60             }61             public Builder addBuddiesInfo(62                 int index, com.lsk.lyphone.LYPhoneMessage.BuddyInfo.Builder builderForValue) {63                 ... ...64             }65             public Builder addAllBuddiesInfo(66                 java.lang.Iterable<? extends com.lsk.lyphone.LYPhoneMessage.BuddyInfo> values) {67                 ... ...68             }69             public Builder clearBuddiesInfo() {70                 ... ...71             }72             public Builder removeBuddiesInfo(int index) {73                 ... ...74             }75         }76     }復(fù)制代碼

      下面是讀寫(xiě)RetrieveBuddiesResp對(duì)象的Java測(cè)試代碼和說(shuō)明性注釋。

復(fù)制代碼
 1     private static void testRepeatedMessage() { 2         System.out.println("==================This is repeated message.================"); 3         RetrieveBuddiesResp.Builder retrieveBuddiesBuilder = RetrieveBuddiesResp.newBuilder(); 4         retrieveBuddiesBuilder.setBuddiesCnt(2); 5         BuddyInfo.Builder buddyInfoBuilder = BuddyInfo.newBuilder(); 6         buddyInfoBuilder.setGroupID(20); 7         UserInfo.Builder userInfoBuilder = UserInfo.newBuilder(); 8         userInfoBuilder.setAcctID(200); 9         userInfoBuilder.setName("user1");10         userInfoBuilder.setStatus(UserStatus.OFFLINE);11         buddyInfoBuilder.setUserInfo(userInfoBuilder.build());12         retrieveBuddiesBuilder.addBuddiesInfo(buddyInfoBuilder.build());13         14         buddyInfoBuilder = BuddyInfo.newBuilder();15         buddyInfoBuilder.setGroupID(21);16         userInfoBuilder = UserInfo.newBuilder();17         userInfoBuilder.setAcctID(201);18         userInfoBuilder.setName("user2");19         userInfoBuilder.setStatus(UserStatus.ONLINE);20         buddyInfoBuilder.setUserInfo(userInfoBuilder);21         retrieveBuddiesBuilder.addBuddiesInfo(buddyInfoBuilder);22         RetrieveBuddiesResp buddiesResp = retrieveBuddiesBuilder.build();23         24         int length = buddiesResp.getSerializedSize();25         System.out.println("The result length is " + length);26         byte[] buf = buddiesResp.toByteArray();27         28         try {29             RetrieveBuddiesResp buddiesResp2 = RetrieveBuddiesResp.parseFrom(buf);30             System.out.println("BuddiesCount = " + buddiesResp2.getBuddiesCnt());31             System.out.println("Repeated Size = " + buddiesResp2.getBuddiesInfoCount());32             for (int i = 0; i < buddiesResp2.getBuddiesInfoCount(); ++i) {33                 BuddyInfo buddyInfo = buddiesResp2.getBuddiesInfo(i);34                 UserInfo userInfo = buddyInfo.getUserInfo();35                 System.out.println("GroupID = " + buddyInfo.getGroupID() + " UserInfo.acctID = " + userInfo.getAcctID()36                         + " UserInfo.name = " + userInfo.getName() + " UserInfo.status = " + userInfo.getStatus());37             }38             39         } catch (InvalidProtocolBufferException e) {40             e.printStackTrace();41         }42         System.out.println("Reading data from local file generated by C++");43         try {44             RetrieveBuddiesResp buddiesResp3 = RetrieveBuddiesResp.parseFrom(new FileInputStream("C:/Mine/RetrieveBuddiesResp.dat"));45             System.out.println("BuddiesCount = " + buddiesResp3.getBuddiesCnt());46             System.out.println("Repeated Size = " + buddiesResp3.getBuddiesInfoCount());47             List<BuddyInfo> buddiesInfo = buddiesResp3.getBuddiesInfoList();48             for (BuddyInfo buddyInfo : buddiesInfo) {49                 UserInfo userInfo = buddyInfo.getUserInfo();50                 System.out.println("GroupID = " + buddyInfo.getGroupID() + " UserInfo.acctID = " + userInfo.getAcctID()51                         + " UserInfo.name = " + userInfo.getName() + " UserInfo.status = " + userInfo.getStatus());52             }53         } catch (FileNotFoundException e) {54             e.printStackTrace();55         } catch (IOException e) {56             e.printStackTrace();57         }58     }復(fù)制代碼

      對(duì)于Java而言,我們可以通過(guò)Maven工具生成兩個(gè)jar包,其中一個(gè)是protobuf-java-2.4.1.jar,主要用于optimize_for選項(xiàng)為非LITE_RUNTIME的情況,而另一個(gè)protobuf-java-2.4.1-lite.jar文件則恰恰與之相反。另外,我通過(guò)Beyond Compare工具對(duì)這兩個(gè)jar包進(jìn)行了二進(jìn)制比較后發(fā)現(xiàn),他們是完全相同的。這里之所以仍以LITE版本為例,主要還是因?yàn)楹椭耙黄狟log(C++實(shí)例)想匹配。      最后需要說(shuō)明的是,Protocol Buffer仍然提供了很多其它非常有用的功能,特別是針對(duì)序列化的目的地,比如文件流和網(wǎng)絡(luò)流等。與此同時(shí),也提供了完整的官方文檔和規(guī)范的命名規(guī)則,在很多情況下,可以直接通過(guò)函數(shù)的名字便可獲悉函數(shù)所完成的工作。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 屏山县| 离岛区| 永平县| 嵊州市| 昭觉县| 黄冈市| 云梦县| 深州市| 波密县| 黔南| 宿迁市| 奇台县| 丰镇市| 新乡市| 湛江市| 恩施市| 英德市| 瑞昌市| 四子王旗| 丰镇市| 荣昌县| 彭泽县| 镇巴县| 长乐市| 淳化县| 青州市| 鹤庆县| 锡林郭勒盟| 华亭县| 漳浦县| 云霄县| 张家港市| 孟州市| 江口县| 富源县| 凤庆县| 永修县| 筠连县| 富蕴县| 金门县| 盐亭县|