網絡通訊涉及到消息的定義,不論是使用二進制模式、xml、json等格式。消息均可以大致的分爲 命令消息、請求消息、應答消息和指示消息4大消息類型。通常狀況下每一個消息還還有包含一個序列號和一個可以惟一區分類型類型的消息編號,編號可使用字符串、整數或者枚舉等。json
我會爲每一個系統都定義一個MSG枚舉。包含系統用到的全部消息的枚舉編號網絡
enum MSG { Login_Request = 0x00001001; Login_Response = 0x00001002; XXX_Request = 0x00001003; XXX_Request = 0x00001004; XXX_Command = 0x00002001; XXX_Indication = 0x00003001; }
message LoginRequest { required bytes username = 1; required string password = 2; }
也就是我會有下面4個protobuf message:函數
message Command {// 包含全部的 XXXCommand 消息 } message Request {// 包含全部的 XXXRequest消息 } message Response {// 包含全部的Response消息 } message Indication {// 包含全部的Indication消息。 }
message Response { required bool result = 1; optional bytes error_description = 2; required bool last_block = 3; required fixed32 block_index = 4; .....//其餘的字段爲 XXXResponse.. }
message Message { required MSG type = 1; required fixed32 sequence = 2; optional Request request = 3; optional Response response = 4; optional Command command = 5; optional Indication indication = 6; }
用於UDP的時候比較簡單,由於每一個數據包就是一個獨立的Message消息,能夠直接解碼,或者編碼後直接發送。ui
可是若是是使用於TCP的時候,因爲涉及到粘包、拆包等處理,並且Message消息裏面也沒有包含長度相關的字段(很差處理),所以把Message編碼後的消息嵌入另一個二進制消息中。this
使用4字節消息長度+Message(二進制數據)+(2字節CRC校驗(可選))google
其中4字節的內容,只包含Message的長度,不包含自身和CRC的長度。若是須要也能夠包含,當要記得通訊雙方必須一致。編碼
編解碼後,根據Message.type字段,能夠知道要處理的消息,進行分發。不過通常狀況下我不喜歡if、switch。因此我比較傾向於使用虛函數來處理。所以通常狀況下我會定義一下的處理方法。spa
#pragma once #include <Message.pb.h> #include <memory> #include <map> #include "Client.h" using std::shared_ptr; class BaseHandler { public: BaseHandler(pbmsg::MSG type):type_(type){ Register (this); } virtual ~BaseHandler(){} pbmsg::MSG GetType() const { return type_; } //具體處理方法,由派生類實現. virtual void Process( const shared_ptr<pbmsg::Message> & msg, const shared_ptr<Client> & client) = 0; //註冊消息處理方法 static void Register( BaseHandler *); //執行指定的消息,查詢處理方法,調用Process。 static void Execute( const shared_ptr<pbmsg::Message> & msg, const shared_ptr<Client> & client); private: pbmsg::MSG type_; private: static std::map<pbmsg::MSG , BaseHandler *> handers; }; // 每一個消息都實現Process的一個特化版本... template< pbmsg::MSG Type> class MessageHandler : public BaseHandler { public: MessageHandler(void):BaseHandler(Type){} ~MessageHandler(void){} void Process( const shared_ptr<pbmsg::Message> & msg, const shared_ptr<Client> & client); private: static MessageHandler thisHandler; }; ///放在.cpp\.cxx文件中. void BaseHandler::Register( BaseHandler * h ) { handers[h->GetType ()] = h; } void BaseHandler::Execute( const shared_ptr<pbmsg::Message> & msg , ...其它參數) { auto it = handers.find(msg->type()); if( it != handers.end ()) { it->second->Process(msg,client); }else{ LOG(ERROR) <<"消息 "<<msg->type()<<" 沒有對應的處理方法.\n";; } } //對每一個MSG 枚舉的消息值,都會特化一個Process方法。 template<> void MessageHandler<pbmsg::Login_Request>::Process( const shared_ptr<pbmsg::Message> & msg , ...其它參數){} //而且在全局空間建立對象,系統啓動時,自動建立。若是須要在堆空間中分配,另行封裝方法,並調用下面的代碼,讓編譯器實例化類。 MessageHandler<pbmsg::Login_Request> MessageHandler<pbmsg::Login_Request>::thisHandler; // 最後消息處理:很是的easy:shared_ptr<pbmsg::Message> recvMessage( new pbmsg::Message()); bool parserOk = recvMessage->ParseFromArray((msg.rd_ptr ()+4), msg.size ()-4); if( parserOk ){ BaseHandler::Execute (recvMessage, ...其它參數); }
protobuf是二進制的消息,wireshark抓包是沒法直接分析的。不過google上面已經有了插件。 不過插件只支持UDP.本人在google上面的protobuf-wireshark的基礎上修改了支持TCP的抓包解析,前提是頂層Message只有一個,並且封裝在4個字節的長度後面。插件下載地址http://download.csdn.net/detail/chenxiaohong3905/5271945(wireshark 1.8.6版本). CSDN沒分數的能夠call me,留下你的郵箱。.net