网络通讯涉及到消息的定义,不论是使用二进制模式、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