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

Асинхронный NetHTTPClient


DMS

Вопрос

Ребята, подскажите пожалуйста, никогда не решал задач такой сложности.

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

Решил взять NetHTTPClient, установил Asynchronous := True, чтобы приложение не зависало. Анализирую событие OnRequestCompleted и теперь не могу разобраться со всей этой кашей.

Как отличить, что именно и в каком случае пришло (когда сам послал Post серверу или когда по действию пользователя)? Если отправить несколько подряд запросов, как ответы от них там уживаются в OnRequestCompleted?

Somebody help me yeah!
 

 

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

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

  • 0

 

Делаем отдельный поток, в нём пул http клиентов (array of). Ну и добавляем запрос через lock, успешный запрос обрабатываем через Synchronize.

Или делаем отдельный класс-наследник TThread, опять array of и запускаем в каждом экземпляре обращение к сайту в нужный момент. Возврат значения через callback или  Synchronize. Только в этом случае надо не забывать удалять ресурсы завершившихся потоков в главном.
 

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

 

Делаем отдельный поток, в нём пул http клиентов (array of). Ну и добавляем запрос через lock, успешный запрос обрабатываем через Synchronize.

Или делаем отдельный класс-наследник TThread, опять array of и запускаем в каждом экземпляре обращение к сайту в нужный момент. Возврат значения через callback или  Synchronize. Только в этом случае надо не забывать удалять ресурсы завершившихся потоков в главном.
 

 

