七猫的藏经阁

其实只是垃圾箱

VC知识库BLOG 首页 新随笔 联系 聚合 登录
  194 Posts :: 0 Stories :: 622 Comments :: 4 Trackbacks

公告

其实我们每个人都是井底之蛙,最多在不同的井而已。

留言簿(3)

随笔分类

随笔档案

文章分类

文章档案

相册

收藏夹

好友

搜索

最新评论

阅读排行榜

评论排行榜

1、WIN32下面用proactor可以达到几乎RAW IOCP的效率,由于封装关系,应该是差那么一点。

客户端处理类的常规写法:
//处理客户端连接消息
class ClientHandler : public ACE_Service_Handler
{
public:
 /**构造函数
  *
  *
  */
 ClientHandler(unsigned int client_recv_buf_size=SERVER_CLIENT_RECEIVE_BUF_SIZE)
  :_read_msg_block(client_recv_buf_size),_io_count(0)
 {
 }

 
 ~ClientHandler(){}

 /**
  *初始化,因为可能要用到ClientHandler内存池,而这个池又不一定要用NEW
  */
 void init();

 /**清理函数,因为可能要用到内存池
  *
  */
 void fini();

//检查是否超时的函数

 void check_time_out(time_t cur_time);

public:

 /**客户端连接服务器成功后调用
  *
  * \param handle 套接字句柄
  * \param &message_block 第一次读到的数据(未用)
  */

//由Acceptor来调用!!!
 virtual void open (ACE_HANDLE handle,ACE_Message_Block &message_block);

 /**处理网络读操作结束消息
  *
  * \param &result 读操作结果
  */
 virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result);


 /**处理网络写操作结束消息
  *
  * \param &result 写操作结果
  */
 virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result);

private:

//**生成一个网络读请求
  *
  * \param void
  * \return 0-成功,-1失败
  */
 int  initiate_read_stream  (void);

 /**生成一个写请求
  *
  * \param mb 待发送的数据
  * \param nBytes 待发送数据大小
  * \return 0-成功,-1失败
  */
 int  initiate_write_stream (ACE_Message_Block & mb, size_t nBytes );
 
 /**
  *
  * \return 检查是否可以删除,用的是一个引用计数。每一个外出IO的时候+1,每一个IO成功后-1
  */
 int check_destroy();
 
 //异步读
 ACE_Asynch_Read_Stream _rs;

 //异步写
 ACE_Asynch_Write_Stream _ws;

 //接收缓冲区只要一个就够了,因为压根就没想过要多读,我直到现在也不是很清楚为什么要多读,多读的话要考虑很多问题
 ACE_Message_Block _read_msg_block;

 //套接字句柄,这个可以不要了,因为基类就有个HANDLER在里面的。
 //ACE_HANDLE _handle;

 //一个锁,客户端反正有东东要锁的,注意,要用ACE_Recursive_Thread_Mutex而不是用ACE_Thread_Mutex,这里面是可以重入的,而且在WIN32下是直接的EnterCriticalSection,可以达到很高的效率
 ACE_Recursive_Thread_Mutex _lock;
 
 //在外IO数量,其实就是引用计数啦,没啥的。为0的时候就把这个东东关掉啦。
 long _io_count;

//检查超时用的,过段时间没东东就CLOSE他了。

 time_t _last_net_io;

private:

//本来想用另外一种模型的,只用1个或者2个外出读,后来想想,反正一般内存都是足够的,就不管了。

 //ACE_Message_Block _send_msg_blocks[2];

 //ACE_Message_Block &_sending_msg_block;

 //ACE_Message_Block &_idle_msg_block;

private:
 
public:
//TODO:move to prriva and use friend class!!!

//只是为了效率更高,不用STL的LIST是因为到现在我没有可用的Node_Allocator,所以效率上会有问题。
 ClientHandler *_next;

 ClientHandler *next(){return _next;}

 void next(ClientHandler *obj){_next=obj;}

};


//这是具体实现,有些地方比较乱,懒得管了,锁的有些地方不对。懒得改了,反正在出错或者有瓶颈的时候再做也不迟。

void ClientHandler::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result)
{
 _last_net_io=ACE_OS::time(NULL);
 int byterecved=result.bytes_transferred ();
 if ( (result.success ()) && (byterecved != 0))
 {
  //ACE_DEBUG ((LM_DEBUG,  "Receiver completed:%d\n",byterecved));

//处理完数据
  if(handle_received_data()==true)
  {
   //ACE_DEBUG ((LM_DEBUG,  "go on reading...\n"));

//把东东推到头部,处理粘包
   _read_msg_block.crunch();
   initiate_read_stream();
  }
 }

//这个地方不想用ACE_Atom_op,因为反正要有一个锁,而且一般都会用锁,不管了。假如不在意的话,应该直接用ACE_Atom_Op以达到最好的效率

 {
  ACE_Guard<ACE_Recursive_Thread_Mutex> locker (_lock);
  _io_count--;
 }
 check_destroy ();
}

