这两天在学习C++网络编程卷二,第二章开始讲到reactor模式,这两天结合书上的例子,写了个简单的echo server端程序,代码如下:
#include <ace/ACE.h>
#include <ace/OS_NS_string.h>
#include <ace/os_include/os_stdint.h>
#include <ace/OS_NS_stdlib.h>
#include <ace/OS_NS_errno.h>
#include <ace/OS_NS_unistd.h>
#include <ace/OS_NS_sys_time.h>
#include <ace/OS_main.h>
#include <ace/Basic_Types.h>
#include <ace/Handle_Set.h>
#include <ace/Map_Manager.h>
#include <ace/Null_Mutex.h>
#include <ace/Process.h>
#include <ace/Thread_Manager.h>
#include <ace/TSS_T.h>
#include <ace/INET_Addr.h>
#include <ace/SOCK_Acceptor.h>
#include <ace/SOCK_Connector.h>
#include <ace/SOCK_Stream.h>
#include <ace/FILE_Connector.h>
#include <ace/FILE_Addr.h>
#include <ace/FILE_IO.h>
#include <ace/Event_Handler.h>
#include <ace/Reactor.h>
//#include <stdarg.h>
#include <assert.h>
const u_short LISTEN_PORT = 8001;
class EchoEventHandler : public ACE_Event_Handler
{
public:
EchoEventHandler(ACE_Reactor* reactor, ACE_Time_Value timeout_time)
: ACE_Event_Handler(reactor), timeout_time_(timeout_time) , timer_id_(0)
{
}
virtual ~EchoEventHandler()
{
}
int close()
{
return reactor()->remove_handler(this, ACE_Event_Handler::READ_MASK);
}
virtual int open()
{
// 测试退出的流程
// return -1;
// 注册自己为反应器的读处理事件
if (0 != reactor()->register_handler(this, ACE_Event_Handler::READ_MASK))
{
ACE_ERROR_RETURN((LM_ERROR, "register read event fail\n"), -1);
}
// 同步当前时间
last_proc_time_ = ACE_OS::gettimeofday();
// 注册定时器
ACE_Time_Value reschedule(timeout_time_.sec() / 4);
timer_id_ = reactor()->schedule_timer(
this, // timer event handler, should implement virtual function handle_timeout
0, // handle_timeout() callback args
timeout_time_, // delay time
reschedule); // schedule interval
if (timer_id_ < 0)
{
ACE_ERROR_RETURN((LM_ERROR, "register timer schedule fail, errno=%d\n", ACE_ERRNO_GET), -1);
}
// 测试退出的流程
// return -1;
return 0;
}
virtual int handle_input(ACE_HANDLE handle = ACE_INVALID_HANDLE)
{
ACE_DEBUG((LM_ERROR, "get data from stream[%d]\n", peer().get_handle()));
// 更新proc time
last_proc_time_ = ACE_OS::gettimeofday();
// 通过stream收发数据
char recvbuf[64];
ACE_OS::memset(recvbuf, 0, sizeof(recvbuf));
peer().enable(ACE_NONBLOCK);
ssize_t recvlen = peer().recv(recvbuf, sizeof(recvbuf) - 1);
if (recvlen < 0)
{
ACE_ERROR_RETURN((LM_ERROR, "recv buffer fail, errno=%d \n", errno), -1);
}
else if (0 == recvlen)
{
ACE_ERROR_RETURN((LM_ERROR, "client disconnect\n"), -1);
}
ACE_DEBUG((LM_ERROR, "recv buffer(%d):%s\n", recvlen, recvbuf));
ssize_t sendlen = peer().send_n(recvbuf, recvlen);
if (sendlen != recvlen)
{
ACE_ERROR_RETURN((LM_ERROR, "send back buffer fail \n"), -1);
}
return 0;
}
// 当handle_*方法返回-1时,会自动调用handle_close方法
virtual int handle_close(ACE_HANDLE handle = ACE_INVALID_HANDLE,
ACE_Reactor_Mask mask = 0)
{
ACE_DEBUG((LM_ERROR, "event handle [fd=%d] close\n", peer_.get_handle()));
// 尽量使用ID取消ACE_Event_Handler定时器, by sailzeng
if (timer_id_)
{
reactor()->cancel_timer(timer_id_);
}
else
{
reactor()->cancel_timer(this);
}
peer_.close();
delete this;
return 0;
}
// 超时处理
virtual int handle_timeout(const ACE_Time_Value& current_time,
const void* act = 0)
{
if (current_time - last_proc_time_ >= timeout_time_)
{
ACE_DEBUG((LM_ERROR, "timeout, peer fd=%d\n", peer().get_handle()));
// remove handler 会去调用handle_close(), 与返回-1效果一样
reactor()->remove_handler(this, ACE_Event_Handler::READ_MASK);
}
return 0;
}
virtual ACE_HANDLE get_handle() const
{
return peer_.get_handle();
}
ACE_SOCK_Stream& peer()
{
return peer_;
}
private:
ACE_SOCK_Stream peer_;
ACE_Time_Value last_proc_time_;
ACE_Time_Value timeout_time_;
long timer_id_;
};
// event_handler 包含一个ACE_Reactor
class EchoAcceptor : public ACE_Event_Handler
{
public:
EchoAcceptor(ACE_Reactor* reactor = ACE_Reactor::instance(),
ACE_Time_Value conn_timeout = ACE_Time_Value(60))
: ACE_Event_Handler(reactor),
conn_timeout_(conn_timeout) {}
virtual ~EchoAcceptor()
{
}
virtual int open(const ACE_INET_Addr& local)
{
if (0 != acceptor_.open(local, 1))
{
ACE_ERROR_RETURN((LM_ERROR, "acceptor open fail"), -1);
}
// 注册自己为反应器的accpet处理事件
reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK);
ACE_DEBUG((LM_ERROR, "server[%s:%d] listening...\n", local.get_host_addr(), local.get_port_number()));
return 0;
}
// 相应accept事件,作为一个工厂,生成EchoEventHandler对象
virtual int handle_input(ACE_HANDLE handle = ACE_INVALID_HANDLE)
{
EchoEventHandler* echo_handle = 0;
ACE_NEW_RETURN(echo_handle, EchoEventHandler(reactor(), conn_timeout_), -1);
if (-1 == acceptor_.accept(echo_handle->peer()))
{
ACE_DEBUG((LM_ERROR, "accept fail\n"));
delete echo_handle;
return -1;
}
if (0 != echo_handle->open())
{
ACE_DEBUG((LM_ERROR, "echo handler open fail\n"));
echo_handle->close();
return -1;
}
return 0;
}
virtual int handle_close(ACE_HANDLE handle = ACE_INVALID_HANDLE,
ACE_Reactor_Mask close_mask = 0)
{
acceptor_.close();
ACE_DEBUG((LM_ERROR, "accptor handle close, end reactor\n"));
reactor()->end_reactor_event_loop();
delete this;
return 0;
}
virtual ACE_HANDLE get_handle() const
{
return acceptor_.get_handle();
}
// ACE_SOCK_Stream工厂
ACE_SOCK_Acceptor acceptor_;
private:
ACE_Time_Value conn_timeout_;
};
int ACE_TMAIN(int argc, ACE_TCHAR* argv[])
{
EchoAcceptor* echosvr;
ACE_Reactor reactor;
ACE_NEW_RETURN(echosvr, EchoAcceptor(&reactor), -1);
ACE_INET_Addr svraddr(LISTEN_PORT);
echosvr->open(svraddr);
if (0 != reactor.run_reactor_event_loop())
{
ACE_DEBUG((LM_ERROR, "run reactor event loop fail.\n"));
return -1;
}
ACE_DEBUG((LM_ERROR, "event loop end.\n"));
return 0;
}
对比书上给的log的例子,有几点说明一下:
1. 在ACE_Event_Handler的实现中,如果handle_*函数返回了-1,或者remove_handler时,会主动去调用handle_close函数。(ACE_Event_Handler屏蔽了内部的实现,刚开始接触有点不太容易理解)
2. 书上的例子,在handle_close函数中,做了delete this来析构自己,让我觉得很费解。(按照书上的说法,静态分配的池子在close时由分配者自己去处理;如果是动态分配的话,在handle_close函数中处理比较优雅)。
后来发现:如果不再这里析构的话,如果客户端断开连接时,我没有更好的地方做析构,除了此处。。如果是写成预分配连接池的话,也应该是在handle_close里面实现自己的资源处理;
在windows下调试时碰到一处崩溃的bug:
是某次调试过程中,remove_handler之后再做reactor的析构时,程序会崩溃;
查看堆栈,发现在reactor.run_reactor_event_loop中会调用已经析构的EchoEventHandler导致的,对此表示很奇怪,明明已经remove handler并且析构了,为什么reactor中还会有这个指针的(当然是无效指针)。
后来仔细看了一下书,发现了这两句话:
……
3. Ensure that the lifetime of a registered event handler is longer than the reactor it's registered with if it can't be unregistered for some reason.
4. Avoid the use of the ACE_WFMO_Reactor since it defers the removal of event handlers, thereby making it hard toenforce convention 3。
在写测试程序的过程中,参考了《C++网络编程卷2》,以及ACE牛人sailzeng的一篇文章:
http://blog.csdn.net/fullsail/article/details/2915685