[ACE_Proactor網路通訊樣本-Part.1]

來源:互聯網
上載者:User

註:本文僅對使用ACE進行網路通訊進行示範說明。本文中的代碼皆使用doxgen的注釋風格。本文中使用的事件機制,其原理與實現請參考[ 基於C++的事件機制設計[2.0]]一文。

 

ACE的Proactor對Epoll和IOCP進行了良好封裝,因此,使用ACE來進行網路開發是相當的便利,效能也不差。閑言少敘,看代碼。

這裡以TCP協議進行流式通訊。我們需要解析流,得出每次接收到的資料包大小和包含的資料域,假定我們的包結構如下:

 

包序號(32Bit) | 長度(16Bit) | 資料域(大小為長度所表示的位元組)... | (下一包)

 

通過分析由包序號和長度組成的包頭來解決半包,粘包等問題,許多其它文章也有描述,這裡就省略了。

這樣可以確定我們的包頭結構如下:

 

#pragma pack(push)<br />#pragma pack(1)<br />/**<br />* @brief Tcp包頭結構<br />*/<br />typedef struct tag_TTcpPackHeader<br />{<br />unsigned int seq; //< 包序號<br />unsigned short len; //< 包長度<br />}TTcpPackHeader;<br />#pragma pack(pop)<br />/// 包頭尺寸宏<br />#define TCP_PACK_HEADER_SIZE sizeof(tag_TTcpPackHeader) .

需要注意的是,要求在位元組邊界對齊。

 

現在來看看通過ACE來實現TCP通訊需要哪些東西:

INET_Addr 用於地址訪問

Task_Base 用於執行緒模式

Message_Block 用於訊息傳遞和資料容器

Asynch_IO 非同步通訊

Proactor IOCP架構

 

並且,要建立這樣的通訊架構,我們需要:

一個Acceptor:用於接受串連

一個Handler:對應於每個串連控制代碼,並用於資料的發送/接收。

一個事件分發線程:以事件的形式將接收到資料分發出去,並在對應的控制代碼上進行資料發送。

 

本樣本並沒有採用在接收到資料時立即進行處理的方式,而是通過建立一個額外的事件分發線程的形式,將資料包投遞到該線程的訊息佇列中,由該線程向外派送。因此,資料處理與網路層是隔離的,且網路層能專註於通訊,最大的發揮效用。

 

好了,下面來看看實現:

先看Handler,參考ACE_Service_Handler,我們需要重載open(),addresses(), handle_read_stream(),handle_write_stream(),以在串連開啟時進行讀寫流對象的初始化、擷取用戶端地址,處理輸入/輸入資料流。

 

註:以T作為類的開頭而不是C,是出於對曾經偉大的BORLAND的深刻懷念。

註:成員又以m_開頭,是出於對現而今仍偉大的MS的深刻怨念。

 

