《C++ 消息自动派发》系列上篇介绍了IDL解析器,生成的C++代码只支持JSON转C++ struct。 通过新的重构,此次增长了对C++ struct 转JSON的支持。IDL解析器自动为C++ struct生成两个方法。 react
decode:实现json 转C++ struct 转。 linux
encode:实现C++ struct 转json字符串。 json
现实应用中,网络服务器程序处理流程以下: api
1> 网络层异步接收Client消息(本文讨论的应用都是基于json协议) 服务器
2> 对消息进行解析,如判断消息类型,消息体字段检查、解析、赋值等。将解析完成的结果封装到特定的struct中(每个消息类型定义单独一个struct)。注:JSON解析、检查、取值都是再网络线程完成(多线程),一般服务器程序的核心逻辑都是在单线程中完成,故逻辑线程应重点”保护“之。待消息转成struct后,逻辑线程直接操做二进制,尽最大程度提升逻辑线程的实时性、吞吐量。 网络
3> 逻辑线程处理完请求,通常会产生特定的响应结果(有时是一个,如rpc请求,有时多个,如广播消息)。响应结果仍然要同过json协议发送给client。 多线程
4> 逻辑成生成的响应结果为二进制struct,须要转换成json字符串。一样这些耗时的、与逻辑无关的操做应该放到网络线程。道理仍是同样,尽最大程度保证 异步
逻辑层的效率。 socket
完整示例代码 svn co http://ffown.googlecode.com/svn/trunk/fflib/lib/generator/ async
假设一个玩家查询好友信息接口。client发送get_friends_req请求,参数为uid,服务器查询该user的好友,生成好友列表list,返回消息结果。
首先定义IDL文件,其中有两个消息体:
//! 定义请求消息类型: struct get_friends_req_t { uint32 uid; }; //! 定义服务器响应结果消息体类型, ret_t 结尾,表明此消息为响应消息,服务器不须要处理此消息的请求
struct all_friends_ret_t
{
array<uint32> friends;
};
对应的服务器实现代码以下所示,稍微作些解释:
1> socket_t 封装linux socket 文件描述符操做,这里只是个示例,其提供async_write接口,使用preactor模式发送数据。其接受全部消息的基类指针,而且该指针为智能指针,无需手动析构。消息体基类支持encode接口,讲二进制struct转成json字符串,socket则将json字符串经过write系统调用发送给client。
2> logic_service_t 逻辑层,处理全部的消息请求。针对每个消息定义重载一个handle函数,为了不网络层消息传到逻辑层的内存拷贝,这里使用智能指针,同时避免了手动管理。
3> msg_dispather_t, 这个类是由idl 解析器自动生成的,在生产环境,应该有网络层调用此对象。因为本文只是示例,故忽略网络层,由main模拟网络层调用。
class socket_t { public: void async_write(msg_ptr_t msg_) { //! TODO do io write cout <<"wile send:" << msg_->encode_json() <<"\n"; } }; typedef socket_t* socket_ptr_t; class logic_service_t { public: void handle(shared_ptr_t<get_friends_req_t> req_, socket_ptr_t sock_) { cout << "req uid:" << req_->uid <<"\n"; //! DO some logic code shared_ptr_t<all_friends_ret_t> msg(new all_friends_ret_t()); for (int i = 0; i < 10; ++i) msg->friends.push_back(i); sock_->async_write(msg); } }; int main(int argc, char* argv[]) { try { string tmp = "{\"get_friends_req_t\":{\"uid\":12345}}"; logic_service_t logic_service; msg_dispather_t<logic_service_t, socket_ptr_t> msg_dispather(logic_service); //! 这里实际上应该被网络层调用 socket_ptr_t sock = new socket_t(); msg_dispather.dispath(tmp, sock); } catch(exception& e) { cout <<"e:"<< e.what() <<"\n"; } cout <<"main end ok\n"; }
idl_generator.py example.idl msg_def.h
前面定义的example.idl 通过idl_generator.py 分析后生成头文件msg_def.h, 其中包括 msg_dispather_t 的实现,其主要代码为:
struct all_friends_ret_t : public msg_t { vector<uint32> friends; int parse(const json_value_t& jval_) { json_instream_t in("all_friends_ret_t"); in.decode("friends", jval_["friends"], friends); return 0; } string encode_json() const { rapidjson::Document::AllocatorType allocator; rapidjson::StringBuffer str_buff; json_value_t ibj_json(rapidjson::kObjectType); json_value_t ret_json(rapidjson::kObjectType); this->encode_json_val(ibj_json, allocator); ret_json.AddMember("all_friends_ret_t", ibj_json, allocator); rapidjson::Writer<rapidjson::StringBuffer> writer(str_buff, &allocator); ret_json.Accept(writer); string output(str_buff.GetString(), str_buff.Size()); return output; } int encode_json_val(json_value_t& dest, rapidjson::Document::AllocatorType& allocator) const{ json_outstream_t out(allocator); out.encode("friends", dest, friends); return 0; } };
经过不断开发IDL解析器,进一步优化了json的解析和编码。其中:
1> json_instream.h 完成json的decode,依次遍历struct中的字段,为其赋值。json_instream_t中重载了支持全部类型参数的decode参数。
2> json_outstream.h 完成struct 转json,依次遍历struct中的字段,将其转为json value,其重载了支持全部基本类型的encode参数。
示例代码:
json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, int8_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, uint8_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, int16_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, uint16_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, int32_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, uint32_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, int64_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, uint64_t dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, bool dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, float dest_); json_outstream_t& encode(const char* filed_name_, json_value_t& jval_, const string& dest_);
1. IDL 解析器已经实现了基本功能,下次准备利用此IDL 解析器实现一个聊天服务器。
2. IDL 解析器添加对二进制encode/decode的支持。