WebClient Timeout takes longer than expected (Using: Rx, Try Catch, Task) - c#

Problem: I inherited WebClient in ExtendedWebClient where I override the WebRequest's timeout property in the GetWebRequest method. If I set it to 100ms, or even 20ms, it always takes up to more than 30 seconds at least. Sometimes it seems to not get through at all.
Also, when the service (see code below) serving the images comes back online again, the code written in Rx / System.Reactive does not push images into the pictureBox anymore?
How can I get around this, what am I doing wrong? (See code below)
Test case: I have a WinForms test project set up for this, which is doing the following.
GetNextImageAsync
public async Task<Image> GetNextImageAsync()
{
Image image = default(Image);
try {
using (var webClient = new ExtendedWebClient()) {
var data = await webClient.DownloadDataTaskAsync(new Uri("http://SOMEIPADDRESS/x/y/GetJpegImage.cgi"));
image = ByteArrayToImage(data);
return image;
}
} catch {
return image;
}
}
ExtendedWebClient
private class ExtendedWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var webRequest = base.GetWebRequest(address);
webRequest.Timeout = 100;
return webRequest;
}
}
3.1 Presentation code (using Rx)
Note: It has actually never reached the "else" statement in the pictures.Subscribe() body.
var pictures = Observable
.FromAsync<Image>(GetNextImageAsync)
.Throttle(TimeSpan.FromSeconds(.5))
.Repeat()
;
pictures.Subscribe(img => {
if (img != null) {
pictureBox1.Image = img;
} else {
if (pictureBox1.Created && this.Created) {
using (var g = pictureBox1.CreateGraphics()) {
g.DrawString("[-]", new Font("Verdana", 8), Brushes.Red, new PointF(8, 8));
}
}
}
});
3.2 Presentation code (using Task.Run)
Note 1: Here the "else" body is getting called, though WebClient still takes longer than expected to timeout....
Note 2: I don't want to use this method, because this way I can't "Throttle" the image stream, I'm not able to get them in proper order, and do other stuff with my stream of images... But this is just example code of it working...
Task.Run(() => {
while (true) {
GetNextImageAsync().ContinueWith(img => {
if(img.Result != null) {
pictureBox1.Image = img.Result;
} else {
if (pictureBox1.Created && this.Created) {
using (var g = pictureBox1.CreateGraphics()) {
g.DrawString("[-]", new Font("Verdana", 8), Brushes.Red, new PointF(8, 8));
}
}
}
});
}
});
As reference, the code to tranfser the byte[] to the Image object.
public Image ByteArrayToImage(byte[] byteArrayIn)
{
using(var memoryStream = new MemoryStream(byteArrayIn)){
Image returnImage = Image.FromStream(memoryStream);
return returnImage;
}
}

