Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Windows IOCP模式下ConnectEx()超时?_Windows_Sockets_Winapi_Winsock2 - Fatal编程技术网

Windows IOCP模式下ConnectEx()超时?

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

在IOCP Winsock2客户端中,ConnectEx在连接尝试失败时超时后,会发生以下情况:

IO完成将排队到关联的IO完成端口

GetQueuedCompletionStatus返回FALSE

WSAGetOverlappedResult返回WSAETIMEDOUT

什么决定了调用ConnectEx和上述1之间的超时时间?如何缩短此超时时间


我知道,可以通过向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/O

iocp,这不是模式。与套接字与iocp或notIOCP的关联无关的超时值是完成对Winsock的连接请求的一种模式。无论如何,是什么决定了套接字的连接超时值,而不管它是否与IOCP关联?如果完全是-IOCP,则这不是模式,而是io操作完成时获取通知的唯一方式。我不知道/查看允许设置连接超时的套接字选项,但让我们询问-您需要什么?对于快速TCP连接扫描程序,它只检测快速连接的服务器。或者继续使用事件对象,使用RegisterWaitForSingleObject对其进行等待,如果在其过期之前已连接,则取消等待,否则,在Overlapped上调用CancelIoEx您写道:不幸的是,似乎没有设置套接字连接超时的内置选项……这是对我的第二个问题的严峻但可以接受的回答。至于介绍的解决方法,我想知道哪种方法消耗的系统资源更少:使用CreateTimerQueueTimer的方法还是使用RegisterWaitForSingleObject的方法?@GeorgeRobinson-更少的系统资源很难说,这也取决于windows版本-非常不同的实现。我用的计时器。也可能将所有套接字对象链接到列表,并定期(比如每秒1次)检查此列表是否超时。在每个对象中开始连接时间。与这两种方法相比,这需要更少的资源