iErrorCode: array[0..FD_MAX_EVENTS - 1] of Integer;
end;
Константа
FD_MAX_EVENTS
определяет количество разных типов событий и в данной реализации равна 10.
Значения констант
FD_XXX
представляют собой степени двойки, поэтому их можно объединять операцией арифметического ИЛИ без потери информации. Поле
lNetworkEvents
является таким объединением всех констант, задающих события,
которые происходили на сокете. Другими словами, если результат операции (
lNetworkEvents and FD_XXX
) не равен нулю, значит, событие
FD_XXX
происходило на сокете.
Массив
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