Поискал в Интернете примеры по поиску "TArray<TNetHTTPClient>" и "array of TNetHTTPClient" - ничего не нашел ((

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

DMS,
простейший пример (без обработки эксепшенов) отдельного класса на базе TThread:

Скрытый текст



type
  TAsyncHttp = class(TThread)
  private
    FCritical: TCriticalSection;
    [volatile] FCommand: string;
    [volatile] FIsReady: Boolean;
    FHttpClient: TNetHTTPClient;
    function GetIsReady: Boolean;
    procedure SerIsReady(Value: Boolean);
    function GetCommand: string;
    procedure SetCommand(Value: string);
    procedure Post;
  protected
    procedure Execute; override;
  public
    property Command: string read GetCommand write SetCommand;
    property IsReady: Boolean read GetIsReady;
    constructor Create;
    destructor Destroy; override;
    procedure Lock; inline;
    procedure UnLock; inline;
  end;

function TAsyncHttp.GetIsReady: Boolean;
begin
  Lock;
  try
    Result := FIsReady;
  finally
    UnLock;
  end;
end;

procedure TAsyncHttp.SetIsReady(Value: Boolean);
begin
  Lock;
  try
    FIsReady := Value;
  finally
    UnLock;
  end;
end;

function TAsyncHttp.GetCommand: string;
begin
  Lock;
  try
    Result := FCommand;
  finally
    UnLock;
  end;
end;

procedure TAsyncHttp.SetCommand(Value: string);
begin
  Lock;
  try
    FCommand := Value;
  finally
    UnLock;
  end;
end;

procedure TAsyncHttp.Post;
var
  LUrl: string;
  LSource: TStrings;
  LResult: TStringStream;
begin
  Lock;
  try
    LUrl := FCommand;
    FCommand := '';
    FIsReady := False;
  finally
    UnLock;
  end;
  LSource := TStrings.Create;
  try
    LResult := TStringStream.Create;
    try
      FHttpClient.Post(LUrl, LSource, LResult);
      {
        todo : здесь добавить код, который возвращает в нужном виде ответ.
        Можно через Synchronize, можно через callback,
        а можно просто заполнить дополнительное свойство в этом потоке, а главный поток потом считает при IsReady = True
      }
    finally
      LResult.DisposeOf;
    end;
  finally
    LSource.DisposeOf;
  end;
  SetIsReady(True);
end;

procedure TAsyncHttp.Execute;
begin
  while not Terminate do
  begin
    if Command <> '' then
      Post;
    Sleep(1);
  end;
end;

constructor TAsyncHttp.Create;
begin
  inherited Create(True);
  FCritical := TCriticalSection.Create;
  FHttpClient := TNetHTTPClient.Create(nil);
  FHttpClient.Asynchronous := False;
  FCommand := '';
  FIsReady := True;
end;

destructor TAsyncHttp.Destroy;
begin
  try
    FHttpClient.DisposeOf;
    FCritical.DisposeOf;
  finally
    inherited Destroy;
  end;
end;

procedure TAsyncHttp.Lock;
begin
  FCritical.Acquire;
end;

procedure TAsyncHttp.UnLock;
begin
  FCritical.Release;
end;

 

В основном потоке создаешь нужное количество экземпляров (можно начать с одного), запускаешь, потом при необходимостии передаешь команду на выполнение/получаешь ответ выполненной команды. например так (простейший пример):

LThread := TAsyncHttp.Create;

LThread.Start;

LThread.Command := 'http://www.site.com/data?param=value';

while not LThread.IsReady do

 Sleep(1);

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

Только лучше использовать THTTPClient

да, согласен

1 минуту назад, DMS сказал:

Tumaso, спасибо!

Получается, необязательно использовать асинхронный NetHTTPClient? Можно использовать синхронный, но запустить во TThread

именно так, только замените TNetHTTPClient на THTTPClient

 

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

Добрый день!

Все проще, гораздо.

  Net := THTTPClient.Create;
  Net.BeginPost(
    procedure (const Value : IAsyncResult)
    begin
      AsyncResult(Value); // Вот тут можно вызвать любую процедуру, или просто обработать результат.
    end, URL, InputStream, OutputStream, Headers);

В анонимной процедуре просто вызываете нужную процедуру, для интерактивной одну, для периодических запросов другую.

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

ну еще вариант

TTask.Run(procedure
begin
	//тут отправляем данные на сервер через THTTPClient.Post

	TThread.Synchronize(TThread.CurrentThread, procedure 
	begin
		// если нужно делаем что-то в основном потоке
	end)
end)

 

Ссылка на комментарий
  • 0
В 11.02.2018 в 01:07, Равиль Зарипов (ZuBy) сказал:

ну еще вариант


TTask.Run(procedure
begin
	//тут отправляем данные на сервер через THTTPClient.Post

	TThread.Synchronize(TThread.CurrentThread, procedure 
	begin
		// если нужно делаем что-то в основном потоке
	end)
end)

 

 

Скажите, а что происходит с потоком, если во время его выполнения закрывается форма?

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

Скажите, а что происходит с потоком, если во время его выполнения закрывается форма?

Эти вещи ни как не связаны. Т.е. НИЧЕГО, работает дальше. Поэтому нужен менеджер потоков, и нужно убийство не нужного (уже) потока при закрытии формы, или нажатии пользователем на esc и т.д. 
 

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

Эти вещи ни как не связаны. Т.е. НИЧЕГО, работает дальше. Поэтому нужен менеджер потоков, и нужно убийство не нужного (уже) потока при закрытии формы, или нажатии пользователем на esc и т.д. 
 

А как убить поток, который создан по методу Равиля Зарипова?

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

А как убить поток, который создан по методу Равиля Зарипова?

это же анонимный поток, если вы хотите управлять потоком то нужно через другие методы создавать

например http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Classes.TThread.CreateAnonymousThread

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

куда-то вы все полезли...

Давайте не забывать, что создание потока - затратная операция с точки зрения ОС.
И бесконтрольное создание кучи потоков - это плохо. Я уже не говорю про то, что фиг вы корректно прервете доп.поток, если он висит внутри синхронного Post или Get.

Самый правильный вариант в данном случае предложил Кривяков Виталий. Минимальные затраты с точки зрения ресурсов и ожидаемый результат на выходе, вызванный в правильном потоке. И никаких проблем с различением "данные от сервера" vs "данные запрошенные мной".

Вообще, на мой взгляд - сама постановка вопроса некорректна. Структура ответа должна говорить сама за себя. Т.е. начало обработки ответа сервера - единое для всего обмена. И на основе 1-2 полей из полученного (к примеру) JSON определяется - какой именно объект к нам пришел и куда направить его дальнейшую обработку.

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

куда-то вы все полезли...

Давайте не забывать, что создание потока - затратная операция с точки зрения ОС.
И бесконтрольное создание кучи потоков - это плохо. Я уже не говорю про то, что фиг вы корректно прервете доп.поток, если он висит внутри синхронного Post или Get.

Самый правильный вариант в данном случае предложил Кривяков Виталий. Минимальные затраты с точки зрения ресурсов и ожидаемый результат на выходе, вызванный в правильном потоке. И никаких проблем с различением "данные от сервера" vs "данные запрошенные мной".

Вообще, на мой взгляд - сама постановка вопроса некорректна. Структура ответа должна говорить сама за себя. Т.е. начало обработки ответа сервера - единое для всего обмена. И на основе 1-2 полей из полученного (к примеру) JSON определяется - какой именно объект к нам пришел и куда направить его дальнейшую обработку.

Я по запросу delphi THTTPClient BeginPost вообще не нагуглил ни одного примера, что как бы намекает.

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

Самый правильный вариант в данном случае предложил Кривяков Виталий. Минимальные затраты с точки зрения ресурсов и ожидаемый результат на выходе, вызванный в правильном потоке. И никаких проблем с различением "данные от сервера" vs "данные запрошенные мной".

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

А во-вторых это такой-же не контролируемый поток. Слушатель может быть давно уже мёртв, когда придёт ответ от сервера.

Короче в любой более или менее сложной задаче (больше одного запроса и/или больше одной формы) ни куда от пула запросов не деться. Дальше думайте сами как говорится.
 

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

и в следующем же абзаце говорите, что это лучшее из возможного,

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

20 часов назад, Akad сказал:

А во-вторых это такой-же не контролируемый поток.

Пруф в студию. Мне лень лезть в исходники THTTPClient, посему - жду от Вас.

20 часов назад, Akad сказал:

Слушатель может быть давно уже мёртв, когда придёт ответ от сервера.

В случае уничтожения создатель запросов должен обеспечить их завершение.  Например - дождаться окончания выполнения через EndAsyncHTTP  . Если не высвобождать ресурсы, не дожидаться завершения выполнения всяких Synchronize или Queue, и так далее - ну... это всплывет очень быстро.

20 часов назад, Akad сказал:

ни куда от пула запросов не деться.

Вот с этим согласен. С одним уточнением: пул запросов - это не пул потоков.

 

21 час назад, DMS сказал:

вообще не нагуглил ни одного примера, что как бы намекает.

Да, тут нужно включить собственные знания базовых конструкций языка.

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

Даже если на какой-либо платформе асинхронный вызов все-таки приводит к созданию потока - это не ваши проблемы, а проблемы операционной системы.

5 баллов! Нам пофиг что реально происходит, главное, что мы это не видим. Значит этого нет.

2 часа назад, kami сказал:

Пруф в студию. Мне лень лезть в исходники THTTPClient, посему - жду от Вас.

Мне здесь выложить исходники System.Net.HttpClient+ссылки на мсдн как работают сокеты в асинхронном режиме?

3 часа назад, kami сказал:

В случае уничтожения создатель запросов должен обеспечить их завершение.

И опять-же приходим к пулу. И опять-же развивая мысль нужно использовать не THTTPClient, а TIdHTTP и так далее.
 

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

ссылки на мсдн как работают сокеты в асинхронном режиме?

MSDN??? В Windows сокеты в асинхронном режиме используют не потоки, а механизм сообщений windows. Гораздо менее затратную технологию, в которой дополнительными потоками (по крайней мере в ring 3) даже не пахнет.

7 минут назад, Akad сказал:

И опять-же развивая мысль нужно использовать не THTTPClient, а TIdHTTP и так далее.

С чего это вдруг Инди, которые испокон веков работают в блокирующем режиме (создавая на каждый чих потоки, да еще и оправдываясь, что "это нормально"), стали обеспечивать асинхронность? И с каких пор они стали лучше, чем THTTPClient, если Embarcadero призвала отказываться от Indy в http(s)-обмене и использовать именно THTTPClient?

10 минут назад, Akad сказал:

Нам пофиг что реально происходит, главное, что мы это не видим. Значит этого нет.

Есть разница между созданием потока вами и созданием потока ядром операционной системы. Совсем такое небольшое отличие в плане стабильности, оптимальности кода и ресурсов, скорости, покрытии кода тестами и так далее. Не передергивайте. Я сказал "даже если". Будучи абсолютно не уверенным, что где-то во внутренностях все-таки создаются доп.потоки.

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

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

Я сразу сказал про пул запросов. Про асинхронные запросы я ничего не говорил вообще. Ну могу сказать, что это не стабильно, и привязано в wnd и пр, что намекает. Но зачем? Надо сразу всё делать правильно.Т.е. когда ответ на запрос станет доступен заказчик может быть уже давно мёртв. Я с самого начала это повторяю.

7 минут назад, kami сказал:

И с каких пор они стали лучше, чем THTTPClient

По крайней мере HTTPServer это вообще не рабочая вещь. Под хоть какой-то нагрузкой (5-6 обращений в секунду) он начинает пропускать подключения (тестил на 10.2.0). Над клиентом после этого свечку вообще держать не захотелось. Indy везде стабильно себя ведут.

11 минуту назад, kami сказал:

если Embarcadero призвала отказываться от Indy в http(s)-обмене и использовать именно THTTPClient?

Это каким образом можно от indy отказаться???? На что перейти? Кто ещё умеет https, и другие требующие ssl подключения типа почтовых. Или UDP, или ftp. Не представляю. 
 

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

Пруфов про потоки в асинхронных вызовах не будет, если я правильно понял...

2 часа назад, Akad сказал:

Т.е. когда ответ на запрос станет доступен заказчик может быть уже давно мёртв. Я с самого начала это повторяю

Я тоже могу повторить, что завершение всех инициированных собой операций - это проблема создателя этих операций, которую он обязан решить. Если прервать никак - значит дождаться завершения. Более того, возможно (но пока не могу утверждать), что с уничтожением экземпляра THTTPClient его асинхронная операция должна уйти в небытие.

2 часа назад, Akad сказал:

По крайней мере HTTPServer это вообще не рабочая вещь.

А вот здесь ткните меня носом, пожалуйста. Что за HTTPServer - в справке в классах System,Net я такого не нашел. И в исходниках (правда, у меня Берлин) тоже. Возможно - плохо искал.

2 часа назад, Akad сказал:

Кто ещё умеет https

THTTPClient. Причем - без необходимости таскания с собой всяких OpenSSL Library в разных ипостасях. Обратите внимание - я говорил именно за отказ от Indy в http(s) обмене. А не про "полный отказ".

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

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

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

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

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

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

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

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

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

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...