Làm cách nào tôi có thể sử dụng không đồng bộ httpWebRequest (.NET, C #)?
Làm cách nào tôi có thể sử dụng không đồng bộ httpWebRequest (.NET, C #)?
Câu trả lời:
Sử dụng HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
Hàm gọi lại được gọi khi hoạt động không đồng bộ hoàn tất. Bạn cần ít nhất gọi EndGetResponse()
từ chức năng này.
webRequest.Proxy = null
để tăng tốc yêu cầu đáng kể.
Xem xét câu trả lời:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
Bạn có thể gửi con trỏ yêu cầu hoặc bất kỳ đối tượng nào khác như thế này:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Lời chào hỏi
Tất cả mọi người cho đến nay đã sai, bởi vì BeginGetResponse()
một số làm việc trên chủ đề hiện tại. Từ tài liệu :
Phương thức BeginGetResponse yêu cầu một số tác vụ thiết lập đồng bộ để hoàn thành (ví dụ: độ phân giải DNS, phát hiện proxy và kết nối ổ cắm TCP) trước khi phương thức này trở nên không đồng bộ. Do đó, phương thức này không bao giờ được gọi trên luồng giao diện người dùng (UI) vì có thể mất nhiều thời gian (tối đa vài phút tùy thuộc vào cài đặt mạng) để hoàn thành các tác vụ thiết lập đồng bộ ban đầu trước khi ném ngoại lệ hoặc lỗi phương pháp thành công.
Vì vậy, để làm điều này đúng:
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);
}
Sau đó, bạn có thể làm những gì bạn cần với phản ứng. Ví dụ:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
Cho đến nay, cách dễ nhất là sử dụng TaskFactory.FromAsync từ TPL . Đó thực sự là một vài dòng mã khi được sử dụng cùng với các từ khóa async / await mới :
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);
Nếu bạn không thể sử dụng trình biên dịch C # 5 thì có thể thực hiện các thao tác trên bằng cách sử dụng phương thức Task.CContueWith :
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Tôi đã kết thúc bằng BackgroundWorker, nó hoàn toàn không đồng bộ không giống như một số giải pháp trên, nó xử lý trả về chủ đề GUI cho bạn và điều đó rất dễ hiểu.
Cũng rất dễ xử lý các trường hợp ngoại lệ, vì chúng kết thúc bằng phương thức RunWorkerCompleted, nhưng hãy đảm bảo bạn đã đọc điều này: Các ngoại lệ chưa được xử lý trong BackgroundWorker
Tôi đã sử dụng WebClient nhưng rõ ràng bạn có thể sử dụng HttpWebRequest.GetResponse nếu bạn muốn.
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 đã thay đổi do nhiều câu trả lời trong số này đã được đăng và tôi muốn cung cấp câu trả lời cập nhật hơn. Sử dụng phương thức async để bắt đầu Task
chạy trên luồng nề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;
}
Để sử dụng phương thức async:
String response = await MakeRequestAsync("http://example.com/");
Cập nhật:
Giải pháp này không hoạt động đối với các ứng dụng UWP sử dụng WebRequest.GetResponseAsync()
thay thế WebRequest.GetResponse()
và nó không gọi các Dispose()
phương thức khi thích hợp. @dragansr có một giải pháp thay thế tốt giải quyết những vấn đề này.
WebRequest.GetResponseAsync()
và StreamReader.ReadToEndAync()
cần được sử dụng và chờ đợi.
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);
}
}