MyBooks.club
Все категории

А. Григорьев - О чём не пишут в книгах по Delphi

На сайте mybooks.club вы можете бесплатно читать книги онлайн без регистрации, включая А. Григорьев - О чём не пишут в книгах по Delphi. Жанр: Программирование издательство -,. Доступна полная версия книги с кратким содержанием для предварительного ознакомления, аннотацией (предисловием), рецензиями от других читателей и их экспертным мнением.
Кроме того, на сайте mybooks.club вы найдете множество новинок, которые стоит прочитать.

Название:
О чём не пишут в книгах по Delphi
Издательство:
-
ISBN:
-
Год:
-
Дата добавления:
17 сентябрь 2019
Количество просмотров:
244
Читать онлайн
А. Григорьев - О чём не пишут в книгах по Delphi

А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание

А. Григорьев - О чём не пишут в книгах по Delphi - описание и краткое содержание, автор А. Григорьев, читайте бесплатно онлайн на сайте электронной библиотеки mybooks.club
Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.Для программистов

О чём не пишут в книгах по Delphi читать онлайн бесплатно

О чём не пишут в книгах по Delphi - читать книгу онлайн бесплатно, автор А. Григорьев

На первом шаге цикла создаётся множество сокетов, в которое добавляются все сокеты, содержащиеся в массиве. В этом месте в примере пропущена важная проверка того, что сокетов в массиве не больше 64-х. Если их будет больше, то попытки добавить лишние сокеты в множество будут проигнорированы функцией FD_SET и, соответственно, эти сокеты выпадут из дальнейшего рассмотрения, т.е. даже если клиент что-то пришлет, сервер этого не увидит. Решить проблему можно тремя способами. Самый простой — это отказывать в подключении лишним клиентам. Для этого сразу после вызова accept нужно вызывать для нового сокета closesocket. Второй способ — это увеличение количества сокетов в множестве, как это было описано ранее. В этом случае все равно остается та же проблема, хотя если сделать число сокетов в множестве достаточно большим, она практически исчезает. И наконец, можно разделить сокеты на несколько порций, для каждой из которых вызывать select отдельно. Это потребует усложнения примера, потому что сейчас в функции select мы используем бесконечное ожидание. При разбиении сокетов на порции это может привести к тому, что из-за отсутствия готовых сокетов в первой порции программа не сможет перейти к проверке второй порции, в которой готовые сокеты, может быть, есть. Пример разделения сокетов на порции будет рассмотрен в следующем разделе.

При создании множества оно сначала очищается, а потом в него в цикле добавляются сокеты. Для любителей кратких решений есть существенно более быстрый способ формирования множества, при котором не потребуются ни циклы, ни FD_ZERO, ни FD_SET:

Move((PChar(Sockets) - 4)^, FDSet, Length(Sockets) * SizeOf(TSocket) + SizeOf(Integer));

Почему такая конструкция будет работать, предлагаем разобраться самостоятельно, изучив по справке Delphi, как хранятся в памяти динамические массивы, а по MSDN — структуру типа FDSET. Тем же, кто по каким-то причинам не захочет разбираться, настоятельно рекомендуем никогда и ни при каких обстоятельствах не использовать такую конструкцию, потому что в неумелых руках она превращается в мину замедленного действия, из-за которой ошибки могут появиться в самых неожиданных местах программы.

Второй шаг — это собственно выполнение ожидания готовности сокетов с помощью функции select. Готовность к записи и к чтению высокоприоритетной информации нас в данном случае не интересует, поэтому мы ограничиваемся заданием множества readfds. В нашем простом примере не должно выполняться никаких действий, если ни один сокет не готов, поэтому последний параметр тоже равен nil, что означает ожидание, не ограниченное тайм-аутом.

Третий шаг выполняется только после функции select, т.е. тогда, когда хотя бы один из сокетов находится в состоянии готовности. На этом шаге мы проверяем сокеты, созданные для взаимодействия с клиентами на предыдущих итерациях цикла с помощью функции accept. Эти сокеты располагаются в массиве сокетов, начиная с элемента с индексом 1. Программа в цикле просматривает все сокеты и, если они находятся в состоянии готовности, выполняет операцию чтения.

На первый взгляд может показаться странным, почему для перебора элементов массива выбран цикл while, а не for. Но в дальнейшем мы увидим, что размер массива во время выполнения цикла может изменяться. Особенность же цикла for заключается в том, что его границы вычисляются один раз и запоминаются в отдельных ячейках памяти, и дальнейшее изменение значений выражений, задающих эти границы, не изменяет эти границы. В нашем примере это приведет к тому, что в случае уменьшения массива цикл for не остановится на реальной уменьшившейся длине, а продолжит выполнение по уже не существующим элементам, что приведет к трудно предсказуемым последствиям. Поэтому в данном случае предпочтительнее цикл while, в котором условие продолжения цикла заново вычисляется при каждой его итерации.

