Перейти к содержанию
Fire Monkey от А до Я
  • 0

IdTCPClient-IdTCPServer управление соединением


Wovan2

Вопрос

Доброе время суток.

Борюсь с Indy 10 на FMX. Обмен данными между сервером и клиентом наладил. Все работает стабильно.

Стал экспериментировать с разрывом связи. И тут программы ведут себя не очень понятно, особенно на клиенте. Если сервер не запушен. Клиент на команде IdTCPClient.Connect; закрывается. ???? Делаю обработки так
 

procedure TfrmMainClient.TimerTimer(Sender: TObject);
var
 Server : string;
begin
 if not IdTCPClient.Connected
  then
   try
    IdTCPClient.Connect;
   except
   on E : Exception do
    lblInfo.Text := e.Message;
   end;
 Server := 'Сервер ' + IdTCPClient.Socket.Binding.PeerIP +
           ':' + IntToStr(IdTCPClient.Socket.Binding.PeerPort);
 if IdTCPClient.Connected
  then
   begin
    lblInfo.Text := Server + ' подключен!';
    lblInfo.TextSettings.FontColor := TAlphaColors.Green;
   end
  else
   begin
    lblInfo.Text := Server + ' не доступен!';
    lblInfo.TextSettings.FontColor := TAlphaColors.Red;
   end;
end;

Никаких сообщений не появляется. Программа подвисает вместе с Адроидом, потом тупо окно закрывается. Молча. Таймауты на клиенте выставил ConnectTimeOut = 5000; ReadTimeOut = 5000;
В общем вопрос. Как правильно организовать на Indy управление соединением? Чтобы не тормозило, Была возможность переподключиться. 

PS. Почему-то в Delphi 10.2 не могу найти компонент AntiFreeze. ????

Изменено пользователем Wovan2
Ссылка на комментарий

Рекомендуемые сообщения

  • 0
16 часов назад, Wovan2 сказал:

Почему-то в Delphi 10.2 не могу найти компонент AntiFreeze.

Скорее всего, и не будет. Чтобы не висло, вся работа (включая создание) клиента должна вестись в отдельном потоке.

16 часов назад, Wovan2 сказал:

Программа подвисает вместе с Адроидом, потом тупо окно закрывается.

А если сперва на винде попробовать?

Ссылка на комментарий
  • 0

А разве в Indy не реализована многопоточность в самих компонентах? Я так понял из описания компонентов.

И Android  как-то не понятно отрабатывает блоки try.

Наверное у кого-то уже есть наработки по управлению соединениями в Indy? Ну там, как проверить соединение, как отработать разрыв связи и т.п. Пока получается стабильная работа так: соединение создается перед командой и закрывается сразу после ответа. 

 

Ссылка на комментарий
  • 0
42 минуты назад, Wovan2 сказал:

Пока получается стабильная работа так: соединение создается перед командой и закрывается сразу после ответа. 

тогда TCP не нужен. Юзай http, который использует нативность на всех платформах.

Ссылка на комментарий
  • 0
1 час назад, POV сказал:

INDY и TCP вполне себе дружат. На винде попробуй сначала.

Наверное должны дружить, раз написаны компоненты.

На Windows что проверять там связь стабильная. На Android из-за непостоянности связи происходят всякие непонятные зависоны,то на сервере, то на клиенте. Тонкостей Indy не знаю, к сожалению. Наверняка есть какой-то алгоритм, ну там последовательность вызовов всяких там индийских событий и функций для обеспечения стабильности работы в нестабильной сети. Разбираться в нюансах ну очень долго и не продуктивно для разового применения в простеньком  приложении. Подозреваю, что обеспечение стабильности связи займет больше кода, чем само приложение...

Ссылка на комментарий
  • 0
1 час назад, Wovan2 сказал:

На Android из-за непостоянности связи происходят всякие непонятные зависоны,то на сервере, то на клиенте.

Поставь тайм-ауты секунд по 10-20 и в try всё заверни. Будет тебе стабильность работы не смотря на связь. 
 

Ссылка на комментарий
  • 0
2 часа назад, Wovan2 сказал:

Наверное должны дружить, раз написаны компоненты.

На Windows что проверять там связь стабильная. На Android из-за непостоянности 

Нефиг в таймере этот код располагать, всё в поток.

Ссылка на комментарий
  • 0
21 час назад, Wovan2 сказал:

Никаких сообщений не появляется. Программа подвисает вместе с Адроидом, потом тупо окно закрывается. Молча. Таймауты на клиенте выставил ConnectTimeOut = 5000; ReadTimeOut = 5000;
В общем вопрос. Как правильно организовать на Indy управление соединением? Чтобы не тормозило, Была возможность переподключиться. 

PS. Почему-то в Delphi 10.2 не могу найти компонент AntiFreeze. ????

