HttpWebRequest (.NET) अतुल्यकालिक का उपयोग कैसे करें?


156

मैं HttpWebRequest (.NET, C #) का उपयोग कैसे कर सकता हूं?


1
डेवलपर फ्यूजन पर इस लेख को देखें: developerfusion.com/code/4654/asynchronous-httpwebrequest

आप निम्नलिखित भी देख सकते हैं कि जेसन क्या पूछ रहा है, का एक पूरा पूरा उदाहरण के लिए: stuff.seans.com/2009/01/05/ शॉन
शॉन सेक्सटन


1
एक पल के लिए, मुझे आश्चर्य हुआ कि क्या आप पुनरावर्ती धागे पर टिप्पणी करने का प्रयास कर रहे हैं?
काइल हॉजसन ने

जवाबों:


125

उपयोग HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

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

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

कॉलबैक फ़ंक्शन को कहा जाता है जब एसिंक्रोनस ऑपरेशन पूरा होता है। आपको EndGetResponse()इस फ़ंक्शन से कम से कम कॉल करने की आवश्यकता है ।


16
AsG उपयोग के लिए BeginGetResponse उपयोगी नहीं है। संसाधन से संपर्क करने का प्रयास करते समय यह अवरुद्ध लगता है। अपने नेटवर्क केबल को अनप्लग करने का प्रयास करें या इसे विकृत uri दें, और फिर इस कोड को चलाएं। इसके बजाय आपको संभवतः आपके द्वारा प्रदान किए जाने वाले दूसरे थ्रेड पर GetResponse चलाना होगा।
ऐश

2
@AshleyHenderson - क्या आप मुझे एक नमूना प्रदान कर सकते हैं?
Tohid

1
@Tohid यहाँ नमूना के साथ एक पूर्ण वर्ग है जिसे मैंने Unity3D के साथ प्रयोग किया है।
cregox

3
आपको webRequest.Proxy = nullनाटकीय रूप से अनुरोध को तेज करने के लिए जोड़ना चाहिए ।
Trontor

C # मुझे यह बताने में त्रुटि देता है कि यह एक अप्रचलित वर्ग है
AleX_

67

उत्तर को ध्यान में रखते हुए:

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

अभिवादन


7
+1, उस विकल्प के लिए जो 'अनुरोध' चर को ओवर-स्कोप नहीं करता है, लेकिन आप "कीवर्ड" के रूप में उपयोग करने के बजाय एक कास्ट कर सकते थे। किसी
अमान्य

64

अब तक हर कोई गलत है, क्योंकि BeginGetResponse()वर्तमान थ्रेड पर कुछ काम करता है। से प्रलेखन :

इस विधि के अतुल्यकालिक होने से पहले, BeginGetResponse विधि को (उदाहरण के लिए, DNS रिज़ॉल्यूशन, प्रॉक्सी पहचान और टीसीपी सॉकेट कनेक्शन) को पूरा करने के लिए कुछ सिंक्रोनस सेटअप कार्यों की आवश्यकता होती है। परिणामस्वरूप, इस विधि को कभी भी उपयोगकर्ता इंटरफ़ेस (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);
});

2
क्या आप इंतजार कर रहे हैं (यह मानते हुए कि आपने अपने फ़ंक्शन को async बनाया है) का उपयोग करके HttpWebRequest के GetResponseAsync विधि को कॉल नहीं कर सकता है? मैं C # में बहुत नया हूँ इसलिए यह पूरा जिबरिश हो सकता है ...
ब्रैड

GetResponseAsync अच्छा दिखता है, हालाँकि आपको .NET 4.5 (वर्तमान में बीटा) की आवश्यकता होगी।
इसक

15
यीशु। यह कुछ बदसूरत कोड है। Async कोड पठनीय क्यों नहीं हो सकता है?
जॉन शेडलेस्की

आपको अनुरोध की आवश्यकता क्यों है। BeginGetResponse ()? आवरण क्यों। BeginInvoke () पर्याप्त नहीं है?
इगोर गेटिस

2
@ गेटिस में अतुल्यकालिक कॉल के दो स्तर होते हैं - आवरणपर।बिनगिन इंवोक () लैंबडा एक्सप्रेशन के लिए पहला एसिंक्रोनस कॉल है जो कॉल रिक्वेस्ट करता है ।बेगिनगेट्रेसन (), जो दूसरा एसिंक्रोनस कॉल है। जैसा कि इसाक बताते हैं, BeginGetResponse () को कुछ सिंक्रोनस सेटअप की आवश्यकता होती है, यही वजह है कि वह इसे एक अतिरिक्त एसिंक्रोनस कॉल में लपेटता है।
पैदल यात्रा

