этой структуры игнорируется. Вместо взведения события при завершении операции будет вызвана процедура, указанная в качестве параметра функции
WSARecv
. Указатель на структуру, заданный при вызове
WSARecv
, передается в процедуру завершения через параметр
lpOverlapped
. Смысл остальных параметров очевиден:
dwError
— это код ошибки (или ноль, если операция завершена успешно),
cbTransferred
— число полученных байтов (само полученное сообщение копируется в буферы, указанные при вызове функции
WSARecv
), a
dwFlags
— флаги.
Процедура завершения всегда выполняется в той нити, которая инициировала начало операции перекрытого ввода-вывода. Но система не может прерывать нить для выполнения процедуры завершения в любой удобный ей момент — нить должна перейти в состояние ожидания. В это состояние ее можно перевести, например, с помощью функции
SleepEx
, имеющей следующий прототип:
function SleepEx(dwMilliseconds: DWORD; bAlertable: BOOL); DWORD;
Функция
SleepEx
является частью стандартного API системы и импортируется модулем Windows. Она переводит нить в состояние ожидания. Параметр
dwMilliseconds
задает время ожидания в миллисекундах (или значение
INFINITE
для бесконечного ожидания). Параметр bAlertable указывает, допустимо ли прерывание состояния ожидания для выполнения процедуры завершения. Если
bAlertable
равен
False
, функция
SleepEx
ведет себя так же как функция
Sleep
, т.е. просто приостанавливает работу нити на заданное время. Если
bAlertable
равен
True
, нить может быть выведена системой из состояния ожидания раньше, чем истечет заданное время, если возникнет необходимость выполнить процедуру завершения. О причине завершения ожидания программа может судить по результату, возвращаемому функцией
SleepEx
: ноль в случае завершения по тайм-ауту и
WAIT_IO_COMPLETION
в случае завершения из-за выполнения процедуры завершения (в последнем случае сначала выполняется процедура завершения, а потом только происходит возврат из функции
SleepEx
). Если завершились несколько операций перекрытого ввода-вывода, в результате выполнения
SleepEx
будут вызваны процедуры завершения для всех этих операций.
Существует также возможность ожидать выполнения процедуры завершения одновременно с ожиданием взведения событий с помощью функции
WSAWaitForMultipleEvents
. Напомним, что у этой функции также есть параметр
fAlertable
. Если задать его равным
True
, то при необходимости выполнения процедуры завершения функция
WSAWaitForMultipleEvents
, подобно функции
SleepEx
, выполняет эту процедуру и возвращает
WAIT_IO_COMPLETION
.
Если программа выполняет одновременно несколько операций перекрытого ввода-вывода, возникает вопрос, как при вызове процедуры завершения определить, какая из них завершилась. Для каждой такой операции должен быть создан уникальный экземпляр записи
TWSAOverlapped
. Процедура завершения получает указатель на тот экземпляр, который использовался для начала завершившейся операции. Можно сравнил, указатель с теми, которые были заданы при запуске операций перекрытого ввода-вывода, и определить, какая из них завершилась. Это не всегда бывает удобно из-за необходимости где-то хранить список
указателей, заданных при начале операций перекрытого ввода-вывода. Существуют еще два варианта решения этой проблемы. Первый заключается в создании своей процедуры завершения для каждой из выполняющихся параллельно операций. Этот способ приводит к получению громоздкого кода и может быть неудобен, если число одновременно выполняющихся операций заранее неизвестно. Он целесообразен только при одновременном выполнении разнородных операций, требующих разных алгоритмов при обработке их завершения. Другой вариант предлагается в MSDN. Так как при работе через процедуры завершения значение поля
hEvent
структуры
TWSAOverlapped
игнорируется системой, программа может записать туда любое 32-битное значение и с его помощью определить, какая из операций завершена. В строго типизированном языке, каким является Delphi, подобное смещение типа дескриптора и целого выглядит весьма непривлекательно, но, к сожалению, это лучшее из того, что нам предлагают разработчики WinSock API.
Механизм процедур завершения допускает определение статуса операции с с помощью функции
WSAGetOverlappedResult
, но ее параметр
fWait
обязательно должен быть равен
False
, потому что события, необходимые для выполнения ожидания, не взводятся, и попытка дождаться окончания операции может привести к блокировке работы нити.
В процедуре завершения допускается вызывать функции, начинающие новую операцию перекрытого ввода-вывода, в том числе и такую же операцию, которая только что завершена. Эта возможность используется в примере, приведенном в листинге 2.73. Пример иллюстрирует работу клиента, который подключается к серверу и получает от него данные в режиме перекрытого ввода-вывода, выполняя параллельно какие-то другие действия.
Листинг 2.73. Перекрытый ввод-вывод с использованием процедуры завершения