Можете мне сделать тестовый сервер и клиент, чтобы я смог у себя проверить? Тогда мой ответ будет более детальным. А пока:

  1. Какой вы объем данных шлете по соединению?
  2. Чем вызвана потребность использовать именно сокеты?
  3. TimeOut  никогда не ставьте большими. Indy работает по принципу блокировки сокета и всего потока в целом. Поэтому большое значение = зависание всего приложения = нежелательные результаты и зависание. Я ставил 100 мс для работы с маленькими пакетами. С такой задержкой доп. поток не обязателен. Если значения более 500 мс - нужно создавать отдельный поток и работать с сокетами в ней + синхронизация при обработке / отправке данных.
  4. TIdTCPClient на Andoid любит спать и не проверять входящий буфер. Поэтому вручную нужно вызывать по таймеру проверку типа:
  5. procedure T<какое-то имя класа>.Read;
    var
        sz : integer;
        lMsg : string;
    begin
      try
        TMonitor.Enter(Self);
        try
            if not Assigned(Client.IOHandler) then
                Exit;
    		//Client = TIdTCPClient
            if Client.IOHandler.InputBufferIsEmpty then
            begin
                if not Client.IOHandler.CheckForDataOnSource() then
                    exit;
            end;
            sz := Client.IOHandler.InputBuffer.Size;
            if sz <= 0 then
                exit;    
            lMsg := Client.IOHandler.InputBuffer.ExtractToString(-1, IndyTextEncoding_UTF8);
            Client.IOHandler.InputBuffer.Clear;
            <какой-то обработчик входящего сообщения>;
        except
            on e : Exception do
                <какой-то обработчик ошибки>;
        end;
      finally
        TMonitor.Exit(Self);
      end;
    end;

    AntiFreeze - это мягко говоря "костыль" от Indy, использование его плохая практика. На мобильной платформе вряд ли он появится, хотя и реализуется не сложно.

Ссылка на комментарий
  • 0
2 часа назад, POV сказал:

Нефиг в таймере этот код располагать, всё в поток

На самом деле таймер я подготовил, но не использую (не активирую) его. Просто один раз вызываю в начале его функцию TimerTimer.

1 час назад, Fedor K сказал:

Можете мне сделать тестовый сервер и клиент, чтобы я смог у себя проверить? Тогда мой ответ будет более детальным.

Добавил файлы. Код довольно короткий. Частности по работе с данными убрал. Все что касается работы клиента и сервера осталось. Напомню Сервер приложение Win32 VCL. Клиент приложение для Abdroid FMX. Пакеты данных размером от нескольких десятков байт до 5 килобайт максимум и редко.

 

1 час назад, Fedor K сказал:

procedure T<какое-то имя класа>.Read;

Это процедура срабатывания таймера?

Desktop.7z

Изменено пользователем Wovan2
Ссылка на комментарий
  • 0

А проект скинуть нельзя? В pas довольно много компонентов... и не очень хочется собирать проект для тестирования

PS. Отступ в 1 пробел - это зло :)

Изменено пользователем Rusland
Ссылка на комментарий
  • 0
46 минут назад, Rusland сказал:

Отступ в 1 пробел - это зло

Привычка, выработанная годами:huh: Маленький монитор как бы способствует

В Delphi 10 плохо разбираюсь в файлах проекта. Запаковал всю папку проекта. Но кода, конечно, стало больше. Спасибо за проявленное внимание.

 

Socket.7z

Изменено пользователем Wovan2
Ссылка на комментарий
  • 0

Клиентская часть при старте выдает segmentaion fault, без подробностей.

apk-expansion.dex.jar - это что за файл? 

 

Пардон, оказалось что стоит Release-режим... 

 

В таймере 

 Server := 'Сервер ' + IdTCPClient.Socket.Binding.PeerIP +
           ':' + IntToStr(IdTCPClient.Socket.Binding.PeerPort);

вызывает ошибку, если клиент не достучался до сервера.

Изменено пользователем Rusland
Ссылка на комментарий
  • 0
15 минут назад, Rusland сказал:

вызывает ошибку, если клиент не достучался до сервера.

Согласен. Здесь поторопился. Если нет соединения, то и свойства все эти - швах. Надо просто свойства клиента Host и Port. Спасибо. Но основная проблема, я так подозреваю, не в этом.

Изменено пользователем Wovan2
Ссылка на комментарий
  • 0

TimerTimer(nil); - вот это что такое? Почему nil?

Вылеты происходят в FMX.Platform.Timer.Android когда связь с сервером не получилось установить... а почему, я не понял (не понял почему при первой попытке не удается связаться с сервером и не понял откуда вылеты).

PS Серверная часть нормально не закрывается, если были коннекты. Проверьте FormClose

Изменено пользователем Rusland
Ссылка на комментарий
  • 0

И всё же таймер должен идти лесом. Отчего не сделать поток?

procedure TMyThread.Execute;
begin
  while not Terminated do begin
    try
      if (not IdTCPClient1.Connected) then begin
        IdTCPClient1.Connect;
      end;
    except
      Sleep(500);
      continue;
    end;


// а тут если коннект есть уже выгребаем что и как надо

...

А запросы серверу из главного потока. Функции запроса можно также в классе потока реализовать для нахождения этого тисипишного функционала в одном месте

  TMyThread = class(TThread)
  private
  protected
    procedure Execute; override;
  public
    IdTCPClient1: TIdTCPClient;
    function SendPacket(ip: String): Boolean;  // это вызываем из главного потока если надо на сервер что-то послать
    constructor Create(host: String; port: Integer; toconnect: Integer; toread: Integer);
    destructor Destory;
  end;

 

