ACE通信框架的一些例子(包括MFC)

ACE折磨了幾天以後整理出來的一些經驗,和大家分享

一. ace的編譯安裝

環境:XPVC6.0+SP6ACE5.4

1. 解壓縮ace源代碼包,假設在D:\ACE_wrappers

2. 使用vc打開D:\ACE_wrappers\ace\ace.dsw

3. 工作區上有三個工程,在ACE工程的頭文件中找到config.h

4. 雙擊打開這個文件,會有提示這個文件不存在是否創建,點是

5. config.h中寫入#include "ace/config-win32.h"表示windos 32位操作系統

6. ACE工程上右鍵Settings... 選擇c/c++ Caterory中選擇 Code Generation 然後在 Use run-time library 中選擇 Debug Multithreaded DLL

7. ACE工程上右鍵 build(selection only)

8. 編譯後會在D:\ACE_wrappers\lib 目錄中生成ACEd.lib ACEd.dll ACEd.exp ACEd.pdb等文件

9. D:\ACE_wrappers\ ACE-INSTALL.html有完整具體的安裝指南

二. Ace的一些概念

網上找到描述,比較生動容易理解。

前攝器(Proactor異步的事件多路分離器、處理器,是核心處理類。啓動後由3個線程組成(你不需要關心這三個線程,我只是讓你知道一下有這回事存在)。

接受器(Acceptor-用於服務端,監聽在一個端口上,接受用戶的請求。

連接器(Connector-用於客戶端,去連接遠程的監聽。當然,如果遠程是ACE寫的,就是Acceptor

異步模式-即非阻塞模式。網絡的傳輸速度一般來講爲10Mbps100Mbps1000Mbps。拿千兆網來說,實際的傳輸速度爲1000Mbps/8大概爲128KB左右。我們的CPU一般爲P4 3.0GHZ,如果是32位的處理器,一秒鐘大概可以處理6G的字節,那麼,128KB的網絡速度是遠遠及不上處理器的速度的。網絡發送數據是一位一位發送出去的,如果CPU等在這裏,發送完成函數才結束,那麼,處理器浪費了大量時間在網絡傳輸上。

操作系統提供了異步的模式來傳輸網絡數據,工作模式即:應用程序把要發送的數據交給操作系統,操作系統把數據放在系統緩衝區後就告訴應用程序OK了,我幫你發,應用程序該幹嘛幹嘛去。操作系統發送完成後,會給應用系統一個回執,告訴應用程序:剛纔那個包發送完成了!

舉個例子:你有幾封郵件和包裹要發,最有效率的辦法是什麼?你把郵件和包裹及交給總檯,總檯MM說,好了,你幫你發,你忙去吧!然後你去工作了。過了一會,總檯MM打電話告訴你:“剛纔我叫快遞公司的人來了,把你的包裹發出去了。郵局的人也來了,取走了郵件,放心好了”。同樣,如果你知道今天會有包裹來,比如你在淘寶上購物了,你能成天等在總檯?你應該告訴總檯MM:“今天可能有我的一個快遞,你幫我收一下,晚上請你肯德基!”。MM:“看在肯得基的面子上,幫你收了”。某個時間,MM打電話來了:“帥哥,你的包裹到了,我幫你簽收了,快來拿吧。”

因爲操作系統是很有效率的,所有,他在後臺收發是很快的。應用程序也很簡單。Proactor就是這種異步模式的。Proactor就是總檯MMACE_Service_Handle就是總檯代爲收發郵件的公司流程。

三. 配置ace的工程

在使用到ace的工程中都要進行的一些設置

1. 選擇Project->Settings...

2. 選擇c/c++ Caterory中選擇 Code Generation 然後在 Use run-time library 中選擇 Debug Multithreaded DLL

3. Caterory中選擇Preprocessor Preprocessor definitions 中添加ACE_AS_STATIC_LIBS 使用逗號與前面的內容隔開 在Additional include directories 中寫入ACE的根目錄D:\ACE_wrappers

4. 選擇 Link Caterory中選擇Input Additional library path 中加入D:\ACE_wrappers\lib Object/library modules 後追加aced.lib 用空格與前面的內容隔開

5. 設置完整以後重啓vc

四. 基於Console的服務器端

服務器端的功能:在指定端口進行監聽,在後臺打印客戶端發來的信息,然後向客戶端返回“serer say hello”信息。

ACE_Service_Handle主要就是定義了一些回調函數。

當有客戶端連接上來,連接建立成功後Proactor會調用這個方法。

1、virtual void open (ACE_HANDLE handle, ACE_Message_Block&message_block);

當用戶要讀的數據讀好了後,調用這個方法

2 virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result);

當用戶要寫的數據在網卡上發送成功後,Proactor會回調這個方法

3virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result);

4 virtual void handle_time_out (const ACE_Time_Value &tv, const void *act=0);

建立一個基於Console的服務器工程

下面是服務器的代碼,不是很難,結合註釋看,很容易看懂

#include "stdafx.h" #include "ace/Addr.cpp" #include "ace/Time_Value.cpp" #include "ace/Message_Queue.h" #include "ace/Asynch_IO.h" #include "ace/OS.h" #include "ace/Proactor.h" #include "ace/Asynch_Acceptor.h" #include "ace/SOCK_SEQPACK_Association.h" class Proactive_Service : public ACE_Service_Handler { public: ~Proactive_Service () { if (this->handle () != ACE_INVALID_HANDLE) ACE_OS::closesocket (this->handle ()); } /******************************************************/ /*每當客戶端連接到服務器就會調用此函數 */ /******************************************************/ virtual void open (ACE_HANDLE h, ACE_Message_Block&) { this->handle (h); //打開與客戶端的讀取流 if (this->reader_.open (*this) != 0 ) { delete this; return; } //開打與客戶端的寫出流 if (this->writer_.open (*this) != 0 ) { delete this; return; } ACE_Message_Block *mb = new ACE_Message_Block(buffer,1024); if (this->reader_.read (*mb, mb->space ()) != 0) { ACE_OS::printf("Begin read fail\n"); delete this; return; } return; } //異步讀完成後會調用此函數 virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result){ ACE_Message_Block &mb = result.message_block (); //如果讀取失敗說明客戶端關閉,在這裏刪除客戶端連接資源 if (!result.success () || result.bytes_transferred () == 0) { mb.release (); delete this; return; } mb.copy(""); //爲字符串添加結束標記'\0' ACE_OS::printf("rev:\t%s\n",mb.rd_ptr()); mb.release(); ACE_Message_Block *mbb = new ACE_Message_Block(100); mbb->copy("serer say hello"); if (this->writer_.write(*mbb,mbb->length()) !=0) { delete this; return; } ACE_Message_Block *nmb = new ACE_Message_Block(buffer,1024); if (this->reader_.read (*nmb, nmb->space ()) != 0) return; } //異步寫完成後會調用此函數 virtual void handle_write_dgram (const ACE_Asynch_Write_Stream::Result &result) { ACE_Message_Block &mb = result.message_block (); mb.release(); return; } private: ACE_Asynch_Read_Stream reader_; ACE_Asynch_Write_Stream writer_; char buffer[1024]; }; int main(int argc, char *argv[]) { ACE::init();//初始化dll資源 int port=20002;//指定監聽端口 ACE_Asynch_Acceptor<Proactive_Service> acceptor; //在指定的端口上進行監聽 if (acceptor.open (ACE_INET_Addr (port)) == -1) return -1; //開始等待客戶端的連接 ACE_Proactor::instance ()->proactor_run_event_loop(); ACE::fini();//釋放dll資源 return 0; }

五. 基於Console的客戶端

客戶端實現的功能是在連接上服務器的時候給服務器發送當前的系統的時間,接收到服務器信息的時候,把信息打印找後臺輸出屏幕上

建立一個基於Console的客戶端工程

把下面的代碼貼到工程裏面

#include "stdafx.h" #include "ace/SOCK_Connector.h" #include "ace/OS_NS_string.h" #include "ace/OS_NS_stdio.h" #include "ace/Addr.cpp" #include "ace/Time_Value.cpp" #include "ace/Message_Queue.h" #include "ace/Asynch_IO.h" #include "ace/OS.h" #include "ace/Proactor.h" #include "ace/Asynch_Connector.h" class Proactive_Client : public ACE_Service_Handler { public: ~Proactive_Client () { if (this->handle () != ACE_INVALID_HANDLE) ACE_OS::closesocket (this->handle ()); } virtual void open (ACE_HANDLE h, ACE_Message_Block&) { this->handle (h); if (this->reader_.open (*this) != 0 ) { delete this; return; } if (this->writer_.open (*this) != 0 ) { delete this; return; } ACE_Message_Block *mb = new ACE_Message_Block(buffer,1024); if (this->reader_.read (*mb, mb->space ()) != 0) { delete this; return; } ACE_OS::printf("connceted\n"); time_t now = ACE_OS::gettimeofday().sec(); char *time = ctime(&now); //獲取當前時間的字符串格式 ACE_Message_Block *mbb = new ACE_Message_Block(100); mbb->copy(time); if (this->writer_.write(*mbb,mbb->length()) !=0) { delete this; return; } return; } //異步讀完成後會調用此函數 virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) { ACE_Message_Block &mb = result.message_block (); if (!result.success () || result.bytes_transferred () == 0) { mb.release (); delete this; return; } mb.copy(""); //爲字符串添加結束標記'\0' ACE_OS::printf("rev:\t%s\n",mb.rd_ptr()); mb.release(); ACE_Message_Block *nmb = new ACE_Message_Block(buffer,1024); if (this->reader_.read (*nmb, nmb->space ()) != 0) return; } //異步寫完成後會調用此函數 virtual void handle_write_dgram (const ACE_Asynch_Write_Stream::Result &result) { ACE_Message_Block &mb = result.message_block (); mb.release(); return; } private: ACE_Asynch_Write_Stream writer_; ACE_Asynch_Read_Stream reader_; char buffer[1024]; }; int main(int argc, char *argv[]) { ACE::init(); ACE_INET_Addr addr(20002,"127.0.0.1");//服務器地址和端口 ACE_Asynch_Connector<Proactive_Client> connector; connector.open(); if (connector.connect(addr) == -1) return -1; ACE_Proactor::instance()->proactor_run_event_loop(); ACE::fini(); return 0; }

這裏可以發現客戶端和服務器端的代碼基本上一樣的,就是建立連接後一個函數負責發送數據,一個函數負責接收數據。作爲客戶端使用這樣的方式去接受服務器隨時發過來的數據,是不合理ACE_Proactor::instance()-> proactor_run_event_loop(); 這個語句使得這個程序進入一個死循環,從而無法做其他事情了。下面介紹客戶端實現的另一種方式。

六. 基於FMC的客戶端

建立一個基於對話框的FMC程序,在窗體上放置三個按鈕,一個“連接”按鈕,一個“發送”按鈕,一個“退出”按鈕。在放一個文本輸入框,然後在文本輸入框上右鍵選擇ClassWizard->Member Variables 爲文本輸入框建立一個變量爲m_send如下圖

在工程中加入下面的文件

RecvTask.h

#ifndef RECVTASK_H #define RECVTASK_H #include "ace/Task.h" #include "ace/OS.h" #include "ace/INET_Addr.h" #include "ace/SOCK_Connector.h" #define MSG_LEN_BYTES 128 #define TIME_OUT_VALUE 1000000 class RecvTask: public ACE_Task<ACE_MT_SYNCH> { public: RecvTask(); int open(void* p); int close(u_long); //接收服務器的信息 int svc(void); }; #endif

RecvTask.cpp

#include "stdafx.h" #include "RecvTask.h" #include "ace/ACE.h" #include "ace/OS.h" #include "ace/SOCK_Connector.h" #include "ace/INET_Addr.h" #include "ace/Task.h" #include "Client.h" int RecvTask::svc(void) { while(true) { Client::getInstance()->recvMessage(); ACE_OS::sleep(ACE_Time_Value( 0, 5000 )); } } int RecvTask::open(void* p) { activate(); return 0; } int RecvTask::close(u_long) { return 0; } RecvTask::RecvTask(){}

Client.h

#ifndef CLIENR_H #define CLIENR_H #include "stdafx.h" #include "ace/ACE.h" #include "ace/OS.h" #include "ace/SOCK_Connector.h" #include "ace/INET_Addr.h" #include "ace/Task.h" #include "RecvTask.h" class Client { public: ~Client(); /***************************************************************/ /* 根據ip地址和端口號,連接服務器,如果連接成功返回0,失敗返回-1 */ /***************************************************************/ int connect(int port,char * localhost); /***************************************************************/ /* 獲取客戶端實例 */ /***************************************************************/ static Client * getInstance(); /***************************************************************/ /* 給服務器發送數據信息,返回發的字節數 */ /***************************************************************/ int sendMessage(char * msg); /**************************************************************/ /* 關閉與遠程服務器的連接,成功返回0,失敗返回-1 */ /**************************************************************/ int close(); void recvMessage(); private: Client(); ACE_SOCK_Connector connector; ACE_Thread_Mutex mutex; RecvTask * recvTask; static Client * instance; static BOOL hasInstance; ACE_SOCK_Stream stream; }; #endif

Client.cpp

#include "stdafx.h" #include "RecvTask.h" #include "ace/ACE.h" #include "ace/OS.h" #include "ace/SOCK_Connector.h" #include "ace/INET_Addr.h" #include "ace/Task.h" #include "Client.h" #include "ace/OS_NS_string.h" Client * Client::instance=NULL; BOOL Client::hasInstance=false; Client::~Client() { if (recvTask!=NULL) { delete recvTask; recvTask=NULL; } } Client * Client::getInstance() { if (!hasInstance) { instance= new Client(); hasInstance=true; } return instance; } int Client::connect(int port,char * localhost) { recvTask = new RecvTask(); //stream = new ACE_SOCK_Stream(); ACE_INET_Addr remote_addr(port,localhost); int result=connector.connect(stream, remote_addr); if (result==0) { recvTask->open(0); }else{ recvTask->close(0); delete recvTask; } return result; } int Client::sendMessage(char * msg) { return stream.send_n(msg,ACE_OS::strlen(msg)); //return recvTask->getStream().send_n(msg,ACE_OS::strlen(msg)); } void Client::recvMessage() { size_t recv_len; char sLen[MSG_LEN_BYTES + 1]; ACE_Time_Value t(0, TIME_OUT_VALUE / 2); stream.recv_n(sLen, MSG_LEN_BYTES, &t, &recv_len); if (recv_len!=0) { sLen[recv_len]=0; AfxMessageBox(sLen); } } int Client::close() { recvTask->close(0); stream.close(); return 0; } Client::Client() { }

雙擊“連接”按鈕,貼入連接服務器的代碼

ACE::init(); if(Client::getInstance()->connect(20002,"127.0.0.1")==0) { AfxMessageBox("連接成功"); }else{ AfxMessageBox("連接失敗"); }

並在這個文件頭部引入

#include "ace/Addr.cpp"

#include "Client.h"

雙擊“發送”按鈕,寫下發送的代碼

UpdateData(true); Client::getInstance()->sendMessage(m_send.GetBuffer(m_send.GetLength()));

雙擊“退出”按鈕,寫下退出的代碼

Client::getInstance()->close(); ACE::fini(); CDialog::OnCancel();

首先運行前面的服務器程序,然後再運行FMC程序,先連接服務器,然後再發生數據。

下面是運行的效果

注:運行的時候把ACEd.dll拷貝的exe 所在的目錄

(文章發佈還真難用,調文章效果都調了好多次,調好以後,發佈以後格式又亂了,那個鬱悶啊)

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