C++中消息自動派發之三 About JSON Encode

《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

1. 用例

  假設一個玩家查詢好友信息接口。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";
}
複製代碼

2. 使用IDL 生成 C++ 代碼:

  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;
    }

};
複製代碼

3. encode 和 decode 如何實現

  經過不斷開發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_);
複製代碼

 4. TODO 

   1. IDL 解析器已經實現了基本功能,下次準備利用此IDL 解析器實現一個聊天服務器。

   2. IDL 解析器添加對二進制encode/decode的支持。

相關文章
相關標籤/搜索