- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
Если после вызова recv в буфере асинхронного сокета остались данные, в очередь окна снова помещается это же сообщение. Благодаря этому программа может обрабатывать большие массивы по частям. Действительно, пусть в буфер сокета приходят данные, которые программа хочет забирать оттуда по частям. Приход этих данных вызывает событие FD_READ, сообщение о котором помещается в очередь. Когда программа начинает обрабатывать это сообщение, она вызывает recv и читает часть данных из буфера. Так как данные в буфере еще есть, снова генерируется сообщение о событии FD_READ, которое ставится в конец очереди. Через некоторое время программа снова начинает обрабатывать это сообщение. Если и на этот раз данные будут прочитаны не полностью, в очередь снова будет добавлено такое же сообщение. И так будет продолжаться до тех пор, пока не будут прочитаны все полученные данные.
Описанная схема, в принципе, достаточно удобна, но следует учитывать, что в некоторых случаях она может давать ложные срабатывания, т.е. при обработке сообщения о событии FD_READ функция recv завершится с ошибкой WSAEWOULDBLOCK, показывающей, что входной буфер сокета пуст. Если программа читает данные из буфера не только при обработке FD_READ, может возникнуть следующая ситуация: в буфер сокета поступают данные. Сообщение о событии FD_READ помещается в очередь. Программа в это время отрабатывает какое-то другое сообщение, при обработке которого также читаются данные. В результате все данные извлекаются из буфера, и он остается пустым. Когда очередь доходит до обработки FD_READ, читать из буфера уже нечего.
Другой вариант ложного срабатывания возможен, если программа при обработке FD_READ читает данные из буфера по частям, вызывая recv несколько раз. Каждый вызов recv, за исключением последнего, приводит к тому, что в очередь ставится новое сообщение о событии FD_READ. Чтобы избежать появления пустых сообщении в подобных случаях, MSDN рекомендует перед началом чтения отключить для данного сокета реакцию на поступление данных, вызвав для него WSAAsyncSelect без FD_READ, а перед последним вызовом recv — снова включить.
И наконец, следует помнить, что сообщение о событии FD_READ можно получить и после того, как с помощью WSAAsyncSelect сокет будет переведен в синхронный режим. Это может случиться в том случае, когда на момент вызова WSAAsyncSelect в очереди еще остались необработанные сообщения о событиях на данном сокете. Впрочем, это касается не только FD_READ, а вообще любого события.
Событие FD_WRITE информирует программу о том, что в выходном буфере сокета есть место для данных. Вообще говоря, оно там есть практически всегда, если только программа не отправляет постоянно большие объемы данных. Следовательно, механизм генерации этого сообщения должен быть таким, чтобы не забивать очередь программы постоянными сообщениями о том, что в буфере есть место, а посылать эти сообщения только тогда, когда программа действительно нуждается в такой информации.
При использовании TCP первый раз сообщение, уведомляющее о событии FD_WRITE, присылается сразу после успешного завершения операции подключения к серверу с помощью connect, если речь идет о клиенте, или сразу после создания сокета функцией accept или ее аналогом в случае сервера. В случае UDP это событие возникает после привязки сокета к адресу явным или неявным вызовом функции bind. Если на момент вызова WSAAsyncSelect описанные действия уже выполнены, событие FD_WRITE также генерируется.
В следующий раз событие может возникнуть только в том случае, если функция send (или sendto) не смогла положить данные в буфер из-за нехватки места в нем (в этом случае функция вернет значение, меньшее, чем размер переданных данных, или завершится с ошибкой WSAEWOULBBLOCK). Как только в выходном буфере сокета снова появится свободное место, возникнет событие FD_WRITE, показывающее, что программа может продолжить отправку данных. Если же программа отправляет данные не очень большими порциями и относительно редко, не переполняя буфер, то второй раз событие FD_WRITE не возникнет никогда.
Событие FD_ACCEPT во многом похоже на FD_READ, за исключением того, что оно возникает не при получении данных, а при подключении клиента. После постановки сообщения о событии FD_ACCEPT в очередь новые сообщения о FD_ACCEPT для данного сокета в очередь не ставятся, пока не будет вызвана функция accept или WSAAccept. При вызове одной из этих функций сообщение о событии вновь помещается в очередь окна, если в очереди подключений после вызова функции остаются подключения.
Событие FD_CONNECT возникает при установлении соединения для сокетов, поддерживающих соединение. Для клиентских сокетов оно возникает после завершения процедуры установления связи, начатой с помощью функции connect, для серверных — после создания нового сокета с помощью функции accept (событие возникает именно на новом сокете, а не на том, который находится в режиме ожидания подключения). В MSDN написано, что оно должно возникать также и после выполнения connect для сокетов, не поддерживающих соединение, однако для UDP практика это не подтверждает. Событие FD_CONNECT также возникает, если при попытке установить соединение произошла ошибка (например, оказался недоступен указанный сетевой адрес). Поэтому при получении этого события необходимо анализировать старшее слово параметра lParam, чтобы понять, удалось ли установить соединение.
Событие FD_CLOSE возникает только для сокетов, поддерживающих соединение, при разрыве такого соединения нормальным образом или в результате ошибки связи. Если удаленная сторона дня завершения соединения использует функцию shutdown, то FD_CLOSE возникает после вызова этой функции с параметром SD_SEND. При этом соединение закрыто еще не полностью, удаленная сторона еще может получать данные, поэтому при обработке FD_CLOSE можно попытаться отправить те данные, которые в этом нуждаются. Однако гарантии, что вызов функции отправки не завершится неудачей, нет, т.к. удаленная сторона может закрывать сокет сразу, не прибегая к shutdown.
Рекомендуемая последовательность действий при завершении связи такова. Сначала клиент завершает отправку данных через сокет, вызывая функцию shutdown с параметром SD_SEND. Сервер при этом получает событие FD_CLOSE. Сервер отсылает данные клиенту (при этом клиент получает одно или несколько событий FD_READ), а затем также завершает отправку данных с помощью shutdown с параметром SD_SEND. Клиент при этом получает событие FD_CLOSE, в ответ на которое закрывает сокет с помощью closesocket. Сервер, в свою очередь, сразу после вызова shutdown также вызывает closesocket. В листинге 2.49 приведен пример кода сервера, использующего асинхронные сокеты. Сервер работает в режиме запрос-ответ, т.е. посылает какие-то данные клиенту только в ответ на его запросы. Константа WM_SOCKETEVENT, определенная в коде для сообщений, связанных с сокетом, может, в принципе, иметь и другие значения.
Листинг 2.49. Пример простого сервера на асинхронных сокетахunit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, WinSock;
const
WM_SOCKETEVENT = WM_USER + 1;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObjеct);
private
ServSock: TSocket;
procedure WMSocketEvent(var Msg: TMessage); message WM_SOCKETEVENT;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender: TObject);
var
Data: TWSAData;
Addr: TSockAddr;
begin
WSAStartup($101, Data);
// Обычная последовательность действий по созданию сокета,
// привязке его к адресу и установлению на прослушивание
ServSock := socket(AF_INET, SOCK_STREAM, 0);
Addr.sin_family := AF_INET;
Addr.sin_addr.S_addr := INADDR_ANY;
Addr.sin_port := htons(3320);
FillChar(Addr.sin_zero, SizeOf(Addr.sin_zero), 0);
bind(ServSock, Addr, SizeOf(Addr));
listen(ServSock, SOMAXCONN);
// Перевод сокета в асинхронный режим. Кроме события FD_ACCEPT
// указаны также события FD_READ и FD_CLOSE, которые никогда не
// возникают на сокете, установленном в режим прослушивания.
// Это сделано потому, что сокеты, созданные с помощью функции
// accept, наследуют асинхронный режим, установленный для
// слушающего сокета. Таким образом, не придется вызывать

