Bazaprogram.ru

Новости из мира ПК
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Net httpclient delphi

HTTP Client API в Delphi

Как-то относительно незаметно для меня прошло одно из нововведений Delphi — HTTP

Итак, что из себя представляет HTTP Client API в Delphi 10.3 Rio (предыдущие версии не рассматриваю по той причине, что они у меня не установлены). В папке %InstallDir%sourcertlnet обнаружены следующие модули, относящиеся непосредственно к работе HTTP-протоколом:

  • System.Net.FileClient.pas
  • System.Net.HttpClient.pas
  • System.Net.HttpClient.Android.pas
  • System.Net.HttpClient.Mac.pas
  • System.Net.HttpClient.Win.pas
  • System.Net.HttpClientComponent.pas
  • System.Net.Mime.pas
  • System.Net.Socket.pas
  • System.Net.URLClient.pas
  • System.NetConsts.pas

Наличие файлов типа System.Net.HttpClient.ХХХХХХ.pas уже как бы намекает на то, что HTTP Client API можно свободно использовать в разных операционных системах, что хорошо.

В модуле System.Net.HttpClientComponent.pas содержатся два компонента, которые Вы можете обнаружить также в палитре компонентов на вкладке Net:

  1. TNetHTTPClient — это ваш HTTP-клиент для работы с сервером
  2. TNetHTTPRequest — ваш запрос к HTTP-серверу

Возник сразу вопрос: почему не сделали тогда и третий компонент, например, TNetHTTPResponse (ответ сервера), по аналогии с тем, что сделано в REST Clietn Library, тем более, что класс ответа как таковой имеется. Но об ответах — позже.

TNetHTTPClient

TNetHTTPClient в плане построения напоминает аналогичный компонент от Indy, то есть каждый отдельный HTTP-метод (GET, POST, PUT и т.д.) представляет собой отдельный метод компонента и, соответственно, некоторые методы перезагружны (помечены как overload).

Попробуем воспользоваться HTTP-клиентом в Delphi, отправить какой-нибудь запрос и получить ответ. На форме тестового приложения я поместил TMemo для вывода результата, одну кнопку и компонент TNetHTTPClient. Обработчик метода OnClick кнопки сделал следующим:

Результат не заставил себя долго ждать:

Этот пример примечателен сам по себе по следующим причинам:

  • во-первых, TNetHTTPClient сделал правильный редирект на необходимый адрес, так как блог webdelphi в настоящее время доступен по https и запрос на адрес http://webdelphi.ru возвращает код 301 (Moved Permanently).
  • во-вторых, так как мне необходимо было только вывести содержимое главной страницы в Memo — я не создал ни одной лишней переменной в коде.

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

Управление перенаправлением в TNetHTTPClient

У компонента определено следующее свойство:

Это свойство позволяет нам настроить различное поведение компонента в зависимости от используемого HTTP-метода и возвращаемого кода статуса. Например, мы можем делать перенаправление, если при использовании метода POST сервер вернул нам код 301 и не перенаправлять пользователя на другой адрес, если сервер вернет код 302 и так далее.

Также определены и такие свойства как:

Соответственно, свойство HandleRedirects позволяет или запрещает делать автоматические перенаправления, а MaxRedirects определяет максимально возможное количество перенаправлений пользователя.

В отличие от Indy, которая норовит по каждому поводу выкинуть какое-нибудь исключение, TNetHTTPClient работает «по-тихому», как Synapse, то есть не считает, что коды 3хх — это тот случай, когда надо создавать исключение и останавливать работу, а просто сохраняет ответ и ожидает дальнейших действий.

Двигаемся далее. Рассмотрим как работают различные методы в TNetHTTPClient

HTTP-методы в TNetHTTPClient

Компонент поддерживает следующие стандартные HTTP-методы:

Подводные камни HttpClient в .NET

Продолжая серию статей о «подводных камнях» не могу обойти стороной System.Net.HttpClient, который очень часто используется на практике, но при этом имеет несколько серьезных проблем, которые могут быть сразу не видны.

Достаточно частая проблема в программировании — то, что разработчики сфокусированы только на функциональных возможностях того или иного компонента, при этом совершенно не учитывают очень важную нефункциональную составляющую, которая может влиять на производительность, масштабируемость, легкость восстановления в случае сбоев, безопасность и т.д. Например, тот же HttpClient — вроде бы и элементарный компонент, но есть несколько вопросов: сколько он создает параллельных соединений к серверу, как долго они живут, как он себя поведет, если DNS имя, к которому обращался ранее, будет переключено на другой IP адрес? Попробуем ответить на эти вопросы в статье.

  1. Утечка соединений
  2. Лимит одновременных соединений с сервером
  3. Долгоживущие соединения и кеширование DNS

