下面這個用法是參照protobuf-csharp-port的官方wiki,參見:
https://code.google.com/p/protobuf-csharp-port/wiki/GettingStarted
官方原文里的用法與代碼已經有些不匹配了,需要做一些小的修改。
准備工作
1.首先從https://code.google.com/p/protobuf-csharp-port這個上面把源代碼下載下來,我這個版本是protobuf-csharp-port-2.4.1.521-source(r523)
2.下載后是個壓縮包,解壓目錄,如下圖:
點開“Build”文件夾:
看見那個BuildAll.bat,點擊它會自動執行編譯操作,編譯完成后將出現build_output和build_temp兩個輸出文件夾,其中build_output如下圖所示例:
生成的Google.ProtocolBuffers.dll將被用作外部鏈接的DLL使用,這里有個問題,能不能不用做DLL使用呢?因為眾所周知的AOT,JIT問題。
- ProtoGen.exe - 源代碼生成器
- ProtoMunge.exe - tool to remove sensitive data from binary messages
- ProtoDump.exe - tool to dump a binary message as text
- ProtoBench.exe - tool to run performance benchmarks on binary messages
- protoc.exe - 編譯腳本工具
例子: an address book
這塊將會用一個程序來演示如何使用這個protocBuffer
The .proto file
看看下面這個proto文件,分析下這個文件都描述了什么信息。此文件在 你下載的源代碼文件夾的protos/tutorial。
package tutorial; import "google/protobuf/csharp_options.proto"; option (google.protobuf.csharp_file_options).namespace = "Google.ProtocolBuffers.Examples.AddressBook"; option (google.protobuf.csharp_file_options).umbrella_classname = "AddressBookProtos"; option optimize_for = SPEED; 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; }
這個文件和谷歌官方提供的基本一致,但做了一些調整:
- 為了簡單期間去掉了輸出Java代碼的選項。這里如果需要就把Java選項加上即可,和C#沒有沖突。
- 導入項代碼: google/protobuf/csharp_options.proto ,目的是使用C#擴展。
- 指定了兩項C#特有的選項:
- The name of the class containing the descriptor representing the overall .proto file. (This is called the "umbrella" class.) The generated source file is also named after this class. In this case we're using AddressBookProtos.
- 生成類的命名空間。這里用的是: Google.ProtocolBuffers.Examples.AddressBook.
- 指定了一個優化的選項。會多生成一些代碼,但能提高運行效率。
- 還有其他的選項,可以看看code.google上的description里的說明。
生成源代碼
轉到你的源代碼目錄,執行以下操作
protogen ..\..\protos\tutorial\addressbook.proto
..\..\protos\google\protobuf\csharp_options.proto
..\..\protos\google\protobuf\descriptor.proto
--proto_path=..\..\protos
看看build_temp文件夾,如下圖所示:
在GeneratedSource文件夾中,就能找到AddressBookProtos.cs文件了:
這個AddressBookProtos.cs文件是我們后邊需要用到的,其他文件只是Google用到的,我們不用去管,或者能把這些代碼作為源代碼引入主工程?不得而知。
注意事項:
- Make sure that you do not use a non-ASCII encoding with your text file. The protoc.exe compiler will complain with the following message:
tutorial/addressbook.proto:1:1: Expected top-level statement (e.g. "message").
The best way to fix this is with Visual Studio. Open the proto file and select "File" -> "Save as...". From the save dialog click the down arrow next to the save button and select "Save with Encoding". Select the "US-ASCII" codepage (near the bottom) and click save.
2. It's often easiest keep all your proto files in a single directory tree. This allows to omit the --proto_path option by running protoc.exe from the top of the this directory tree. Always keep the 'google/protobuf/*.proto' files available so that you can import them to specify options, or use the options through protogen.
3.Unlike a programming language like C# it is usually expected that your proto file will specify numerous messages, not one. Name your proto files based on the namespace, not the message. Create proto files only as often as you would create a new namespace to organize those classes.
使用生成的源代碼
為了使用上面生成的cs文件,我們需要執行下面這些操作:
- 創建一個builder,后邊將用它來構造Person這個消息體
- 設置builder的屬性
- 使用這個builder來創建 Person 消息體
- 將 Person 寫入內存流
- 使用上面寫好數據的內存流,再創建一個新的Person消息體
- 創建一個AddressBook的builder ,然后把這個剛創建的Person消息體賦值給它
- 使用AddressBook的builder 和上面的數據流創建 AddressBook
將這個AddressBook里的Person和第三步創建的Person進行比較,看數據是否一致。
static void Sample() { byte[] bytes; //Create a builder to start building a message Person.Builder newContact = Person.CreateBuilder(); //Set the primitive properties newContact.SetId(1) .SetName("Foo") .SetEmail("foo@bar"); //Now add an item to a list (repeating) field newContact.AddPhone( //Create the child message inline Person.Types.PhoneNumber.CreateBuilder().SetNumber("555-1212").Build() ); //Now build the final message: Person person = newContact.Build(); //The builder is no longer valid (at least not now, scheduled for 2.4): newContact = null; using(MemoryStream stream = new MemoryStream()) { //Save the person to a stream person.WriteTo(stream); bytes = stream.ToArray(); } //Create another builder, merge the byte[], and build the message: Person copy = Person.CreateBuilder().MergeFrom(bytes).Build(); //A more streamlined approach might look like this: bytes = AddressBook.CreateBuilder().AddPerson(copy).Build().ToByteArray(); //And read the address book back again AddressBook restored = AddressBook.CreateBuilder().MergeFrom(bytes).Build(); //The message performs a deep-comparison on equality: if(restored.PersonCount != 1 || !person.Equals(restored.PersonList[0])) throw new ApplicationException("There is a bad person in here!"); }
用VS2013,創建一個WinForm程序,拖一個button上去,雙擊出事件,調用Sample函數即可,這樣就通了。里面還有很多細節沒說清楚,不過好歹有整塊的東西了。那么后邊的工作,需要分成幾步來執行:
1.將上述流程分析清楚,有一個初步的架構圖和UML圖。
2.C#客戶端的二進制內存流,顯示需要鋪設一個二進制的內存流管理器,是否需要參考之前的那個二進制管理器呢。
3.如何集成到Unity里,首先要寫一份關於Unity的代碼規范和內存處理規范。如果集成到Unity里,那么勢必兩頭都要寫代碼,加油啊。
4.如何搭建一個Java服務器,支持解析數據,並發送和接收。
5.連接Java服務器通信。客戶端和服務器兩頭看看能否順利工作。