void ClientHandler::init()
{

//初始化数据,并不在构造函数里做。
 _last_net_io=ACE_OS::time(NULL);
 _read_msg_block.rd_ptr(_read_msg_block.base());
 _read_msg_block.wr_ptr(_read_msg_block.base());
 this->handle(ACE_INVALID_HANDLE);
}

bool ClientHandler::handle_received_data()
{

...........自己处理
 return true;
}


//==================================================================
void ClientHandler::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result)
{
 //发送成功,RELEASE掉
 //这个不可能有多个RELEASE,直接XX掉
 //result.message_block ().release ();
 MsgBlockManager::get_instance().release_msg_block(&result.message_block());

 {
  ACE_Guard<ACE_Recursive_Thread_Mutex> locker (_lock);
  _io_count--;
 }
 check_destroy ();
}

//bool ClientHandler::destroy ()
//{
// FUNC_ENTER;
// ClientManager::get_instance().release_client_handle(this);
// FUNC_LEAVE;
// return false ;
//}


int  ClientHandler::initiate_read_stream  (void)
{
 ACE_Guard<ACE_Recursive_Thread_Mutex> locker (_lock);

//考虑到粘包的呀
 if (_rs.read (_read_msg_block, _read_msg_block.space()) == -1)
 {
  ACE_ERROR_RETURN ((LM_ERROR,"%p\n","ACE_Asynch_Read_Stream::read"),-1);
 }
 _io_count++;
 return 0;
}

/**生成一个写请求
*
* \param mb 待发送的数据
* \param nBytes 待发送数据大小
* \return 0-成功,-1失败
*/
int  ClientHandler::initiate_write_stream (ACE_Message_Block & mb, size_t nBytes )
{
 ACE_Guard<ACE_Recursive_Thread_Mutex> locker (_lock);
 if (_ws.write (mb , nBytes ) == -1)
 {
  mb.release ();
  ACE_ERROR_RETURN((LM_ERROR,"%p\n","ACE_Asynch_Write_File::write"),-1);
 }
 _io_count++;
 return 0;
}

void ClientHandler::open (ACE_HANDLE handle,ACE_Message_Block &message_block)
{
 //FUNC_ENTER;
 _last_net_io=ACE_OS::time(NULL);
 _io_count=0;
 if(_ws.open(*this,this->handle())==-1)
 {
  ACE_ERROR ((LM_ERROR,"%p\n","ACE_Asynch_Write_Stream::open"));
 }
 else if (_rs.open (*this, this->handle()) == -1)
 {
  ACE_ERROR ((LM_ERROR,"%p\n","ACE_Asynch_Read_Stream::open"));
 }
 else
 {
  initiate_read_stream ();
 }

 check_destroy();
 //FUNC_LEAVE;
}

void ClientHandler::fini()
{
}

void ClientHandler::check_time_out(time_t cur_time)
{
 //ACE_Guard<ACE_Recursive_Thread_Mutex> locker (_lock);
 //ACE_DEBUG((LM_DEBUG,"cur_time is %u,last io is %u\n",cur_time,_last_net_io));

 //检测是否已经为0了
 if(this->handle()==ACE_INVALID_HANDLE)
  return;
 if(cur_time-_last_net_io>CLIENT_TIME_OUT_SECONDS)
 {
  ACE_OS::shutdown(this->handle(),SD_BOTH);
  ACE_OS::closesocket(this->handle());
  this->handle(ACE_INVALID_HANDLE);
 }
}

int ClientHandler::check_destroy()
{
 {
  ACE_Guard<ACE_Recursive_Thread_Mutex> locker (_lock);
  if (_io_count> 0)
   return 1;
 }
 ACE_OS::shutdown(this->handle(),SD_BOTH);
 ACE_OS::closesocket(this->handle());
 this->handle(ACE_INVALID_HANDLE);

//这个地方给内存池吧。
 ClientManager::get_instance().release_client_handle(this);
 //delete this;
 return 0;
}

posted on 2005-08-14 22:30 Diviner 阅读(7822) 评论(31)  编辑 收藏