Первая проблема HttpClient — неочевидная утечка соединений. Достаточно часто мне приходилось встречать код, где он создается на выполнение каждого запроса:

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

И по завершении посмотреть список открытых соединений через netstat:

Здесь ключ -n использован для того, чтобы ускорить вывод результата, так как в противном случае netstat для каждого IP будет искать доменное имя, а 178.248.237.68 — IP адрес habr.com на момент написания этой статьи.

Итого, мы видим, что несмотря на конструкцию using, и даже несмотря на то, что выполнение программы полностью завершилось, соединения с сервером остались «висеть». И висеть они будут столько времени, сколько указано в ключе реестра HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersTcpTimedWaitDelay.

С ходу может возникнуть вопрос — а как ведет себя .NET Core в таких случаях? Что в Windows, что в Linux — точно также, потому что подобное удержание соединений происходит на уровне системы, а не на уровне приложения. Статус TIME_WAIT является специальным состоянием сокета после его закрытия приложением, и нужно это для обработки пакетов, которые все еще могут идти по сети. Для Linux длительность такого состояния указана в секундах в /proc/sys/net/ipv4/tcp_fin_timeout, и ее, конечно же, можно менять, если нужно.

Вторая проблема HttpClient — неочевидный лимит одновременных соединений с сервером. Предположим, вы используете привычный вам .NET Framework 4.7, с помощью которого разрабатываете высоконагруженный сервис, где есть обращения к другим сервисам по HTTP. Потенциальная проблема с утечкой соединений учтена, поэтому для всех запросов используется один и тот же экземпляр HttpClient. Что может быть не так?

Проблему легко можно увидеть, выполнив следующий код:

Указанный в ссылке ресурс позволяет задержать ответ сервера на указнное время, в данном случае — 5 секунд.

Как несложно заметить после выполнения приведенного выше кода — через каждые 5 секунд приходит всего по 2 ответа, хотя было создано 10 одновременных запросов. Связано это с тем, что взаимодействие с HTTP в обычном .NET фреймворке, помимо всего прочего, идет через специальный класс System.Net.ServicePointManager, контролирующий различные аспекты HTTP соединений. В этом классе есть свойство DefaultConnectionLimit, указывающее, сколько одновременных подключений можно создавать для каждого домена. И так исторически сложилось, что по умолчанию значение свойства равно 2.

Читать еще:  Rpcss что это

Если в указанный выше пример кода добавить в самом начале

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

И прежде чем перейти к тому, как это работает в .NET Core, следует чуть больше сказать о ServicePointManager. Рассмотренное выше свойство указывает количество соединений по умолчанию, которое будет использоваться при последующих соединениях с любым доменом. Но вместе с этим, есть возможность управления параметрами для каждого доменного имени индивидуально и делается это через класс ServicePoint:

После выполнения этого кода любое взаимодействие с Хабром через один и тот же экземпляр HttpClient будет использовать 5 одновременных соединений, а с сайтом «slowwly» — 3 соединения.

Здесь есть еще интересный нюанс — лимит количества соединений для локальных адресов (localhost) по умолчанию равен int.MaxValue. Просто посмотрите результаты выполнения этого кода, предварительно не устанавливая DefaultConnectionLimit:

Теперь все-таки перейдем к .NET Core. Хоть ServicePointManager и по-прежнему существует в пространстве имен System.Net, на поведение HttpClient в .NET Core он не влияет. Вместо этого, параметрами HTTP подключения можно управлять с помощью HttpClientHandler (или SocketsHttpHandler, о котором поговорим позже):

Приведенный выше пример будет себя вести точно также, как и начальный пример для обычного .NET Framework — устанавливать только 2 соединения одновременно. Но если убрать строчку с установкой свойства MaxConnectionsPerServer, количество одновременных соединений будет намного выше, так как по умолчанию в .NET Core значение этого свойства равно int.MaxValue.

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

Представим, что разрабатываемая нами система должна нормально работать без принудительного перезапуска в случае, если сервер, с которым она взаимодействует, перешел на другой IP адрес. Например, в случае переключения на другой датацентр из-за сбоя в текущем. Даже если постоянное соединение будет разорвано из-за сбоя в первом датацентре (что тоже может произойти небыстро), кеш DNS не позволит нашей системе быстро отреагировать на такое изменение. То же самое актуально для обращений к адресу, на котором балансировка нагрузки делается через DNS round-robin.