Напомним, что функция select модифицирует переданные ей множества таким образом, что в них остаются лишь сокеты, находящиеся в состоянии готовности. Поэтому чтобы проверить, готов ли конкретный сокет, достаточно с помощью функции FD_ISSET проверить, входит ли он в множество FDSet. Если входит, то вызываем для него функцию recv. Если эта функция возвращает положительное значение, значит, данные в буфере есть, программа их читает и отвечает. Если функция возвращает 0 или -1 (SOCKET_ERROR) значит, соединение закрыто или разорвано, и данный сокет больше не может быть использован. Поэтому мы должны освободить связанные с ним ресурсы (closesocket) и убрать его из массива сокетов (как раз на этом шаге размер массива уменьшается). При удалении оставшиеся сокеты смещаются на одну позицию влево, поэтому переменную цикла необходимо уменьшить на единицу, иначе следующий сокет будет пропущен.

И наконец, на четвертом шаге мы проверяем состояние готовности исходного сокета, который хранится в нулевом элементе массива. Так как этот сокет находится в режиме ожидания соединения, для него состояние готовности означает, что в очереди соединений появились клиенты, и необходимо вызвать функцию accept, чтобы создать сокеты для взаимодействия с этими клиентами.

Хотя приведенный пример вполне работоспособен, следует отметить, что это только один из возможных вариантов организации сервера. Так что лучше не относиться к нему как к догме, потому что именно в вашем случае может оказаться предпочтительнее какой-либо другой вариант. Ценность этого примера заключается в том, что он иллюстрирует работу функции select, а не в том, что он дает готовое решение на все случаи жизни.

2.1.14. Примеры использования функции select

Рассмотрим два практических примера использования функции select для получения информации о готовности сокета. Оба примера станут развитием рассмотренных ранее.

Сначала модифицируем UDP-чат (см. разд. 2.1.10) таким образом, чтобы он использовал один сокет и для отправки, и для получения сообщений (пример SelectChat на компакт-диске). Вторая нить нам теперь не понадобится, всё будет делать главная форма. Процедуры создания сокета и отправки сообщений изменений не претерпели, главное дополнение — это то, что на форме появился таймер, в обработчике события OnTimer которого мы будем проверять с помощью select, пришло ли сообщение для сокета (листинг 2.24). С помощью таких простейших модификаций мы получили чат, который работает без распараллеливания и использует всего один сокет. Работать с таким чатом стало намного проще, потому что теперь ответ нужно посылать на тот же порт, с которого пришло сообщение, а не запоминать, какой порт для отправки какому из экземпляров чата соответствует.

Примечание

Несмотря на эти изменения, новая версия UDP-чата может обмениваться сообщениями со старой, т.к. протокол обмена остался неизменным.

Листинг 2.24. Проверка готовности сокетов при обработке сообщения от таймера

// Реакция на таймер. С периодичностью, заданной таймером,

// проверяем, не пришли ли сообщения, и если пришли,

// получаем их.

procedure TChatForm.TimerChatTimer(Sender: TObject);

var

 // Множество сокетов для функции select.

 // Будет содержать только один сокет FSocket.

 SocketSet: TFDSet;

 // Тайм-аут для функции select

 Timeout: TTimeVal;

 // Буфер для получения сообщения.

 // Размер равен максимальному размеру UDP-дейтаграммы

 Buffer: array[0..65506] of Byte;

 Msg: string;

 // Адрес, с которого пришло сообщение

 RecvAddr: TSockAddr;

 RecvLen, AddrLen: Integer;

begin

 // Инициализируем множество сокетов,

 // т.е. очищаем его от случайного мусора

 FD_ZERO(SocketSet);

 // Добавляем в это множество сокет FSocket

 FD_SET(FSocket, SocketSet);

 // Устанавливаем тайм-аут равным нулю, чтобы

 // функция select ничего не ждала, а возвращала

 // готовность сокетов на момент вызова.

 Timeout.tv_sec := 0;

 Timeout.tv_usec := 0;

 // Проверяем готовность сокета для чтения

 if select(0, @SocketSet, nil, nil, @Timout) = SOCKET_ERROR then

 begin

  AddMessageToLog('Ошибка при проверке готовности сокета: ' + GetErrorString);

  Exit;

 end;

 // Проверяем, оставила ли функция select сокет в множестве.

 //Если оставила, значит, во входном буфере сокета есть данные.

 if FD_ISSET(FSocket, SocketSet) then

 begin

  AddrLen := SizeOf(RecvAddr); // Получаем дейтаграмму

  RecvLen :=

   recvfrom(FSocket, Buffer, SizeOf(Buffer), 0, RecvAddr, AddrLen);

  // Так как UDP не поддерживает соединение, ошибку при вызове recvfrom


А. Григорьев читать все книги автора по порядку

А. Григорьев - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки mybooks.club.


О чём не пишут в книгах по Delphi отзывы

Отзывы читателей о книге О чём не пишут в книгах по Delphi, автор: А. Григорьев. Читайте комментарии и мнения людей о произведении.

Прокомментировать
Подтвердите что вы не робот:*
Подтвердите что вы не робот:*
Все материалы на сайте размещаются его пользователями.
Администратор сайта не несёт ответственности за действия пользователей сайта..
Вы можете направить вашу жалобу на почту librarybook.ru@gmail.com или заполнить форму обратной связи.