64

अब तक सबसे आसान तरीका है TPL से TaskFactory.FromAsync का उपयोग करना । जब यह नए async / प्रतीक्षा कीवर्ड के साथ संयोजन में उपयोग किया जाता है तो यह शाब्दिक रूप से कोड की एक जोड़ी है :

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

.NET 4 के बाद से यह TAP दृष्टिकोण बेहतर है। MS से एक समान उदाहरण देखें - "How to: Erap पैटर्न्स इन ए टास्क" ( msdn.microsoft.com/en-us/library/ee622454.aspx )
एलेक्स क्लॉस

अन्य तरीकों की तुलना में आसान तरीका
डॉन रोलिंग

8

मैंने बैकग्राउंडवॉकर का उपयोग करके समाप्त किया, यह निश्चित रूप से उपरोक्त कुछ समाधानों के विपरीत अतुल्यकालिक है, यह आपके लिए GUI थ्रेड पर वापस लौटता है, और इसे समझना बहुत आसान है।

अपवादों को संभालना भी बहुत आसान है, क्योंकि वे रनवॉकर कम्पीटेड पद्धति में समाप्त हो जाते हैं, लेकिन सुनिश्चित करें कि आप इसे पढ़ते हैं: 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();

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

इन उत्तरों में से कई पोस्ट किए जाने के बाद .NET बदल गया है, और मैं एक और अप-टू-डेट उत्तर प्रदान करना चाहता हूं। एक async विधि का प्रयोग करें जो 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;
}

Async विधि का उपयोग करने के लिए:

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

अपडेट करें:

यह समाधान UWP ऐप्स के लिए काम नहीं करता WebRequest.GetResponseAsync()है WebRequest.GetResponse(), जो इसके बजाय उपयोग करते हैं , और यह उन Dispose()तरीकों को कॉल नहीं करता है जहां उपयुक्त है। @dragansr के पास एक अच्छा वैकल्पिक समाधान है जो इन मुद्दों को संबोधित करता है।


1
धन्यवाद ! एक async उदाहरण खोजने की कोशिश कर रहा है, बहुत सारे उदाहरण पुराने दृष्टिकोण का उपयोग कर रहे हैं जो जटिल है।
WDUK

क्या यह प्रत्येक प्रतिक्रिया के लिए एक धागा ब्लॉक नहीं करेगा? यह काफी अलग सा लगता है जैसे docs.microsoft.com/en-us/dotnet/standard/parallel-programming/…
Pete Kirkham

@PeteKirkham एक पृष्ठभूमि थ्रेड अनुरोध कर रहा है, UI थ्रेड नहीं। लक्ष्य यूआई धागा को अवरुद्ध करने से बचने के लिए है। अनुरोध करने के लिए आपके द्वारा चुनी गई कोई भी विधि अनुरोध करने वाले धागे को अवरुद्ध कर देगी। आपके द्वारा संदर्भित Microsoft उदाहरण कई अनुरोध करने का प्रयास कर रहा है, लेकिन वे अभी भी अनुरोधों के लिए एक टास्क (एक पृष्ठभूमि धागा) बना रहे हैं।
ट्रॉनमैन

3
स्पष्ट होने के लिए, यह 100% तुल्यकालिक / अवरोधक कोड है। Async का उपयोग करने के लिए, WebRequest.GetResponseAsync()और StreamReader.ReadToEndAync()उपयोग करने और प्रतीक्षा करने की आवश्यकता है।
रिचर्ड साज़ले

4
@ Tronman एक असंगत समतुल्य उपलब्ध होने पर टास्क में अवरुद्ध तरीकों को चलाना एक अत्यधिक हतोत्साहित विरोधी पैटर्न है। हालांकि यह कॉलिंग थ्रेड को अनब्लॉक करता है, यह वेब होस्टिंग परिदृश्यों के लिए पैमाने के लिए कुछ भी नहीं करता है क्योंकि आप एसिंक्रोनस को प्राप्त करने के लिए IO पूरा करने वाले बंदरगाहों का उपयोग करने के बजाय सिर्फ दूसरे थ्रेड पर काम कर रहे हैं।
रिचर्ड साज़ले

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);
        } 
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.