В случае «обычного» .NET фреймворка этим поведением можно управлять через ServicePointManager и ServicePoint (все приведенные ниже параметры принимают значения в миллисекундах):

  • ServicePointManager.DnsRefreshTimeout — указывает, сколько времени будет закеширован полученный IP адрес для каждого доменного имени, значение по умолчанию — 2 минуты (120000).
  • ServicePoint.ConnectionLeaseTimeout — указывает, сколько времени соединение может удерживаться открытым. По умолчанию лимита времени жизни для соединений нет, любое соединение может удержаться сколь угодно долго, так как этот параметр равен -1. Установка его в 0 приведет к тому, что каждое соединение будет закрываться сразу после выполнения запроса.
  • ServicePoint.MaxIdleTime — указывает, после какого времени бездействия соединение будет закрыто. Бездействие означает отсутствие передачи данных через соединение. По умолчанию значение этого параметра равно 100 секунд (100000).

Теперь для улучшения понимания этих параметров соединим их все в одном сценарии. Предположим, DnsRefreshTimeout и MaxIdleTime никто не менял и они равны 120 и 100 секунд соответственно. При этом ConnectionLeaseTimeout был установлен в 60 секунд. Приложение устанавливает всего одно соединение, через которое раз в 10 секунд посылает запросы.

С такими настройками соединение будет закрываться каждые 60 секунд (ConnectionLeaseTimeout) даже несмотря на то, что по нему периодически идет передача данных. Закрытие и пересоздание будет проиходить таким образом, чтобы не мешать корректному выполнению запросов — если время истекло, а в данный момент запрос еще выполняется, соединение будет закрыто после завершения запроса. При каждом пересоздании соединения соответствующий IP адрес в первую очередь будет браться из кеша, и только если время жизни его разрешения истекло (120 секунд), система пошлет запрос на DNS сервер.

Параметр MaxIdleTime в этом сценарии не будет играть роли, так как соединение не бездействует дольше чем 10 секунд.

Оптимальное соотношение этих параметров сильно зависит от конкретной ситуации и нефункциональных требований:

  • Если вообще не предполагается прозрачное переключение IP адресов за доменным именем, к которому обращается ваше приложение, и при этом необходимо минимизировать затраты на сетевые подключения, то настройки по умолчанию выглядят хорошим вариантом.
  • Если есть надобность в переключении между IP адресами в случае сбоев, то можно поставить DnsRefreshTimeout в 0, а ConnectionLeaseTimeout — в подходящее вам неотрицательное значение. Какое конкретно — очень зависит от того, насколько быстро нужно переключиться на другой IP. Очевидно, что хочется иметь как можно более быструю реакцию на сбой, но здесь нужно найти оптимальное значение, которое, с одной стороны, обеспечивает допустимое время переключения, с другой стороны — не ухуджает пропускную способность и время отклика системы слишком частыми пересозданиями соединений.
  • Если нужна как можно более быстрая реакция на изменение IP адреса, например, как в случае балансировки через DNS round-robin, можно попробовать поставить DnsRefreshTimeout и ConnectionLeaseTimeout в 0, но это будет крайне расточительно: для каждого запроса сперва будет опрашиваться DNS сервер, после чего будет заново устанавливаться соединение с целевым узлом.
  • Возможно, есть и ситуации, когда установка ConnectionLeaseTimeout в 0 при ненулевом DnsRefreshTimeout может быть полезной, но я с ходу не могу придумать соответствующий сценарий. Логически это будет означать, что для каждого запроса соединения будут создаваться заново, но при этом IP адреса по возможности будут браться из кеша.
Читать еще:  Как настроить ява скрипт

Ниже приведен пример кода, с помощью которого можно понаблюдать за поведением описанных выше параметров:

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

Тут же следует сказать, как управлять описанными параметрами в .NET Core. Настройки из ServicePointManager, как и в случае с ConnectionLimit, работать не будут. В Core есть специальный тип HTTP обработчика, который реализует два из трех описанных выше параметров — SocketsHttpHandler:

Параметра, который управляет временем кеширования DNS записей, в .NET Core пока нет. Тестовые примеры показывают, что кеширование не работает — при создании нового соединения DNS разрешение выполняется заново, соответственно для нормальной работы в условиях, когда запрашиваемое доменное имя может переключаться между разными IP адресами, достаточно выставить PooledConnectionLifetime в нужное значение.

Using an HTTP Client

The RTL provides two components that you can use to send HTTP requests to servers and handle their responses:

  • TNetHTTPClient
  • TNetHTTPRequest