The Other Problem...
I'll address cancellation below, but there is also a misunderstanding of the behaviour of the following code, which is going to cause issues regardless of the cancellation problem:
var pictures = Observable
.FromAsync<Image>(GetNextImageAsync)
.Throttle(TimeSpan.FromSeconds(.5))
.Repeat()
You probably think that the Throttle here will limit the invocation rate of GetNextImageAsync. Sadly that is not the case. Consider the following code:
var xs = Observable.Return(1)
.Throttle(TimeSpan.FromSeconds(5));
How long do you think it will take the OnNext(1) to be invoked on a subscriber? If you thought 5 seconds, you'd be wrong. Since Observable.Return sends an OnCompleted immediately after its OnNext(1) the Throttle deduces that there are no more events that could possibly throttle the OnNext(1) and it emits it immediately.
Contrast with this code where we create a non-terminating stream:
var xs = Observable.Never<int>().StartWith(1)
.Throttle(TimeSpan.FromSeconds(5));
Now the OnNext(1) arrives after 5 seconds.
The upshot of all this is that your indefinite Repeat is going to batter your code, requesting images as fast as they arrive - how exactly this is causing the effects you are witnessing would take further analysis.
There are several constructions to limit the rate of querying, depending on your requirements. One is to simply append an empty delay to your result:
var xs = Observable.FromAsync(GetValueAsync)
.Concat(Observable.Never<int>()
.Timeout(TimeSpan.FromSeconds(5),
Observable.Empty<int>()))
.Repeat();
Here you would replace int with the type returned by GetValueAsync.
Cancellation
As #StephenCleary observed, setting the Timeout property of WebRequest will only work on synchronous requests. Having looked at this necessary changes to implement cancellation cleanly with WebClient, I have to concur it's such a faff with WebClient you are far better off converting to HttpClient if at all possible.
Sadly, even then the "easy" methods to pull back data such as GetByteArrayAsync don't (for some bizarre reason) have an overload accepting a CancellationToken.
If you do use HttpClient then one option for timeout handling is via the Rx like this:
void Main()
{
Observable.FromAsync(GetNextImageAsync)
.Timeout(TimeSpan.FromSeconds(1), Observable.Empty<byte[]>())
.Subscribe(Console.WriteLine);
}
public async Task<byte[]> GetNextImageAsync(CancellationToken ct)
{
using(var wc = new HttpClient())
{
var response = await wc.GetAsync(new Uri("http://www.google.com"),
HttpCompletionOption.ResponseContentRead, ct);
return await response.Content.ReadAsByteArrayAsync();
}
}
Here I have used the Timeout operator to cause an empty stream to be emitted in the event of timeout - other options are available depending on what you need.
When Timeout does timeout it will cancel it's subscription to FromAsync which in turn will cancel the cancellation token it passes indirectly to HttpClient.GetAsync via GetNextImageAsync.
You could use a similar construction to call Abort on a WebRequest too, but as I said, it's a lot more of a faff given there's no direct support for cancellation tokens.

To quote the MSDN docs:
The Timeout property affects only synchronous requests made with the GetResponse method. To time out asynchronous requests, use the Abort method.
You could mess around with the Abort method, but it's easier to convert from WebClient to HttpClient, which was designed with asynchronous operations in mind.

Related

RestSharp - Asynchronous Request Reply Pattern

