• 0
DMS

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

Вопросы

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

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

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

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

Somebody help me yeah!
 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 ответа на этот вопрос

  • 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

Tumaso, спасибо!

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 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

Кривяков Виталий, не совсем понял ваш код. Он корректно написан?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Посмотрите здесь описпние и возможные прототипы функции. 

http://docwiki.embarcadero.com/Libraries/Berlin/en/System.Net.HttpClient.THTTPClient.BeginPost

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 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) обмене. А не про "полный отказ".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

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

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти


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

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