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)