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

var

 // Адрес, к которому привязывается слушающий сокет

 ServerAddr: TSockAddr;

 NonBlockingArg: u_long;

begin

 // Формируем адрес для привязки.

 FillChar(ServerAddr.sin_zero, SizeOf(ServerAddr.sin_zero), 0);

 ServerAddr.sin_family := AF_INET;

 ServerAddr.sin_addr.S_addr := INADDR_ANY;

 try

  ServerAddr.sin_port := htons(StrToInt(EditPortNumber.Text));

  if ServerAddr.sin_port = 0 then

  begin

   MessageDlg('Номер порта должен находиться в диапазоне 1-65535',

    mtError, [mbOK], 0);

   Exit;

  end;

  // Создание сокета

  FServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  if FServerSocket = INVALID_SOCKET then

  begin

   MessageDlg('Ошибка при создании сокета: '#13#10 + GetErrorString,

    mtError, [mbOK], 0);

   Exit;

  end;

  // Привязка сокета к адресу

  if bind(FServerSocket, ServerAddr, SizeOf(ServerAddr)) = SOCKET_ERROR then

  begin

   MessageDlg('Ошибка при привязке сокета к адреcу: '#13#10 +

    GetErrorString, mtError, [mbOK], 0);

   closesocket(FServerSocket);

   Exit;

  end;

  // Перевод сокета в режим прослушивания

  if listen(FServerSocket, SOMAXCONN) = SOCKET_ERROR then

  begin

   MessageDlg('Ошибка при переводе сокета в режим прослушивания:'#13#10 +

    GetErrorString, mtError, [mbOK], 0);

   closesocket(FServerSocket);

   Exit;

  end;

  // Перевод сокета в неблокирующий режим

  NonBlockingArg := 1;

  if ioctlsocket(FServerSocket, FIONBIO, NonBlockingArg) = SOCKET_ERROR then

  begin

   MessageDlg('Ошибка при переводе сокета в неблокирующий режим:'#13#10 +

    GetErrorString, mtError, [mbOK], 0);

   closesocket(FServerSocket);

   Exit;

  end;

  // Перевод элементов управления в состояние "Сервер работает"

  LabelPortNumber.Enabled := False;

  EditРоrtNumber.Enabled := False;

  BtnStartServer.Enabled := False;

  TimerRead.Interval := TimerInterval;

  LabelServerState.Caption := 'Сервер работает';

 except

  on EConvertError do

   // Это исключение может возникнуть только в одном месте -

   // при вызове StrToInt(EditPortNumber.Text)

   MessageDlg('"' + EditPortNumber.Text +

    '" не является целым числом', mtError, [mbOK], 0);

  on ERangeError do

   // Это исключение может возникнуть только в одном месте -

   // при присваивании значения номеру порта

   MessageDlg('Номер порта должен находиться в диапазоне 1-65535',

    mtError, [mbOK], 0);

 end;

end;

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

Взаимодействие сервера с клиентом состоит из трех этапов. На первом этапе сервер получает от клиента четырёхбайтное значение — длину строки. На втором этапе сервер получает от клиента саму строку, размер которой уже известен из величины, полученной на первом этапе. На третьем этапе сервер отправляет ответ клиенту, состоящий из строки, завершающейся нулем. Чтобы при очередном "тике" таймера сервер мог продолжить общение с клиентом, прерванное в произвольном месте, необходимо запоминать, на каком этапе было прервано взаимодействие в предыдущий раз, сколько байтов на данном этапе уже прочитано или отправлено и сколько еще осталось прочитать или отправить. Для хранения этих данных мы будем использовать типы TTransportPhase и TConnection (листинг 2.31).

Листинг 2.31. Типы TTransportPhase и TConnection 

