Массив 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
.
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;
// В любом случае нужно закрыть сокет и завершить нить