2017-02-23 6 views
12

नीचे दिए गए WPF कोड को हमेशा के लिए लटका दिया जाता है जब नेटवर्क कनेक्शन 3 या अधिक मिनटों के लिए खो जाता है। जब कनेक्शन बहाल किया जाता है तो न तो फेंकता है और न ही डाउनलोड जारी रहता है और न ही टाइमआउट। यदि छोटी अवधि के लिए नेटवर्क कनेक्शन गुम हो जाता है तो आधे मिनट का कहना है, कनेक्शन बहाल होने के बाद यह फेंकता है। मैं नेटवर्क आउटेज से बचने के लिए इसे और अधिक मजबूत कैसे बना सकता हूं?.Net DownloadFileTaskAsync मजबूत WPF कोड

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; 
     } 
    } 
} 

अद्यतन

वर्तमान समाधान DownloadProgressChangedEventHandler में Timer को पुन: प्रारंभ करने पर आधारित है, इसलिए टाइमर आग कोई 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; 
     } 
    } 
} 
+2

मेरा सुझाव है कि आप डाउनलोड के कोड को शामिल करने के लिए एक नई कक्षा बना रहे हैं, फिर btnDownload_Click से इस क्लास को डाउनलोड करें इस फ़ाइल को डाउनलोड करें। फिर आप कोड को साफ कर सकते हैं, और डीबग आसान कर सकते हैं। – Tony

उत्तर

9

आपको उस डाउनलोड के लिए उचित टाइमआउट लागू करने की आवश्यकता है। लेकिन आप टाइमर का उपयोग करने के लिए, बस Task.Delay और Task.WaitAny. का उपयोग उदाहरण के लिए की जरूरत नहीं है:

static async Task DownloadFile(string url, string output, TimeSpan timeout) {    
    using (var wcl = new WebClient()) 
    { 
     wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;             
     var download = wcl.DownloadFileTaskAsync(url, output); 
     // await two tasks - download and delay, whichever completes first 
     await Task.WhenAny(Task.Delay(timeout), download); 
     var exception = download.Exception; // need to observe exception, if any 
     bool cancelled = !download.IsCompleted && exception == null; 

     // download is not completed yet, nor it is failed - cancel 
     if (cancelled) { 
      wcl.CancelAsync(); 
     } 

     if (cancelled || exception != null) { 
      // delete partially downloaded file if any (note - need to do with retry, might not work with a first try, because CancelAsync is not immediate) 
      int fails = 0; 
      while (true) { 
       try { 
        File.Delete(output); 
        break; 
       } 
       catch { 
        fails++; 
        if (fails >= 10) 
         break; 

        await Task.Delay(1000); 
       } 
      } 
     } 
     if (exception != null) { 
      throw new Exception("Failed to download file", exception); 
     } 
     if (cancelled) { 
      throw new Exception($"Failed to download file (timeout reached: {timeout})"); 
     } 
    } 
} 

उपयोग:

const string SRC = "http://ovh.net/files/10Mio.dat"; 
const string TARGET = @"d:\stuff\10Mio.dat"; 
// Time needed to restore network connection 
TimeSpam TIMEOUT = TimeSpan.FromSeconds(30); 
DownloadFile(SRC,TARGET, TIMEOUT); // might want to await this to handle exceptions 

टिप्पणी के जवाब में अद्यतन। यदि आप प्राप्त डेटा के आधार पर टाइमआउट चाहते हैं, पूरे ऑपरेशन समय पर नहीं, तो Task.Delay के साथ भी यह संभव है। उदाहरण के लिए:

