最近工作中做了一个以ACE框架为基础的项目。所要做的功能很简单,就是服务器接收http报文请求,添加一些业务记录到数据库,然后设计一个定时器来轮训判断之前的业务记录是否发生状态变化,然后同步http请求方提供的数据库中业务记录的状态。从开发到测试一直问题不断,其中遇到和解决的问题,以及所思所感,在此记录一下。因此我不会讲具体技术,只讲我实践中遇到的问题和如何解决的。
一、ACE框架的学习
之前工作一直没有使用过这个框架。可以说一点都不熟悉,但是怎么办呢,项目以交到你的手上,硬着头皮看文档。
#include "ace/Svc_Handler.h"
#include "ace/SOCK_Stream.h"
#include "ace/SSL/SSL_SOCK_Stream.h"
class XXXManager: public ACE_Svc_Handler <ACE_SOCK_STREAM,ACE_NULL_SYNCH>
{
public:
typedef ACE_Svc_Handler <ACE_SOCK_STREAM, ACE_NULL_SYNCH> inherited;
XXXManager(void);
~XXXManager(void);
void destroy (void);
int open (void *acceptor);
int close (u_long flags = 0);
virtual int handle_close (ACE_HANDLE handle = ACE_INVALID_HANDLE,
ACE_Reactor_Mask mask = ACE_Event_Handler::ALL_EVENTS_MASK);
public:
/*注册过的*/
int handle_input (ACE_HANDLE handle);
int process (char *rdbuf, int rdbuf_len);
/**
* 定时器回调函数.
* 使用外部定时器时,注册这个回调函数即可
* @param lTimerID 定时器ID
* @param pPara1 NULL
* @param pPara2 unused
*/
static void TimerCallBack(long lTimerID, void* pPara1, void* pPara2);
private:
int timer_strategy(int last_timer,int current_timer);
/**
* 返回报文字符串函数
*/
std::string ResponseStr(int state);
DBAccess* db_object_;
};
通常ACE框架首先需要我们自己定义一个事件类继承于ACE_Svc_Handler,用于处理socket流,说白了就是有没有网络通信。这里留心一下handle_input函数即可。其他函数都是为了这个具体的工程准备的。
接下来,还需要定义一个接收器类,这个也是ACE框架范畴的。ACE_Acceptor类模板第一个参数我们填之前写的类即可,第二个参数表示接收器接收的类型是socket事件。
#include "ace/Acceptor.h"
#include "ace/SOCK_Acceptor.h"
#include "ace/SSL/SSL_SOCK_Acceptor.h"
typedef ACE_Acceptor <XXXManager, ACE_SOCK_ACCEPTOR> Client_Acceptor_Base;
class Client_Acceptor : public Client_Acceptor_Base
{
//to do...
};
我们在大main函数里调用open函数将端口告诉reactor(ACE框架的调度器)。
ACE_UINT16 g_usAgentSrvPort = 88; //监听88端口
ACE_Reactor reactor;
Client_Acceptor peer_acceptor;
if (peer_acceptor.open (ACE_INET_Addr (g_usAgentSrvPort),
&reactor, ACE_NONBLOCK) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
"%p\n",
"open"),
-1);
在我们定义的事件类的open函数中我们做一件事:就是向ACE框架注册我们的事件类句柄。这样当监听的端口有socket请求时,ACE框架会将这个事件调度到我们定义的事件类,并调用handle_input函数。我们对请求的所有处理就可以在这个函数中展开。
int
XXXManager::open (void *void_acceptor)
{
ACE_INET_Addr addr;
ACE_INET_Addr local;
if (this->peer().get_remote_addr(addr) == -1)
return -1;
if (this->peer().get_local_addr(local) == -1)
return -1;
// 防止回环
if (addr.get_ip_address() == local.get_ip_address())
{
if (g_debug)
printf("local to local\n");
return -1;
}
//创建接收器
Client_Acceptor *acceptor = (Client_Acceptor *) void_acceptor;
/*通过ACE传统的方式,监听的端口有请求时,会调用handle_input()*/
this->reactor(acceptor->reactor());
/*向ACE框架注册接收器*/
if (this->reactor()->register_handler(this,
ACE_Event_Handler::READ_MASK) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
"(%P|%t) can't register with reactor\n"),
-1);
return 0;
}
可以在handle_input函数中调用peer().recv()函数来接收报文,也可以通过peer().send()函数来返回报文。
#define BUFSIZE 1024;
char buf[BUFSIZE];
ACE_Time_Value timeout(0,100000);
//接收报文
this->peer().recv(buf, sizeof (buf), &timeout);
//处理报文...
//返回报文
this->peer().send(ResponseStr(1).c_str(),ResponseStr(1).length(),&timeout);
最后我们在大main函数里这样,不断处理一次次的socket请求事件。
while (true)
{
std::cout << "========wait request========" << std::endl;
reactor.handle_events();
}
以上这些都是ACE框架最基本的内容,万变不离其宗。但是对于一个从未接触过的人,了解各个函数功能,及相互耦合关系,还是需要花一些功夫的。
最后一个问题是,每次接收到一个符合我们定义的事件类的事件,ACE框架都会生成一个事件类对象,在调用完handle_input函数如果成功的话,返回值通常我们都认为应该是0。如果是0返回给上层,ACE框架不会释放对这个事件的处理,事件类对象还存在;而如果返回的是-1,框架则会中断对这个事件的处理,会析构掉事件类,从而等待新的请求到来。所以这里我在实践中return的是-1,而我最开始都是return的0。
我接手时遇到的问题就是,完全对框架没什么概念。花了好多功夫去熟悉。查看文档,请教别人。不懂得时候是真的着急。但了解了之后,后边再遇到的话就熟悉了。希望我这篇粗浅的文章,可以帮助到大家。
未完待续…