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 - читать книгу онлайн бесплатно, автор А. Григорьев

     // Сразу выделяем память под строку

     SetLength(Connection.Msg, Connection.MsgSize);

    end;

   end

   elsе if Res = 0 then

   begin

    AddMessageToLog('Клиент ' + Connection.ClientAddr +

     ' закрыл соединение');

    RemoveConnection;

    Exit;

   end

   else

    // Ошибку WSAEWOULDBLOCK игнорируем, т.к. она говорит

    // только о том, что входной буфер сокета пуст, но в целом

    // все в порядке - такое вполне возможно при ложных

    // срабатываниях сообщения

    if WSAGetLastError <> WSAEWOULDBLOCK then

    begin

     AddMessageToLog('Ошибка при получении данных от клиента ' +

      Connection.ClientAddr + ': ' + GetErrorString);

     RemoveConnection;

     Exit;

    end;

  end

  else if Connection.Phase = tpReceiveString then

  begin

   // Следующий этап - чтение строки. Он практически не отличается

   // по реализации от этапа чтения длины строки, за исключением

   // того, что теперь буфером, куда помещаются полученные от

   // клиента данные, служит не Connection.MsgSize,

   // a Connection.Msg.

   Res :=

    recv(Connection.ClientSocket, Connection.Msg(Connection.Offset + 1),

     Connection.BytesLeft, 0);

   if Res > 0 then

   begin

    Inc(Connection.Offset, Res);

    Dec(Connection.BytesLeft, Res);

    // Если количество оставшихся байтов равно нулю, можно

    // переходить к следующему этапу.

    if Connection.BytesLeft = 0 then

    begin

     AddMessageToLog('От клиента ' + Connection.ClientAddr +

      ' получена строка: ' + Connection.Msg);

     // Преобразуем строку. В отличие от предыдущих примеров,

     // здесь мы явно добавляем к строке #0. Это связано с тем,

     // что при отправке, которая тоже может быть выполнена не

     // за один раз, мы указываем индекс того символа строки,

     // начиная с которого нужно отправлять данные. И (хотя

     // теоретически вероятность этого очень мала) может

     // возникнуть ситуация, когда за один раз будут отправлены

     // все символы строки, кроме завершающего #0, и тогда при

     // следующей отправке начинать придется с него. Если мы

     // будем использовать тот #0, который добавляется к концу

     // строки автоматически, то в этом случае индекс выйдет за

     // пределы диапазона. Поэтому мы вручную добавляем ещё один

     // #0 к строке, чтобы он стал законной ее частью.

     Connection.Msg :=

      AnsiUpperCase(StringReplace(Connection.Msg, #0, '#0', [rfReplaceAll])) +

      '(AsyncSelect server)'#0;

     // Следующий этап - отправка строки клиенту

     Connection.Phase := tpSendString;

     // Отправлено на этом этапе 0 байт

     Connection.Offset := 0;

     // Осталось отправить Length(Connection.Msg) байтов.

     // Единицу к длине строки, в отличие от предыдущих

     // примеров, не добавляем, т.к. там эта единица нужна была

     // для того, чтобы учесть добавляемый к строке

     // автоматически символ #0. Здесь мы еще один #0 добавили

     // к строке явно, поэтому он уже учтен в функции Length.

     Connection.BytesLeft := Length(Connection.Msg);

     // Ставим в очередь сообщение с событием FW_WRITE.

     // Его получение заставит сервер отправить данные

     PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_WRITE);

    end;

   end

else if Res = 0 then

   begin

    AddMessageToLog('Клиент ' + Connection.ClientAddr +

     ' закрыл соединение');

    RemoveConnection;

    Exit;

   end

   elsе

    // Как обычно, "ошибку" WSAEWOULDBLOCK просто игнорируем

    if WSAGetLastError <> WSAEWOULDBLOCK then

    begin

     AddMessageToLog('Ошибка при получении данных от клиента ', +

      Connection.ClientAddr + ': ' + GetErrorString);

     RemoveConnection;

     Exit;

    end;

   end

   else if Connection.Phase = tpSendString then

    // Если сервер находится на этапе отправки данных,

    // а событие FD_READ все же произошло, отмечаем это

    Connection.SendRead := True;

 end;

 FD_WRITE: begin

  if Connection.Phase = tpSendString then

  begin

   // При наступлении события FD_WRITE проверяем, находится ли

   // сервер на этапе отправки данных, и если да, отправляем их

   Res :=

    send(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1],

    Connection.BytesLeft, 0);

   if Res > 0 then

   begin

    Inc(Connection.Offset, Res);

    Dec(Connection.BytesLeft, Res);

    // Если Connections. BytesLeft = 0, значит, строка отправлена

    // полностью.

    if Connection.BytesLeft = 0 then

    begin

     AddMessageToLog('Клиенту ' + Connection.ClientAddr +

      ' отправлена строка: ' + Connection.Msg);

     // Очищаем строку, просто чтобы сэкономить память

     Connection.Msg := '';

     // Следующий этап - снова получение длины строки от клиента

     Connection.Phase := tpReceiveLength;

     // Получено - 0 байт

     Connection.Offset := 0;

     // Осталось прочитать столько, сколько занимает целое число

     Connection.BytesLeft := SizeOf(Integer);

     // Если были промежуточные события FD_READ, вызываем их

     // снова искусственно

     it Connection.SendRead then

     begin

      PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_READ);

      Connection.SendRead := False;

     end;

    end;

   end

   else if WSAGetLastError <> WSAEWOULDBLOCK then

   begin

    AddMessageToLog('Ошибка при отправке данных клиенту ' +

     Connection.ClientAddr + ': ' + GetErrorString);

    RemoveConnection;

    Exit;

   end;

  end;

 end;

 FD_CLOSE: begin

  // Клиент вызвал функцию shutdown. Закрываем соединение.

  AddMessageToLog('Клиент ' + Connection.ClientAddr +

   ' закрыл соединение');

  shutdown(Connection.ClientSocket, SD_BOTH);

  RemoveConnection;

 end

 else

 begin

  AddMessageToLog('Неверное событие при обмене с клиентом ' +

   Connection.ClientAddr);

  RemoveConnection;

 end;

 end;