Feedback

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-08-15 11:56 cpunion
check_destroy里不用锁啊,锁是要保证中间的一段代码以原子方式执行,这地方不必吧,应该考虑给_io_count加上volatile关键字

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-08-15 12:10 SevenCat
有一个地方好像会造成两个线程同时存取这个_io_count,所以还是锁了一下,稍微注意一下,这个锁确实可以去掉,只不过人懒了...


volatile可以不用的。

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-09-22 14:08 总是好心情
int ClientHandler::check_destroy()
{
 {
  ACE_Guard<ACE_Recursive_Thread_Mutex> locker (_lock);
  if (_io_count> 0)
   return 1;
 }

按你后面的分配管理
一个ClientHandle对应的是一个Client吧.
如果这个Client断开了,把这个ClientHandle就行了.为什么要计数....如果计数的是全部的Client就应该用静态,为0时,结束管理的实例.

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-09-22 14:09 总是好心情
把这个ClientHandle放回内存池就行了

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-09-22 14:09 总是好心情
把这个ClientHandle放回内存池就行了

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-09-22 14:35 总是好心情
不好意思...
是计录的IO的当前操作个数


# re: ACE高效PROACTOR编程框架一ClientHandle 2005-09-22 16:09 总是好心情
多读,有时会做会话处理...但做这样的会话,可以用一个会话队列来管理

# TCP的多读非常麻烦。多读包还要注意序号。 2005-09-22 16:50 Diviner
rt

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-10-04 02:30 cpunion
使用Proactor框架,不知道能不能在服务器端定时PING客户端,我试了一下没有成功,因为已经请求了一个READ,没有取消的情况下好像是无法发出一个WRITE请求的。

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-10-04 12:30 Diviner
可以,read,write请求都是独立的。

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-12-08 19:28 jainghy
你好,我现在工作中正在用ace有关的东西,但是遇到很多的难题,由于对ace很不了解。冒昧的向您求助,在网站上看到你提供的部分代码,真诚的希望您能够帮助一下。如果可以能够借我看看你的客户端和服务器端的源码吗?

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-12-08 19:28 jainghy
你好,我现在工作中正在用ace有关的东西,但是遇到很多的难题,由于对ace很不了解。冒昧的向您求助,在网站上看到你提供的部分代码,真诚的希望您能够帮助一下。如果可以能够借我看看你的客户端和服务器端的源码吗?

# 十万火急 2005-12-08 19:28 jainghy
你好,我现在工作中正在用ace有关的东西,但是遇到很多的难题,由于对ace很不了解。冒昧的向您求助,在网站上看到你提供的部分代码,真诚的希望您能够帮助一下。如果可以能够借我看看你的客户端和服务器端的源码吗?

# re: ACE高效PROACTOR编程框架一ClientHandle 2005-12-09 08:42 Diviner
ace本身的例子就足够了。

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-13 17:38 blue
有个问题:
如果超时的话,为什么没有release_client_handle

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-14 09:27 Diviner
因为超时的时候,有可能还有外出的read或者write,只有等他们全结束了,才能关闭。

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-14 11:34 blue
有这个问题:
在Win32下,客户端已经connect time out,proactor还有pending request,如果closesocket,会出错。如何unregister  pending request呢,ACE_Asynch_Read_Stream 有cancel(),但是根本不起作用?
我查了一些资料,都说的不是很清楚,有些干脆说,不可能cancel pending request,只能等TCP超时(2个小时),如果这样的话,还设置超时有什么意义?
Diviner :
或者有其他什么好的方法吗?
比如:模拟一个读完成事件是否可行?

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-14 12:02 Diviner
closesocket不会出错的,这里的cancel最终调用的是CancelIo,但这个调用只有在发起调用的线程里才可以执行,所以根本没用(我曾经向ACE新闻组发过帖,不过没人回),所以我都是直接closesocket(),然后完成端口等请求全部结束,再把这个client关掉(这时候套接字已经被关掉了)

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-14 12:05 Diviner
关于这个2小时的超时是keepalive的机制,可以改注册表为30秒,但这没什么太大意义,还可以向这个套接字发送一个写请求来触发。

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-14 12:27 Diviner
这里面的IO计数可以直接用atomop做(在WIN是Interlocked系列的封装),不过等要优化的时候才去优化是我的原则。

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-14 16:03 blue
刚才说错了,不是closesocket报错
我是在超时处理函数中,判断超时时调用
delete this;
执行完析构函数后,程序报错。
~ClientHandler(){
    ACE_OS::shutdown(this->handle(),SD_BOTH);
    ACE_OS::closesocket(this->handle());
    this->handle(ACE_INVALID_HANDLE);

    //这个地方给内存池吧。
   ClientManager::get_instance().release_client_handle(this);
}
上面处理是有问题,因为存在pending request。
但是,有几个问题:
1、ClientHandler,是那个对象管理ClientHandler的life cycle?什么时候释放?ACE programmer's guide中,Proactor例子中都有delete this 是不是有误导嫌疑?在你的例子中,delete this 都是注释掉了。
2、你提到了完成端口等请求全部结束。如果,对方拔网线,完成端口是否还能等到请求全部结束?如果可以,那多长时间可以等到请求结束?在我的程序中,handle_read_stream,如果不是对方主动关闭,我都会发起新的读操作ACE_Asynch_Read_Stream.read(),如果客户端不回响应,服务端如何完成这个请求?
谢谢
不吝赐教




# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-15 09:52 Diviner
1、不应该在析构函数里做,
2、这是一个引用计数概念,引用计数为零的时候(就是io pending为零的时候)就可以释放。
3、书里面用的delete this是因为不需要用对象池来实现。当然比较容易些。
4、可以参看我另一篇http://blog.vckbase.com/bastet/archive/2005/08/14/10868.html
是个定时器过段时间检查用户是否在某段时间内没有输入,假如没有的话,就可以删除,这需要客户端不停的发心跳包,另一种做法是服务器向客户端发心跳包。so_keepalive缺省是2个小时(虽然改注册表可以,但感觉不好),在这个时候检测到超时后直接closesocket(在我的check_time_out函数里有),然后这个请求就会结束掉(要是CancelIo能执行就好了,可惜不行)

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-15 11:19 blue
ClientHandler对象如何释放呢?
如果不释放的话,是否会有memory leak

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-15 11:28 Diviner
用的对象池啊。不会memory leak的。不过你要是async_read_stream用的缺省的话就会有问题。

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-02-16 18:17 blue
谢谢了

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-04-07 01:45 阿彪
我在windows里写了一个proactor方式的server

但是现在需要移到linux里去使用.这个要如何做呢.

问题出在这个创建一个proactor不同了,不知道如何做.

大师有没有什么好的办法呢.

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-04-07 03:07 demax
Diviner:
请教3个问题:
1.上面示例的check_time_out怎么调用的?
2.handle_timeout函数被调用时,是否可能在IOCP队列中有另一个到期事件,如果在handle_timeout中delete this,在proactor处理下一个到期事件时产生异常(即使已经cancel_timer)
3.在多处理器机器上,如果Proactor的线程数>1,Asynchrous cancel机制无法达到预期的效果,是吗?



# re: ACE高效PROACTOR编程框架一ClientHandle 2006-04-07 08:35 sevencat
我在windows里写了一个proactor方式的server

但是现在需要移到linux里去使用.这个要如何做呢.

问题出在这个创建一个proactor不同了,不知道如何做.

大师有没有什么好的办法呢.

LINUX下proactor没有好的解决方案,用devreactor取代他(2.6或者 以上,或者打过epoll补丁)



1.上面示例的check_time_out怎么调用的?
proactor->schedule_timer
2.handle_timeout函数被调用时,是否可能在IOCP队列中有另一个到期事件,如果在handle_timeout中delete this,在proactor处理下一个到期事件时产生异常(即使已经cancel_timer)
我只是closesocket,并不会delete this,这里是一个引用计数。为零的时候才会delete this.新版本的ace也许不会出错,里面的机制加强了不少。这些都是以效率为代价的。
3.在多处理器机器上,如果Proactor的线程数>1,Asynchrous cancel机制无法达到预期的效果,是吗?
他本身的cacel是没用的,因为CanCelIo只对本线程发出的请求有效。
直接closesocket是有效的。

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-04-07 14:51 demax
1.ClientHandler里面并没有handle_timeout方法呀?

有些问题想立即跟你沟通,能否QQ?
QQ: 106331506



# re: ACE高效PROACTOR编程框架一ClientHandle 2006-04-07 14:52 demax
ACE既然是across-platform的,移植到Linux应该不会有什么代码级改动的

# re: ACE高效PROACTOR编程框架一ClientHandle 2006-04-08 08:16 sevencat
QQ:43791167


ACE既然是across-platform的,移植到Linux应该不会有什么代码级改动的 

//===================================
假如想用ace,最好对底层的东东和ACE的结构比较熟悉,不然比较麻烦。
他的linux下的proactor实现一般是用thread还是什么来实现的,效率比较低。linux下最好的是epoll的reactor封装。(devpoll)



标题  
姓名  
主页
验证码 *
内容   
  登录  使用高级评论  Top
[使用Ctrl+Enter键可以直接提交]