Читаем О чём не пишут в книгах по Delphi полностью

Если программа выполняет одновременно несколько операций перекрытого ввода-вывода, возникает вопрос, как при вызове процедуры завершения определить, какая из них завершилась. Для каждой такой операции должен быть создан уникальный экземпляр записи TWSAOverlapped. Процедура завершения получает указатель на тот экземпляр, который использовался для начала завершившейся операции. Можно сравнил, указатель с теми, которые были заданы при запуске операций перекрытого ввода-вывода, и определить, какая из них завершилась. Это не всегда бывает удобно из-за необходимости где-то хранить список указателей, заданных при начале операций перекрытого ввода-вывода. Существуют еще два варианта решения этой проблемы. Первый заключается в создании своей процедуры завершения для каждой из выполняющихся параллельно операций. Этот способ приводит к получению громоздкого кода и может быть неудобен, если число одновременно выполняющихся операций заранее неизвестно. Он целесообразен только при одновременном выполнении разнородных операций, требующих разных алгоритмов при обработке их завершения. Другой вариант предлагается в MSDN. Так как при работе через процедуры завершения значение поля hEvent структуры TWSAOverlapped игнорируется системой, программа может записать туда любое 32-битное значение и с его помощью определить, какая из операций завершена. В строго типизированном языке, каким является Delphi, подобное смещение типа дескриптора и целого выглядит весьма непривлекательно, но, к сожалению, это лучшее из того, что нам предлагают разработчики WinSock API.

Механизм процедур завершения допускает определение статуса операции с с помощью функции WSAGetOverlappedResult, но ее параметр fWait обязательно должен быть равен False, потому что события, необходимые для выполнения ожидания, не взводятся, и попытка дождаться окончания операции может привести к блокировке работы нити.

В процедуре завершения допускается вызывать функции, начинающие новую операцию перекрытого ввода-вывода, в том числе и такую же операцию, которая только что завершена. Эта возможность используется в примере, приведенном в листинге 2.73. Пример иллюстрирует работу клиента, который подключается к серверу и получает от него данные в режиме перекрытого ввода-вывода, выполняя параллельно какие-то другие действия.

Листинг 2.73. Перекрытый ввод-вывод с использованием процедуры завершения

var

 S: TSocket;

 Overlapped: TWSAOverlapped;

 BufPtr: TWSABuf;

 RecvBuf: array[1..100] of Char;

 Cnt, Flags: Cardinal;

 Connected: Boolean;


procedure GetData(Err, Cnt:DWORD; OvPtr: PWSAOverlapped; Flags: DWORD): stdcall;

begin

 if Err <> 0 then

 begin

  // Произошла ошибка. Соединение нужно устанавливать заново

  closesocket(S);

  Connected := False;

 end;

 else

 begin

  // Получены данные, обрабатываем

  ......

  // Запускаем новую операцию перекрытого чтения

  Flags := 0;

  WSARecv(S, @BufPtr, 1, Cnt, Flags, OvPtr, GetData);

 end;

end;


procedure ProcessConnection;

begin

 // Устанавливаем начальное состояние - сокет не соединен

 Connected := False;

 // Задаем буфер

 BufPtr.Buf := @RecvBuf;

 BufPtr.Len := SizeOf(RecvBuf);

 while True do

 begin

  if not Connected then

  begin

   Connected := True;

   // Создаем и подключаем сокет

   S := socket(AF_INET, SOCK_STREAM, 0);

   connect(S, ...);

   // Запускаем первую для данного сокета операцию чтения

   Flags := 0;

   WSARecv(S, @BufPtr, 1, Cnt, Flags, @Overlapped, GetData);

  end;

  // Позволяем системе выполнить процедуру завершения,

  // если это необходимо

  SleepEx(0, True);

  // Выполняем какие-либо дополнительные действия

  ......

 end;

end;

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже