|
本帖最后由 tempus 于 2012-8-7 09:16 编辑
我目前是在linux下用ACE开发一个关于TCP的通道模块,运用了ACE_Reactor的Accetpor-Connector框架,其中实现了一个服务处理器,ChannelSvcHandler:public ACE_SVC_Handler<>,并在其open函数activate,激活了一个主动线程,用于从队列中取数据并发送给对端,若取出的是MB_STOP类型的消息则退出。
对于连接的反应式关闭我是这样设计的,当网络异常或对端关闭时,handle_input肯定会接收到反应器通知,在handle_input中,当recv返回值为0时,我向队列中插入类型为MB_STOP的消息,然后wait等待SVC线程退出,之后直接返回0。因为我想主动线程退出时,会调用handle_close来进行清理工作。
现在出现的问题是,我在调用wait时,一直不能返回,导致我的handle_input不能正常结束,阻塞在那里,我确定我的svc线程肯定是返回了,因为svc返回前的打印输出成功了。后来我又试了将activate的参数加入THR_DETACHED,然后不调用wait直接返回,但问题又出现了,在对端关闭时,handle_input函数被调用了多次,最少3次,最多7,8次,这我实在搞不懂。
现在我的问题是,首先,对于我这种带主动线程的服务处理器,反应式关闭时,利用退出线程来关闭连接不知道合不合适,因为我觉得线程肯定是要退出的,既然线程退出会调用handle_close,何不就这样退出了,就不需要在handle_input里面返回-1了吧?如果合适,那上面出现的两个问题是什么原因呢,会不会跟linux里面反应器的默认实现是select_reactor有关?如果不合适,那我到底该怎样处理反应式关闭呢??
这个问题比较急,希望有高人指点,感激不尽!!!
代码倒没啥隐私的,现将我到代码贴下,我也是个初学者,可能有写到不合适的地方,望大家指点。。。谢谢
class ChannelSvcHandler : public ACE_Svc_Handler<ACE_SOCK_STREAM,ACE_MT_SYNCH> {
typedef ACE_Svc_Handler<ACE_SOCK_STREAM,ACE_MT_SYNCH> PARENT;
public:
ChannelSvcHandler();
virtual ~ChannelSvcHandler();
public:
//打开服务处理器
virtual int open (void *p);
//接收处理函数
virtual int handle_input (ACE_HANDLE fd);
//数据发送线程
virtual int svc();
//退出线程并关闭连接
int QuitSvcAndClose(bool bupdate = true);
private:
//通道连接器指针
ChannelConnector * pConnector_;
};
int ChannelSvcHandler::open(void *p) {
//调用父类函数,完成向反应器注册
PARENT::open();
//保存通道连接器指针
if (0 != p) {
pConnector_ = reinterpret_cast<ChannelConnector *>(p);
}
//激活数据发送线程
this->activate();
return 0;
}
int ChannelSvcHandler::handle_input(ACE_HANDLE fd) {
//创建接收缓存
ACE_Message_Block *mbk = 0;
ACE_NEW_RETURN(mbk,ACE_Message_Block(DEFAULT_BLOCK_SIZE),0);
//接收数据
ssize_t count = this->peer().recv(mbk->wr_ptr(),mbk->size());
if (count > 0) {
//接收成功,移动写指针
mbk->wr_ptr(count);
//返回接收数据
if (!pConnector_->PutReceiveMessage(mbk)) {
mbk->release();
ACE_ERROR((LM_ERROR,ACE_TEXT("接收数据返回失败!\n")));
}
} else {
//接收失败,表示对端关闭或网络异常
mbk->release();
ACE_DEBUG((LM_DEBUG,ACE_TEXT("连接异常,将断开与对端到连接!\n")));
//退出线程并关闭连接
QuitSvcAndClose();
}
return 0;
}
int ChannelSvcHandler::svc() {
ACE_Message_Block *mbk;
while (1) {
//从队列获取数据
if (-1 != this->getq(mbk)) {
//判断是否为退出消息
if (mbk->size() == 0 && mbk->msg_type() == ACE_Message_Block::MB_STOP) {
//是则跳出循环,退出线程,关闭连接
mbk->release();
msg_queue()->close();
ACE_DEBUG((LM_DEBUG,ACE_TEXT("收到退出消息,数据发送线程退出,并断开连接!\n")));
break;
} else {
//否则为需发送到数据
while (mbk->length() > 0) {
//发送数据到对端
ssize_t count = this->peer().send(mbk->rd_ptr(),mbk->length());
if (count > 0) {
//发送成功,移动读指针
mbk->rd_ptr(count);
} else {
//发送失败,表示网络异常,直接跳出,等待接收处理函数处理
break;
}
}
//发送完毕后释放资源
mbk->release();
}
}
}
return 0;
}
int ChannelSvcHandler::QuitSvcAndClose(bool bupdate) {
//判断是否需要更新通道连接器中到服务处理器指针
if (bupdate) {
pConnector_->UpdateSvcHandler();
}
//构造退出消息
ACE_Message_Block *quit_msg = 0;
ACE_NEW_RETURN(quit_msg,ACE_Message_Block(0,ACE_Message_Block::MB_STOP),0);
//放入队列头
if (-1 == this->ungetq(quit_msg)) {
ACE_ERROR_RETURN((LM_ERROR,ACE_TEXT("退出消息压入队列失败!\n")),0);
}
//等待数据发送线程退出
this->wait();
return 1;
}
我再将问题具体说说,如果因为某种原因或主动关闭程序,从程序主线程中调用QuitSvcAndClose,该服务处理器可正常关闭;如果所对端关闭或网络异常等原因,导致handle_input中接收到的数据为0,这时我也会调用QuitSvcAndClose来先关闭线程,进而关闭服务处理器,但这时候QuitSvcAndClose中到this->wait就一直不返回阻塞在那,svc线程中的ACE_DEBUG((LM_DEBUG,ACE_TEXT("收到退出消息,数据发送线程退出,并断开连接!\n")))这条日志已经打印,表示线程已退出,为什么会不返回呢,不知道我在handle_input中调用这样到这样到退出线程到函数再等待线程退出是否合适????
后来我想直接把open函数中的activate参数设成THR_DETACHED,然后QuitSvcAndClose中不掉用wait直接返回,程序没有阻塞正常运行了,但通过打印到信息可以看出,hande_input函数貌似被调用了N次,这是我最搞不懂到地方!!!至于开启到线程到底是该设成THR_DETACHED还算THR_JOINABLE,两者到区别除了一个所系统自动回收资源,另一个是需要wait才能回收外,还有啥区别,我们该如何选择呢??? |
|