Note: Alternatively, you can use an instance of THTTPClient to manage your HTTP requests.

Contents

Configuring Your Application at Design Time

Creating an HTTP Client

To create an HTTP client that you can use to send HTTP request, drag a TNetHTTPClient from the Tool Palette onto a form or data module of your application. The TNetHTTPClient does not require any initial configuration.

Creating and Configuring HTTP Requests

You also need to drop one or more TNetHTTPRequest components onto your form or data module.

After you drop your new HTTP request component, double-click its Client property on the Object Inspector so that its value is the HTTP client component that you previously dropped.

You can use any number of request components that you like. However, if you need to send several different HTTP requests, a better approach might be to use a single HTTP request component and reconfigure that component at run time to send each request.

Sending a Request and Handling its Response

Sending an HTTP Request

To send an HTTP request, you must specify at least the following:

The HTTP request method determines the action that you want the server to perform on the resource located at the specified URL, such as sending you the data of the resource or updating the resource with data that you include in your request.

How you send an HTTP request using a TNetHTTPRequest component depends on the HTTP request method that you want to use.

Note: Functions that send a request, either Execute or method-specific functions, all are blocking. That is, these functions do not return until there is a response from the server. See Tracking the Progress of the Download of the Response Data below.

Sending an HTTP Request Using the DELETE, GET, HEAD, OPTIONS, or TRACE HTTP Request Methods

The following HTTP request methods do not require input data other than the target resource URL:

To send an HTTP request using any of these HTTP request methods, first configure your request at design time:

  1. Configure the URL property with the URL of the target resource.
  2. Configure the MethodString property with the name of the HTTP request method that you want to use. Note: HTTP request method names are case-sensitive.

Then call Execute to send your request.

Sending an HTTP Request Using the MERGE, PATCH, or PUT HTTP Request Methods

In addition to the target resource URL, the following HTTP request methods require input data:

To send an HTTP request using any of these HTTP request methods, call its matching function as shown in the table below. In your call, specify the URL of the target resource as your first argument and an instance of TStream as your second argument.

HTTP Request MethodTNetHTTPRequest Function
PUTPut
PATCHPatch
MERGEMerge
PATCH through PUT*PatchAlternative
MERGE through PUT*MergeAlternative

* Some HTTP proxies do not support the latest standard HTTP request methods (such as PATCH) or non-standard HTTP request methods (such as MERGE). In these cases, you can proxy HTTP request methods through another HTTP request method. To do this, the request includes headers that specify the intended HTTP request method.

Sending an HTTP Request Using the POST HTTP Request Method

In addition to the target resource URL, the POST HTTP request method requires input data.

To send an HTTP request using the POST HTTP request method, call Post. In your call, you must specify the URL of the target resource as your first argument. The other arguments depend on how you want to provide the input data of your request:

  • To send the content of a local file, specify the local path to that file as the second argument of your call.
  • To send the content of a stream, prov >

Handling an HTTP Response

When the target server sends a response to your request, the OnRequestCompleted event of your HTTP request component occurs. Define an event handler for this event to read the data from the server response. If there is an error during your request, the OnRequestError event of your HTTP request component occurs instead.

The event handler of OnRequestCompleted receives two parameters:

  • Sender is your HTTP request component.
  • AResponse in an object that implements the IHTTPResponse interface.

You can obtain all the response data from AResponse :

  • Version indicates the version of the HTTP protocol that the server used.
  • StatusCode contains the status code of the response, and StatusText contains a human-readable text that describes the status, such as «OK» when StatusCode is 200.
  • ContentAsString contains the body of the response as a string. You may also read the raw response data (including both headers and body) as a stream from ContentStream.
  • You may read the response headers using Headers, HeaderValue, ContainsHeader, or any of the following helper properties that prov >Managing Cookies

The AllowCookies property of the HTTP client component allows you to specify whether you want to accept the cookies that the server sends in the response. If AllowCookies is True , the HTTP client saves the received cookies in his CookieManager.

Note: The HTTP client always adds its existing cookies to the request, regardless of the value of AllowCookies.

Handling Redirects

Use the HandleRedirects property to control how the HTTP client component handles redirects. The default value is True , which means that the HTTP client component automatically follows redirects.

Use the MaxRedirects property of the HTTP client component to specify a maximum number of redirects that you allow the component to follow. If the HTTP client component reaches the specified number of maximum redirects, it raises an exception of type ENetHTTPRequestException.

Handling the Download of the Response Data

