原文地址:http://blog.csdn.net/zhjb1025/article/details/4017001
被ACE折磨了几天以后整理出来的一些经验,和大家分享
一. ace的编译安装
环境:XP,VC6.0+SP6,ACE5.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。
异步模式-即非阻塞模式。网络的传输速度一般来讲为10Mbps、100Mbps、1000Mbps。拿千兆网来说,实际的传输速度为1000Mbps/8大概为128KB左右。我们的CPU一般为P4 3.0GHZ,如果是32位的处理器,一秒钟大概可以处理6G的字节,那么,128KB的网络速度是远远及不上处理器的速度的。网络发送数据是一位一位发送出去的,如果CPU等在这里,发送完成函数才结束,那么,处理器浪费了大量时间在网络传输上。
操作系统提供了异步的模式来传输网络数据,工作模式即:应用程序把要发送的数据交给操作系统,操作系统把数据放在系统缓冲区后就告诉应用程序OK了,我帮你发,应用程序该干嘛干嘛去。操作系统发送完成后,会给应用系统一个回执,告诉应用程序:刚才那个包发送完成了!
举个例子:你有几封邮件和包裹要发,最有效率的办法是什么?你把邮件和包裹及交给总台,总台MM说,好了,你帮你发,你忙去吧!然后你去工作了。过了一会,总台MM打电话告诉你:“刚才我叫快递公司的人来了,把你的包裹发出去了。邮局的人也来了,取走了邮件,放心好了”。同样,如果你知道今天会有包裹来,比如你在淘宝上购物了,你能成天等在总台?你应该告诉总台MM:“今天可能有我的一个快递,你帮我收一下,晚上请你肯德基!”。MM:“看在肯得基的面子上,帮你收了”。某个时间,MM打电话来了:“帅哥,你的包裹到了,我帮你签收了,快来拿吧。”
因为操作系统是很有效率的,所有,他在后台收发是很快的。应用程序也很简单。Proactor就是这种异步模式的。Proactor就是总台MM;ACE_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会回调这个方法
3、virtual 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
[cpp] view plain copy print ?
- #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
#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
[cpp] view plain copy print ?
- #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(){}
#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
[cpp] view plain copy print ?
- #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
#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
[cpp] view plain copy print ?
- #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()
- {
-
- }
#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(){}
双击“连接”按钮,贴入连接服务器的代码
[cpp] view plain copy print ?
- ACE::init();
- if(Client::getInstance()->connect(20002,"127.0.0.1")==0)
- {
- AfxMessageBox("连接成功");
- }else{
- AfxMessageBox("连接失败");
-
- }
ACE::init(); if(Client::getInstance()->connect(20002,"127.0.0.1")==0) { AfxMessageBox("连接成功"); }else{ AfxMessageBox("连接失败"); }
并在这个文件头部引入
#include "ace/Addr.cpp"
#include "Client.h"
双击“发送”按钮,写下发送的代码
[cpp] view plain copy print ?
- UpdateData(true);
- Client::getInstance()->sendMessage(m_send.GetBuffer(m_send.GetLength()));
UpdateData(true);Client::getInstance()->sendMessage(m_send.GetBuffer(m_send.GetLength()));
双击“退出”按钮,写下退出的代码
[cpp] view plain copy print ?
- Client::getInstance()->close();
- ACE::fini();
- CDialog::OnCancel();
Client::getInstance()->close();ACE::fini();CDialog::OnCancel();
首先运行前面的服务器程序,然后再运行FMC程序,先连接服务器,然后再发生数据。
下面是运行的效果

注:运行的时候把ACEd.dll拷贝的exe 所在的目录
(文章发布还真难用,调文章效果都调了好多次,调好以后,发布以后格式又乱了,那个郁闷啊)