2008-10-14 8 views
138

Как я могу использовать HttpWebRequest (.NET, C#) асинхронно?Как использовать HttpWebRequest (.NET) асинхронно?

+1

использование асинхронным http://msdn.microsoft.com/en-us /library/system.net.webrequest.endgetrequeststream.aspx – 2010-03-26 22:55:41

+1

на какое-то время, я задавался вопросом, пытаетесь ли вы прокомментировать рекурсивный поток? – 2010-05-20 03:17:18

+0

Вы также можете увидеть следующее, для довольно полного примера того, что делает Джейсон: http://stuff.seans.com/2009/01/05/using-httpwebrequest-for-asynchronous-downloads/ Sean – 2009-04-08 19:43:41

ответ

115

Применение HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest; 

void StartWebRequest() 
{ 
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); 
} 

void FinishWebRequest(IAsyncResult result) 
{ 
    webRequest.EndGetResponse(result); 
} 

Функция обратного вызова вызывается при завершении асинхронной операции. Вам нужно как минимум позвонить EndGetResponse() с этой функции.

+12

BeginGetResponse не так полезен для использования async. Кажется, он блокируется при попытке связаться с ресурсом. Попробуйте отключить сетевой кабель или дать ему неправильный uri, а затем запустить этот код. Вместо этого вам, вероятно, нужно запустить GetResponse во втором потоке, который вы предоставляете. – Ash 2012-10-21 03:52:46

+2

@AshleyHenderson - Не могли бы вы предоставить мне образец? – Tohid 2012-11-14 14:28:01

62

Учитывая ответ:

HttpWebRequest webRequest; 

void StartWebRequest() 
{ 
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null); 
} 

void FinishWebRequest(IAsyncResult result) 
{ 
    webRequest.EndGetResponse(result); 
} 

Вы можете отправить указатель запроса или любой другой объект, как это:

void StartWebRequest() 
{ 
    HttpWebRequest webRequest = ...; 
    webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest); 
} 

void FinishWebRequest(IAsyncResult result) 
{ 
    HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse; 
} 

Greetings

3
public void GetResponseAsync (HttpWebRequest request, Action<HttpWebResponse> gotResponse) 
    { 
     if (request != null) { 
      request.BeginGetRequestStream ((r) => { 
       try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash 
        HttpWebResponse response = request.EndGetResponse (r); 
        if (gotResponse != null) 
         gotResponse (response); 
       } catch (Exception x) { 
        Console.WriteLine ("Unable to get response for '" + request.RequestUri + "' Err: " + x); 
       } 
      }, null); 
     } 
    } 
57

Все до сих пор было не так, потому что BeginGetResponse() выполняет некоторую работу над текущей нитью. Из documentation:

Метод BeginGetResponse требует некоторых синхронных задач настройки для полной (разрешение DNS, прокси обнаружения и сокет-соединение TCP, к примеру), прежде чем этот метод становится асинхронным. В результате этот метод никогда не следует вызывать в потоке пользовательского интерфейса (UI) , потому что для завершения начальных синхронных задач установки может потребоваться значительное время (до нескольких минут ) в качестве исключения для ошибка или метод преуспевает.

Так, чтобы сделать это правильно:

void DoWithResponse(HttpWebRequest request, Action<HttpWebResponse> responseAction) 
{ 
    Action wrapperAction =() => 
    { 
     request.BeginGetResponse(new AsyncCallback((iar) => 
     { 
      var response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar); 
      responseAction(response); 
     }), request); 
    }; 
    wrapperAction.BeginInvoke(new AsyncCallback((iar) => 
    { 
     var action = (Action)iar.AsyncState; 
     action.EndInvoke(iar); 
    }), wrapperAction); 
} 

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

HttpWebRequest request; 
// init your request...then: 
DoWithResponse(request, (response) => { 
    var body = new StreamReader(response.GetResponseStream()).ReadToEnd(); 
    Console.Write(body); 
}); 
7

Я закончил с использованием BackgroundWorker, это, безусловно, асинхронно в отличие от некоторых из перечисленных выше решений, он обрабатывает возвращение к GUI нить для вас, и это очень легко понять.

Это также очень легко обрабатывать исключения, так как они в конечном итоге в методе RunWorkerCompleted, но убедитесь, что вы читаете это: Unhandled exceptions in BackgroundWorker

Я использовал WebClient, но очевидно, что вы могли бы использовать HttpWebRequest.GetResponse, если вы хотите.

var worker = new BackgroundWorker(); 

worker.DoWork += (sender, args) => { 
    args.Result = new WebClient().DownloadString(settings.test_url); 
}; 

worker.RunWorkerCompleted += (sender, e) => { 
    if (e.Error != null) { 
     connectivityLabel.Text = "Error: " + e.Error.Message; 
    } else { 
     connectivityLabel.Text = "Connectivity OK"; 
     Log.d("result:" + e.Result); 
    } 
}; 

connectivityLabel.Text = "Testing Connectivity"; 
worker.RunWorkerAsync(); 
47

К самым простым способом является использование TaskFactory.FromAsync из TPL. Это буквально пару строк кода при использовании в сочетании с новыми async/await ключевые слова:

var request = WebRequest.Create("http://www.stackoverflow.com"); 
var response = (HttpWebResponse) await Task.Factory 
    .FromAsync<WebResponse>(request.BeginGetResponse, 
          request.EndGetResponse, 
          null); 
Debug.Assert(response.StatusCode == HttpStatusCode.OK); 

Если вы не можете использовать C# 5 компилятор, то выше можно выполнить с помощью Task.ContinueWith метода:

Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, 
            request.EndGetResponse, 
            null) 
    .ContinueWith(task => 
    { 
     var response = (HttpWebResponse) task.Result; 
     Debug.Assert(response.StatusCode == HttpStatusCode.OK); 
    }); 
2

.NET изменилась, поскольку многие из этих ответов были опубликованы, и я хотел бы предоставить более современный ответ. Используйте метод асинхронного начать Task, который будет работать в фоновом потоке:

private async Task<String> MakeRequestAsync(String url) 
{  
    String responseText = await Task.Run(() => 
    { 
     try 
     { 
      HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; 
      WebResponse response = request.GetResponse();    
      Stream responseStream = response.GetResponseStream(); 
      return new StreamReader(responseStream).ReadToEnd();    
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Error: " + e.Message); 
     } 
     return null; 
    }); 

    return responseText; 
} 

Чтобы использовать метод асинхронного:

String response = await MakeRequestAsync("http://example.com/");