- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
// ***** Описание на C++ *****
int WSAEventSelect(SOCKET s, WSAEVENT hEventObject, long lNetworkEvents);
// ***** описание на Delphi *****
function WSAEventSelect(S: TSocket; hEventObject: TWSAEvent; lNetworkEvents: LongInt): Integer;
Эта функция очень похожа на функцию WSAAsyncSelect, за исключением того, что события FD_XXX привязываются не к оконным сообщениям, а к сокетным событиям. Параметр S определяет сокет, события которого отслеживаются, параметр hEventObject — событие, которое должно взводиться при наступлении отслеживаемых событий, lNetworkEvents — комбинация констант FD_XXX, определяющая, с какими событиями на сокете связывается событие hSocketEvent.
Функция WSAEventSelect возвращает ноль, если операция прошла успешно, и SOCKET_ERROR при возникновении ошибки.
Событие, связанное с сокетом функцией WSAEventSelect, взводится при тех же условиях, при которых в очередь окна помещается сообщение при использовании WSAAsyncSelect. Так, например, функция recv взводит событие, если после ее вызова в буфере сокета еще остаются данные. Но, с другой стороны, функция recv не сбрасывает событие, если данных в буфере сокета нет. А поскольку сокетные события не сбрасываются автоматически функцией WSAWaitForMultipleEvents, программа всегда должна сбрасывать события сама. Так, при обработке FD_READ наиболее типична ситуация, когда сначала сбрасывается событие, а потом вызывается функция recv, которая при необходимости снова взводит событие. Здесь мы снова имеем проблему ложных срабатываний в тех случаях, когда данные извлекаются из буфера по частям с помощью нескольких вызовов recv, но в данном случае проблему решить легче: не нужно отменять регистрацию событий, достаточно просто сбросить событие непосредственно перед последним вызовом recv.
В принципе, события FD_XXX разных сокетов можно привязать к одному сокетному событию, но этой возможностью обычно не пользуются, т.к. в WinSock2 отсутствуют средства, позволяющие определить, событие на каком из сокетов привело к взведению сокетного события. Поэтому приходится для каждого сокета создавать отдельное событие.
Как и в случае с WSAAsyncSelect при вызове WSAEventSelect сокет переводится в неблокирующий режим. Повторный вызов WSAEventSelect для данного сокета отменяет результаты предыдущего вызова (т.е. невозможно связать разные события FD_XXX одного сокета с разными сокетными событиями). Сокет, созданный в результате вызова accept или WSAAccept наследует связь с сокетными событиями, установленную для слушающего сокета.
Существует весьма важное различие между использованием оконных сообщений и сокетных событий для оповещения о том, что происходит на сокете.
Предположим, с помощью функции WSAAsyncSelect события FD_READ, FD_WRITE и FD_CONNECT связаны с некоторым оконным сообщением. Пусть происходит событие FD_CONNECT. В очередь окна помещается соответствующее сообщение. Затем, до того, как предыдущее сообщение будет обработано, происходит FD_WRITE. В очередь окна помещается еще одно сообщение, которое информирует об этом. И наконец, при возникновении FD_READ в очередь будет помещено третье сообщение. Затем оконная процедура получит их по очереди и обработает.
Теперь рассмотрим ситуацию, когда те же события связаны с сокетным событием. Когда происходит FD_CONNECT, сокетное событие взводится. Теперь если FD_WRITE и FD_READ произойдут до того, как сокетное событие будет сброшено, оно уже не изменит своего состояния. Таким образом, программа, работающая с асинхронными сокетами, основанными на событиях, должна, во-первых, учитывать, что взведенное событие может означать несколько событий FD_XXX, а во-вторых, иметь возможность узнать, какие именно события произошли с момента последней проверки. Для получения этой информации предусмотрена функция WSAEnumNetworkEvents, прототип которой приведен в листинге 2.60.
Листинг 2.60. Функция WSAEnumNetworkEvents// ***** Описание на C++ *****
int WSAEnumNetworkEvents(SOCKET s, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);
// ***** Описание на Delphi *****
function WSAEnumNetworkEvents(S: TSocket; hEventObject: TWSAEvent; var NetworkEvents: TWSANetworkEvents): Integer;
Функция WSAEnumNetworkEvents через параметр NetworkEvents возвращает информацию о том, какие события произошли на сокете S с момента последнего вызова этой функции для данного сокета (или с момента запуска программы, если функция вызывается в первый раз). Параметр hEventObject необязательный, он определяет сокетное событие, которое нужно сбросить. Использование этого параметра позволяет обойтись без явного вызова функции WSAResetEvent для сброса события. Как и большинство функций WinSock, функция WSAEnumNetworkEvents возвращает ноль в случае успеха и ненулевое значение при возникновении ошибки.
Запись TWSANetworkEvents содержит информацию о произошедших событиях об ошибках (листинг 2.61).
Листинг 2.61. Тип TWSANetworkEvents// ***** Описание на C++ *****
typedef struct _WSANETWORKEVENTS {
long lNetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
// ***** Описание на Delphi *****
TWSANetworkEvents = packed record
lNetworkEvents: LongInt;
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 байта, такое

