How to implement a Timeout on WebClient.DownloadFileAsync - c#

So I thought Webclient.DownloadFileAysnc would have a default timeout but looking around the documentation I cannot find anything about it anywhere so I'm guessing it doesn't.
I am trying to download a file from the internet like so:
using (WebClient wc = new WebClient())
{
wc.DownloadProgressChanged += ((sender, args) =>
{
IndividualProgress = args.ProgressPercentage;
});
wc.DownloadFileCompleted += ((sender, args) =>
{
if (args.Error == null)
{
if (!args.Cancelled)
{
File.Move(filePath, Path.ChangeExtension(filePath, ".jpg"));
}
mr.Set();
}
else
{
ex = args.Error;
mr.Set();
}
});
wc.DownloadFileAsync(new Uri("MyInternetFile", filePath);
mr.WaitOne();
if (ex != null)
{
throw ex;
}
}
But if I turn off my WiFi (simulating a drop of internet connection) my application just pauses and the download stops but it will never report that through to the DownloadFileCompleted method.
For this reason I would like to implement a timeout on my WebClient.DownloadFileAsync method. Is this possible?
As an aside I am using .Net 4 and don't want to add references to third party libraries so cannot use the Async/Await keywords

You can use WebClient.DownloadFileAsync(). Now inside a timer you can call CancelAsync() like so:
System.Timers.Timer aTimer = new System.Timers.Timer();
System.Timers.ElapsedEventHandler handler = null;
handler = ((sender, args)
=>
{
aTimer.Elapsed -= handler;
wc.CancelAsync();
});
aTimer.Elapsed += handler;
aTimer.Interval = 100000;
aTimer.Enabled = true;
Else create your own weclient
public class NewWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var req = base.GetWebRequest(address);
req.Timeout = 18000;
return req;
}
}

Create a WebClientAsync class that implements the timer in the constructor. This way you aren't copying and pasting the timer code into every implementation.
public class WebClientAsync : WebClient
{
private int _timeoutMilliseconds;
public EdmapWebClientAsync(int timeoutSeconds)
{
_timeoutMilliseconds = timeoutSeconds * 1000;
Timer timer = new Timer(_timeoutMilliseconds);
ElapsedEventHandler handler = null;
handler = ((sender, args) =>
{
timer.Elapsed -= handler;
this.CancelAsync();
});
timer.Elapsed += handler;
timer.Enabled = true;
}
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
request.Timeout = _timeoutMilliseconds;
((HttpWebRequest)request).ReadWriteTimeout = _timeoutMilliseconds;
return request;
}
protected override voidOnDownloadProgressChanged(DownloadProgressChangedEventArgs e)
{
base.OnDownloadProgressChanged(e);
timer.Reset(); //If this does not work try below
timer.Start();
}
}
This will allow you to timeout if you lose Internet connection while downloading a file.

Here is another implementation, I tried to avoid any shared class/object variables to avoid trouble with multiple calls:
public Task<string> DownloadFile(Uri url)
{
var tcs = new TaskCompletionSource<string>();
Task.Run(async () =>
{
bool hasProgresChanged = false;
var timer = new Timer(new TimeSpan(0, 0, 20).TotalMilliseconds);
var client = new WebClient();
void downloadHandler(object s, DownloadProgressChangedEventArgs e) => hasProgresChanged = true;
void timerHandler(object s, ElapsedEventArgs e)
{
timer.Stop();
if (hasProgresChanged)
{
timer.Start();
hasProgresChanged = false;
}
else
{
CleanResources();
tcs.TrySetException(new TimeoutException("Download timedout"));
}
}
void CleanResources()
{
client.DownloadProgressChanged -= downloadHandler;
client.Dispose();
timer.Elapsed -= timerHandler;
timer.Dispose();
}
string filePath = Path.Combine(Path.GetTempPath(), Path.GetFileName(url.ToString()));
try
{
client.DownloadProgressChanged += downloadHandler;
timer.Elapsed += timerHandler;
timer.Start();
await client.DownloadFileTaskAsync(url, filePath);
}
catch (Exception e)
{
tcs.TrySetException(e);
}
finally
{
CleanResources();
}
return tcs.TrySetResult(filePath);
});
return tcs.Task;
}

Related

Vlc.DotNet.Core taking snapshot while streaming