end;

В этом примере можно найти много общего с кодом из листинга 2.32 — получение и отправка данных в зависимости от этапа выполняется практически одинаково, различаются только условия, при которых эти участки кода выполняются. Обратите внимание, что теперь проверка того, какой этап чтения выполняется, сделана взаимоисключающей, т.е. при обработке одного сообщения не может быть прочитана и длина строки, и сама строка. Это сделано, чтобы убрать ложные срабатывания. Рассмотрим два возможных варианта. Первый вариант — когда во входном буфере сокета оказывается сразу длина и строка (или ее часть). После того как будет прочитана длина, сообщение WM_SOCKETMESSAGE с параметром FD_READ вновь будет помещено в очередь, поскольку функция recv помещает это сообщение в очередь, если после ее вызова во входном буфере сокета остались данные. Если мы немедленно перейдем ко второму этапу, то прочитаем из буфера сокета все оставшиеся там данные, но сообщение в очереди все равно останется, что даст нам ложное срабатывание, когда петля сообщений извлечет и диспетчеризует это сообщение. Таким образом, выполнение сразу двух этапов при обработке одного сообщения не даст выигрыша в производительности, т.к. все равно придется извлекать и обрабатывать два сообщения.

Второй вариант — когда на момент обработки события FD_READ во входном буфере находится только длина строки. В этом случае функция recv не будет помещать в очередь второе сообщение WM_SOCKETMESSAGE, т.к. данных в буфере после ее выполнения не останется, но и попытка выполнить этап чтения строки окажется бесполезной работой, т.к. строка еще не получена. В любом случае этап чтения строки будет выполнен только при обработке следующего сообщения WM_SOCKETMESSAGE, когда от клиента будут получены новые данные.

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


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

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


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

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

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