As your HTTP client downloads the response to your request, the OnReceiveData event of your HTTP request object keeps occurring. Provide an event handler for OnReceiveData to keep track of the progress of the download of the response data. Your event handler receives the following parameters:

  • Sender , which is your HTTP request object.
  • AContentLength , which is the expected length of the response data in number of bytes.
  • AReadCount , which is the number of bytes of the response data that the HTTP client has downloaded so far.
  • Abort , which is a Boolean parameter that allows you to cancel the download.

When the download finishes, OnReceiveData occurs for the last time, and the values of AContentLength and AReadCount are the same. OnRequestCompleted occurs soon afterwards.

Tracking the Progress of the Download of the Response Data

To track the progress of the downloaded data, use the values of AContentLength and AReadCount in the OnReceiveData event handler. This is an example that calculates the percentage of the downloaded data:

Разработка клиент-серверного приложения на Delphi

В этой статье я постараюсь поделиться опытом по созданию клиент-серверных приложений с использованием стандартных компонентов Indy HTTPServer и Indy HTTPClient.

Создадим 2 проекта: MyServer.dpr и MyClient.dpr. На главную форму MyServer добавим компонент TIdHTTPServer (закладка Indy Servers) и TMemo (для отображения клиентского запроса). Немного отредактируем параметры сервера: «Active=true», «DefaultPort=56001» (можно указать любой другой) и «Name=MyServer». У этого компонента есть событие OnCommandGet. Переходим к его редактированию.

Значение ARequestInfo.Params.Text хранит в себе ту информацию, которая пришла от клиентского приложения. AResponseInfo.ContentText — результат, который сервер должен вернуть. Пока не будем его трогать, а перейдём к настройке приложения MyClient.

Всё, что здесь нужно сделать — положить на форму компонент TIdHTTPClient (закладка Indy Clients). Настраивать у него ничего не надо, разве что изменим имя на «MyClient». Теперь переходим к самому интересному. Кладём на форму обыкновенную кнопку и по событию OnClick делаем следующее:

Разберём этот код по частям. Про его начало «http://127.0.0.1:56001/?» писать особо нечего. Это обычный http запрос. Главное правильно указать IP-адрес и порт сервера. Дальше начинается перечисление параметров запроса и их значений, разделенных символом &. Как правило, основной целью работы клиент-серверного приложения является передача и выполнение команд, поэтому назовём первый параметр «command» . Используется он для того, чтобы сервер знал, что ему следует выполнять. Вот пример рабочего запроса:

В данном случае мы просим сервер выполнить вход в систему пользователя «admin» с паролем, зашифрованным при помощи md5.

Теперь немного перепишем обработку события сервера OnCommandGet таким образом, чтобы при получении команды «userLogin» он выполнял функцию UserLogin . В случае успешного её выполнения, клиенту вернётся результат «result_OK», иначе «result_Error».

Вот таким нехитрым способом можно создавать клиент-серверные приложения. Чтобы не запутаться в именах параметров, рекомендую сделать отдельный unit с константами, который знали бы и сервер, и клиент. В следующей статье я объясню, как оптимизировать передачу данных по http и упростить их обработку, а также поделюсь своими компонентами, упрощающими подобную разработку.

Узнать подробнее можно здесь

Ссылки по теме

Помощь
Задать вопрос
программы
обучение
экзамены
компьютеры
Бесплатный звонок
ICQ-консультанты
Skype-консультанты
Общая справка
Как оформить заказ
Тарифы доставки
Способы оплаты
Прайс-лист
Карта сайта

Популярные статьи
Информационная безопасность Антивирусное ПО и защита от спама Eset Software Лаборатория Касперского


Бестселлеры
Курсы обучения «Atlassian JIRA — система управления проектами и задачами на предприятии»
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год. Электронный ключ
Microsoft Windows 10 Профессиональная 32-bit/64-bit. Все языки. Электронный ключ
Microsoft Office для Дома и Учебы 2019. Все языки. Электронный ключ
Курс «Oracle. Программирование на SQL и PL/SQL»
Курс «Основы TOGAF® 9»
Microsoft Windows Professional 10 Sngl OLP 1 License No Level Legalization GetGenuine wCOA (FQC-09481)
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год. Электронный ключ
Windows Server 2016 Standard
Курс «Нотация BPMN 2.0. Ее использование для моделирования бизнес-процессов и их регламентации»
Антивирус ESET NOD32 Antivirus Business Edition
Corel CorelDRAW Home & Student Suite X8

О нас
Интернет-магазин ITShop.ru предлагает широкий спектр услуг информационных технологий и ПО.

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

Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.

Ссылка на основную публикацию
Adblock
detector