[WorldWind学习]19.WebDownload

  1 using System;
  2 using System.Diagnostics;
  3 using System.Globalization;
  4 using System.Net;
  5 using System.IO;
  6 using System.IO.Compression;
  7 using System.Threading;
  8 using System.Xml;
  9 using Utility;
 10 using WorldWind;
 11 
 12 namespace WorldWind.Net
 13 {
 14     public delegate void DownloadProgressHandler(int bytesRead, int totalBytes);
 15     public delegate void DownloadCompleteHandler(WebDownload wd);
 16 
 17     public enum DownloadType
 18     {
 19         Unspecified,
 20         Wms
 21     }
 22     /// <summary>
 23     /// 网络下载对象,负责下载数据
 24     /// </summary>
 25     public class WebDownload : IDisposable
 26     {
 27         #region Static proxy properties
 28 
 29         static public bool Log404Errors = false;
 30         static public bool useWindowsDefaultProxy = true;
 31         static public string proxyUrl = "";
 32         static public bool useDynamicProxy;
 33         static public string proxyUserName = "";
 34         static public string proxyPassword = "";
 35 
 36         #endregion
 37         public static string UserAgent = String.Format(
 38             CultureInfo.InvariantCulture,
 39             "World Wind v{0} ({1}, {2})",
 40             System.Windows.Forms.Application.ProductVersion,
 41             Environment.OSVersion.ToString(),
 42             CultureInfo.CurrentCulture.Name);
 43 
 44         //下载连接字符串
 45         public string Url;
 46 
 47         /// <summary>
 48         /// Memory downloads fills this stream
 49         /// </summary>
 50         public Stream ContentStream;
 51 
 52         public string SavedFilePath;
 53         public bool IsComplete;
 54 
 55         /// <summary>
 56         /// Called when data is being received.  
 57         /// Note that totalBytes will be zero if the server does not respond with content-length.
 58         /// 开始接收数据时回调
 59         /// 总的字节数为0,如果服务没有返回目录长度
 60         /// </summary>
 61         public DownloadProgressHandler ProgressCallback;
 62 
 63         /// <summary>
 64         /// Called to update debug window.
 65         /// 更新debug窗体,回调函数
 66         /// </summary>
 67         public static DownloadCompleteHandler DebugCallback;
 68 
 69         /// <summary>
 70         /// Called when a download has ended with success or failure
 71         /// 当下载成功或者失败时回调
 72         /// </summary>
 73         public static DownloadCompleteHandler DownloadEnded;
 74 
 75         /// <summary>
 76         /// Called when download is completed.  Call Verify from event handler to throw any exception.
 77         /// 下载完成时回调,调用验证事件是否抛出异常。
 78         /// </summary>
 79         public DownloadCompleteHandler CompleteCallback;
 80 
 81         public DownloadType DownloadType = DownloadType.Unspecified;
 82         public string ContentType;
 83         public int BytesProcessed;
 84         public int ContentLength;
 85 
 86         // variables to allow placefinder to use gzipped requests
 87         //  *default to uncompressed requests to avoid breaking other things
 88         public bool Compressed = false;
 89         public string ContentEncoding;
 90 
 91         /// <summary>
 92         /// The download start time (or MinValue if not yet started)
 93         /// </summary>
 94         public DateTime DownloadStartTime = DateTime.MinValue;
 95 
 96         internal HttpWebRequest request;
 97         internal HttpWebResponse response;
 98 
 99         protected Exception downloadException;
100 
101         protected bool isMemoryDownload;
102         /// <summary>
103         /// used to signal thread abortion; if true, the download thread was aborted
104         /// </summary>
105         private bool stopFlag = false;
106         //下载线程
107         protected Thread dlThread;
108 
109         /// <summary>
110         /// Initializes a new instance of the <see cref="WebDownload"/> class.
111         /// 构造函数,初始化下载对象
112         /// </summary>
113         /// <param name="url">The URL to download from.</param>
114         public WebDownload(string url)
115         {
116             this.Url = url;
117         }
118 
119         /// <summary>
120         /// Initializes a new instance of the <see cref="T:WorldWind.Net.WebDownload"/> class.
121         /// </summary>
122         public WebDownload()
123         {
124         }
125 
126         /// <summary>
127         /// Whether the download is currently being processed (active).
128         /// 当前下载是否仍在进行
129         /// </summary>
130         public bool IsDownloadInProgress
131         {
132             get
133             {
134                 return dlThread != null && dlThread.IsAlive;
135             }
136         }
137 
138         /// <summary>
139         /// Contains the exception that occurred during download, or null if successful.
140         /// </summary>
141         public Exception Exception
142         {
143             get
144             {
145                 return downloadException;
146             }
147         }
148 
149         /// <summary>
150         /// Asynchronous download of HTTP data to file. 
151         /// 异步下载Http数据,通过开启新线程实现。
152         /// </summary>
153         public void BackgroundDownloadFile()
154         {
155             if (CompleteCallback == null)
156                 throw new ArgumentException("No download complete callback specified.");
157             //实例化下载线程
158             dlThread = new Thread(new ThreadStart(Download));
159             dlThread.Name = "WebDownload.dlThread";
160             dlThread.IsBackground = true;
161             dlThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
162             //开启后台下载线程
163             dlThread.Start();
164         }
165 
166         /// <summary>
167         /// Asynchronous download of HTTP data to file.
168         /// 异步下载Http数据,绑定下载完成后的回调函数。
169         /// </summary>
170         public void BackgroundDownloadFile(DownloadCompleteHandler completeCallback)
171         {
172             CompleteCallback += completeCallback;
173             BackgroundDownloadFile();
174         }
175 
176         /// <summary>
177         /// Download image of specified type. (handles server errors for wms type)
178         /// </summary>
179         public void BackgroundDownloadFile(DownloadType dlType)
180         {
181             DownloadType = dlType;
182             BackgroundDownloadFile();
183         }
184 
185         /// <summary>
186         /// Asynchronous download of HTTP data to in-memory buffer.
187         /// 异步下载Http数据到内存缓冲区
188         /// </summary>
189         public void BackgroundDownloadMemory()
190         {
191             if (CompleteCallback == null)
192                 throw new ArgumentException("No download complete callback specified.");
193 
194             isMemoryDownload = true;
195             dlThread = new Thread(new ThreadStart(Download));
196             dlThread.Name = "WebDownload.dlThread(2)";
197             dlThread.IsBackground = true;
198             dlThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
199             dlThread.Start();
200         }
201 
202         /// <summary>
203         /// Asynchronous download of HTTP data to in-memory buffer. 
204         /// </summary>
205         public void BackgroundDownloadMemory(DownloadCompleteHandler completeCallback)
206         {
207             CompleteCallback += completeCallback;
208             BackgroundDownloadMemory();
209         }
210 
211         /// <summary>
212         /// Download image of specified type. (handles server errors for WMS type)
213         /// </summary>
214         /// <param name="dlType">Type of download.</param>
215         public void BackgroundDownloadMemory(DownloadType dlType)
216         {
217             DownloadType = dlType;
218             BackgroundDownloadMemory();
219         }
220 
221         /// <summary>
222         /// Synchronous download of HTTP data to in-memory buffer. 
223         /// </summary>
224         public void DownloadMemory()
225         {
226             isMemoryDownload = true;
227             Download();
228         }
229 
230         /// <summary>
231         /// Download image of specified type. (handles server errors for WMS type)
232         /// </summary>
233         public void DownloadMemory(DownloadType dlType)
234         {
235             DownloadType = dlType;
236             DownloadMemory();
237         }
238 
239         /// <summary>
240         /// HTTP downloads to memory.
241         /// </summary>
242         /// <param name="progressCallback">The progress callback.</param>
243         public void DownloadMemory(DownloadProgressHandler progressCallback)
244         {
245             ProgressCallback += progressCallback;
246             DownloadMemory();
247         }
248 
249         /// <summary>
250         /// Synchronous download of HTTP data to in-memory buffer. 
251         /// </summary>
252         public void DownloadFile(string destinationFile)
253         {
254             SavedFilePath = destinationFile;
255 
256             Download();
257         }
258 
259         /// <summary>
260         /// Download image of specified type to a file. (handles server errors for WMS type)
261         /// </summary>
262         public void DownloadFile(string destinationFile, DownloadType dlType)
263         {
264             DownloadType = dlType;
265             DownloadFile(destinationFile);
266         }
267 
268         /// <summary>
269         /// Saves a http in-memory download to file.
270         /// </summary>
271         /// <param name="destinationFilePath">File to save the downloaded data to.</param>
272         public void SaveMemoryDownloadToFile(string destinationFilePath)
273         {
274             if (ContentStream == null)
275                 throw new InvalidOperationException("No data available.");
276 
277             // Cache the capabilities on file system
278             ContentStream.Seek(0, SeekOrigin.Begin);
279             using (Stream fileStream = File.Create(destinationFilePath))
280             {
281                 if (ContentStream is MemoryStream)
282                 {
283                     // Write the MemoryStream buffer directly (2GB limit)
284                     MemoryStream ms = (MemoryStream)ContentStream;
285                     fileStream.Write(ms.GetBuffer(), 0, (int)ms.Length);
286                 }
287                 else
288                 {
289                     // Block copy
290                     byte[] buffer = new byte[4096];
291                     while (true)
292                     {
293                         int numRead = ContentStream.Read(buffer, 0, buffer.Length);
294                         if (numRead <= 0)
295                             break;
296                         fileStream.Write(buffer, 0, numRead);
297                     }
298                 }
299             }
300             ContentStream.Seek(0, SeekOrigin.Begin);
301         }
302 
303         /// <summary>
304         /// Aborts the current download. 
305         /// 终止当前下载
306         /// </summary>
307         public void Cancel()
308         {
309             CompleteCallback = null;
310             ProgressCallback = null;
311             if (dlThread != null && dlThread != Thread.CurrentThread)
312             {
313                 if (dlThread.IsAlive)
314                 {
315                     Log.Write(Log.Levels.Verbose, "WebDownload.Cancel() : stopping download thread...");
316                     stopFlag = true;
317                     if (!dlThread.Join(500))
318                     {
319                         Log.Write(Log.Levels.Warning, "WebDownload.Cancel() : download thread refuses to die, forcing Abort()");
320                         dlThread.Abort();
321                     }
322                 }
323                 dlThread = null;
324             }
325         }
326 
327         /// <summary>
328         /// Notify event subscribers of download progress.
329         /// </summary>
330         /// <param name="bytesRead">Number of bytes read.</param>
331         /// <param name="totalBytes">Total number of bytes for request or 0 if unknown.</param>
332         private void OnProgressCallback(int bytesRead, int totalBytes)
333         {
334             if (ProgressCallback != null)
335             {
336                 ProgressCallback(bytesRead, totalBytes);
337             }
338         }
339 
340         /// <summary>
341         /// Called with detailed information about the download.
342         /// </summary>
343         /// <param name="wd">The WebDownload.</param>
344         private static void OnDebugCallback(WebDownload wd)
345         {
346             if (DebugCallback != null)
347             {
348                 DebugCallback(wd);
349             }
350         }
351 
352         /// <summary>
353         /// Called when downloading has ended.
354         /// </summary>
355         /// <param name="wd">The download.</param>
356         private static void OnDownloadEnded(WebDownload wd)
357         {
358             if (DownloadEnded != null)
359             {
360                 DownloadEnded(wd);
361             }
362         }
363 
364         /// <summary>
365         /// Synchronous HTTP download
366         /// 线程异步调用的方法Download中,采用请求响应同步下载数据
367         /// </summary>
368         protected void Download()
369         {
370             Log.Write(Log.Levels.Debug, "Starting download thread...");
371 
372             Debug.Assert(Url.StartsWith("http://"));
373             DownloadStartTime = DateTime.Now;
374             try
375             {
376                 try
377                 {
378                     // If a registered progress-callback, inform it of our download progress so far.
379                     OnProgressCallback(0, 1);
380                     OnDebugCallback(this);
381 
382                     // check to see if thread was aborted (multiple such checks within the thread function)
383                     if (stopFlag)
384                     {
385                         IsComplete = true;
386                         return;
387                     }
388 
389 
390                     // create content stream from memory or file
391                     if (isMemoryDownload && ContentStream == null)
392                     {
393                         ContentStream = new MemoryStream();
394                     }
395                     else
396                     {
397                         // Download to file
398                         string targetDirectory = Path.GetDirectoryName(SavedFilePath);
399                         if (targetDirectory.Length > 0)
400                             Directory.CreateDirectory(targetDirectory);
401                         ContentStream = new FileStream(SavedFilePath, FileMode.Create);
402                     }
403 
404                     // Create the request object.
405                     request = (HttpWebRequest)WebRequest.Create(Url);
406                     request.UserAgent = UserAgent;
407 
408 
409                     if (this.Compressed)
410                     {
411                         request.Headers.Add("Accept-Encoding", "gzip,deflate");
412                     }
413                     if (stopFlag)
414                     {
415                         IsComplete = true;
416                         return;
417                     }
418                     request.Proxy = ProxyHelper.DetermineProxyForUrl(
419                         Url,
420                         useWindowsDefaultProxy,
421                         useDynamicProxy,
422                         proxyUrl,
423                         proxyUserName,
424                         proxyPassword);
425 
426                     // TODO: probably better done via BeginGetResponse() / EndGetResponse() because this may block for a while
427                     // causing warnings in thread abortion.
428                     using (response = request.GetResponse() as HttpWebResponse)
429                     {
430                         // only if server responds 200 OK
431                         if (response.StatusCode == HttpStatusCode.OK)
432                         {
433                             ContentType = response.ContentType;
434                             ContentEncoding = response.ContentEncoding;
435 
436                             // Find the data size from the headers.
437                             string strContentLength = response.Headers["Content-Length"];
438                             if (strContentLength != null)
439                             {
440                                 ContentLength = int.Parse(strContentLength, CultureInfo.InvariantCulture);
441                             }
442                             //缓存字节数组,大小1500byte
443                             byte[] readBuffer = new byte[1500];
444                             using (Stream responseStream = response.GetResponseStream())
445                             {
446                                 while (true)
447                                 {
448                                     if (stopFlag)
449                                     {
450                                         IsComplete = true;
451                                         return;
452                                     }
453 
454                                     //  Pass do.readBuffer to BeginRead.
455                                     int bytesRead = responseStream.Read(readBuffer, 0, readBuffer.Length);
456                                     if (bytesRead <= 0)
457                                         break;
458 
459                                     //TODO: uncompress responseStream if necessary so that ContentStream is always uncompressed
460                                     //  - at the moment, ContentStream is compressed if the requesting code sets
461                                     //    download.Compressed == true, so ContentStream must be decompressed 
462                                     //    by whatever code is requesting the gzipped download
463                                     //  - this hack only works for requests made using the methods that download to memory,
464                                     //    requests downloading to file will result in a gzipped file
465                                     //  - requests that do not explicity set download.Compressed = true should be unaffected
466 
467                                     ContentStream.Write(readBuffer, 0, bytesRead);
468 
469                                     BytesProcessed += bytesRead;
470 
471                                     // If a registered progress-callback, inform it of our download progress so far.
472                                     OnProgressCallback(BytesProcessed, ContentLength);
473                                     OnDebugCallback(this);
474                                 }
475                             }
476 
477                         }
478                     }
479 
480                     HandleErrors();
481                 }
482                 catch (ThreadAbortException)
483                 {
484                     // re-throw to avoid it being caught by the catch-all below
485                     Log.Write(Log.Levels.Verbose, "Re-throwing ThreadAbortException.");
486                     throw;
487                 }
488                 catch (System.Configuration.ConfigurationException)
489                 {
490                     // is thrown by WebRequest.Create if App.config is not in the correct format
491                     // TODO: don't know what to do with it
492                     throw;
493                 }
494                 catch (Exception caught)
495                 {
496                     try
497                     {
498                         // Remove broken file download
499                         if (ContentStream != null)
500                         {
501                             ContentStream.Close();
502                             ContentStream = null;
503                         }
504                         if (SavedFilePath != null && SavedFilePath.Length > 0)
505                         {
506                             File.Delete(SavedFilePath);
507                         }
508                     }
509                     catch (Exception)
510                     {
511                     }
512                     SaveException(caught);
513                 }
514 
515                 if (stopFlag)
516                 {
517                     IsComplete = true;
518                     return;
519                 }
520 
521                 if (ContentLength == 0)
522                 {
523                     ContentLength = BytesProcessed;
524                     // If a registered progress-callback, inform it of our completion
525                     OnProgressCallback(BytesProcessed, ContentLength);
526                 }
527 
528                 if (ContentStream is MemoryStream)
529                 {
530                     ContentStream.Seek(0, SeekOrigin.Begin);
531                 }
532                 else if (ContentStream != null)
533                 {
534                     ContentStream.Close();
535                     ContentStream = null;
536                 }
537 
538                 OnDebugCallback(this);
539 
540                 if (CompleteCallback == null)
541                 {
542                     Verify();
543                 }
544                 else
545                 {
546                     CompleteCallback(this);
547                 }
548             }
549             catch (ThreadAbortException)
550             {
551                 Log.Write(Log.Levels.Verbose, "Download aborted.");
552             }
553             finally
554             {
555                 IsComplete = true;
556             }
557 
558             OnDownloadEnded(this);
559         }
560 
561         /// <summary>
562         /// Handle server errors that don't get trapped by the web request itself.
563         /// </summary>
564         private void HandleErrors()
565         {
566             // HACK: Workaround for TerraServer failing to return 404 on not found
567             if (ContentStream.Length == 15)
568             {
569                 // a true 404 error is a System.Net.WebException, so use the same text here
570                 Exception ex = new FileNotFoundException("The remote server returned an error: (404) Not Found.", SavedFilePath);
571                 SaveException(ex);
572             }
573 
574             // TODO: WMS 1.1 content-type != xml
575             // TODO: Move WMS logic to WmsDownload
576             if (DownloadType == DownloadType.Wms && (
577                 ContentType.StartsWith("text/xml") ||
578                 ContentType.StartsWith("application/vnd.ogc.se")))
579             {
580                 // WMS request failure
581                 SetMapServerError();
582             }
583         }
584 
585         /// <summary>
586         /// If exceptions occurred they will be thrown by calling this function.
587         /// </summary>
588         public void Verify()
589         {
590             if (Exception != null)
591                 throw Exception;
592         }
593 
594         /// <summary>
595         /// Log download error to log file
596         /// </summary>
597         /// <param name="exception"></param>
598         private void SaveException(Exception exception)
599         {
600             // Save the exception 
601             downloadException = exception;
602 
603             if (Exception is ThreadAbortException)
604                 // Don't log canceled downloads
605                 return;
606 
607             if (Log404Errors)
608             {
609                 Log.Write(Log.Levels.Error, "HTTP", "Error: " + Url);
610                 Log.Write(Log.Levels.Error + 1, "HTTP", "     : " + exception.Message);
611             }
612         }
613 
614         /// <summary>
615         /// Reads the xml response from the server and throws an error with the message.
616         /// </summary>
617         private void SetMapServerError()
618         {
619             try
620             {
621                 XmlDocument errorDoc = new XmlDocument();
622                 ContentStream.Seek(0, SeekOrigin.Begin);
623                 errorDoc.Load(ContentStream);
624                 string msg = "";
625                 foreach (XmlNode node in errorDoc.GetElementsByTagName("ServiceException"))
626                     msg += node.InnerText.Trim() + Environment.NewLine;
627                 SaveException(new WebException(msg.Trim()));
628             }
629             catch (XmlException)
630             {
631                 SaveException(new WebException("An error occurred while trying to download " + request.RequestUri.ToString() + "."));
632             }
633         }
634 
635         #region IDisposable Members
636 
637         /// <summary>
638         /// Performs application-defined tasks associated with freeing, releasing, or
639         /// resetting unmanaged resources.
640         /// </summary>
641         public void Dispose()
642         {
643             if (dlThread != null && dlThread != Thread.CurrentThread)
644             {
645                 if (dlThread.IsAlive)
646                 {
647                     Log.Write(Log.Levels.Verbose, "WebDownload.Dispose() : stopping download thread...");
648                     stopFlag = true;
649                     if (!dlThread.Join(500))
650                     {
651                         Log.Write(Log.Levels.Warning, "WebDownload.Dispose() : download thread refuses to die, forcing Abort()");
652                         dlThread.Abort();
653                     }
654                 }
655                 dlThread = null;
656             }
657 
658             if (request != null)
659             {
660                 request.Abort();
661                 request = null;
662             }
663 
664             if (ContentStream != null)
665             {
666                 ContentStream.Close();
667                 ContentStream = null;
668             }
669 
670             if (DownloadStartTime != DateTime.MinValue)
671                 OnDebugCallback(this);
672 
673             GC.SuppressFinalize(this);
674         }
675         #endregion
676     }
677 }
View Code

该类基于Http协议,应用层。

Socket有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。传输层

  流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;

  数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。

原文地址:https://www.cnblogs.com/yhlx125/p/3334568.html