- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
......
// Выходим из цикла Break;
end
else if WSAGetLastError <> WSA_IO_INCOMPLETE then
begin
// Произошла ошибка, анализируем ее
......
// Выходим из цикла
Break;
end
else
begin
// Операция чтения не завершена
// Занимаемся другими действиями
end;
end;
Теперь перейдем к рассмотрению перекрытого ввода-вывода на основе процедур завершения. Для этого при вызове функции WSARecv нужно задать указатель на процедуру завершения, описанную в программе. Процедура завершения должна иметь прототип, приведенный в листинге 2.72.
Листинг 2.72. Прототип процедуры завершения// ***** Описание на C++ *****
void CALLBACK CompletionROUTINE(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);
// ***** Описание на Delphi *****
TWSAOverlappedCompletionRoutine =
procedure(dwError: DWORD; cbTransferred: DWORD; lpOverlapped: PWSAOverlapped; dwFlags: DWORD); stdcall;
При использовании процедур завершения в функцию WSARecv также нужно передавать указатель на запись TWSAOverlapped через параметр lpOverlapped, но значение поля hEvent этой структуры игнорируется. Вместо взведения события при завершении операции будет вызвана процедура, указанная в качестве параметра функции WSARecv. Указатель на структуру, заданный при вызове WSARecv, передается в процедуру завершения через параметр lpOverlapped. Смысл остальных параметров очевиден: dwError — это код ошибки (или ноль, если операция завершена успешно), cbTransferred — число полученных байтов (само полученное сообщение копируется в буферы, указанные при вызове функции WSARecv), a dwFlags — флаги.
Процедура завершения всегда выполняется в той нити, которая инициировала начало операции перекрытого ввода-вывода. Но система не может прерывать нить для выполнения процедуры завершения в любой удобный ей момент — нить должна перейти в состояние ожидания. В это состояние ее можно перевести, например, с помощью функции SleepEx, имеющей следующий прототип:
function SleepEx(dwMilliseconds: DWORD; bAlertable: BOOL); DWORD;
Функция SleepEx является частью стандартного API системы и импортируется модулем Windows. Она переводит нить в состояние ожидания. Параметр dwMilliseconds задает время ожидания в миллисекундах (или значение INFINITE для бесконечного ожидания). Параметр bAlertable указывает, допустимо ли прерывание состояния ожидания для выполнения процедуры завершения. Если bAlertable равен False, функция SleepEx ведет себя так же как функция Sleep, т.е. просто приостанавливает работу нити на заданное время. Если bAlertable равен True, нить может быть выведена системой из состояния ожидания раньше, чем истечет заданное время, если возникнет необходимость выполнить процедуру завершения. О причине завершения ожидания программа может судить по результату, возвращаемому функцией SleepEx: ноль в случае завершения по тайм-ауту и WAIT_IO_COMPLETION в случае завершения из-за выполнения процедуры завершения (в последнем случае сначала выполняется процедура завершения, а потом только происходит возврат из функции SleepEx). Если завершились несколько операций перекрытого ввода-вывода, в результате выполнения SleepEx будут вызваны процедуры завершения для всех этих операций.
Существует также возможность ожидать выполнения процедуры завершения одновременно с ожиданием взведения событий с помощью функции WSAWaitForMultipleEvents. Напомним, что у этой функции также есть параметр fAlertable. Если задать его равным True, то при необходимости выполнения процедуры завершения функция WSAWaitForMultipleEvents, подобно функции SleepEx, выполняет эту процедуру и возвращает WAIT_IO_COMPLETION.
Если программа выполняет одновременно несколько операций перекрытого ввода-вывода, возникает вопрос, как при вызове процедуры завершения определить, какая из них завершилась. Для каждой такой операции должен быть создан уникальный экземпляр записи 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;
Основная процедура здесь — ProcessConnection. Эта процедура в бесконечном цикле устанавливает соединение, если оно не установлено, дает системе выполнить процедуру завершения, если это требуется, и выполняет какие-либо иные действия, не связанные с получением данных от сокета. Процедура завершения GetData получает и обрабатывает данные, а если произошла ошибка, закрывает сокет и сбрасывает флаг Connected, что служит для процедуры ProcessConnection сигналом о необходимости установить соединение заново.
Из этого примера хорошо видны достоинства и недостатки процедур заверения. Получение и обработка данных выносится в отдельную процедуру, и с одной стороны, позволяет разгрузить основную процедуру, но, с другой стороны, заставляет прибегнуть к глобальным переменным для буфера и сокета.
Для протоколов, не поддерживающих соединение, существует другая функция для перекрытого получения данных — WSARecvFrom. Из названия очевидно, что она позволяет узнать адрес отправителя. Прототип функции WSARecvFrom приведен в листинге 2.74.

