HttpWebRequest (.NET) zaman uyumsuz olarak nasıl kullanılır?


156

HttpWebRequest (.NET, C #) zaman uyumsuz olarak nasıl kullanabilirim?


1
Developer Fusion hakkındaki bu makaleye göz atın: developerfusion.com/code/4654/asynchronous-httpwebrequest

Jason'ın sorduğu şeyleri yapmak için aşağıdakileri de görebilirsiniz: stuff.seans.com/2009/01/05/… Sean
Sean Sexton


1
Bir an için, özyinelemeli bir konu hakkında yorum yapmaya çalışıp çalışmadığınızı merak ettim?
Kyle Hodgson

Yanıtlar:


125

kullanım HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

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

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

Eşzamansız işlem tamamlandığında geri arama işlevi çağrılır. En azından EndGetResponse()bu işlevden çağrı yapmanız gerekir .


16
BeginGetResponse, zaman uyumsuz kullanım için kullanışlı değildir. Kaynakla bağlantı kurmaya çalışırken engel gibi görünüyor. Ağ kablonuzu çıkarmayı veya hatalı biçimlendirilmiş bir uri vermeyi ve ardından bu kodu çalıştırmayı deneyin. Bunun yerine, GetResponse'yi sağladığınız ikinci bir iş parçacığında çalıştırmanız gerekir.
Kül

2
@AshleyHenderson - Bana bir örnek verebilir misiniz?
Tohid

1
@Tohid burada Unity3D ile kullandığım örnek ile tam bir sınıf .
cregox

3
webRequest.Proxy = nullİsteği önemli ölçüde hızlandırmak için eklemeniz gerekir .
Trontor

C # bana bu eski bir sınıf olduğunu söyleyen bir hata atar
AleX_

67

Cevap göz önüne alındığında:

HttpWebRequest webRequest;

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

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

İstek işaretçisini veya bunun gibi başka bir nesneyi gönderebilirsiniz:

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

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

Selamlar


7
'İstek' değişkenini kapsamaz, ancak "as" anahtar kelimesini kullanmak yerine bir döküm yapmış olabilirsiniz.
Şaşkınlık

64

Şimdiye kadar herkes yanlış, çünkü BeginGetResponse()mevcut konu üzerinde çalışıyor. Gönderen belgeler :

BeginGetResponse yöntemi, bu yöntem eşzamansız hale gelmeden önce tamamlanması için bazı eşzamanlı kurulum görevleri gerektirir (örneğin DNS çözümlemesi, proxy algılama ve TCP soket bağlantısı). Sonuç olarak, bu yöntem hiçbir zaman bir kullanıcı arabirimi (UI) iş parçacığında çağrılmamalıdır çünkü bir hata istisnası atılmadan önce ilk eşzamanlı kurulum görevlerinin tamamlanması uzun sürebilir (ağ ayarlarına bağlı olarak birkaç dakikaya kadar sürebilir) veya yöntem başarılı olur.

Bunu doğru yapmak için:

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);
}

Daha sonra yanıtla ihtiyacınız olanı yapabilirsiniz. Örneğin:

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

2
HttpWebRequest'in GetResponseAsync yöntemini yalnızca await kullanarak (işlevinizi zaman uyumsuz yaptığınızı varsayarak) çağıramaz mısınız? C # için çok yeniyim, bu yüzden bu tam bir saçmalık olabilir ...
Brad

GetResponseAsync iyi görünüyor, ancak .NET 4.5'e (şu anda beta) ihtiyacınız olacak.
Isak

15
İsa. Bu çirkin bir kod. Eşzamansız kod neden okunamıyor?
John Shedletsky

Neden request.BeginGetResponse () adresine ihtiyacınız var? Neden wrapperAction.BeginInvoke () yeterli değil?
Igor Gatis

2
@Gatis İki düzey eşzamansız çağrı vardır - wrapperAction.BeginInvoke (), ikinci eşzamansız çağrı olan request.BeginGetResponse () öğesini çağıran lambda ifadesine ilk eşzamansız çağrıdır. Isak'ın işaret ettiği gibi, BeginGetResponse () bazı eşzamanlı kurulum gerektirir, bu yüzden ek bir eşzamansız çağrıda sarar.
walkingTarget