The following situation is given:
A new job is sent to an API via Post Request. This API returns a JobID and the HTTP ResponseCode 202.
This JobID is then used to request a status endpoint. If the end point has a "Finished" property set in the response body, you can continue with step 3.
The results are queried via a result endpoint using the JobID and can be processed.
My question is how I can solve this elegantly and cleanly. Are there perhaps already ready-to-use libraries that implement exactly this functionality? I could not find such functionality for RestSharp or another HttpClient.
The current solution looks like this:
async Task<string> PostNewJob()
{
var restClient = new RestClient("https://baseUrl/");
var restRequest = new RestRequest("jobs");
//add headers
var response = await restClient.ExecutePostTaskAsync(restRequest);
string jobId = JsonConvert.DeserializeObject<string>(response.Content);
return jobId;
}
async Task WaitTillJobIsReady(string jobId)
{
string jobStatus = string.Empty;
var request= new RestRequest(jobId) { Method = Method.GET };
do
{
if (!String.IsNullOrEmpty(jobStatus))
Thread.Sleep(5000); //wait for next status update
var response = await restClient.ExecuteGetTaskAsync(request, CancellationToken.None);
jobStatus = JsonConvert.DeserializeObject<string>(response.Content);
} while (jobStatus != "finished");
}
async Task<List<dynamic>> GetJobResponse(string jobID)
{
var restClient = new RestClient(#"Url/bulk/" + jobID);
var restRequest = new RestRequest(){Method = Method.GET};
var response = await restClient.ExecuteGetTaskAsync(restRequest, CancellationToken.None);
dynamic downloadResponse = JsonConvert.DeserializeObject(response.Content);
var responseResult = new List<dynamic>() { downloadResponse?.ToList() };
return responseResult;
}
async main()
{
var jobId = await PostNewJob();
WaitTillJobIsReady(jobID).Wait();
var responseResult = await GetJobResponse(jobID);
//handle result
}
As #Paulo Morgado said, I should not use Thread.Sleep / Task Delay in production code. But in my opinion I have to use it in the method WaitTillJobIsReady() ? Otherwise I would overwhelm the API with Get Requests in the loop?
What is the best practice for this type of problem?
Long Polling
There are multiple ways you can handle this type of problem, but as others have already pointed out no library such as RestSharp currently has this built in. In my opinion, the preferred way of overcoming this would be to modify the API to support some type of long-polling like Nikita suggested. This is where:
The server holds the request open until new data is available. Once
available, the server responds and sends the new information. When the
client receives the new information, it immediately sends another
request, and the operation is repeated. This effectively emulates a
server push feature.
Using a scheduler
Unfortunately this isn't always possible. Another more elegant solution would be to create a service that checks the status, and then using a scheduler such as Quartz.NET or HangFire to schedule the service at reoccurring intervals such as 500ms to 3s until it is successful. Once it gets back the "Finished" property you can then mark the task as complete to stop the process from continuing to poll. This would arguably be better than your current solution and offer much more control and feedback over whats going on.
Using Timers
Aside from using Thread.Sleep a better choice would be to use a Timer. This would allow you to continuously call a delegate at specified intervals, which seems to be what you are wanting to do here.
Below is an example usage of a timer that will run every 2 seconds until it hits 10 runs. (Taken from the Microsoft documentation)
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static Timer timer;
static void Main(string[] args)
{
var timerState = new TimerState { Counter = 0 };
timer = new Timer(
callback: new TimerCallback(TimerTask),
state: timerState,
dueTime: 1000,
period: 2000);
while (timerState.Counter <= 10)
{
Task.Delay(1000).Wait();
}
timer.Dispose();
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: done.");
}
private static void TimerTask(object timerState)
{
Console.WriteLine($"{DateTime.Now:HH:mm:ss.fff}: starting a new callback.");
var state = timerState as TimerState;
Interlocked.Increment(ref state.Counter);
}
class TimerState
{
public int Counter;
}
}
Why you don't want to use Thread.Sleep
The reason that you don't want to use Thread.Sleep for operations that you want on a reoccurring schedule is because Thread.Sleep actually relinquishes control and ultimately when it regains control is not up to the thread. It's simply saying it wants to relinquish control of it's remaining time for a least x milliseconds, but in reality it could take much longer for it to regain it.
Per the Microsoft documentation:
The system clock ticks at a specific rate called the clock resolution.
The actual timeout might not be exactly the specified timeout, because
the specified timeout will be adjusted to coincide with clock ticks.
For more information on clock resolution and the waiting time, see the
Sleep function from the Windows system APIs.
Peter Ritchie actually wrote an entire blog post on why you shouldn't use Thread.Sleep.
EndNote
Overall I would say your current approach has the appropriate idea on how this should be handled however, you may want to 'future proof' it by doing some refactoring to utilize on of the methods mentioned above.

Unresponsive UI While Waiting

