1. 程式人生 > 其它 >C++中訊息自動派發之三 About JSON Encode

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

  《C++ 訊息自動派發》系列上篇介紹了IDL解析器,生成的C++程式碼只支援JSON轉C++ struct。 經過新的重構,這次增加了對C++ struct 轉JSON的支援。IDL解析器自動為C++ struct生成兩個方法。

  decode:實現json 轉C++ struct 轉。

  encode:實現C++ struct 轉json字串。

  現實應用中,網路伺服器程式處理流程如下:

  1> 網路層非同步接收Client訊息(本文討論的應用都是基於json協議)

  2> 對訊息進行解析,如判斷訊息型別,訊息體欄位檢查、解析、賦值等。將解析完成的結果封裝到特定的struct中(每一個訊息型別定義單獨一個struct)。注:JSON解析、檢查、取值都是再網路執行緒完成(多執行緒),通常伺服器程式的核心邏輯都是在單執行緒中完成,故邏輯執行緒應重點”保護“之。待訊息轉成struct後,邏輯執行緒直接操作二進位制,盡最大程度提高邏輯執行緒的實時性、吞吐量。

  3> 邏輯執行緒處理完請求,一般會產生特定的響應結果(有時是一個,如rpc請求,有時多個,如廣播訊息)。響應結果仍然要同過json協議傳送給client。

  4> 邏輯成生成的響應結果為二進位制struct,需要轉換成json字串。同樣這些耗時的、與邏輯無關的操作應該放到網路執行緒。道理還是一樣,盡最大程度保證

    邏輯層的效率。

  完整示例程式碼 svn co http://ffown.googlecode.com/svn/trunk/fflib/lib/generator/

1. 用例

  假設一個玩家查詢好友資訊介面。client傳送get_friends_req請求,引數為uid,伺服器查詢該user的好友,生成好友列表list,返回訊息結果。

  首先定義IDL檔案,其中有兩個訊息體:

//! 定義請求訊息型別:
struct get_friends_req_t
{
    uint32 uid;
};

//! 定義伺服器響應結果訊息體型別, ret_t 結尾,代表此訊息為響應訊息,伺服器不需要處理此訊息的請求

     對應的伺服器實現程式碼如下所示,稍微做些解釋:

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

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的支援。