问题
ACE_NonBlocking_Connect_Handler在处理异步时存在问题
分析
当connect选择的同步参数为ACE_Synch_Options::USE_REACTOR
时,连接超时时间为ACE_Time_Value::zero
,在同步发起连接返回的错误码为EWOULDBLOCK
时,会发起异步连接nonblocking_connect
,事件类型为CONNECT_MASK
,事件处理器为ACE_NonBlocking_Connect_Handler
。 CONNECT_MASK
在window平台上select reactor会注册可读,可写,异常,在linux平台下dev poll reactor中注册可读可写
select reactor和dev poll reactor在处理io事件时,处理优先顺序为可写,异常,可读
template <typename SVC_HANDLER, typename PEER_CONNECTOR> int
ACE_Connector<SVC_HANDLER, PEER_CONNECTOR>::connect_i
(SVC_HANDLER *&sh,
SVC_HANDLER **sh_copy,
const typename PEER_CONNECTOR::PEER_ADDR &remote_addr,
const ACE_Synch_Options &synch_options,
const typename PEER_CONNECTOR::PEER_ADDR &local_addr,
int reuse_addr,
int flags,
int perms)
{
ACE_TRACE ("ACE_Connector<SVC_HANDLER, PEER_CONNECTOR>::connect_i");
// If the user hasn't supplied us with a <SVC_HANDLER> we'll use the
// factory method to create one. Otherwise, things will remain as
// they are...
if (this->make_svc_handler (sh) == -1)
return -1;
ACE_Time_Value *timeout = 0;
int const use_reactor = synch_options[ACE_Synch_Options::USE_REACTOR];
if (use_reactor)
timeout = const_cast<ACE_Time_Value *> (&ACE_Time_Value::zero);
else
timeout = const_cast<ACE_Time_Value *> (synch_options.time_value ());
int result;
if (sh_copy == 0)
result = this->connect_svc_handler (sh,
remote_addr,
timeout,
local_addr,
reuse_addr,
flags,
perms);
else
result = this->connect_svc_handler (sh,
*sh_copy,
remote_addr,
timeout,
local_addr,
reuse_addr,
flags,
perms);
// Activate immediately if we are connected.
if (result != -1)
return this->activate_svc_handler (sh);
// Delegate to connection strategy.
if (use_reactor && ACE_OS::last_error () == EWOULDBLOCK)
{
// If the connection hasn't completed and we are using
// non-blocking semantics then register
// ACE_NonBlocking_Connect_Handler with the ACE_Reactor so that
// it will call us back when the connection is complete or we
// timeout, whichever comes first...
if (sh_copy == 0)
result = this->nonblocking_connect (sh, synch_options);
else
result = this->nonblocking_connect (*sh_copy, synch_options);
// If for some reason the <nonblocking_connect> call failed, then <errno>
// will be set to the new error. If the call succeeds, however,
// we need to make sure that <errno> remains set to
// <EWOULDBLOCK>.
if (result == 0)
errno = EWOULDBLOCK;
}
else
{
// Save/restore errno.
ACE_Errno_Guard error (errno);
// Make sure to close down the service handler to avoid handle
// leaks.
if (sh_copy == 0)
{
if (sh)
sh->close (CLOSE_DURING_NEW_CONNECTION);
}
else if (*sh_copy)
(*sh_copy)->close (CLOSE_DURING_NEW_CONNECTION);
}
return -1;
}
template <typename SVC_HANDLER, typename PEER_CONNECTOR> int
ACE_Connector<SVC_HANDLER, PEER_CONNECTOR>::nonblocking_connect
(SVC_HANDLER *sh,
const ACE_Synch_Options &synch_options)
{
ACE_TRACE ("ACE_Connector<SVC_HANDLER, PEER_CONNECTOR>::nonblocking_connect");
// Must have a valid Reactor for non-blocking connects to work.
if (this->reactor () == 0)
return -1;
// Register the pending SVC_HANDLER so that it can be activated
// later on when the connection completes.
ACE_HANDLE handle = sh->get_handle ();
long timer_id = -1;
ACE_Time_Value *tv = 0;
NBCH *nbch = 0;
ACE_NEW_RETURN (nbch,
NBCH (*this,
sh,
-1),
-1);
ACE_Event_Handler_var safe_nbch (nbch);
// Exclusive access to the Reactor.
ACE_GUARD_RETURN (ACE_Lock, ace_mon, this->reactor ()->lock (), -1);
// Register handle with the reactor for connection events.
ACE_Reactor_Mask mask = ACE_Event_Handler::CONNECT_MASK;
if (this->reactor ()->register_handler (handle,
nbch,
mask) == -1)
goto reactor_registration_failure;
// Add handle to non-blocking handle set.
this->non_blocking_handles ().insert (handle);
// If we're starting connection under timer control then we need to
// schedule a timeout with the ACE_Reactor.
tv = const_cast<ACE_Time_Value *> (synch_options.time_value ());
if (tv != 0)
{
timer_id =
this->reactor ()->schedule_timer (nbch,
synch_options.arg (),
*tv);
if (timer_id == -1)
goto timer_registration_failure;
// Remember timer id.
nbch->timer_id (timer_id);
}
return 0;
// Undo previous actions using the ol' "goto label and fallthru"
// trick...
timer_registration_failure:
// Remove from Reactor.
this->reactor ()->remove_handler (handle, mask);
// Remove handle from the set of non-blocking handles.
this->non_blocking_handles ().remove (handle);
/* FALLTHRU */
reactor_registration_failure:
// Close the svc_handler
sh->close (CLOSE_DURING_NEW_CONNECTION);
return -1;
}
但是在ACE_NonBlocking_Connect_Handler
代码注释中为,handle_input
是处理连接失败, handle_output
是处理连接成功,但是在可写时需要获取套接字的错误码,如果错误码为0,则表示真正的连接成功
/// Called by ACE_Reactor when asynchronous connections fail.
virtual int handle_input (ACE_HANDLE);
/// Called by ACE_Dev_Poll_Reactor when asynchronous connections fail.
virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask);
/// Called by ACE_Reactor when asynchronous connections succeed.
virtual int handle_output (ACE_HANDLE);
/// Called by ACE_Reactor when asynchronous connections suceeds (on
/// some platforms only).
virtual int handle_exception (ACE_HANDLE fd);