Final edit: Problem turned out to be unrelated to this async implementation. The first answer helped me move stuff around enough to look at the issue with a fresh set of eyes. Thanks, guys.
I'm talking with an IP camera in my app, both through httpclient (to request and receive the image) and a websocket (via websocket-sharp, to receive data).
Right now I'm just opening the websocket and asking for one image (loops will come later). Asking for the image is the last thing I do.
When I define the request for the image as
string picloc = "[redacted]";
Stream imgstream = await client.GetStreamAsync(picloc).ConfigureAwait(false);
Bitmap blah2 = new Bitmap(imgstream);
BMPReadyEventArgs args = new BMPReadyEventArgs();
args.BMP = blah2;
BitmapReady(this, args);
the app runs through all the code and freezes up. If I leave the ConfigureAwait term off, the await will surrender control back to the UI code, it will reach the end of said code, freeze for a couple seconds and then load the image.
With configureawait(false) on there it will load the image and then freeze up. I think that the stream I get from the request starts immediately so if it doesn't have to wait for context (?) it essentially runs synchronously. To be honest, I still don't really understand what configureawait actually does, or what the context people talk about actually means, but this behavior makes me think that the UI freezing has nothing to do with the async nature of the code.
I can't see the debugger jumping to an unexpected place by stepping through with F11 (though this seems to have some shortcomings when used with asynchronous code), it really does seem to just reach the end of the code and freeze up for a couple of seconds.
This leads me to a couple questions.
Will the UI always freeze up when the end of code is reached and, if so, do I need to make some sort of UI refresh ticker?
And, alternatively but more nebulously,
Is there an issue with my async implementation that could be causing this freeze?
Edit: Complete method:
public async void DownloadJPG()
{
string picloc = "[redacted]";
Stream imgstream = await client.GetStreamAsync(picloc).ConfigureAwait(false);
Bitmap blah2 = new Bitmap(imgstream);
BMPReadyEventArgs args = new BMPReadyEventArgs();
args.BMP = blah2;
BitmapReady(this, args);
}
called from
private async void HoldingPattern()
{
textBox1.Text = wsmanager.passer;
connector.BitmapReady += (sender, e) =>
pictureBox1.Image = e.BMP;
connector.DownloadJPG();
}
edit2: event handler:
public event EventHandler<BMPReadyEventArgs> BitmapReady;
BMPReadyEventArgs
class BMPReadyEventArgs:EventArgs
{
public Bitmap BMP {get;set;}
}
Your approach is way to complicate
public async Task<Image> DownloadJPGAsync()
{
string picloc = "[redacted]";
Stream imgstream = await client.GetStreamAsync(picloc).ConfigureAwait(false);
Bitmap blah2 = new Bitmap(imgstream);
return blah2;
}
Now from an async event handler
public async void Button1_Click()
{
var image = await connector.DownloadJPGAsync();
pictureBox1.Image = image;
}

Multiple WebClients finishing very slowly

