The following asynchronous C# code runs through a list of 7 URLs and tries to get HTML from each one. Right now I just have it outputting simple debug responses to the console like, "Site HTML", "No Response", or "Bad URL". It seems to work fine, but I need to fire off an event once all 7 queries have been made. How would I do this? It's important that all cases are taken into account: 1) Site HTML has been received, 2) Site timed out, 3) Site was an improper URL and couldn't be loaded. I'm covering all these cases already, but can't figure out how to connect everything to trigger a global "OnComplete" event.
Thank you.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Threading;
using System.Timers;
using System.Collections.Concurrent;
using System.Diagnostics;
namespace AsyncApp_05
{
class Program
{
static int _count = 0;
static int _total = 0;
static void Main(string[] args)
{
ArrayList alSites = new ArrayList();
alSites.Add("http://www.google.com");
alSites.Add("http://www.yahoo.com");
alSites.Add("http://www.ebay.com");
alSites.Add("http://www.aol.com");
alSites.Add("http://www.bing.com");
alSites.Add("adsfsdfsdfsdffd");
alSites.Add("http://wwww.fjasjfejlajfl");
alSites.Add("http://mundocinema.com/noticias/the-a-team-2/4237");
alSites.Add("http://www.spmb.or.id/?p=64");
alSites.Add("http://gprs-edge.ru/?p=3");
alSites.Add("http://blog.tmu.edu.tw/MT/mt-comments.pl?entry_id=3141");
_total = alSites.Count;
//Console.WriteLine(_total);
ScanSites(alSites);
Console.Read();
}
private static void ScanSites(ArrayList sites)
{
foreach (string uriString in sites)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
request.Method = "GET";
request.Proxy = null;
RequestState state = new RequestState();
state.Request = request;
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
// Timeout comes here
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
new WaitOrTimerCallback(TimeOutCallback), request, 100, true);
}
catch (Exception ex)
{
Console.WriteLine("Bad URL");
Interlocked.Increment(ref _count);
}
}
}
static void ReadCallback(IAsyncResult result)
{
try
{
// Get RequestState
RequestState state = (RequestState)result.AsyncState;
// determine how many bytes have been read
int bytesRead = state.ResponseStream.EndRead(result);
if (bytesRead > 0) // stream has not reached the end yet
{
// append the read data to the ResponseContent and...
state.ResponseContent.Append(Encoding.ASCII.GetString(state.BufferRead, 0, bytesRead));
// ...read the next piece of data from the stream
state.ResponseStream.BeginRead(state.BufferRead, 0, state.BufferSize,
new AsyncCallback(ReadCallback), state);
}
else // end of the stream reached
{
if (state.ResponseContent.Length > 0)
{
Console.WriteLine("Site HTML");
// do something with the response content, e.g. fill a property or fire an event
//AsyncResponseContent = state.ResponseContent.ToString();
// close the stream and the response
state.ResponseStream.Close();
state.Response.Close();
//OnAsyncResponseArrived(AsyncResponseContent);
}
}
}
catch (Exception ex)
{
// Error handling
RequestState state = (RequestState)result.AsyncState;
if (state.Response != null)
{
state.Response.Close();
}
}
}
static void ResponseCallback(IAsyncResult result)
{
Interlocked.Increment(ref _count);
Console.WriteLine("Count: " + _count);
try
{
// Get and fill the RequestState
RequestState state = (RequestState)result.AsyncState;
HttpWebRequest request = state.Request;
// End the Asynchronous response and get the actual resonse object
state.Response = (HttpWebResponse)request.EndGetResponse(result);
Stream responseStream = state.Response.GetResponseStream();
state.ResponseStream = responseStream;
// Begin async reading of the contents
IAsyncResult readResult = responseStream.BeginRead(state.BufferRead, 0, state.BufferSize, new AsyncCallback(ReadCallback), state);
}
catch (Exception ex)
{
// Error handling
RequestState state = (RequestState)result.AsyncState;
if (state.Response != null)
{
state.Response.Close();
}
Console.WriteLine("No Response");
}
}
static void TimeOutCallback(object state, bool timedOut)
{
if (timedOut)
{
HttpWebRequest request = state as HttpWebRequest;
if (request != null)
{
request.Abort();
}
}
}
}
public class RequestState
{
public int BufferSize { get; private set; }
public StringBuilder ResponseContent { get; set; }
public byte[] BufferRead { get; set; }
public HttpWebRequest Request { get; set; }
public HttpWebResponse Response { get; set; }
public Stream ResponseStream { get; set; }
public RequestState()
{
BufferSize = 1024;
BufferRead = new byte[BufferSize];
ResponseContent = new StringBuilder();
Request = null;
ResponseStream = null;
}
}
}
You can use a CountdownEvent to find out when all the sites have been scanned. It'd be initially set to sites.Count, and then wait on that event. On each completion (either by error, timeout or success) you'd signal the event. When the event count reaches zero, the wait will return and you can have your "OnComplete" event.
The simplest way IMHO is to create a semaphore, make each OnComplete handler to Release it and to WaitOne on it N times in a master thread (where N is number of sites).
private static void ScanSites(ArrayList sites)
{
var semaphore = new Semaphore(0,sites.Count);
foreach (string uriString in sites)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
request.Method = "GET";
request.Proxy = null;
RequestState state = new RequestState();
state.Request = request;
state.Semaphore = semaphore;
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
// Timeout comes here
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
(o, timeout => { TimeOutCallback }, request, 100, true);
}
catch (Exception ex)
{
Console.WriteLine("Bad URL");
Interlocked.Increment(ref _count);
}
}
for(var i =0; i <sites.Count; i++) semaphore.WaitOne();
}
static void ReadCallback(IAsyncResult result)
{
try
{ ... }
finally{
var state = result.State as RequestState;
if (state != null) state.Semaphore.Release();
}
}
Another option is to pass some WaitHandle (ManualResetEvent fits well) to each of handler and WaitHandle.WaitAll for them in master thread.
private static void ScanSites(ArrayList sites)
{
var handles = new List<WaitHandle>();
foreach (string uriString in sites)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uriString);
request.Method = "GET";
request.Proxy = null;
RequestState state = new RequestState();
state.Request = request;
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(ResponseCallback), state);
handles.Add(result.AsyncWaitHandle);
// Timeout comes here
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
new WaitOrTimerCallback(TimeOutCallback), request, 100, true);
}
catch (Exception ex)
{
Console.WriteLine("Bad URL");
Interlocked.Increment(ref _count);
}
}
WaitHandle.WaitAll(handles.ToArray());
}
Surely, you can achieve the same with Interlocked as well, by using, e.g. Exchange or CompareExchange methods but IMHO, WaitHandles are more straightforward here (and performance hit for using them is not significant).
Related
I need to understand how SemaphoreSlim works with ThreadPool.
I need to read a file from the local disk of a computer, read each line, and then send that line as a HttpWebRequest to 2 different servers, and correspondingly get 2 HttpWebRequest back.
So lets say that file has 100 requests (this number can be in thousands or even more in a real-time scenario), and when all these requests will be sent I should get back 200 responses (like I mentioned each request should go to 2 different servers and fetch 2 responses from them). Here is my code:
static void Main(string[] args)
{
try
{
SendEntriesInFile(someFileOnTheLocaldisk);
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine("Regression Tool Error: Major Unspecified Error:\n" + e);
}
}
}
public class MyParameters
{
}
private void SendEntriesInFile(FileInfo file)
{
static SemaphoreSlim threadSemaphore = new SemaphoreSlim(5, 10);
using (StreamReader reader = file.OpenText())
{
string entry = reader.ReadLine();
while (!String.IsNullOrEmpty(entry))
{
MyParameters myParams = new MyParameters(entry, totalNumberOfEntries, serverAddresses, requestType, fileName);
threadSemaphore.Wait();
ThreadPool.QueueUserWorkItem(new WaitCallback(Send), requestParams);
entry = reader.ReadLine();
}
}
}
private void Send(object MyParameters)
{
MyParameters myParams = (MyParameters)MyParameters;
for(int i=0; i < myParams.ServerAddresses.Count; i++)
{
byte[] bytesArray = null;
bytesArray = Encoding.UTF8.GetBytes(myParams.Request);
HttpWebRequest webRequest = null;
if (reqParams.TypeOfRequest == "tlc")
{
webRequest = (HttpWebRequest)WebRequest.Create(string.Format("http://{0}:{1}{2}", myParams .ServerAddresses[i], port, "/SomeMethod1"));
}
else
{
webRequest = (HttpWebRequest)WebRequest.Create(string.Format("http://{0}:{1}{2}", myParams .ServerAddresses[i], port, "/SomeMethod2"));
}
if (webRequest != null)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = bytesArray.Length;
webRequest.Timeout = responseTimeout;
webRequest.ReadWriteTimeout = transmissionTimeout;
webRequest.ServicePoint.ConnectionLimit = maxConnections;
webRequest.ServicePoint.ConnectionLeaseTimeout = connectionLifeDuration;
webRequest.ServicePoint.MaxIdleTime = maxConnectionIdleTimeout;
webRequest.ServicePoint.UseNagleAlgorithm = nagleAlgorithm;
webRequest.ServicePoint.Expect100Continue = oneHundredContinue;
using (Stream requestStream = webRequest.GetRequestStream())
{
//Write the request through the request stream
requestStream.Write(bytesArray, 0, bytesArray.Length);
requestStream.Flush();
}
string response = "";
using (HttpWebResponse httpWebResponse = (HttpWebResponse)webRequest.GetResponse())
{
if (httpWebResponse != null)
{
using (Stream responseStream = httpWebResponse.GetResponseStream())
{
using (StreamReader stmReader = new StreamReader(responseStream))
{
response = stmReader.ReadToEnd();
string fileName = "";
if(i ==0)
{
fileName = Name is generated through some logic here;
}
else
{
fileName = Name is generated through some logic here;
}
using (StreamWriter writer = new StreamWriter(fileName))
{
writer.WriteLine(response);
}
}
}
}
}
}
}
Console.WriteLine(" Release semaphore: ---- " + threadSemaphore.Release());
}
The only thing is that I'm confused with that when I do something like above my Semaphore allow 5 threads to concurrently execute the Send() method and 5 other threads they wait in a queue for their turn. Since my file contains 100 requests so I should get back 200 responses. Every time I ended up getting only 109 or 107 or 108 responses back. Why I don't get all the 200 responses back. Though using a different code (not discussed here) when I send lets say 10 requests in parallel on 10 different created threads (not using the ThreadPool rather creating threads on-demand) I get back all the 200 responses.
Lots of very helpful comments. To add to them, I would recommend using just pure async io for this.
I would recommend applying the Semaphore as a DelegatingHandler.
You want to register this class.
public class LimitedConcurrentHttpHandler
: DelegatingHandler
{
private readonly SemaphoreSlim _concurrencyLimit = new(8);
public LimitedConcurrentHttpHandler(){ }
public LimitedConcurrentHttpHandler(HttpMessageHandler inner) : base(inner) {}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
await _concurrencyLimit.Wait(cancellationToken);
try
{
return await base.SendAsync(request, cancellationToken);
}
finally
{
_concurrencyLimit.Release();
}
}
}
This way, you can concentrate on your actual business logic within your actual code.
my environment version is 2.0, I am currently working with Unity 5.5, c#.
I am experiencing a stall the 3rd time i try to get an HttpWebRequest request stream.
What I want to achieve is having a mechanism of "retry": when I send a request I wait for the response and if it times out I send another request and refresh the time out. I stop this loop when I hit MAX_ATTEMPTS or one of the requests get a response (even if it is from a request that has timed out).
The code that handles this loop is here:
IEnumerator DoRequest()
{
List<IAsyncResult> results = new List<IAsyncResult>(MAX_ATTEMPTS);
int attemptNumber = 1;
while (attemptNumber <= MAX_ATTEMPTS)
{
IAsyncResult result = null;
try
{
result = SendRequest();
}
catch (Exception e)
{
ProcessException(e);
yield break;
}
if (result != null)
{
results.Add(result);
DateTime timeOutTime = DateTime.UtcNow + TIMEOUT;
yield return null;
while (DateTime.UtcNow < timeOutTime)
{
int completedIndex = -1;
if (IsAnyResultCompleted(results, out completedIndex) == true)
{
ProcessResponse(results[completedIndex]);
yield break;
}
else
{
yield return null;
}
}
}
attemptNumber++;
}
if (results.Count != 0)
{
ProcessResponseFail("Timed out");
}
else
{
ProcessResponseFail("Unable to generate a request");
}
}
Where TIMEOUT is a TimeSpan of 10 seconds. And IsAnyResultCompleted is just a for loop that checks if any request has IsCompleted set to true.
Here I create and send the request:
IAsyncResult SendRequest()
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(RequestActionPath);
request.ContentType = "application/json";
request.Accept = "application/json";
request.Method = "POST";
string json = JsonUtility.ToJson(_dependency);
using (StreamWriter sw = new StreamWriter(request.GetRequestStream()))
{
sw.Write(json);
sw.Flush();
}
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(onResponse), request);
return result;
}
"onResponse" is a cached delegate and it is assigned to this:
void OnResponse(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
request.EndGetResponse(asynchronousResult);
}
catch (WebException e)
{
string responseStream;
using (var streamReader = new StreamReader(e.Response.GetResponseStream()))
{
responseStream = streamReader.ReadToEnd();
}
Utility.Console.LogError(responseStream);
}
}
I am well aware of this and I also tried to set ServicePointManager.DefaultConnectionLimit = 1000000 but the result is always the same, after 2 sent requests GetRequestStream() in SendRequest stalls for 5 seconds and I cannot understand why.
I am having a hard time convert the below code which i have created in 4.0 to 4.5 using HttpClient.
According to my understand i guess if i create multiple web requests in the GUI thread itself without blocking the GUI if i got with asynchronous requeest.
how to convert the below code to Asynchronous using HttpClient in 4.5
// This is what called when button is clicked
Task t3 = new Task(SpawnTask);
t3.Start();
//if noofthreads are less 50 then GUI is woking fine.. if number increases then takes much time for repaint..
//where as other softwares are working without any problem even if the threads are more than 500!! in the same system
public void SpawnTask()
{
try
{
ParallelOptions po = new ParallelOptions();
po.CancellationToken = cts.Token;
po.MaxDegreeOfParallelism = noofthreads;
Parallel.ForEach(
urls,
po,
url => checkpl(url));
}
catch (Exception ex)
{
}
}
public void checkpl(string url)
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Timeout = 60*1000;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
string stext = "";
using (BufferedStream buffer = new BufferedStream(response.GetResponseStream()))
{
using (StreamReader reader = new StreamReader(buffer))
{
stext = reader.ReadToEnd();
}
}
response.Close();
if (stext .IndexOf("domainname.com") != -1)
{
tfound = tfound + 1;
string lext = "Total Found : "+tfound.ToString();
label3.BeginInvoke(new InvokeDelegate(UpdateLabel), ltext);
slist.Add(url);
textBox2.BeginInvoke(new InvokeDelegate4(UpdateText), "Working Url " + url);
}
}
catch (Exception ex)
{
}
}
Since you are using .NET 4.5 you can use the new async and await keywords. Here is what it might look like.
private async void YourButton_Click(object sender, EventArgs args)
{
YourButton.Enabled = false;
try
{
var tasks = new List<Task>();
foreach (string url in Urls)
{
tasks.Add(CheckAsync(url));
}
await TaskEx.WhenAll(tasks);
}
finally
{
YourButton.Enabled = true;
}
}
private async Task CheckAsync(string url)
{
bool found = await UrlResponseContainsAsync(url, "domainname.com");
if (found)
{
slist.Add(url);
label3.Text = "Total Found: " + slist.Count.ToString();
textbox2.Text = "Working Url " + url;
}
}
private async Task<bool> UrlResponseContainsAsync(string url, string find)
{
var request = WebRequest.Create(url);
request.Timeout = 60 * 1000;
using (WebResponse response = await request.GetResponseAsync())
{
using (var buffer = new BufferedStream(response.GetResponseStream()))
using (var reader = new StreamReader(buffer))
{
string text = reader.ReadToEnd();
return text.Contains(find);
}
}
}
Environemnt : Windows CE / .NET Compact FrameWork 3.5.
I need some guidance in
1) Implementing a Timeout functionality for an Asynchronous Web request.
ThreadPool::RegisterWaitForSingleObject() is not available for .NetCf and I'm bit stuck.
2) How to determine if network itself is not avaialable?
Googling didn't help.
Note : ThreadPool::RegisterWaitForSingleObject is not available for .NET Compact FrameWork.
Here is my Async implementation:
void StartRequest ()
{
try
{
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create("http://192.78.221.11/SomeFunc/excpopulatedept");
RqstState myRequestState = new RqstState();
myRequestState.request = myHttpWebRequest;
// Start the asynchronous request.
IAsyncResult result =
(IAsyncResult)myHttpWebRequest.BeginGetResponse(new AsyncCallback(RespCallback), myRequestState);
// Release the HttpWebResponse resource.
myRequestState.response.Close();
}
catch (WebException ex)
{
;
}
catch (Exception ex)
{
;
}
}
private void RespCallback(IAsyncResult asynchronousResult)
{
try
{
//State of request is asynchronous.
RqstState myRequestState = (RqstState)asynchronousResult.AsyncState;
HttpWebRequest myHttpWebRequest = myRequestState.request;
myRequestState.response = (HttpWebResponse)myHttpWebRequest.EndGetResponse(asynchronousResult);
// Read the response into a Stream object.
Stream responseStream = myRequestState.response.GetResponseStream();
myRequestState.streamResponse = responseStream;
// Begin the Reading of the contents of the HTML page and print it to the console.
IAsyncResult asynchronousInputRead = responseStream.BeginRead(myRequestState.BufferRead, 0, 1024, new AsyncCallback(ReadCallBack), myRequestState);
return;
}
catch (WebException e)
{
Console.WriteLine("\nRespCallback Exception raised!");
Console.WriteLine("\nMessage:{0}", e.Message);
Console.WriteLine("\nStatus:{0}", e.Status);
}
}
private void ReadCallBack(IAsyncResult asyncResult)
{
try
{
RqstState myRequestState = (RqstState)asyncResult.AsyncState;
Stream responseStream = myRequestState.streamResponse;
int read = responseStream.EndRead(asyncResult);
// Read the HTML page and then print it to the console.
if (read > 0)
{
myRequestState.requestData.Append(Encoding.ASCII.GetString(myRequestState.BufferRead, 0, read));
IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, 1024, new AsyncCallback(ReadCallBack), myRequestState);
return;
}
else
{
//Console.WriteLine("\nThe contents of the Html page are : ");
if (myRequestState.requestData.Length > 1)
{
string stringContent;
stringContent = myRequestState.requestData.ToString();
responseStream.Close();
}
catch (WebException e)
{
}
}
}
}
Thank you for your time.
To continue what Eric J commented on, you have myRequestState.response.Close() just before your catches. That's almost always going to throw an exception because either response will be null, or response won't be opened. This is because you are asynchronously calling BeginGetResponse and the callback you give it will likely not be called by the time the next line (response.close) is called. You'll want to fix that instead of just hiding the exceptions because you don't know why they occur.
In terms of a timeout, because you're dealing with something that doesn't inherently have a configurable timeout, you'll have to set a timer and simply close the connection at the end of the time-out. e.g.
HttpWebRequest myHttpWebRequest; // ADDED
Timer timer; // ADDED
private void StartRequest()
{
myHttpWebRequest = (HttpWebRequest)WebRequest.Create("http://192.78.221.11/SomeFunc/excpopulatedept");
RqstState myRequestState = new RqstState();
myRequestState.request = myHttpWebRequest;
timer = new Timer(delegate { if (!completed) myHttpWebRequest.Abort(); }, null, waitTime, Timeout.Infinite); // ADDED
// Start the asynchronous request.
IAsyncResult result =
(IAsyncResult)myHttpWebRequest.BeginGetResponse(new AsyncCallback(RespCallback), myRequestState);
}
//...
private void ReadCallBack(IAsyncResult asyncResult)
{
try
{
RqstState myRequestState = (RqstState)asyncResult.AsyncState;
Stream responseStream = myRequestState.streamResponse;
int read = responseStream.EndRead(asyncResult);
// Read the HTML page and then print it to the console.
if (read > 0)
{
myRequestState.requestData.Append(Encoding.ASCII.GetString(myRequestState.BufferRead, 0, read));
IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, 1024, new AsyncCallback(ReadCallBack), myRequestState);
}
else
{
completed = true; // ADDED
using(timer) // ADDED
{
timer = null;
}
if (myRequestState.requestData.Length > 1)
{
string stringContent;
stringContent = myRequestState.requestData.ToString();
responseStream.Close();
}
}
}
}
I only copy and pasted your code, so it's as likely to compile as what you originally provided, but there should be enough there you get in going in the right direction.
I have the following code:
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
Stream postStream = request.EndGetRequestStream(asynchronousResult);
//Console.WriteLine("Please enter the input data to be posted:");
//string postData = Console.ReadLine();
string postData = "my data";
// Convert the string into a byte array.
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
// Write to the request stream.
postStream.Write(byteArray, 0, postData.Length);
postStream.Close();
// Start the asynchronous operation to get the response
IAsyncResult result =
(IAsyncResult)request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
Console.WriteLine(responseString);
// Close the stream object
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse
response.Close();
allDone.Set();
Dispatcher.BeginInvoke((Action)(() => Debug.WriteLine("George")));
}
However when my code hits BeginGetResponse it never exits (and I do not hit a breakpoint in the GetResponseCallback function). I tried adding the BeginInvoke call, but I still never enter this method. This code works in a windows console app - it's on Windows Phone 7 that it doesn'teorg
Can anyone see what I am doing wrong?
Thanks.
If you have created the HttpWebRequest on the UI thread, then make sure you don't block the UI thread, otherwise you can deadlock.
The sample from the desktop .NET you have linked isn't optimized for the current phone networking stack. You should change the code so that you create the HttpWebRequest on a background thread.
I can't see what's wrong with your code (maybe a complete example of what you're trying to do may help) but here's a simple working example of a way of performing the action you want to do.
It posts some data to a URI and then passes the repsonse to a callback function:
Simply execute like this (use of a BackgroundWorker is not necessary but is recommended)
var bw = new BackgroundWorker();
bw.DoWork += (o, args) => PostDataToWebService("http://example.com/something", "key=value&key2=value2", MyCallback);
bw.RunWorkerAsync();
Here's the callback function it refers to:
(You can change this however is appropriate to your needs.)
public static void MyCallback(string aString, Exception e)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (e == null)
{
// aString is the response from the web server
MessageBox.Show(aString, "success", MessageBoxButton.OK);
}
else
{
MessageBox.Show(e.Message, "error", MessageBoxButton.OK);
}
});
}
Here's the actual method:
public void PostDataToWebService(string url, string data, Action<string, Exception> callback)
{
if (callback == null)
{
throw new Exception("callback may not be null");
}
try
{
var uri = new Uri(url, UriKind.Absolute);
var req = HttpWebRequest.CreateHttp(uri);
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
AsyncCallback GetTheResponse = ar =>
{
try
{
var result = ar.GetResponseAsString();
callback(result, null);
}
catch (Exception ex)
{
callback(null, ex);
}
};
AsyncCallback SetTheBodyOfTheRequest = ar =>
{
var request = ar.SetRequestBody(data);
request.BeginGetResponse(GetTheResponse, request);
};
req.BeginGetRequestStream(SetTheBodyOfTheRequest, req);
}
catch (Exception ex)
{
callback(null, ex);
}
}
and here are the extension/helper methods it uses:
public static class IAsyncResultExtensions
{
public static string GetResponseAsString(this IAsyncResult asyncResult)
{
string responseString;
var request = (HttpWebRequest)asyncResult.AsyncState;
using (var resp = (HttpWebResponse)request.EndGetResponse(asyncResult))
{
using (var streamResponse = resp.GetResponseStream())
{
using (var streamRead = new StreamReader(streamResponse))
{
responseString = streamRead.ReadToEnd();
}
}
}
return responseString;
}
public static HttpWebRequest SetRequestBody(this IAsyncResult asyncResult, string body)
{
var request = (HttpWebRequest)asyncResult.AsyncState;
using (var postStream = request.EndGetRequestStream(asyncResult))
{
using (var memStream = new MemoryStream())
{
var content = body;
var bytes = System.Text.Encoding.UTF8.GetBytes(content);
memStream.Write(bytes, 0, bytes.Length);
memStream.Position = 0;
var tempBuffer = new byte[memStream.Length];
memStream.Read(tempBuffer, 0, tempBuffer.Length);
postStream.Write(tempBuffer, 0, tempBuffer.Length);
}
}
return request;
}
}