ascs 簡明開發教程(十三):發送接收消息類型

QQ交流羣:198941541

發送消息類型由打包器決定,打包器都繼承自 template<typename MsgType> class i_packer,繼承之後,你將自動得到如下定義:

template<typename MsgType>
class i_packer
{
public:
	typedef MsgType msg_type;
	typedef const msg_type msg_ctype;
	typedef list<msg_type> container_type;
};

注意這裏的container_type並不是ascs需要的容器,而是i_packer接口裏面需要的容器(打包器輸出值)。那麼打包器返回一個容器有什麼意義呢?這其實是一種內存優化,比如你的數據已經存放在msg_type類型裏面了,那麼打包的時候,直接把包頭打在另一個msg_type裏面,加上你的數據通過鏈表返回,這樣就避免了重新分配內存和拷貝內存,這是流式socket的好處,UDP就不行了,但好在UDP無需包頭。

顯然的,不是任意類都可以做爲發送消息類型,它必須要提供如下接口:

bool empty() const
size_t size() const
const char* data() const

注意ascs只支持按對象訪問發送消息,不支持對象指針、裸指針和數組。對於對象指針(必須是動態分配的),你可以用auto_buffer或者shared_buffer來包裝它(在packer裏面包裝),它會把->操作轉換成.操作(跟std::string類似),讓ascs覺得它是在訪問對象而不是對象指針;對於裸指針和數組(必須是動態分配的),你可以直接用ascs::ext::basic_buffer或者仿照它自己實現一個類似的。

ascs通過打包器得到消息(your_packer::msg_type類型或者list<your_packer::msg_type>類型)之後,會swap到in_msg對象裏面(不會有拷貝,除非你用了自己寫的消息類型,且用拷貝實現了swap,這是違背標準的,除非對於非常少量的數據,你做了優化,比如在棧上存儲,高版本VC++裏面的std::string採用了類似優化),in_msg的定義如下(其中InMsgType是個模板參數,等於your_packer::msg_type):

#ifdef ASCS_SYNC_SEND
	typedef obj_with_begin_time_promise<InMsgType> in_msg;
#else
	typedef obj_with_begin_time<InMsgType> in_msg;
#endif

你無需瞭解obj_with_begin_time_promise或者obj_with_begin_time的詳細功能(它其實就是把消息打上時間戳用於性能統計),因爲它們繼承自your_packer::msg_type,你直接把它們當成your_packer::msg_type類型使用即可。而且ascs會盡量不讓你感知到in_msg的存在,所以在on_msg_send裏面傳入的參數仍然是your_packer::msg_type類型,但是如果定義了宏ASCS_WANT_BATCH_MSG_SEND_NOTIFY,on_msg_send裏面傳入的參數將會是一個容器(這個容器是上一篇裏面說的容器,不是i_packer接口裏面用的容器),此時你需要知道,容器裏面其實是in_msg類型,但你可以當成your_packer::msg_type類型使用。

 

接收消息類型由解包器決定,解包器都繼承自 template<typename MsgType> class i_unpacker,繼承之後,你將自動得到如下定義:

template<typename MsgType>
class i_unpacker
{
public:
	typedef MsgType msg_type;
	typedef const msg_type msg_ctype;
	typedef list<msg_type> container_type;
};

注意這裏的container_type並不是ascs需要的容器,而是i_unpacker接口裏面需要的容器(解包器輸出值)。那麼解包器返回一個容器有什麼意義呢?這主要是考慮到粘包的問題,這是流式socket必須要面對且解決的問題,UDP每次讀取都是一個且只有一個完整的消息,所以理論上就不需要容器了,但爲了統一,仍然使用了i_unpacker接口,所以仍然以容器的方式返回消息。

顯然的,不是任意類都可以做爲接收消息類型,它必須要提供如下接口(如果你不處理消息的話):

size_t size() const
const char* data() const

且當數據爲空時,data() 仍能返回有效指針(跟std::string類似),這是因爲ascs在處理接收消息時,沒有判斷過數據是否爲空而是直接打印 data() 內容。但由於你肯定要重寫消息處理函數自己處理消息,所以ascs的消息處理函數是調用不到的,那麼接收消息對象的 data() 函數的行爲就完全由你的消息處理函數決定了。如果接收消息是對象指針(必須是動態分配的),你同樣需要用auto_buffer或者shared_buffer來包裝它(在unpacker裏面包裝),因爲ascs只支持按對象訪問接收消息(跟發送消息一樣);對於裸指針和數組(必須是動態分配的),你可以直接用ascs::ext::basic_buffer或者仿照它自己實現一個類似的。

ascs通過解包器得到消息(list<your_unpacker::msg_type>類型)之後,會swap到out_msg對象裏面,out_msg的定義如下(其中OutMsgType是個模板參數,等於your_unpacker::msg_type):

typedef obj_with_begin_time<OutMsgType> out_msg;

你無需瞭解obj_with_begin_time的詳細功能(它其實就是把消息打上時間戳用於性能統計),因爲它繼承自your_unpacker::msg_type,你直接把它當成your_unpacker::msg_type類型使用即可。而且ascs會盡量不讓你感知到out_msg的存在,所以在on_msg_handle裏面傳入的參數仍然是your_unpacker::msg_type類型,但是如果定義了宏ASCS_DISPATCH_BATCH_MSG,on_msg_handle裏面傳入的參數將會是一個容器(這個容器是上一篇裏面說的容器,不是i_unpacker接口裏面用的容器),此時你需要知道,容器裏面其實是out_msg類型,但你可以當成your_unpacker::msg_type類型使用。

注意如果定義了宏ASCS_SYNC_DISPATCH,on_msg裏面的容器類型是list<your_unpacker::msg_type>或者list<udp_msg<your_unpacker::msg_type>>,與ascs用的容器沒有關係。

 

那麼重寫時,到底怎麼寫才能通用呢(怎麼換打包解包器都能用,TCP和UDP也能通用),可以參考如下:

#ifdef ASCS_WANT_MSG_SEND_NOTIFY
	virtual void on_msg_send(in_msg_type& msg)
#elif defined(ASCS_WANT_BATCH_MSG_SEND_NOTIFY)
	virtual void on_msg_send(in_container_type& msg_can)
#endif

#ifdef ASCS_WANT_ALL_MSG_SEND_NOTIFY
	virtual void on_all_msg_send(in_msg_type& msg)
#endif

#ifdef ASCS_SYNC_DISPATCH
	virtual size_t on_msg(list<out_msg_type>& msg_can)
#endif

#ifdef ASCS_DISPATCH_BATCH_MSG
	virtual size_t on_msg_handle(out_queue_type& msg_can)
#else
	virtual bool on_msg_handle(out_msg_type& msg)
#endif

其中in_msg_type和out_msg_type是ascs::tcp::socket_base對your_packer::msg_type和your_unpacker::msg_type的typedef。on_all_msg_send傳出in_container_type而不是in_queue_type是因爲它無需考慮線程安全。注意UDP稍有區別,因爲UDP消息需要記錄對方地址,所以它們是ascs::udp::udp_msg<your_packer::msg_type>和ascs::udp::udp_msg<your_unpacker::msg_type>的typedef。

上一篇:ascs 簡明開發教程(12)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章