, к сожалению, не похоже на встроенную опцию для установки времени ожидания подключения к сокету.как минимум, я не вижу это и на основе этого вопроса - Как настроить время ожидания подключения сокета - никто не просматривает тоже.
один из возможных решений передать дескриптор события в запрос ввода-вывода, и если мыполучил ERROR_IO_PENDING
- позвоните RegisterWaitForSingleObject
для этого события.если этот вызов будет успешным - будет вызвана наша функция обратного вызова WaitOrTimerCallback
- или потому что ввод / вывод будет завершен (с любым окончательным статусом) и в этот момент событие (которое мы передаем как I /Запрос O и RegisterWaitForSingleObject
) будут установлены или из-за истечения времени ожидания ( dwMilliseconds ) - в этом случае нам нужно вызвать функцию CancelIoEx
.
так скажем, у нас есть class IO_IRP : public OVERLAPPED
с подсчетом ссылок (нам нужно сохранить указатель на OVERLAPPED
, используемый в запросе ввода / вывода для передачи его в CancelIoEx
. И нужноуверен, что это OVERLAPPED
все еще не используется в другом новом вводе / выводе - так что еще не бесплатно).в этом случае возможна реализация:
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);
}
, другое возможное решение установить таймер через CreateTimerQueueTimer
, а если таймер истек - вызовите CancellIoEx
или закройте ручку ввода / вывода отсюда.разница с решением события - если ввод / вывод будет завершен до истечения таймера - функция обратного вызова WaitOrTimerCallback
не будет вызываться автоматически.в случае события - устанавливается событие подсистемы ввода / вывода, когда завершается ввод / вывод (после первоначального состояния ожидания) и благодаря этому (событие в состоянии сигнала) будет вызываться обратный вызов.но в случае таймера - ни в коем случае не передавать его в запрос io как параметр (I / O принимает только дескриптор события).В результате нам нужно сохранить указатель на объект таймера самостоятельно и вручную освободить его, когда завершится ввод-вывод.так что здесь будет 2 указателя на объект таймера - один из пула (сохраненный CreateTimerQueueTimer
) и один из нашего класса объекта (сокета) (он нам нужен для объекта разыменования после завершения ввода / вывода).это требует подсчета ссылок на объект, который тоже инкапсулирует таймер.с другой стороны, мы можем использовать таймер не для одной операции ввода-вывода, а для нескольких операций ввода-вывода (поскольку это не прямое связывание с некоторыми операциями ввода-вывода)