Windows IOCP模式下ConnectEx()超时?
在IOCP Winsock2客户端中,ConnectEx在连接尝试失败时超时后,会发生以下情况: IO完成将排队到关联的IO完成端口 GetQueuedCompletionStatus返回FALSE WSAGetOverlappedResult返回WSAETIMEDOUT 什么决定了调用ConnectEx和上述1之间的超时时间?如何缩短此超时时间Windows IOCP模式下ConnectEx()超时?,windows,sockets,winapi,winsock2,Windows,Sockets,Winapi,Winsock2,在IOCP Winsock2客户端中,ConnectEx在连接尝试失败时超时后,会发生以下情况: IO完成将排队到关联的IO完成端口 GetQueuedCompletionStatus返回FALSE WSAGetOverlappedResult返回WSAETIMEDOUT 什么决定了调用ConnectEx和上述1之间的超时时间?如何缩短此超时时间 我知道,可以通过向ConnectEx传递一个已填写的结构OVERLAPPED.hEvent=WSACreateEvent,然后等待此事件来等待Conn
我知道,可以通过向ConnectEx传递一个已填写的结构OVERLAPPED.hEvent=WSACreateEvent,然后等待此事件来等待ConnectEx,例如,使用WaitForSingleObjectOverlapped.hEvent,在毫秒时间段内未建立连接后,毫秒到超时。但是,该解决方案超出了这个问题的范围,因为它没有引用IOCP通知模型。不幸的是,似乎没有内置的设置套接字连接超时选项。我怎么不看这个,基于这个问题——没有人也不看 一种可能的解决方案是将事件句柄传递给I/O请求,如果我们得到错误消息,则调用此事件。如果调用成功-我们的回调函数将被调用-或者因为I/O将以任何最终状态完成,此时我们将传递给I/O请求并设置事件,或者因为超时时间已过-在这种情况下,我们需要调用函数 假设我们有一个类IO_IRP:public OVERLAPPED,它有引用计数,我们需要将指针保存到I/O请求中使用的OVERLAPPED,以便将其传递给。并且需要确保这个重叠仍然没有在另一个新的I/O中使用-所以还不是免费的。在这种情况下,可能的实施:
class WaitTimeout
{
IO_IRP* _Irp;
HANDLE _hEvent, _WaitHandle, _hObject;
static VOID CALLBACK WaitOrTimerCallback(
__in WaitTimeout* lpParameter,
__in BOOLEAN TimerOrWaitFired
)
{
UnregisterWaitEx(lpParameter->_WaitHandle, NULL);
if (TimerOrWaitFired)
{
// the lpOverlapped unique here (because we hold reference on it) - not used in any another I/O
CancelIoEx(lpParameter->_hObject, lpParameter->_Irp);
}
delete lpParameter;
}
~WaitTimeout()
{
if (_hEvent) CloseHandle(_hEvent);
_Irp->Release();
}
WaitTimeout(IO_IRP* Irp, HANDLE hObject) : _hEvent(0), _Irp(Irp), _hObject(hObject)
{
Irp->AddRef();
}
BOOL Create(PHANDLE phEvent)
{
if (HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL))
{
*phEvent = hEvent;
_hEvent = hEvent;
return TRUE;
}
return FALSE;
}
public:
static WaitTimeout* Create(PHANDLE phEvent, IO_IRP* Irp, HANDLE hObject)
{
if (WaitTimeout* p = new WaitTimeout(Irp, hObject))
{
if (p->Create(phEvent))
{
return p;
}
delete p;
}
return NULL;
}
void Destroy()
{
delete this;
}
// can not access object after this call
void SetTimeout(ULONG dwMilliseconds)
{
if (RegisterWaitForSingleObject(&_WaitHandle, _hEvent,
(WAITORTIMERCALLBACK)WaitOrTimerCallback, this,
dwMilliseconds, WT_EXECUTEONLYONCE|WT_EXECUTEINWAITTHREAD))
{
// WaitOrTimerCallback will be called
// delete self here
return ;
}
// fail register wait
// just cancel i/o and delete self
CancelIoEx(_hObject, _Irp);
delete this;
}
};
使用类似
if (IO_IRP* Irp = new IO_IRP(...))
{
WaitTimeout* p = 0;
if (dwMilliseconds)
{
if (!(p = WaitTimeout::Create(&Irp->hEvent, Irp, (HANDLE)socket)))
{
err = ERROR_NO_SYSTEM_RESOURCES;
}
}
if (err == NOERROR)
{
DWORD dwBytes;
err = ConnectEx(socket, RemoteAddress, RemoteAddressLength,
lpSendBuffer, dwSendDataLength, &dwBytes, Irp)) ?
NOERROR : WSAGetLastError();
}
if (p)
{
if (err == ERROR_IO_PENDING)
{
p->SetTimeout(dwMilliseconds);
}
else
{
p->Destroy();
}
}
Irp->CheckErrorCode(err);
}
另一种可能的解决方案是通过设置计时器,如果计时器过期,则从这里调用Cancelioex或关闭I/O句柄。与事件解决方案不同-如果I/O将在计时器过期之前完成-则不会自动调用回调函数。如果事件-I/O子系统在初始挂起状态后I/O完成时设置事件,并且由于信号状态中的事件,将调用回调。但在定时器情况下,无法将其作为参数I/O仅接受事件句柄传递给io请求。因此,我们需要自行保存指向计时器对象的指针,并在I/O完成时手动释放它。所以这里有两个指向计时器对象的指针——一个来自由保存的池,另一个来自我们的对象套接字类,当I/O完成时,我们需要它来解引用对象。这需要对包含计时器的对象进行引用计数。从另一方面来说,我们可以使用定时器,而不是单个I/O操作,而是多个I/O操作,因为它不会直接绑定到某些I/Oiocp,这不是模式。与套接字与iocp或notIOCP的关联无关的超时值是完成对Winsock的连接请求的一种模式。无论如何,是什么决定了套接字的连接超时值,而不管它是否与IOCP关联?如果完全是-IOCP,则这不是模式,而是io操作完成时获取通知的唯一方式。我不知道/查看允许设置连接超时的套接字选项,但让我们询问-您需要什么?对于快速TCP连接扫描程序,它只检测快速连接的服务器。或者继续使用事件对象,使用RegisterWaitForSingleObject对其进行等待,如果在其过期之前已连接,则取消等待,否则,在Overlapped上调用CancelIoEx您写道:不幸的是,似乎没有设置套接字连接超时的内置选项……这是对我的第二个问题的严峻但可以接受的回答。至于介绍的解决方法,我想知道哪种方法消耗的系统资源更少:使用CreateTimerQueueTimer的方法还是使用RegisterWaitForSingleObject的方法?@GeorgeRobinson-更少的系统资源很难说,这也取决于windows版本-非常不同的实现。我用的计时器。也可能将所有套接字对象链接到列表,并定期(比如每秒1次)检查此列表是否超时。在每个对象中开始连接时间。与这两种方法相比,这需要更少的资源