static async Task DownloadFile(string url, string output, TimeSpan timeout) 
{ 
    using (var wcl = new WebClient()) 
    { 
     wcl.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials; 
     DateTime? lastReceived = null; 
     wcl.DownloadProgressChanged += (o, e) => 
     { 
      lastReceived = DateTime.Now; 
     }; 
     var download = wcl.DownloadFileTaskAsync(url, output); 
     // await two tasks - download and delay, whichever completes first 
     // do that until download fails, completes, or timeout expires 
     while (lastReceived == null || DateTime.Now - lastReceived < timeout) { 
      await Task.WhenAny(Task.Delay(1000), download); // you can replace 1 second with more reasonable value 
      if (download.IsCompleted || download.IsCanceled || download.Exception != null) 
       break; 
     } 
     var exception = download.Exception; // need to observe exception, if any 
     bool cancelled = !download.IsCompleted && exception == null; 

     // download is not completed yet, nor it is failed - cancel 
     if (cancelled) 
     { 
      wcl.CancelAsync(); 
     } 

     if (cancelled || exception != null) 
     { 
      // delete partially downloaded file if any (note - need to do with retry, might not work with a first try, because CancelAsync is not immediate) 
      int fails = 0; 
      while (true) 
      { 
       try 
       { 
        File.Delete(output); 
        break; 
       } 
       catch 
       { 
        fails++; 
        if (fails >= 10) 
         break; 

        await Task.Delay(1000); 
       } 
      } 
     } 
     if (exception != null) 
     { 
      throw new Exception("Failed to download file", exception); 
     } 
     if (cancelled) 
     { 
      throw new Exception($"Failed to download file (timeout reached: {timeout})"); 
     } 
    } 
} 
+0

वास्तव में मुझे परवाह नहीं है कि डाउनलोड कब तक चलता रहेगा। यदि कोई कनेक्शन खो गया/पुनर्स्थापित किया गया है और डाउनलोड कई बार जारी रहता है तो यह ठीक है। किसी दिए गए टाइमआउट के लिए डाउनलोड चालू होने पर मुझे मामले का पता लगाने की आवश्यकता है, 'WebClient' मेरे लिए क्या नहीं करेगा। लेकिन विचार के लिए धन्यवाद, मैं 'प्रतीक्षा टास्क' का प्रयास करने की कोशिश करूंगा। जब कोई (टास्क। डेले (टाइमआउट) डाउनलोड करें) 'इसे लूप में रखकर,' { IsInProgress = false; कार्य का इंतजार है। जब कोई (कार्य। डेले (TIMEOUT), डाउनलोड करें); } जबकि (IsInProgress &&! Download.Is पूर्ण); ' – Serg

+0

@ सर्ग मैंने उसी संरचना का उपयोग करके उस से निपटने के लिए एक तरीके से उत्तर अपडेट किया है। – Evk

+0

मैं इसे आज़मा दूंगा, thx। – Serg

1

व्यक्तिगत रूप से, अगर मैं एक मजबूत डाउनलोड समाधान बनाने के लिए थे, मैं एक नेटवर्क कनेक्शन पर नजर रखने, क्योंकि जो हम वास्तव में के लिए इंतजार कर रहे हैं कि जोड़ना होगा। सादगी के लिए, ऐसा कुछ पर्याप्त होगा।

online = true; 

NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged; 
_isNetworkOnline = NetworkInterface.GetIsNetworkAvailable(); 

void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) 
{ 
    online = e.IsAvailable; 
} 

तो फिर तुम वास्तव में नेटवर्क की उपलब्धता के लिए जाँच करें और आप से पहले डाउनलोड या प्रगति करने का प्रयास इंतजार के रूप में उपयुक्त कर सकते हैं ... मैं निश्चित रूप से स्वीकार करेंगे एक सरल पिंग समाधान अनुभव के आधार पर समय पर इस की तुलना में बेहतर काम करने के लिए लगता है कि।

आप जो डाउनलोड कर रहे हैं उसके आकार के आधार पर, नेटवर्क की गति की निगरानी करने से भी मदद मिल सकती है ताकि आप चतुर कनेक्शन के मामले में कैसे छेड़छाड़ कर सकें। विचारों के लिए this project पर एक नज़र डालें।

+0

क्या कोई उपयोगकर्ता कैप्टिव पोर्टल के माध्यम से पीछे/पास हो जाता है, तो 'नेटवर्क उपलब्धता उपलब्धता' आग लगती है? – Serg

+1

आह, नहीं ... यही कारण है कि मैंने कहा कि एक पिंग समाधान आमतौर पर वास्तविक जीवन में बेहतर काम करता है, क्योंकि कैप्टिव पोर्टल, प्रॉक्सी, आदि। सरल पिंग क्लास जो तीन विफल होने के बाद अनुपलब्ध ट्रिगर करता है, कुछ अच्छा होगा। 'नेटवर्क उपलब्धि चेंज' अभी भी यह निर्धारित करने में मदद करता है कि पिंग परीक्षण किया जाना चाहिए या नहीं। –

संबंधित मुद्दे