Код WPF ниже висит навсегда, когда сетевое соединение теряется в течение 3 или более минут. Когда соединение восстанавливается, он не бросает и не продолжает загрузку или таймауты. Если сетевое соединение теряется в течение более короткого периода, скажем, полминуты, оно срабатывает после восстановления соединения. Как я могу сделать его более надежным, чтобы выжить в сети?
using System;
using System.Net;
using System.Net.NetworkInformation;
using System.Windows;
namespace WebClientAsync
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            NetworkChange.NetworkAvailabilityChanged +=
                (sender, e) => Dispatcher.Invoke(delegate()
                    {
                        this.Title = "Network is " + (e.IsAvailable ? " available" : "down");
                    });
        }
        const string SRC = "http://ovh.net/files/10Mio.dat";
        const string TARGET = @"d:\stuff\10Mio.dat";
        private async void btnDownload_Click(object sender, RoutedEventArgs e)
        {
            btnDownload.IsEnabled = false;
            btnDownload.Content = "Downloading " + SRC;
            try {
                using (var wcl = new WebClient())
                {
                    wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
                    await wcl.DownloadFileTaskAsync(new Uri(SRC), TARGET);
                    btnDownload.Content = "Downloaded";
                }
            }
            catch (Exception ex)
            {
                btnDownload.Content = ex.Message + Environment.NewLine
                    + ((ex.InnerException != null) ? ex.InnerException.Message : String.Empty);
            }
            btnDownload.IsEnabled = true;
        }
    }
}
ОБНОВЛЕНИЕ
Текущее решение основано на перезапуске Timer в DownloadProgressChangedEventHandler, поэтому таймер срабатывает, только если в течение таймаута не происходит событий DownloadProgressChanged. Похож на уродливый взлом, все еще ищущее лучшее решение.
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace WebClientAsync
{
    public partial class MainWindow : Window
    {
        const string SRC = "http://ovh.net/files/10Mio.dat";
        const string TARGET = @"d:\stuff\10Mio.dat";
        // Time needed to restore network connection
        const int TIMEOUT = 30 * 1000;
        public MainWindow()
        {
            InitializeComponent();
        }
        private async void btnDownload_Click(object sender, RoutedEventArgs e)
        {
            btnDownload.IsEnabled = false;
            btnDownload.Content = "Downloading " + SRC;
            CancellationTokenSource cts = new CancellationTokenSource();
            CancellationToken token = cts.Token;
            Timer timer = new Timer((o) =>
                {
                    // Force async cancellation
                    cts.Cancel();
                }
                , null //state
                , TIMEOUT
                , Timeout.Infinite // once
            );
            DownloadProgressChangedEventHandler handler = (sa, ea) =>
                {
                    // Restart timer
                    if (ea.BytesReceived < ea.TotalBytesToReceive && timer != null)
                    {
                        timer.Change(TIMEOUT, Timeout.Infinite);
                    }
                };
            btnDownload.Content = await DownloadFileTA(token, handler);
            // Note ProgressCallback will fire once again after awaited.
            timer.Dispose();
            btnDownload.IsEnabled = true;
        }
        private async Task<string> DownloadFileTA(CancellationToken token, DownloadProgressChangedEventHandler handler)
        {
            string res = null;
            WebClient wcl = new WebClient();
            wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
            wcl.DownloadProgressChanged += handler;
            try
            {
                using (token.Register(() => wcl.CancelAsync()))
                {
                    await wcl.DownloadFileTaskAsync(new Uri(SRC), TARGET);
                }
                res = "Downloaded";
            }
            catch (Exception ex)
            {
                res = ex.Message + Environment.NewLine
                    + ((ex.InnerException != null) ? ex.InnerException.Message : String.Empty);
            }
            wcl.Dispose();
            return res;
        }
    }
}
