Protobuf的設(shè)計(jì)非常適用于在網(wǎng)絡(luò)通訊中的數(shù)據(jù)載體,它序列化出來的數(shù)據(jù)量少再加上以K-V的方式來存儲(chǔ)數(shù)據(jù),對(duì)消息的版本兼容性非常強(qiáng);還有一個(gè)比較大的優(yōu)點(diǎn)就是有著很多的語言平臺(tái)支持。下面講解一下如何在TCP通訊應(yīng)用中集成Protobuf.
Protobuf提供一種簡(jiǎn)單的對(duì)象消息方式來描述數(shù)據(jù)的存儲(chǔ),通過相關(guān)實(shí)現(xiàn)可以簡(jiǎn)單地實(shí)現(xiàn)數(shù)據(jù)流和對(duì)象之間的轉(zhuǎn)換。但由于Protobuf序列化后的信息并不包括一些相應(yīng)對(duì)象類型描述,只有消息體的內(nèi)容;因此在進(jìn)行通信交互過程中默認(rèn)情況是不知道這些數(shù)據(jù)和對(duì)象的對(duì)應(yīng)關(guān)系;既然對(duì)方不清楚這些數(shù)據(jù)對(duì)應(yīng)那種類型,那數(shù)據(jù)還源成對(duì)象就根本沒辦法入手。所以把Protobuf用到網(wǎng)絡(luò)通訊中我們還需要外加一些協(xié)議來規(guī)范數(shù)據(jù)的對(duì)應(yīng)關(guān)系。
在通訊協(xié)議上只需要在消息體中先寫入類型然后再寫入Protobuf數(shù)據(jù),TypeName主要是用于對(duì)方配匹數(shù)據(jù)對(duì)應(yīng)的對(duì)象關(guān)系,這個(gè)TypeName是string還是int或其他就根據(jù)自己喜好來制定了。
在通訊上問題就解決了,但還要面對(duì)實(shí)際中使用的一種情況消息太多了……那處理TypeName->Object則一個(gè)蛋痛的事情。還好在.net和android下有類元素表這玩意還能在運(yùn)行行進(jìn)行操作,從而可以大大節(jié)省這些if判斷處理。由于本人熟悉的語言平臺(tái)有限,在這里分別提供java和c#的解決方法。
public static void Register(Class protobufclass) {
try {
ProtoMessageHandler mb;
Class[] protoObjs = protobufclass.getClasses();
for (Class item : protoObjs) {
if(item==null)
continue;
if (!item.isInterface() && item.getSuperclass().equals(
com.google.protobuf.GeneratedMessage.class)) {
mb = new ProtoMessageHandler();
mb.SetType(item);
mMessageTbl.put(item.getSimpleName(), mb);
}
}
} catch (Exception e) {
}
}
由于使用Protoc工具生成的類都會(huì)生成一個(gè)大類里,所以只需要注冊(cè)指定的類然后對(duì)所有繼承com.google.protobuf.GeneratedMessage的內(nèi)嵌類找出來并記錄在一個(gè)Map中即可。
String name= stream.ReadUTF();
ProtoMessageHandler handler = ProtoPackage.GetMsgHandler(name);
if(handler==null)
throw new Exception(name+" message type notfound!");
Message=(com.google.protobuf.GeneratedMessage) handler.GetObject(stream.GetStream());
以上是通過類型值獲取對(duì)應(yīng)的對(duì)象,并填充Protobuf數(shù)據(jù)。
相對(duì)于android平臺(tái)下的實(shí)現(xiàn),.net的實(shí)現(xiàn)就更簡(jiǎn)單些了。
public static void Register(System.Reflection.Assembly assembly)
{
foreach(Type type in assembly.GetTypes())
{
if (!type.IsAbstract && !type.IsInterface && type.GetCustomAttributes(typeof(ProtoBuf.ProtoContractAttribute), false).Length > 0)
mProtoTbl[type.Name] = type;
}
}
string name = reader.ReadUTF();
Type type = ProtoPakcage.GetProtoType(name);
Message = ProtoBuf.Meta.RuntimeTypeModel.Default.Deserialize(reader.Stream, null, type);
通過以上方法在使用Protobuf的時(shí)候就不用擔(dān)心消息太多寫if寫到手軟了,也不容易出錯(cuò)。不過有一點(diǎn)要注意的就是類的名稱一定要對(duì)應(yīng),否則就無法匹配到消息了。如果想得到完全整的代碼可以到https://beetlenp.codeplex.com獲取。