ACE中的reactor模式学习

这两天在学习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。

终于清楚了,windows下默认的reactor实现是ACE_WFMO_Reactor, 实际上析构EchoEventHandler调用remove_handler时,内部实现延迟了真正的remove动作,但是自己已经被析构了,导致后面reactor真正做remove动作时内存不合法直接崩溃了;
后来换成 ACE_Select_Reactor试了一下,发现就ok了

在写测试程序的过程中,参考了《C++网络编程卷2》,以及ACE牛人sailzeng的一篇文章:

http://blog.csdn.net/fullsail/article/details/2915685


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值