64

Bugüne kadar en kolay yolu kullanmaktır TaskFactory.FromAsync gelen VUK . Yeni zaman uyumsuz / beklemedeki anahtar kelimelerle birlikte kullanıldığında, kelimenin tam anlamıyla birkaç kod satırı vardır :

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 derleyicisini kullanamıyorsanız yukarıdaki işlem Task.ContinueWith yöntemi kullanılarak gerçekleştirilebilir :

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

.NET 4'ten beri bu TAP yaklaşımı tercih edilir. MS - "Nasıl yapılır: EAP Desenlerini Görevde Sarma" ( msdn.microsoft.com/en-us/library/ee622454.aspx ) ile benzer bir örneğe bakın
Alex Klaus

Diğer yollardan çok daha kolay
Don Rolling

8

BackgroundWorker kullanarak sona erdi, yukarıdaki çözümlerin bazılarının aksine kesinlikle asenkron, sizin için GUI iş parçacığına dönen kolları ve anlaşılması çok kolay.

İstisnaları işlemek de çok kolaydır, çünkü bunlar RunWorkerCompleted yönteminde bulunur, ancak bunu okuduğunuzdan emin olun: BackgroundWorker'da işlenmeyen istisnalar

WebClient kullandım ama isterseniz HttpWebRequest.GetResponse'yi kullanabilirsiniz.

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();

7
public static async Task<byte[]> GetBytesAsync(string url) {
    var request = (HttpWebRequest)WebRequest.Create(url);
    using (var response = await request.GetResponseAsync())
    using (var content = new MemoryStream())
    using (var responseStream = response.GetResponseStream()) {
        await responseStream.CopyToAsync(content);
        return content.ToArray();
    }
}

public static async Task<string> GetStringAsync(string url) {
    var bytes = await GetBytesAsync(url);
    return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}

6

Bu yanıtların çoğunun gönderilmesinden bu yana .NET değişti ve daha güncel bir yanıt vermek istiyorum. TaskBir arka plan iş parçacığında çalışacak olanı başlatmak için zaman uyumsuz bir yöntem kullanın :

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;
}

Zaman uyumsuz yöntemi kullanmak için:

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

Güncelleme:

Bu çözüm, WebRequest.GetResponseAsync()yerine kullanılan UWP uygulamaları WebRequest.GetResponse()için çalışmaz ve Dispose()uygun olan yerlerde yöntemleri çağırmaz . @dragansr bu sorunları ele alan iyi bir alternatif çözüme sahiptir.


1
Teşekkür ederim ! Bir async örneği bulmaya çalışıyorum, çok karmaşık olan eski yaklaşımı kullanan birçok örnek.
WDUK

Bu, her yanıt için bir iş parçacığını engellemez mi? örneğin docs.microsoft.com/en-us/dotnet/standard/parallel-programming/… '
Pete Kirkham

@PeteKirkham Bir arka plan iş parçacığı, UI iş parçacığı değil isteği yapıyor. Amaç UI iş parçacığını engellemekten kaçınmaktır. Bir istekte bulunmayı seçtiğiniz herhangi bir yöntem, istekte bulunan iş parçacığını engeller. Başvurduğunuz Microsoft örneği birden çok istekte bulunmaya çalışıyor, ancak yine de istekler için bir Görev (arka plan iş parçacığı) oluşturuyorlar.
tronman

3
Açıkça söylemek gerekirse, bu% 100 senkron / engelleme kodudur. Asenkron kullanmak WebRequest.GetResponseAsync()ve StreamReader.ReadToEndAync()kullanılması ve beklenmesi gerekir.
Richard Szalay

4
@tronman Zaman uyumsuz eşdeğerler kullanılabilir olduğunda bir Görevde engelleme yöntemlerini çalıştırmak son derece cesaret kırıcı bir modeldir. Arama iş parçacığının engelini kaldırmasına rağmen, zaman uyumsuzluğa ulaşmak için IO tamamlama bağlantı noktalarını kullanmak yerine işi başka bir iş parçacığına taşıdığınız için web barındırma senaryoları için ölçek yapmaz.
Richard Szalay

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);
        } 
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.