[C#] HttpWebRequest (.NET)를 비동기 적으로 사용하는 방법은 무엇입니까?

HttpWebRequest (.NET, C #)를 비동기 적으로 사용하려면 어떻게해야합니까?



답변

사용하다 HttpWebRequest.BeginGetResponse()

HttpWebRequest webRequest;

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

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

콜백 함수는 비동기 작업이 완료되면 호출됩니다. 최소한 EndGetResponse()이 함수에서 호출해야 합니다.


답변

답을 고려할 때 :

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

인사말


답변

BeginGetResponse()현재 스레드에서 일부 작업을 수행 하기 때문에 지금까지 모두 잘못되었습니다 . 로부터 문서 :

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


답변

가장 쉬운 방법은 TPL 에서 TaskFactory.FromAsync 를 사용하는 것 입니다. 새로운 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);
    });


답변

BackgroundWorker를 사용하여 결국 위의 솔루션과 달리 비동기식이며 GUI 스레드로 돌아가는 것을 처리하며 이해하기가 매우 쉽습니다.

예외가 RunWorkerCompleted 메소드로 끝나기 때문에 예외를 처리하는 것도 매우 쉽지만 다음을 읽으십시오. 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();


답변

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


답변

이 답변 중 다수가 게시 된 후 .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/");

최신 정보:

이 솔루션은 WebRequest.GetResponseAsync()대신 사용 하는 UWP 앱에서는 작동 WebRequest.GetResponse()하지 않으며 Dispose()적절한 경우 메서드를 호출하지 않습니다 . @dragansr에는 이러한 문제를 해결하는 훌륭한 대안 솔루션이 있습니다.