I have a class that creates multiple WebClient classes with different proxies on multiple threads simultaneously.
Unfortunately, some instances of WebClient class take quite long to finish. Usually, I end up with ~20 threads that take a few minutes to finish. On the other hand, I spawn hundreds of threads which finish fast.
I tried to create extend the WebClient class and set the Timeout property to 20 seconds (as posted here), but it didn't change anything.
I'm not showing the whole code, because there would be quite a lot of it (WebClient is wrapped in another class). Still, I know the bottle-neck is WebClient.DownloadString(url), because all of the worker threads are processing this specific line whenever I pause debugging during that last step of executing code.
Here's how I use the extended WebClient:
public string GetHtml(string url)
{
this.CheckValidity(url);
var html = "";
using (var client = new WebDownload())
{
client.Proxy = this.Proxy;
client.Headers[HttpRequestHeader.UserAgent] = this.UserAgent;
client.Timeout = this.Timeout;
html = client.DownloadString(url);
}
return html;
}
EDIT
I have just ran a few tests, and some of the threads take up to 7 minutes to finish, all contemplating the WebClient.DownloadString() statement.
Furthermore, I have tried setting ServicePointManager.DefaultConnectionLimit to int.MaxValue, unfortunately to no avail.
Here's what I ended up doing.
I realized that the problem was, I needed simply to cancel WebClient.DownloadString() when it reached the specified timeout. Since I haven't found anything that would help me in WebClient, I simply called WebClient.DownloadStringTaskAsync(). This way, I could use Task.WaitAll with timeout to wait for WebClient to finish downloading string and then check if the task has finished (to rule out timeout).
Here's the code:
public string GetHtml(string url)
{
var html = "";
using (var client = new WebClient())
{
// Assign all the important stuff
client.Proxy = this.Proxy;
client.Headers[HttpRequestHeader.UserAgent] = this.UserAgent;
// Run DownloadString() as a task.
var task = client.DownloadStringTaskAsync(url);
// Wait for the task to finish, or timeout
Task.WaitAll(new Task<string>[] { task }, this.Timeout);
// If timeout was reached, cancel task and throw an exception.
if (task.IsCompleted == false)
{
client.CancelAsync();
throw new TimeoutException();
}
// Otherwise, happy. :)
html = task.Result;
}

How to 'await' a WebClient.UploadStringAsync request?

The async pattern seems to be call an asynchronous, then yield control after awaiting a result, which makes a lot of sense.
However the WebClient class UploadStringAsync method does not return a Task, instead it return void and so cannot be awaited. Instead an event handler can be defined. e.g.
public async Task FlushQueue() {
attempt = 0;
WebClient wc = new WebClient();
while ((queue.Count > 0) && (attempt < ALLOWED_ATTEMPTS)) {
// Copy 10 items from queue and put into buffer ...
...
wc.UploadStringCompleted += (s, e) => {
// if response 200
// Remove 10 sent items from queue
// else attempt++
};
wc.UploadStringAsync("http://example.com/blah", "POST", buffer);
// In an ideal world we could call UploadStringAsync like,
// var response = await wc.UploadStringAsync("http://example.com/blah", "POST", buffer);
}
}
However, this does not await a response and instead quickly rattles through lauching the maximum number of web requests.
Is there a way to yield flow back outside of FlushQueue until the event handler callback is executed?
Edit: This is for a Windows Phone 7.5 project.
You need to use UploadStringTaskAsync, which returns a Task<string>.
The async suffixes on pre-4.5 WebClient methods are unfortunate as they don't match the TAP signatures you'd expect. In general, in that situation it's recommended that API designers use TaskAsync instead of a Async as a suffix - which is exactly what WebClient did... hence DownloadStringTaskAsync etc.
You might also want to consider using HttpClient instead of WebClient.

Changing from Synchronous mindset to Asynchronous

I'm busy with a windows phone application that of course uses silverlight. This means that calling any webservices has to be done asynchronously, and since this is all good and well in regards to best practice in preventing your entire app in hanging when waiting for a resource, I'm still stuck in the "synchronous mindset"...
Because the way I see it now is that you end up having 2 methods that needs to handle one function, e.g:
1)The method that actually calls the webservice:
public void myAsyncWebService(DownloadStringCompletedEventHandler callback)
{
//Url to webservice
string servletUrl = "https://deangrobler.com/someService/etc/etc"
//Calls Servlet
WebClient client = new WebClient();
client.DownloadStringCompleted += callback;
client.DownloadStringAsync(new Uri(servletUrl, UriKind.Absolute));
}
2) and the method that handles the data when it eventually comes back:
private void serviceReturn(object sender, DownloadStringCompletedEventArgs e)
{
var jsonResponse = e.Result;
//and so on and so forth...
}
So instead of having to just create and call a single method that goes to the webservice, gets the returned result and sent it back to me like this:
public string mySyncWebService(){
//Calls the webservice
// ...waits for return
//And returns result
}
I have to in a Class call myAsyncWebService, AND create another method in the calling class that will handle the result returned by myAsyncWebService. Just, in my opinion, creates messy code. With synchronous calls you could just call one method and be done with it.
Am I just using Asynchronous calls wrong? Is my understanding wrong? I need some enlightment here, I hate doing this messy-async calls. It makes my code too complex and readability just goes to... hell.
Thanks for anyone willing to shift my mind!
You have to turn your mind inside out to program asynchronously. I speak from experience. :)
Am I just using Asynchronous calls wrong? Is my understanding wrong?
No. Asynchronous code is fairly difficult to write (don't forget error handling) and extremely difficult to maintain.
This is the reason that async and await were invented.
If you're able to upgrade to VS2012, then you can use Microsoft.Bcl.Async (currently in beta) to write your code like this:
string url1 = "https://deangrobler.com/someService/etc/etc";
string jsonResponse1 = await new WebClient().DownloadStringTaskAsync(url1);
string url2 = GetUriFromJson(jsonResponse1);
string jsonResponse2 = await new WebClient().DownloadStringTaskAsync(url2);
Easy to write. Easy to maintain.
Async is like when you make a telephone call and get an answering machine, if you want a return call you leave your number. The first method is your call asking for data, the second is the "number" you've left for the return call.
It all becomes much easier and readable if you use lambdas instead. This also enables you to access variables declared in the "parent" method, like in the following example:
private void CallWebService()
{
//Defined outside the callback
var someFlag = true;
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//Using lambdas, we can access variables defined outside the callback
if (someFlag)
{
//Do stuff with the result.
}
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
EDIT: Here is another example with two chained service calls. It still isn't very pretty, but imho it is a little more readable than the OPs original code.
private void CallTwoWebServices()
{
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//1st call completed. Now make 2nd call.
var client2 = new WebClient();
client2.DownloadStringCompleted += (s2, e2) =>
{
//Both calls completed.
};
client2.DownloadStringAsync(new Uri("http://www.google.com/"));
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
To avoid creating messy code, if you can't use the async / await pattern because you are on older framework, you will find helpful check CoRoutines in their Caliburn Micro implemantation. With this pattern you create an enumerable yielding at each turn a new asynchronous segment to execute: by the reader point of view asynchronous steps appear as a sequence, but walking among the steps ( so yielding the next one ) is done externally by asynchronously wait the single task. It is a nice pattern easy to implement and really clear to read.
BTW if you don't want to use Caliburn Micro as your MVVM tool because you are using something else, you can use just the coroutine facility, it is very insulated inside the framework.
Let me just post some code from an example in this blog post.
public IEnumerable<IResult> Login(string username, string password)
{
_credential.Username = username;
_credential.Password = password;
var result = new Result();
var request = new GetUserSettings(username);
yield return new ProcessQuery(request, result, "Logging In...");
if (result.HasErrors)
{
yield return new ShowMessageBox("The username or password provided is incorrect.", "Access Denied");
yield break;
}
var response = result.GetResponse(request);
if(response.Permissions == null || response.Permissions.Count < 1)
{
yield return new ShowMessageBox("You do not have permission to access the dashboard.", "Access Denied");
yield break;
}
_context.Permissions = response.Permissions;
yield return new OpenWith<IShell, IDashboard>();
}
Isn't it easy to read? But it is is actually asynchronous: each yield steps are executed in an asynchronous manner and the execution flow again after the yield statement as soon the previous task completed.
With synchronous calls you could just call one method and be done with it.
Sure, but if you do that from the UI thread you will block the entire UI. That is unacceptable in any modern application, in particular in Silverlight applications running in the browser or in the phone. A phone that is unresponsive for 30 seconds while a DNS lookup times out is not something anybody wants to use.
So on the UI thread, probably because the user did some action in the UI, you start an asynchronous call. When the call completes a method is called on a background thread to handle the result of the call. This method will most likely update the UI with the result of the asynchronous call.
With the introduction of async and await in .NET 4.5 some of this "split" code can be simplified. Luckily async and await is now available for Windows Phone 7.5 in a beta version using the NuGet package Microsoft.Bcl.Async.
Here is a small (and somewhat silly) example demonstrating how you can chain two web service calls using async. This works with .NET 4.5 but using the NuGet package linked above you should be able to do something similar on Windows Phone 7.5.
async Task<String> GetCurrencyCode() {
using (var webClient = new WebClient()) {
var xml = await webClient.DownloadStringTaskAsync("http://freegeoip.net/xml/");
var xElement = XElement.Parse(xml);
var countryName = (String) xElement.Element("CountryName");
return await GetCurrencyCodeForCountry(countryName);
}
}
async Task<String> GetCurrencyCodeForCountry(String countryName) {
using (var webClient = new WebClient()) {
var outerXml = await webClient.DownloadStringTaskAsync("http://www.webservicex.net/country.asmx/GetCurrencyByCountry?CountryName=" + countryName);
var outerXElement = XElement.Parse(outerXml);
var innerXml = (String) outerXElement;
var innerXElement = XElement.Parse(innerXml);
var currencyCode = (String) innerXElement.Element("Table").Element("CurrencyCode");
return currencyCode;
}
}
However, you still need to bridge between the UI thread and the async GetCurrencyCode. You can't await in an event handler but you can use Task.ContinueWith on the task returned by the async call:
void OnUserAction() {
GetCurrencyCode().ContinueWith(GetCurrencyCodeCallback);
}
void GetCurrencyCodeCallback(Task<String> task) {
if (!task.IsFaulted)
Console.WriteLine(task.Result);
else
Console.WriteLine(task.Exception);
}

Categories

Resources