I am using Vlc.DotNet.Core for multicast video streaming. Also, I want to take snapshots. For testing, I am working on a simple program that calling snapshot function for every 2 seconds. However snapshot function is not working, when TakeSnapshot function called, it is not calling OnMediaPlayerSnapshotTaken event.
Besides that, I made a snapshot tests for Vlc.DotNet.Forms library and its working quite well.
The simple code I am working on is given below :
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Vlc.DotNet.Core;
namespace SnapshotTest
{
class Program
{
static void Main(string[] args)
{
VlcStreamerSnapshotText myObject = new VlcStreamerSnapshotText();
myObject.streamFile();
}
}
class VlcStreamerSnapshotText
{
System.Timers.Timer aTimer = new System.Timers.Timer();
static Vlc.DotNet.Core.VlcMediaPlayer mediaPlayer;
public bool streamFile(string source = "")
{
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Interval = 2000;
aTimer.Enabled = true;
var currentDirectory = Directory.GetCurrentDirectory();
var libDirectory = new DirectoryInfo(Path.Combine(currentDirectory, "libvlc", IntPtr.Size == 4 ? "x86" : "x64"));
var options = new string[]
{
"--no-snapshot-preview",
"--no-audio"
//"--no-video"
// VLC options can be given here. Please refer to the VLC command line documentation.
};
mediaPlayer = new Vlc.DotNet.Core.VlcMediaPlayer(new DirectoryInfo(libDirectory.FullName), options);
string[] mediaOptions = new string[]
{
":sout=#duplicate{dst=rtp{sdp=rtsp://127.0.0.1:1000/},dst=display}",
":sout-all",
":sout-keep"
};
//string[] mediaOptions = new string[] { string.Concat("--sout=#duplicate{dst=rtp{sdp=rtsp://", IpAddress, ":", "1000", "/}} --sout-all --sout-keep") };
mediaPlayer.SetMedia(new Uri("http://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_480p_h264.mov"), mediaOptions);
mediaPlayer.SnapshotTaken += OnMediaPlayerSnapshotTaken;
bool playFinished = false;
mediaPlayer.PositionChanged += (sender, e) =>
{
Console.Write("\r" + Math.Floor(e.NewPosition * 100) + "%");
};
mediaPlayer.EncounteredError += (sender, e) =>
{
Console.Error.Write("An error occurred");
playFinished = true;
};
mediaPlayer.EndReached += (sender, e) =>
{
playFinished = true;
};
Task.Factory.StartNew(() => mediaPlayer.Play());
//mediaPlayer.Play();
// Ugly, sorry
while (!playFinished)
{
Thread.Sleep(TimeSpan.FromMilliseconds(500));
}
return true;
}
ManualResetEvent m_mreSnapshot = new ManualResetEvent(false);
private readonly object m_snapLock = new object();
private bool m_SnapshotIsTaken = false;
public bool GetSnapshot()
{
m_SnapshotIsTaken = false;
if (mediaPlayer == null)
{
return false;
}
string currentTime = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string uniqueId = generateID(string.Empty);
string snapName = string.Format("{0}_{1}.png", currentTime, uniqueId);
string tempFilePathCand = Path.GetTempPath();
if (!tempFilePathCand.EndsWith("\\"))
tempFilePathCand = tempFilePathCand + "\\" + snapName;
else
tempFilePathCand = tempFilePathCand + snapName;
FileInfo snapPathInfo = new FileInfo(tempFilePathCand);
//mediaPlayer.Manager.TakeSnapshot();
mediaPlayer.TakeSnapshot(snapPathInfo);
m_mreSnapshot.WaitOne(TimeSpan.FromSeconds(2));
if (m_SnapshotIsTaken)
return true;
return false;
}
private string generateID(string tip)
{
return string.Format(#"{0}_{1}", tip, Guid.NewGuid().ToString("N"));
}
void OnMediaPlayerSnapshotTaken(object sender, VlcMediaPlayerSnapshotTakenEventArgs e)
{
mediaPlayer.SnapshotTaken -= OnMediaPlayerSnapshotTaken;
m_SnapshotIsTaken = true;
mediaPlayer.SnapshotTaken += OnMediaPlayerSnapshotTaken;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
GetSnapshot();
}
}
}
So, What am I missing?
Maybe a bug in Vlc.DotNet: https://github.com/ZeBobo5/Vlc.DotNet/issues/447
Are you using Vlc.DotNet v3?

WebClient progress reporting using TAP

I'm wondering if there is a way to report WebClient progress without using EAP(Event-based Asynchronous Pattern).
Old way(using EAP) would be:
var client = new WebClient();
client.DownloadProgressChanged += (s,e) => { //progress reporting }
client.DownloadFileCompleted += (s,e) => { Console.Write("download finished" }
client.DownloadFileAsync(file);
With async/await this can be written as:
var client = new WebClient();
client.DownloadProgressChanged += (s,e) => { //progress reporting }
await client.DownloadFileTaskAsync(file);
Console.Write("downlaod finished");
But in the second example i'm using both EAP and TAP(Task-based Asynchronous Pattern).
Isn't mixing two patterns of asynchrony considered as a bad practice?
Is there a way to achieve the same without using EAP?
I have read about IProgress interface, but I think there is no way to use it to report WebClient progress.
The bad news is that the answer is NO!
The good news is that any EAP API can be converted into a TAP API.
Try this:
public static class WebClientExtensios
{
public static async Task DownloadFileTaskAsync(
this WebClient webClient,
Uri address,
string fileName,
IProgress<Tuple<long, int, long>> progress)
{
// Create the task to be returned
var tcs = new TaskCompletionSource<object>(address);
// Setup the callback event handler handlers
AsyncCompletedEventHandler completedHandler = (cs, ce) =>
{
if (ce.UserState == tcs)
{
if (ce.Error != null) tcs.TrySetException(ce.Error);
else if (ce.Cancelled) tcs.TrySetCanceled();
else tcs.TrySetResult(null);
}
};
DownloadProgressChangedEventHandler progressChangedHandler = (ps, pe) =>
{
if (pe.UserState == tcs)
{
progress.Report(
Tuple.Create(
pe.BytesReceived,
pe.ProgressPercentage,
pe.TotalBytesToReceive));
}
};
try
{
webClient.DownloadFileCompleted += completedHandler;
webClient.DownloadProgressChanged += progressChangedHandler;
webClient.DownloadFileAsync(address, fileName, tcs);
await tcs.Task;
}
finally
{
webClient.DownloadFileCompleted -= completedHandler;
webClient.DownloadProgressChanged -= progressChangedHandler;
}
}
}
And just use it like this:
void Main()
{
var webClient = new WebClient();
webClient.DownloadFileTaskAsync(
new Uri("http://feeds.paulomorgado.net/paulomorgado/blogs/en"),
#"c:\temp\feed.xml",
new Progress<Tuple<long, int, long>>(t =>
{
Console.WriteLine($#"
Bytes received: {t.Item1,25:#,###}
Progress percentage: {t.Item2,25:#,###}
Total bytes to receive: {t.Item3,25:#,###}");
})).Wait();
}

How to get OnPresence and OnRosterList to not be non-responsive

Currently, I am able to be connected to the server, and I was able to set my "status" and my presence. However, I am unable to get my "friends/buddies/contacts" to come up.
xmpp = new XmppClientConnection();
xmpp.ConnectServer =
xmpp.UseSSL = true;
xmpp.Port = 5223;
xmpp.Server =
xmpp.Resource =
xmpp.UseSSL = true;
xmpp.Port = 5223;
xmpp.Username = username;
xmpp.Password = password;
xmpp.SocketConnectionType = SocketConnectionType.Direct;
xmpp.AutoRoster = true;
xmpp.AutoPresence = true;
xmpp.AutoResolveConnectServer = false;
xmpp.AutoAgents = false;
xmpp.RegisterAccount = false;
xmpp.UseCompression = false;
xmpp.OnReadXml += new XmlHandler(OnReadXml);
xmpp.OnWriteXml += new XmlHandler(OnWriteXml);
xmpp.OnStreamError += new XmppElementHandler(OnStreamError);
xmpp.OnSocketError += new ErrorHandler(OnSocketError);
xmpp.ClientSocket.OnValidateCertificate += new RemoteCertificateValidationCallback(ClientSocket_OnValidateCertificate);
xmpp.OnIq += new IqHandler(OnIq);
xmpp.OnRosterStart += new ObjectHandler(OnRosterStart);
xmpp.OnRosterItem += new agsXMPP.XmppClientConnection.RosterHandler(OnRosterItem);
xmpp.OnRosterEnd += new ObjectHandler(OnRosterEnd);
xmpp.OnPresence += new PresenceHandler(OnPresence);
xmpp.OnMessage += new MessageHandler(OnMessage);
xmpp.OnError += new ErrorHandler(OnError);
xmpp.OnLogin += new ObjectHandler(OnLogin);
xmpp.OnAuthError += new XmppElementHandler(OnAuthError);
xmpp.OnClose += new ObjectHandler(OnClose);
xmpp.OnSaslStart += new agsXMPP.sasl.SaslEventHandler(OnSaslStart);
try { xmpp.Open(); }
catch { }
do { Thread.Sleep(300); }
while (!xmpp.Authenticated);
for (bool IsBinding = false; IsBinding == false; Thread.Sleep(1000))
if (xmpp.XmppConnectionState.ToString().CompareTo("Binding") != 0)
IsBinding = true;
And for my OnPresence and OnRosterList I have (unfortunately, none of the following works.):
private void OnRosterStart(object sender)
{
Console.WriteLine("Roster has started.");
}
private void OnRosterItem(object sender, agsXMPP.protocol.iq.roster.RosterItem item)
{
messenger.AddToRoster(item.Name);
messenger.AddToRoster(item.Jid.User);
}
private void OnRosterEnd(object sender)
{
Console.WriteLine("Roster has ended.");
}
private void OnPresence(object sender, Presence pres)
{
MessageBox.Show("Hello");
Console.WriteLine("Here are your contacts: ");
Console.WriteLine("{0}#{1} {2}", pres.From.User, pres.From.Server, pres.Type);
}
http://puu.sh/gr73T/a8595b59aa.png
Thanks.
Problem Statement: Initially, OnPresence doesn't do anything. (I understand it is an async function, however, it is clearly not working after keeping the application open for 30 minutes waiting.) Furthermore, I'd thought I heard somewhere that OnPresence should be handled once you send your presence to the server, however I am not getting my "contacts/friends/ buddies" presence at all.
Solution:
I have gotten this resolved. The solution is to not run any of these handlers in a thread. ;)

Call callback through 1 variable/object

I work on a download system that downloads up to 4 files at once and if there is an error while downloading then it retries up to 5 times.
I would like it to call different callbacks with their variables once it finished downloading.
This is what I wrote:
private void Download(string url, (something) callback)
{
if (asyncworkers++ < Global.paralleldownloads -1) // async download
{
using (WebClient client = new WebClient())
{
client.Proxy = (Global.proxymode == 2 ? new WebProxy(dynamicproxy) : (Global.proxymode == 1 ? new WebProxy(Global.proxy) : null));
System.Timers.Timer timer = new System.Timers.Timer(5000);
timer.Enabled = true;
client.DownloadStringCompleted += (sender, e) =>
{
asyncworkers--;
if (timer.Enabled)
{
timer.Stop();
if (e.Error == null && e.Result.Length > 0)
{
AppendTextBox("successful async\r\n");
errors = 0;
//call "callback" and its variables here
}else{
AppendTextBox("empty async\r\n");
if (errors++ > 3)
{
//stop trying
}else{
Download(url, callback);
}
}
}
};
client.DownloadStringAsync(new Uri(url));
timer.Elapsed += (sender, e) =>
{
AppendTextBox("timeout async\r\n");
timer.Enabled = false;
client.CancelAsync();
Download(url, callback);
};
}
}else{ // sync download to delay it
var request = WebRequest.Create(url);
request.Proxy = (Global.proxymode == 2 ? new WebProxy(dynamicproxy) : (Global.proxymode == 1 ? new WebProxy(Global.proxy) : null));
request.Timeout = 5000;
using (var response = request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
using (var reader = new StreamReader(stream))
{
string data = reader.ReadToEnd();
if (data.Length > 0)
{
AppendTextBox("successful sync\r\n");
asyncworkers--;
errors = 0;
//call "callback" and its variables here
return;
}
}
}
}
asyncworkers--;
AppendTextBox("error sync\r\n");
if (errors++ > 3)
{
//stop trying
}else{
Download(url, callback);
}
}
}
This is how I would like to use it:
Download("http://.....", GetDataDone(var1, var2, var3, var4));
or
Download("http://.....", UpdateDone());
I hope the was I described it is at least a little bit clear to you. How could I get it working the way I wish? Thanks!
Declare callback as Action<T1,T2,T3,T4> where T1 is the type of var1, T2 is var2, etc. Invoke the callback like this:
public void Download(string url, Action<T1,..> callback)
{
//....
callback(var1,var2,var3,var4);
//...
}
Pass a function with matching signature by name as the callback or use a lambda to match the signature:
public void OnDownload(T1 var1, T2 var2, T3 var3, T4 var4)
{
// do stuff here
}
//later
Download("http...", OnDownload);
Update after comment
Use a lambda expression to pass arguments upfront:
public void Download(string url, Action callback)
{
//...
callback();
//..
}
Download("http://..", () => OnDownload(42, "request", user, 0.5f));

Leaking Webservice Completed events?

In my C# application I have a service reference where I do the same call over and over again.
void GetData()
{
var request = new GetDataRequest { someData = "blabla" } ;
service.GetDataCompleted += (sender, args) =>
{
// do something; i'm afraid that this is executed multiple times, as GetData() is called multiple times
}
service.GetDataAsync(request);
}
void GetData2()
{
var request = new GetDataRequest { someData = "blabla2" } ;
service.GetDataCompleted += (sender, args) =>
{
// do something different here!
}
service.GetDataAsync(request);
}
I have the suspicion that this leaks somehow, since i register a new event handler every time.
Is it true, and if yes, how can i avoid that?
You can do this:
void GetData()
{
var request = new GetDataRequest { someData = "blabla" } ;
EventHandler handler = null;
handler = (sender, args) =>
{
// do something; i'm afraid that this is executed multiple times, as GetData() is called multiple times
//remove itself
service.GetDataCompleted -= handler;
};
service.GetDataCompleted += handler;
service.GetDataAsync(request);
}
void GetData2()
{
var request = new GetDataRequest { someData = "blabla2" } ;
EventHandler handler = null;
handler = (sender, args) =>
{
// do something different here!
//remove itself
service.GetDataCompleted -= handler;
};
service.GetDataCompleted += handler;
service.GetDataAsync(request);
}
EDIT : About synchronizing calls only to its handler: If you call GetData2() immediately followed by GetData() you will get unpredictable results from above code. Mostly, both handlers will be called for both requests.
The simplest solution for this is to have separate service proxy object per GetDataX() method. This way you can make both requests concurrently.
However, if you are forced to use only a single proxy object, and make concurrent requests - a wrong handler could be called. One option is you can use a UserState object to identify your request, so even if wrong handler is called, it can detect that its being called for the wrong request.
void GetData()
{
var request = new GetDataRequest { someData = "blabla" } ;
Guid userState = Guid.NewGuid();
EventHandler<GetDataCompletedEventArgs> handler = null;
handler = (sender, args) =>
{
if (userState.Equals(args.UserState))
{
// do something; i'm afraid that this is executed multiple times, as GetData() is called multiple times
//remove itself
service.GetDataCompleted -= handler;
}
};
service.GetDataCompleted += handler;
service.GetDataAsync(request, userState);
}
void GetData2()
{
var request = new GetDataRequest { someData = "blabla2" } ;
Guid userState = Guid.NewGuid();
EventHandler<GetDataCompletedEventArgs> handler = null;
handler = (sender, args) =>
{
if (userState.Equals(args.UserState))
{
// do something different here!
//remove itself
service.GetDataCompleted -= handler;
}
};
service.GetDataCompleted += handler;
service.GetDataAsync(request, userState);
}
Now if you make concurrent requests and even if both handlers are called for both requests, only the right code for the right request will run inside the if condition.
In case you don't want to use UserState there are some ways you can synchronize the calls so that handlers don't step over each other. Following is one non-blocking way using Tasks that makes sure requests happen one after another.
Task<GetDataResponse> GetData()
{
var src = new TaskCompletionSource<GetDataResponse>();
var request = new GetDataRequest { someData = "blabla" } ;
EventHandler handler = null;
handler = (sender, args) =>
{
// do something; i'm afraid that this is executed multiple times, as GetData() is called multiple times
src.SetResult(args.Result);
//remove itself
service.GetDataCompleted -= handler;
};
service.GetDataCompleted += handler;
service.GetDataAsync(request);
return src.Task;
}
Task<GetDataResponse> GetData2()
{
var src = new TaskCompletionSource<GetDataResponse>();
var request = new GetDataRequest { someData = "blabla2" } ;
EventHandler handler = null;
handler = (sender, args) =>
{
// do something different here!
src.SetResult(args.Result);
//remove itself
service.GetDataCompleted -= handler;
};
service.GetDataCompleted += handler;
service.GetDataAsync(request);
return src.Task;
}
And in the calling method, you make sure they are called one after another:
GetData().ContinueWith( t => GetData2() );

Categories