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

// Обработка клиента. Index задает индекс записи в списке

procedure TServerForm.ProcessConnection(Index: Integer);

var

 // Вспомогательная переменная, чтобы не приводить каждый раз

 // FConnections[Index] к PConnection

 Connection: PConnection;

 // Результат вызова recv и send

 Res: Integer;

 // Вспомогательная процедура, освобождающая ресурсы, связанные

 // с клиентом и удаляющая запись подключения из списка

 procedure RemoveConnection;

 begin

  closesocket(Connection.ClientSocket);

  Dispose(Connection);

  FConnections.Delete(Index);

 end;


begin

 Connection := PConnection(PConnections[Index]);

 // Проверяем, на каком этапе находится взаимодействие с клиентом.

 // Используется оператор if, а не case, потому, что в случае case

 // выполняется только одна альтернатива, а в нашем случае в ходе

 // выполнения этапа он может завершиться, и взаимодействие

 // перейдет к следующему этапу. Использование if позволяет выполнить

 // все три этапа, если это возможно, а не один из них.

 if Connection.Phase = tpReceiveLength then

 begin

  // Этап получения от клиента длины строки. При выполнении этого

  // этапа сервер получает от клиента длину строки и размещает ее

  // в поле Connection.MsgSize. Здесь приходится учитывать, что

  // теоретически даже такая маленькая (4 байта) посылка может

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

  // этап не будет завершен, и второй раз его придется продолжать,

  // загружая оставшиеся байты. Connection.Offset — количество

  // уже прочитанных на данном этапе байтов - одновременно является

  // смещением, начиная с которого заполняется буфер.

  Res := recv(Connection.ClientSocket, (PChar(@Connection.MsgSize) +

   Connection.Offset)^, Connection.BytesLeft, 0);

  if Res > 0 then

  begin

   // Если Res > 0, это означает, что получено Res байтов.

   // Соответственно, увеличиваем на Res количество прочитанных

   // на данном этапе байтов и на такую же величину уменьшаем

   // количество оставшихся.

   Inc(Connection.Offset, Res);

   Dec(Connection.BytesLeft, Res);

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

   // к следующему этапу.

   if Connection.BytesLeft = 0 then

   begin

    // Проверяем корректность принятой длины строки

    if Connection.MsgSize <= 0 then

    begin

     AddMessageToLog('Неверная длина строки от клиента ' +

      Connection.ClientAddr + ': ' + IntToStr(Connection.MsgSize));

     RemoveConnection;

     Exit;

    end;

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

    Connection.Phase := tpReceiveString;

    // Пока на этом этапе не прочитано ни одного байта

    Connection.Offset := 0;

    // Осталось прочитать Connection.MsgSize байтов

    Connection.BytesLeft := Connection.MsgSize;

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

    SetLength(Connection.Msg, Connection.MsgSize);

   end;

  end

  else 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;

  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])) + ' (Non-blocking server)'#0;

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

     Connection.Phase := tpSendString;

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

     Connection.Offset := 0;

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

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

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

     // чтобы учесть добавляемый к строке автоматически символ #0.

     // Здесь мы еще один #0 добавили к строке явно, поэтому

     // он уже учтен в функции Length.

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

    end;

   end

   else 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;

  if Connection.Phase = tpSendString then

  begin

   // Следующий этап — отправка строки. Код примерно такой же,

   // как и в предыдущем этапе, но вместо recv используется send.

   // Кроме того, отсутствует проверка на Res = 0, т.к. при

   // использовании TCP send никогда не возвращает 0.

   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);

    // Если Connection.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);

   end;

  end

  else

   if WSAGetLastError <> WSAEWOULDBLOCK then

   begin

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

     Connection.ClientAddr + ': ' + GetErrorString);

    RemoveConnection;

    Exit;

   end;

 end;

end;

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


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

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


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

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

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