Как я могу указать тайм-аут только для подключения при выполнении веб-запросов?

В настоящее время я использую код, который обрабатывает HTTP-запросы, используя класс HttpClient. Хотя вы можете указать тайм-аут для запроса, это значение применяется ко всему запросу (которое включает в себя разрешение имени хоста, установление соединения, отправку запроса и получение ответа).

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

Есть ли способ достичь этого, используя либо встроенный (BCL) класс, либо альтернативный стек HTTP-клиента?

Я кратко посмотрел на RestSharp и ServiceStack, но ни один из них не предоставляет тайм-аут только для части подключения (но поправьте меня, если я ошибаюсь).

Ответ 1

Вы можете использовать Timer, чтобы прервать запрос, если соединение занимает слишком много времени. Добавьте событие, когда время истекло. Вы можете использовать что-то вроде этого:

static WebRequest request;
private static void sendAndReceive()
{
    // The request with a big timeout for receiving large amout of data
    request = HttpWebRequest.Create("http://localhost:8081/index/");
    request.Timeout = 100000;

    // The connection timeout
    var ConnectionTimeoutTime = 100;
    Timer timer = new Timer(ConnectionTimeoutTime);
    timer.Elapsed += connectionTimeout;
    timer.Enabled = true;

    Console.WriteLine("Connecting...");
    try
    {
        using (var stream = request.GetRequestStream())
        {
            Console.WriteLine("Connection success !");
            timer.Enabled = false;

            /*
             *  Sending data ...
             */
            System.Threading.Thread.Sleep(1000000);
        }

        using (var response = (HttpWebResponse)request.GetResponse())
        {
            /*
             *  Receiving datas...
             */
        }
    }
    catch (WebException e)
    {
        if(e.Status==WebExceptionStatus.RequestCanceled) 
            Console.WriteLine("Connection canceled (timeout)");
        else if(e.Status==WebExceptionStatus.ConnectFailure)
            Console.WriteLine("Can't connect to server");
        else if(e.Status==WebExceptionStatus.Timeout)
            Console.WriteLine("Timeout");
        else
            Console.WriteLine("Error");
    }
}

static void connectionTimeout(object sender, System.Timers.ElapsedEventArgs e)
{
    Console.WriteLine("Connection failed...");
    Timer timer = (Timer)sender;
    timer.Enabled = false;

    request.Abort();
}

Времена здесь просто, например, вы должны приспособить их к вашим потребностям.

Ответ 2

.NET HttpWebRequest предоставляет 2 свойства для указания таймаута для подключения к удаленному HTTP-серверу:

  • Timeout - Получает или задает значение тайм-аута в миллисекундах для методов GetResponse и GetRequestStream.
  • ReadWriteTimeout - количество миллисекунд до истечения времени записи или чтения. Значение по умолчанию - 300 000 миллисекунд (5 минут).

Свойство Timeout является самым близким к тому, что вам нужно, но оно предполагает, что независимо от значения таймаута разрешение DNS может занимать до 15 секунд:

Запрос системы доменных имен (DNS) может занять до 15 секунд для возврата или тайм-аута. Если ваш запрос содержит имя хоста, для которого требуется разрешение, и вы установите Timeout на значение менее 15 секунд, это может занять 15 секунд или более до того, как будет выведено WebException, чтобы указать время ожидания вашего запроса.

Один из способов начать более низкий тайм-аут, чем 15 секунд для поиска DNS, - искать имя хоста самостоятельно, но для многих решений P/Invoke требует указания низкоуровневых настроек.

Задание тайм-аутов в HTTP-клиентах ServiceStack

Основные свойства HttpWebRequest Тайм-аут и ReadWriteTimeout также могут быть указаны в высокоуровневых HTTP-клиентах ServiceStack, то есть в Клиенты службы С# с:

var client = new JsonServiceClient(BaseUri) {
    Timeout = TimeSpan.FromSeconds(30)
};

Или используя ServiceStack HTTP Utils с помощью:

var timeoutMs = 30 * 1000;
var response = url.GetStringFromUrl(requestFilter: req => 
    req.Timeout = timeoutMs);

Ответ 3

Я считаю, что RestSharp имеет свойства тайм-аута в RestClient.

        var request = new RestRequest();
        var client = new RestClient
        {
            Timeout = timeout, //Timeout in milliseconds to use for requests made by this client instance
            ReadWriteTimeout = readWriteTimeout //The number of milliseconds before the writing or reading times out.
        };

        var response = client.Execute(request);
        //Handle response

Ответ 4

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

Я предлагаю вам использовать другой подход. Вы пытаетесь сделать здесь две разные вещи, которые HttpRequest делают сразу:

  • Попробуйте найти хост /stabblish соединение;
  • Передача данных;

Вы можете попытаться отделить это в два этапа.

  • Используйте Ping class (проверить это), чтобы попытаться добраться до вашего хоста и установить для него таймаут;
  • Используйте HttpRequest IF, он работает для ваших нужд (времени ожидания,

Этот процесс не должен замедлять все, так как часть разрешающих имен/маршрутов будет выполнена на первом этапе. Это не было бы полностью одноразовым.

Есть недостаток в этом решении: ваш удаленный хост должен принимать пинги. Надеюсь, это поможет.

Ответ 5

Я использовал этот метод, чтобы проверить, можно ли установить соединение. Это, однако, не гарантирует, что соединение может быть установлено путем последующего вызова в HttpWebRequest.

private static bool CanConnect(string machine)
{
    using (TcpClient client = new TcpClient())
    {
        if (!client.ConnectAsync(machine, 443).Wait(50)) // Check if we can connect in 50ms
        {
            return false;
        }
    }

    return true;
}

Ответ 6

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

что-то вроде этого возможно:

 var handler = new ManualResetEvent(false);

 request = (HttpWebRequest)WebRequest.Create(url)
 {
    // initialize parameters such as method
 }

 request.BeginGetResponse(new AsyncCallback(delegate(IAsyncResult result)
 {
      try
      {
          var request = (HttpWebRequest)result.AsyncState;

          using (var response = (HttpWebResponse)request.EndGetResponse(result))
          {
              using (var stream = response.GetResponseStream())
              {
                  // success 
              }

              response.Close();
          }
      }
      catch (Exception e)
      {
          // fail operations go here
      }
      finally
      {
          handler.Set(); // whenever i succeed or fail
      }
 }), request);

 handler.WaitOne(); // wait for the operation to complete

Ответ 7

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

webRequest.Method = "HEAD";