/**<br />* @class TTcpHandler<br />* @brief Tcp串連控制代碼<br />*/<br />class TTcpHandler : public ACE_Service_Handler<br />{<br />public:<br />/**<br />* @brief 用戶端串連事件類型定義<br />* @param [in] ACE_UINT32 用戶端地址<br />* @param [in] ACE_UINT16 用戶端連接埠<br />* @param [in] TTcpHandler* 串連控制代碼<br />*/<br />typedef TEvent<void, ACE_UINT32, ACE_UINT16, TTcpHandler *> TOnClientConnect;<br />/**<br />* @brief 用戶端中斷連線事件類型定義<br />* @param [in] ACE_UINT32 用戶端地址<br />* @param [in] ACE_UINT16 用戶端連接埠<br />*/<br />typedef TEvent<void, ACE_UINT32, ACE_UINT16> TOnClientDisconnect;<br />/**<br />* @brief 用戶端串連驗證事件<br />* @param [in] ACE_UINT32 用戶端地址<br />* @param [in] ACE_UINT16 用戶端連接埠<br />* @return bool<br />* - true 驗證通過<br />* - false 驗證失敗<br />*/<br />typedef TEvent<bool, ACE_UINT32, ACE_UINT16> TOnClientValidate;<br />/**<br />* @brief 接收到用戶端資料事件類型定義<br />* @param [in] ACE_UINT32 用戶端地址<br />* @param [in] ACE_UINT16 用戶端連接埠<br />* @param [in] unsigned int 資料包序號<br />* @param [in] const char* 資料區域指標<br />* @param [in] size_t 資料長度<br />*/<br />typedef TEvent<void, ACE_UINT32, ACE_UINT16, unsigned int, const char*, unsigned short> TOnDataReceive;</p><p>/**<br />* @brief 成功發送用戶端資料事件類型定義<br />* @param [in] ACE_UINT32 用戶端地址<br />* @param [in] ACE_UINT16 用戶端連接埠<br />* @param [in] unsigned int 資料包序號<br />* @param [in] const char* 資料區域指標<br />* @param [in] size_t 資料長度<br />*/<br />typedef TEvent<void, ACE_UINT32, ACE_UINT16, unsigned int, const char*, unsigned short> TOnDataSendSucceeded;</p><p>/**<br />* @brief 失敗發送用戶端資料事件類型定義<br />* @param [in] ACE_UINT32 用戶端地址<br />* @param [in] ACE_UINT16 用戶端連接埠<br />* @param [in] unsigned int 資料包序號<br />* @param [in] const char* 資料區域指標<br />* @param [in] size_t 資料長度<br />*/<br />typedef TEvent<void, ACE_UINT32, ACE_UINT16, unsigned int, const char*, unsigned short> TOnDataSendFailed;<br />private:<br />ACE_Asynch_Read_Stream m_Reader; //< 非同步讀資料流<br />ACE_Asynch_Write_Stream m_Writer; //< 非同步寫資料流<br />ACE_Message_Block* m_CurDataMB; //< 當前讀取資料<br />ACE_INET_Addr m_ClientAddr; //< 用戶端地址<br />public:<br />/**<br />* @name 事件控制代碼<br />* @{<br />*/<br />DECL_PROP(TOnClientConnect, OnClientConnect) //< 用戶端串連事件控制代碼<br />DECL_PROP(TOnClientDisconnect, OnClientDisconnect) //< 用戶端斷開事件控制代碼<br />DECL_PROP(TOnDataReceive, OnDataReceive) //< 接收到資料的事件控制代碼<br />DECL_PROP(TOnDataSendSucceeded, OnDataSendSucceeded) //< 成功發送資料的事件控制代碼<br />DECL_PROP(TOnDataSendFailed, OnDataSendFailed) //< 發送資料失敗的事件控制代碼<br />/**<br />* @}<br />*/<br />public:<br />/// ctor<br />TTcpHandler();</p><p>/// dtor<br />~TTcpHandler();</p><p>/**<br />* @brief 發送資料<br />* @param [in] unsigned int 資料包序號<br />* @param [in] const char* 要發送的資料區域指標<br />* @param [in] size_t 要發送的資料長度<br />* @return int<br />* - 0 成功<br />* - 1 失敗<br />*/<br />int send(unsigned int seq, const char* data, unsigned short size);</p><p>/**<br />* @brief 開啟控制代碼<br />* @see ACE_Service_Handler<br />*/<br />virtual void open(ACE_HANDLE h, ACE_Message_Block& mb);<br />/**<br />* @brief 擷取地址<br />* @see ACE_Service_Handler<br />*/<br />virtual void addresses (const ACE_INET_Addr &remote_address,<br /> const ACE_INET_Addr &local_address);</p><p>/**<br />* @brief 讀取流資料<br />* @see ACE_Service_Handler<br />*/<br />virtual void handle_read_stream(const ACE_Asynch_Read_Stream::Result& result);<br />/**<br />* @brief 寫入流資料<br />* @see ACE_Service_Handler<br />*/<br />virtual void handle_write_stream(const ACE_Asynch_Write_Stream::Result& result);<br />/**<br />* @brief 初始化當前資料接收緩衝事件<br />*/<br />void initCurDataMB();<br />}; // class TTcpHandler

 

而相應滴,Acceptor在接受串連時,產生出的Handler應該是TTcpHandler類型,其定義如下:

注意,為了將事件控制代碼與串連控制代碼(TTcpHandler)掛鈎,這裡重載了make_handler()。而重載validate_connection則是為了讓串連驗證事件能夠在恰當的時機被激發。

/**<br />* @class TTcpAcceptor<br />* @brief TCP接受器<br />* @see ACE_Asynch_Acceptor<br />* @see TTcpHandler<br />*/<br />class TTcpAcceptor : public ACE_Asynch_Acceptor<TTcpHandler><br />{<br />public:<br />/**<br />* @name TCP事件控制代碼<br />* @see TTcpHandler<br />* @{<br />*/<br />DECL_PROP(TTcpHandler::TOnClientConnect, OnClientConnect)<br />DECL_PROP(TTcpHandler::TOnClientDisconnect, OnClientDisconnect)<br />DECL_PROP(TTcpHandler::TOnClientValidate, OnClientValidate)<br />DECL_PROP(TTcpHandler::TOnDataReceive, OnDataReceive)<br />DECL_PROP(TTcpHandler::TOnDataSendSucceeded, OnDataSendSucceeded)<br />DECL_PROP(TTcpHandler::TOnDataSendFailed, OnDataSendFailed)<br />/**<br />* @}<br />*/<br />protected:<br />/**<br />* @brief 串連驗證<br />* @note 激發 OnClientValidate 事件 @see TOnClientValidate<br />* @see ACE_Asynch_Acceptor<br />*/<br />virtual int validate_connection (const ACE_Asynch_Accept::Result& result,<br /> const ACE_INET_Addr &remote,<br /> const ACE_INET_Addr& local);<br />/**<br />* @brief 建立串連控制代碼事件<br />* @see ACE_Asynch_Acceptor<br />*/<br />virtual TTcpHandler* make_handler(void);<br />}; // class TTcpAcceptor

 

有了Acceptor和Handler,還需要使之運行於Proactor模式下,因此有了以下線程:

/**<br />* @class TTcpNetThread<br />* @brief TCP網路線程<br />* @see ACE_Task_Base<br />* @see ACE_Proactor<br />*/<br />class TTcpNetThread : public ACE_Task_Base<br />{<br />public:<br />/**<br />* @name TCP事件控制代碼<br />* @see TTcpHandler<br />* @{<br />*/<br />DECL_PROP(TTcpHandler::TOnClientConnect, OnClientConnect)<br />DECL_PROP(TTcpHandler::TOnClientDisconnect, OnClientDisconnect)<br />DECL_PROP(TTcpHandler::TOnClientValidate, OnClientValidate)<br />DECL_PROP(TTcpHandler::TOnDataReceive, OnDataReceive)<br />DECL_PROP(TTcpHandler::TOnDataSendSucceeded, OnDataSendSucceeded)<br />DECL_PROP(TTcpHandler::TOnDataSendFailed, OnDataSendFailed)<br />/**<br />* @}<br />*/<br />/// 運行<br />int open();<br />/// 停止運行<br />int close();<br />protected:<br />/// 線程函數<br />virtual int svc();<br />};<br />

 

最後再看看事件分發線程,該線程也是對上述實現的彙總和封裝,對外暴露事件和發送方法:

注意,該類也負責響應TTcpNetThread所激發的事件,所以需要派生自TObject。

 

/**<br />* @class TTcp<br />* @brief TCP接收和事件處理代理線程<br />*/<br />class TTcp : public TObject, public ACE_Task<ACE_MT_SYNCH><br />{<br />public:<br />/**<br />* @name 重定義事件類型<br />* @see TTcpHandler<br />* @{<br />*/<br />typedef TTcpHandler::TOnClientConnect TOnClientConnect;<br />typedef TTcpHandler::TOnClientDisconnect TOnClientDisconnect;<br />typedef TTcpHandler::TOnClientValidate TOnClientValidate;<br />typedef TTcpHandler::TOnDataReceive TOnDataReceive;<br />typedef TTcpHandler::TOnDataSendSucceeded TOnDataSendSucceeded;<br />typedef TTcpHandler::TOnDataSendFailed TOnDataSendFailed;<br />/**<br />* @}<br />*/<br />private:<br />/**<br />* @name 成員變數<br />* @{<br />*/<br />ACE_Recursive_Thread_Mutex m_Lock; //< 線程鎖<br />hash_map<unsigned __int64, TTcpHandler *> m_AddrMap; //< 地址/控制代碼映射<br />TTcpNetThread* m_TcpNetThd;<br />/**<br />* @}<br />*/<br />public:<br />/**<br />* @name TCP事件控制代碼<br />* @see TTcpHandler<br />* @{<br />*/<br />DECL_PROP(TTcpHandler::TOnClientConnect, OnClientConnect)<br />DECL_PROP(TTcpHandler::TOnClientDisconnect, OnClientDisconnect)<br />DECL_PROP(TTcpHandler::TOnClientValidate, OnClientValidate)<br />DECL_PROP(TTcpHandler::TOnDataReceive, OnDataReceive)<br />DECL_PROP(TTcpHandler::TOnDataSendSucceeded, OnDataSendSucceeded)<br />DECL_PROP(TTcpHandler::TOnDataSendFailed, OnDataSendFailed)<br />/**<br />* @}<br />*/<br />public:<br />/// ctor<br />TTcp();<br />/// dtor<br />~TTcp();<br />/// 運行<br />void open();<br />/// 停止<br />void close();</p><p>/// 發送資料<br />int send(ACE_UINT32 ip, ACE_UINT16 port, unsigned int seq, const char* buf, unsigned short len);<br />private:<br />/// 線程函數<br />virtual int svc();<br />private:<br />/**<br />* @name TTcpNetThread 事件處理方法<br />* 關於事件原型的定義,請參考 @see TTcpHandler<br />* @{<br />*/<br />void tcpNetThread_OnClientConnect(ACE_UINT32 ip, ACE_UINT16 port, TTcpHandler* handler);<br />void tcpNetThread_OnClientDisconnect(ACE_UINT32 ip, ACE_UINT16 port);<br />void tcpNetThread_OnDataReceive(ACE_UINT32 ip, ACE_UINT16 port, unsigned int seq, const char* data, unsigned short size);<br />void tcpNetThread_OnDataSendSucceeded(ACE_UINT32 ip, ACE_UINT16 port, unsigned int seq, const char* data, unsigned short size);<br />void tcpNetThread_OnDataSendFailed(ACE_UINT32 ip, ACE_UINT16 port, unsigned int seq, const char* data, unsigned short size);<br />/**<br />* @}<br />*/<br />}; // class TTcp

 

現在基本格調已經確定,需要做的是編寫具體實現代碼了。

 

 

此乃末技。

 

 

應用ACE來作為底層通訊的架構,已經是許多年前的技術了,這裡純粹是湊字數,騙更新滴。這樣的老東西,確實是相當的讓人無語。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.