Ссылка на комментарий
  • 0
В 17.11.2017 в 16:19, Wovan2 сказал:

Посмотрел ваш пример, все зависания и вылеты с ошибками связаны с обращением к пустым объектам, попыткой обработать все в одном обработчике. Исправить клиент дело не благодарное, поэтому сделал пример по работе с TCP сокетом с возможностью автоподключения (тык). Проверил на нескольких устройствах, полет нормальный.

Основные замечания:

  1. Не используйте FormActivate событие, тем более на мобильной платформе. Его обработка замораживает приложение. В примере посмотрите вариант обхода.
  2. TIniFile нет смысла использовать каждый раз для считывания настроек. 1 раз считали при старте приложения и больше к файлу не обращаемся.
  3. Хранить настройки в компонентах (edSettingHost.Text и т.п.). Создание свойства отнимет максимум минуту, а выгоду даст существенную.
  4. TCP сокет соединения следуют принимать как асинхронные, а не как запрос-ответ. Это предусматривает получение команды сервером, какое-то выполнение и лишь потом отправка на клиент. Поэтому попробуйте отказаться от использования GetFromServer.

Сервер только запускал для проверки клиента, пару раз ловил outofmemory и access violation, закрыться тоже не захотел по-хорошему. Поэтому желательно его тоже довести до ума.

Ссылка на комментарий
  • 0
В 18.11.2017 в 21:38, Fedor K сказал:

Не используйте FormActivate событие

Вариант обхода с помощью стартового таймера, в общих чертах понял.

В 18.11.2017 в 21:38, Fedor K сказал:

TIniFile нет смысла использовать каждый раз для считывания настроек

Я, собственно, и читаю этот файл один раз. Второй раз, что встречается в коде, это изменение настроек в этом файле. Возможно используется 1 раз за все время жизни программы.

В 18.11.2017 в 21:38, Fedor K сказал:

Хранить настройки в компонентах (edSettingHost.Text и т.п.).

И это есть в коде. Хотя и не совсем красиво. В Window я обычно так не делаю.

 

В 18.11.2017 в 21:38, Fedor K сказал:

TCP сокет соединения следуют принимать как асинхронные, а не как запрос-ответ. Это предусматривает получение команды сервером, какое-то выполнение и лишь потом отправка на клиент. Поэтому попробуйте отказаться от использования GetFromServer.

Этот пункт самый непонятный. Не понимаю как получать ответ с сервера в асинхронном режиме. Ведь так или иначе запрос и ответ надо как-то синхронизировать? И не понятно причем здесь отказ от GetFromServer, это  всего навсего процедура реализующая запрос - ответ в одном месте (асинхронность, наверное, можно организовать и в ее рамках?). В итоге, на мой взгляд, получилась ситуация как в Tethering, запрос в одной процедуре, ответ в другой, и не понятно какой ответ от какого запроса(если запросов было несколько). Как это можно соединить?

На клиенте потоки используются исключительно для уменьшения подвисаний интерфейса программы? Ведь соединение с сервером принципиально одно.

Спасибо за проявленную заинтересованность:)

Изменено пользователем Wovan2
Ссылка на комментарий
  • 0
В 17.11.2017 в 16:57, Rusland сказал:

TimerTimer(nil); - вот это что такое? Почему nil?

Не знаю как в D10 в D7 это просто вызов процедуры с пустым параметром. Формально туда можно было засунуть Sender от вызывающей процедуры, если бы этот параметр как-то обрабатывался в TimerTimer. А раз он никак не обрабатывается, то можно втиснуть пустышку.

Ссылка на комментарий
  • 0
В 18.11.2017 в 21:38, Fedor K сказал:

сделал пример по работе с TCP сокетом с возможностью автоподключения (тык).

Почему-то отказывается компилироваться. Что не так?

CreateTimer.thumb.png.a649b566f7b55f82f85425d6c0628baa.png

Изменено пользователем Rusland
Ссылка на комментарий
  • 0

Просветите, пожалуйста, насчет IOHandler. Я явно его не задаю, однако код IdTCPClient.IOHandler.Write(s, s.Size, True); отрабатывает без ошибок и корректно. Нужно его задавать явно?

И еще на сервере ошибка происходит при закрытии формы на коде 

if Assigned(IdTCPServer)
  then IdTCPServer.Active := False;

а именно на IdTCPServer.Active := False, поскольку сам объект существует. (ошибка обращения к памяти, посмотрел по отладчику пару раз проходит по exception и в конце концов AV). Если убрать вообще остановку сервера, то при закрытии ошибок нет. Интересно как в этом случае насчет утечки памяти?

Ссылка на комментарий
  • 0

И еще подскажите, кто знает, аналог виндовских часиков ожидания на Androidе как реализовать. Видел какие-то вращающиеся кружки, или что-то подобное.

Изменено пользователем Wovan2
Ссылка на комментарий

Присоединяйтесь к обсуждению

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

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...