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

Массив iErrorCode содержит информацию об ошибках, которыми сопровождались события FD_XXX. Для каждого события FD_XXX определена соответствующая константа FD_XXX_BIT (т.е. константы FD_READ_BIT, FD_WRITE_BIT и т.д.). Элемент массива с индексом FD_XXX_BIT содержит информацию об ошибке, связанной с событием FD_XXX. Если операция прошла успешно, этот элемент содержит ноль, в противном случае — код ошибки, которую в аналогичной ситуации вернула бы функция WSAGetLastError после выполнения соответствующей операции на синхронном сокете.

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

Сокетные события могут взводиться не только в результате событий на сокете, но и вручную, с помощью функции WSASetEvent. Это дает нити, вызвавшей функцию WSAWaitForMultipleEvents, возможность выходить из состояния ожидания не только при возникновении событий на сокете, но и по сигналам от других нитей. Типичная область применения этой возможности — для тех случаев, когда программа может как отвечать на запросы от удаленного партнера, так и отправлять ему что-то по собственной инициативе. В этом случае могут использоваться два сокетных события: одно связывается с событием FD_READ для оповещения о поступлении данных, а второе не связывается ни с одним из событий FD_XXX, а устанавливается другой нитью тогда, когда необходимо отправить сообщение. Нить, работающая с сокетом, ожидает взведения одного из этих событий и в зависимости от того, какое из них взведено, читает или отправляет данные.

В листинге 2.62 приведен пример кода такой нити. Она задействует три сокетных события: одно для уведомления о событиях на сокете, второе — для уведомления о необходимости отправить данные, третье — для уведомления о необходимости завершиться. В данном примере мы предполагаем, что, во-первых, сокет создан и подключен до создания нити и передается ей в качестве параметра, а во-вторых, три сокетных события хранятся в глобальном массиве SockEvents: array[0..2] of TWSAEvent, причем нулевой элемент этого массива содержит событие, связываемое с событиями FD_XXX, первый элемент — событие отправки данных, второй — событие завершения нити. Прототип функции, образующей нить, совместим с функцией BeginThread из модуля SysUtils.

Листинг 2.62. Схема нити, использующей события асинхронного сокета

function ProcessSockEvents(Parameter: Pointer): Integer;

var

 S: TSocket;

 NetworkEvents: TWSANetworkEvents;

begin

 // Так как типы TSocket и Pointer занимают по 4 байта, такое

 // приведение типов вполне возможно, хотя и некрасиво

 S := TSocket(Parameter);

 // Связываем событие SockEvents[0] с FD_READ и FD_CLOSE

 WSAEventSelect(S, SockEvents[0], FD_READ or FD_CLOSE);

 while True do

 begin

  case WSAWaitForMultipleEvents(3, @SockEvents[0], True, WSA_INFINITE, False) of

  WSA_WAIT_EVENT_0: begin

   WSAEnumNetworkEvents(S, SockEvents[0], NetworkEvents);

   if NetworkEvents.lNetworkEvents and FD_READ > 0 then

    if NetworkEvents.iErrorCode[FD_READ_BIT] = 0 then

    begin

     // Пришли данные, которые нужно прочитать

    end

    else

    begin

     // произошла ошибка. Нужно сообщить о ней и завершить нить

     closesocket(3);

     Exit;

    end;

   if NetworkEvents.lNetworkEvents and FD_CLOSE > 0 then

   begin

    // Связь разорвана

    if NetworkEvents.iErrorCode[FD_CLOSE_BIT] = 0 then begin

     // Связь закрыта корректно

    end

    else

    begin

     // Связь разорвана в результате сбоя сети

    end;

    // В любом случае нужно закрыть сокет и завершить нить

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