一. 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的服务器工程
下面是服务器的代码,不是很难,结合注释看,很容易看懂
五. 基于Console的客户端
客户端实现的功能是在连接上服务器的时候给服务器发送当前的系统的时间,接收到服务器信息的时候,把信息打印找后台输出屏幕上
建立一个基于Console的客户端工程
把下面的代码贴到工程里面
这里可以发现客户端和服务器端的代码基本上一样的,就是建立连接后一个函数负责发送数据,一个函数负责接收数据。作为客户端使用这样的方式去接受服务器随时发过来的数据,是不合理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
- {
- 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 所在的目录