type

 // Этап взаимодействия с клиентом:

 // tpReceiveLength - сервер ожидает от клиента длину строки

 // tpReceiveString - сервер ожидает от клиента строку

 // tpSendString - сервер посылает клиенту строку

 TTransportPhase = (tpReceiveLength, tpReceiveString, tpSendString);

 // Информация о соединении с клиентом:

 // СlientSocket - сокет, созданный для взаимодействия с клиентом

 // ClientAddr - строковое представление адреса клиента

 // MsgSize - длина строки, получаемая от клиента

 // Msg - строка, получаемая от клиента или отправляемая ему,

 // Phase - этап взаимодействия с данным клиентом

 // Offset - количество байтов, уже полученных от клиента

 // или отправленных ему на данном этапе

 // BytesLeft - сколько байтов осталось получить от клиента

 // или отправить ему на данном этапе

 PConnection = ^TConnection;

 TConnection = record

  ClientSocket: TSocket;

  ClientAddr: string;

  MsgSize: Integer;

  Msg: string;

  Phase: TTransportPhase;

  Offset: Integer;

  BytesLeft: Integer;

 end;

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

Проверка подключения клиентов и взаимодействие с подключившимися ранее реализуется, как уже было сказано, при обработке события таймера. Код обработчика приведен в листинге 2.32. 

Листинг 2.32. Обработчик события таймера

// Обработка сообщения от таймера

// В ходе обработки проверяется наличие вновь подключившихся клиентов

// а также осуществляется обмен данными с клиентами

procedure TServerForm.TimerReadTimer(Sender: TObject);

var

 // Сокет, который создается для вновь подключившегося клиента

 ClientSocket: TSocket;

 // Адрес подключившегося клиента

 ClientAddr: TSockAddr;

 // Длина адреса

 AddrLen: Integer;

 // Вспомогательная переменная для создания нового подключения

 NewConnection: PConnection;

 I: Integer;

begin

 AddrLen := SizeOf(TSockAddr);

 // Проверяем наличие подключении. Так как сокет неблокирующий,

 // accept не будет блокировать нить даже в случае отсутствия

 // подключений.

 ClientSocket := accept(FServerSocket, @ClientAddr, @AddrLen);

 if ClientSocket = INVALID_SOCKET then

 begin

  // Если произошедшая ошибка - WSAEWOULDBLOCK, это просто означает,

  // что на данный момент подключений нет, а вообще все в порядке,

  // поэтому ошибку WSAEWOULDBLOCK мы просто игнорируем. Прочие же

  // ошибки могут произойти только в случае серьезных проблем,

  // которые требуют остановки сервера.

  if WSAGetLastError <> WSAEWOULDBLOCK then

  begin

   MessageDlg('Ошибка при подключении клиента:'#13#10 +

    GetErrorString + #13#10'Сервер будет остановлен', mtError, [mbOK], 0);

   ClearConnections;

   closesocket(FServerSocket);

   OnStopServer;

  end;

 end

 else

 begin

  // Создаем запись для нового подключения и заполняем ее

  New(NewConnection);

  NewConnection.ClientSocket := ClientSocket;

  NewConnection.СlientAddr :=

   Format('%u.%u.%u.%u:%u', [

    Ord(ClientAddr.sin_addr.S_un_b.s_b1),

    Ord(ClientAddr.sin_addr.S_un_b.s_b2),

    Ord(ClientAddr.sin_addr.S_un_b.s_b3),

    Ord(ClientAddr.sin_addr.S_un_b.s_b4),

    ntohs(ClientAddr.sin_port)]);

  NewConnection.Phase := tpReceiveLength;

  NewConnection.Offset := 0;

  NewConnection.BytesLeft := SizeOf(Integer);

  // Добавляем запись нового соединения в список

  FConnections.Add(NewConnection);

  AddMessageToLog('Зафиксировано подключение с адреса ' +

   NewConnection.ClientAddr);

 end;

 // Обрабатываем все существующие подключения.

 // Цикл идет от конца списка к началу потому, что в ходе

 // обработки соединение может быть удалено из списка.

 for I := FConnections.Count - 1 downto 0 do processConnection(I);

end;

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

Собственно взаимодействие сервера с клиентом вынесено в метод ProcessConnection (листинг 2.33). который осуществляет чтение данных от клиента и отправку данных в соответствии с этапом, на котором остановилось взаимодействие. При реализации этого метода необходимо просто аккуратно следить за тем, куда и сколько данных нужно передать.


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

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


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

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

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