using System;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Xml;
using Utility;
using WorldWind; namespace WorldWind.Net
{
public delegate void DownloadProgressHandler(int bytesRead, int totalBytes);
public delegate void DownloadCompleteHandler(WebDownload wd); public enum DownloadType
{
Unspecified,
Wms
}
/// <summary>
/// 网络下载对象,负责下载数据
/// </summary>
public class WebDownload : IDisposable
{
#region Static proxy properties static public bool Log404Errors = false;
static public bool useWindowsDefaultProxy = true;
static public string proxyUrl = "";
static public bool useDynamicProxy;
static public string proxyUserName = "";
static public string proxyPassword = ""; #endregion
public static string UserAgent = String.Format(
CultureInfo.InvariantCulture,
"World Wind v{0} ({1}, {2})",
System.Windows.Forms.Application.ProductVersion,
Environment.OSVersion.ToString(),
CultureInfo.CurrentCulture.Name); //下载连接字符串
public string Url; /// <summary>
/// Memory downloads fills this stream
/// </summary>
public Stream ContentStream; public string SavedFilePath;
public bool IsComplete; /// <summary>
/// Called when data is being received.
/// Note that totalBytes will be zero if the server does not respond with content-length.
/// 开始接收数据时回调
/// 总的字节数为0,如果服务没有返回目录长度
/// </summary>
public DownloadProgressHandler ProgressCallback; /// <summary>
/// Called to update debug window.
/// 更新debug窗体,回调函数
/// </summary>
public static DownloadCompleteHandler DebugCallback; /// <summary>
/// Called when a download has ended with success or failure
/// 当下载成功或者失败时回调
/// </summary>
public static DownloadCompleteHandler DownloadEnded; /// <summary>
/// Called when download is completed. Call Verify from event handler to throw any exception.
/// 下载完成时回调,调用验证事件是否抛出异常。
/// </summary>
public DownloadCompleteHandler CompleteCallback; public DownloadType DownloadType = DownloadType.Unspecified;
public string ContentType;
public int BytesProcessed;
public int ContentLength; // variables to allow placefinder to use gzipped requests
// *default to uncompressed requests to avoid breaking other things
public bool Compressed = false;
public string ContentEncoding; /// <summary>
/// The download start time (or MinValue if not yet started)
/// </summary>
public DateTime DownloadStartTime = DateTime.MinValue; internal HttpWebRequest request;
internal HttpWebResponse response; protected Exception downloadException; protected bool isMemoryDownload;
/// <summary>
/// used to signal thread abortion; if true, the download thread was aborted
/// </summary>
private bool stopFlag = false;
//下载线程
protected Thread dlThread; /// <summary>
/// Initializes a new instance of the <see cref="WebDownload"/> class.
/// 构造函数,初始化下载对象
/// </summary>
/// <param name="url">The URL to download from.</param>
public WebDownload(string url)
{
this.Url = url;
} /// <summary>
/// Initializes a new instance of the <see cref="T:WorldWind.Net.WebDownload"/> class.
/// </summary>
public WebDownload()
{
} /// <summary>
/// Whether the download is currently being processed (active).
/// 当前下载是否仍在进行
/// </summary>
public bool IsDownloadInProgress
{
get
{
return dlThread != null && dlThread.IsAlive;
}
} /// <summary>
/// Contains the exception that occurred during download, or null if successful.
/// </summary>
public Exception Exception
{
get
{
return downloadException;
}
} /// <summary>
/// Asynchronous download of HTTP data to file.
/// 异步下载Http数据,通过开启新线程实现。
/// </summary>
public void BackgroundDownloadFile()
{
if (CompleteCallback == null)
throw new ArgumentException("No download complete callback specified.");
//实例化下载线程
dlThread = new Thread(new ThreadStart(Download));
dlThread.Name = "WebDownload.dlThread";
dlThread.IsBackground = true;
dlThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
//开启后台下载线程
dlThread.Start();
} /// <summary>
/// Asynchronous download of HTTP data to file.
/// 异步下载Http数据,绑定下载完成后的回调函数。
/// </summary>
public void BackgroundDownloadFile(DownloadCompleteHandler completeCallback)
{
CompleteCallback += completeCallback;
BackgroundDownloadFile();
} /// <summary>
/// Download image of specified type. (handles server errors for wms type)
/// </summary>
public void BackgroundDownloadFile(DownloadType dlType)
{
DownloadType = dlType;
BackgroundDownloadFile();
} /// <summary>
/// Asynchronous download of HTTP data to in-memory buffer.
/// 异步下载Http数据到内存缓冲区
/// </summary>
public void BackgroundDownloadMemory()
{
if (CompleteCallback == null)
throw new ArgumentException("No download complete callback specified."); isMemoryDownload = true;
dlThread = new Thread(new ThreadStart(Download));
dlThread.Name = "WebDownload.dlThread(2)";
dlThread.IsBackground = true;
dlThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
dlThread.Start();
} /// <summary>
/// Asynchronous download of HTTP data to in-memory buffer.
/// </summary>
public void BackgroundDownloadMemory(DownloadCompleteHandler completeCallback)
{
CompleteCallback += completeCallback;
BackgroundDownloadMemory();
} /// <summary>
/// Download image of specified type. (handles server errors for WMS type)
/// </summary>
/// <param name="dlType">Type of download.</param>
public void BackgroundDownloadMemory(DownloadType dlType)
{
DownloadType = dlType;
BackgroundDownloadMemory();
} /// <summary>
/// Synchronous download of HTTP data to in-memory buffer.
/// </summary>
public void DownloadMemory()
{
isMemoryDownload = true;
Download();
} /// <summary>
/// Download image of specified type. (handles server errors for WMS type)
/// </summary>
public void DownloadMemory(DownloadType dlType)
{
DownloadType = dlType;
DownloadMemory();
} /// <summary>
/// HTTP downloads to memory.
/// </summary>
/// <param name="progressCallback">The progress callback.</param>
public void DownloadMemory(DownloadProgressHandler progressCallback)
{
ProgressCallback += progressCallback;
DownloadMemory();
} /// <summary>
/// Synchronous download of HTTP data to in-memory buffer.
/// </summary>
public void DownloadFile(string destinationFile)
{
SavedFilePath = destinationFile; Download();
} /// <summary>
/// Download image of specified type to a file. (handles server errors for WMS type)
/// </summary>
public void DownloadFile(string destinationFile, DownloadType dlType)
{
DownloadType = dlType;
DownloadFile(destinationFile);
} /// <summary>
/// Saves a http in-memory download to file.
/// </summary>
/// <param name="destinationFilePath">File to save the downloaded data to.</param>
public void SaveMemoryDownloadToFile(string destinationFilePath)
{
if (ContentStream == null)
throw new InvalidOperationException("No data available."); // Cache the capabilities on file system
ContentStream.Seek(, SeekOrigin.Begin);
using (Stream fileStream = File.Create(destinationFilePath))
{
if (ContentStream is MemoryStream)
{
// Write the MemoryStream buffer directly (2GB limit)
MemoryStream ms = (MemoryStream)ContentStream;
fileStream.Write(ms.GetBuffer(), , (int)ms.Length);
}
else
{
// Block copy
byte[] buffer = new byte[];
while (true)
{
int numRead = ContentStream.Read(buffer, , buffer.Length);
if (numRead <= )
break;
fileStream.Write(buffer, , numRead);
}
}
}
ContentStream.Seek(, SeekOrigin.Begin);
} /// <summary>
/// Aborts the current download.
/// 终止当前下载
/// </summary>
public void Cancel()
{
CompleteCallback = null;
ProgressCallback = null;
if (dlThread != null && dlThread != Thread.CurrentThread)
{
if (dlThread.IsAlive)
{
Log.Write(Log.Levels.Verbose, "WebDownload.Cancel() : stopping download thread...");
stopFlag = true;
if (!dlThread.Join())
{
Log.Write(Log.Levels.Warning, "WebDownload.Cancel() : download thread refuses to die, forcing Abort()");
dlThread.Abort();
}
}
dlThread = null;
}
} /// <summary>
/// Notify event subscribers of download progress.
/// </summary>
/// <param name="bytesRead">Number of bytes read.</param>
/// <param name="totalBytes">Total number of bytes for request or 0 if unknown.</param>
private void OnProgressCallback(int bytesRead, int totalBytes)
{
if (ProgressCallback != null)
{
ProgressCallback(bytesRead, totalBytes);
}
} /// <summary>
/// Called with detailed information about the download.
/// </summary>
/// <param name="wd">The WebDownload.</param>
private static void OnDebugCallback(WebDownload wd)
{
if (DebugCallback != null)
{
DebugCallback(wd);
}
} /// <summary>
/// Called when downloading has ended.
/// </summary>
/// <param name="wd">The download.</param>
private static void OnDownloadEnded(WebDownload wd)
{
if (DownloadEnded != null)
{
DownloadEnded(wd);
}
} /// <summary>
/// Synchronous HTTP download
/// 线程异步调用的方法Download中,采用请求响应同步下载数据
/// </summary>
protected void Download()
{
Log.Write(Log.Levels.Debug, "Starting download thread..."); Debug.Assert(Url.StartsWith("http://"));
DownloadStartTime = DateTime.Now;
try
{
try
{
// If a registered progress-callback, inform it of our download progress so far.
OnProgressCallback(, );
OnDebugCallback(this); // check to see if thread was aborted (multiple such checks within the thread function)
if (stopFlag)
{
IsComplete = true;
return;
} // create content stream from memory or file
if (isMemoryDownload && ContentStream == null)
{
ContentStream = new MemoryStream();
}
else
{
// Download to file
string targetDirectory = Path.GetDirectoryName(SavedFilePath);
if (targetDirectory.Length > )
Directory.CreateDirectory(targetDirectory);
ContentStream = new FileStream(SavedFilePath, FileMode.Create);
} // Create the request object.
request = (HttpWebRequest)WebRequest.Create(Url);
request.UserAgent = UserAgent; if (this.Compressed)
{
request.Headers.Add("Accept-Encoding", "gzip,deflate");
}
if (stopFlag)
{
IsComplete = true;
return;
}
request.Proxy = ProxyHelper.DetermineProxyForUrl(
Url,
useWindowsDefaultProxy,
useDynamicProxy,
proxyUrl,
proxyUserName,
proxyPassword); // TODO: probably better done via BeginGetResponse() / EndGetResponse() because this may block for a while
// causing warnings in thread abortion.
using (response = request.GetResponse() as HttpWebResponse)
{
// only if server responds 200 OK
if (response.StatusCode == HttpStatusCode.OK)
{
ContentType = response.ContentType;
ContentEncoding = response.ContentEncoding; // Find the data size from the headers.
string strContentLength = response.Headers["Content-Length"];
if (strContentLength != null)
{
ContentLength = int.Parse(strContentLength, CultureInfo.InvariantCulture);
}
//缓存字节数组,大小1500byte
byte[] readBuffer = new byte[];
using (Stream responseStream = response.GetResponseStream())
{
while (true)
{
if (stopFlag)
{
IsComplete = true;
return;
} // Pass do.readBuffer to BeginRead.
int bytesRead = responseStream.Read(readBuffer, , readBuffer.Length);
if (bytesRead <= )
break; //TODO: uncompress responseStream if necessary so that ContentStream is always uncompressed
// - at the moment, ContentStream is compressed if the requesting code sets
// download.Compressed == true, so ContentStream must be decompressed
// by whatever code is requesting the gzipped download
// - this hack only works for requests made using the methods that download to memory,
// requests downloading to file will result in a gzipped file
// - requests that do not explicity set download.Compressed = true should be unaffected ContentStream.Write(readBuffer, , bytesRead); BytesProcessed += bytesRead; // If a registered progress-callback, inform it of our download progress so far.
OnProgressCallback(BytesProcessed, ContentLength);
OnDebugCallback(this);
}
} }
} HandleErrors();
}
catch (ThreadAbortException)
{
// re-throw to avoid it being caught by the catch-all below
Log.Write(Log.Levels.Verbose, "Re-throwing ThreadAbortException.");
throw;
}
catch (System.Configuration.ConfigurationException)
{
// is thrown by WebRequest.Create if App.config is not in the correct format
// TODO: don't know what to do with it
throw;
}
catch (Exception caught)
{
try
{
// Remove broken file download
if (ContentStream != null)
{
ContentStream.Close();
ContentStream = null;
}
if (SavedFilePath != null && SavedFilePath.Length > )
{
File.Delete(SavedFilePath);
}
}
catch (Exception)
{
}
SaveException(caught);
} if (stopFlag)
{
IsComplete = true;
return;
} if (ContentLength == )
{
ContentLength = BytesProcessed;
// If a registered progress-callback, inform it of our completion
OnProgressCallback(BytesProcessed, ContentLength);
} if (ContentStream is MemoryStream)
{
ContentStream.Seek(, SeekOrigin.Begin);
}
else if (ContentStream != null)
{
ContentStream.Close();
ContentStream = null;
} OnDebugCallback(this); if (CompleteCallback == null)
{
Verify();
}
else
{
CompleteCallback(this);
}
}
catch (ThreadAbortException)
{
Log.Write(Log.Levels.Verbose, "Download aborted.");
}
finally
{
IsComplete = true;
} OnDownloadEnded(this);
} /// <summary>
/// Handle server errors that don't get trapped by the web request itself.
/// </summary>
private void HandleErrors()
{
// HACK: Workaround for TerraServer failing to return 404 on not found
if (ContentStream.Length == )
{
// a true 404 error is a System.Net.WebException, so use the same text here
Exception ex = new FileNotFoundException("The remote server returned an error: (404) Not Found.", SavedFilePath);
SaveException(ex);
} // TODO: WMS 1.1 content-type != xml
// TODO: Move WMS logic to WmsDownload
if (DownloadType == DownloadType.Wms && (
ContentType.StartsWith("text/xml") ||
ContentType.StartsWith("application/vnd.ogc.se")))
{
// WMS request failure
SetMapServerError();
}
} /// <summary>
/// If exceptions occurred they will be thrown by calling this function.
/// </summary>
public void Verify()
{
if (Exception != null)
throw Exception;
} /// <summary>
/// Log download error to log file
/// </summary>
/// <param name="exception"></param>
private void SaveException(Exception exception)
{
// Save the exception
downloadException = exception; if (Exception is ThreadAbortException)
// Don't log canceled downloads
return; if (Log404Errors)
{
Log.Write(Log.Levels.Error, "HTTP", "Error: " + Url);
Log.Write(Log.Levels.Error + , "HTTP", " : " + exception.Message);
}
} /// <summary>
/// Reads the xml response from the server and throws an error with the message.
/// </summary>
private void SetMapServerError()
{
try
{
XmlDocument errorDoc = new XmlDocument();
ContentStream.Seek(, SeekOrigin.Begin);
errorDoc.Load(ContentStream);
string msg = "";
foreach (XmlNode node in errorDoc.GetElementsByTagName("ServiceException"))
msg += node.InnerText.Trim() + Environment.NewLine;
SaveException(new WebException(msg.Trim()));
}
catch (XmlException)
{
SaveException(new WebException("An error occurred while trying to download " + request.RequestUri.ToString() + "."));
}
} #region IDisposable Members /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or
/// resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (dlThread != null && dlThread != Thread.CurrentThread)
{
if (dlThread.IsAlive)
{
Log.Write(Log.Levels.Verbose, "WebDownload.Dispose() : stopping download thread...");
stopFlag = true;
if (!dlThread.Join())
{
Log.Write(Log.Levels.Warning, "WebDownload.Dispose() : download thread refuses to die, forcing Abort()");
dlThread.Abort();
}
}
dlThread = null;
} if (request != null)
{
request.Abort();
request = null;
} if (ContentStream != null)
{
ContentStream.Close();
ContentStream = null;
} if (DownloadStartTime != DateTime.MinValue)
OnDebugCallback(this); GC.SuppressFinalize(this);
}
#endregion
}
}

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

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

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

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

[WorldWind学习]19.WebDownload的更多相关文章

  1. (C/C++学习)19.单目标遗传算法的C程序实现

    说明:在学习生活中,经常会遇到各种各样的最优问题,其中最常见的就是求某个多维(多个自变量)函数在各个自变量各取何值时的最大值或最小值:例如求函数 f(x) = (x-5)2+(y-6)2+(z-7)2 ...

  2. C++学习19 类的多继承

    在前面的例子中,派生类都只有一个基类,称为单继承.除此之外,C++也支持多继承,即一个派生类可以有两个或多个基类. 多继承容易让代码逻辑复杂.思路混乱,一直备受争议,中小型项目中较少使用,后来的 Ja ...

  3. Python学习--19 网络编程

    TCP编程 Client 创建一个基于TCP连接的Socket: # coding: utf-8 import socket # 创建一个TCP连接: s = socket.socket(socket ...

  4. [WorldWind学习]18.High-Performance Timer in C#

    In some applications exact time measurement methods are very important. 一些应用程序中精确的时间测量是非常重要的. The of ...

  5. Drupal学习(19) 使用jQuery

    本节学习如果在Drupal里交互使用jQuery. jQuery在Drupal是内置支持的.存在根目录的misc目录中. 当调用drupal_add_js方法,会自动加载jQuery. 在Drupal ...

  6. Kubernetes 学习19基于canel的网络策略

    一.概述 1.我们说过,k8s的可用插件有很多,除了flannel之外,还有一个流行的叫做calico的组件,不过calico在很多项目中都会有这个名字被应用,所以他们把自己称为project cal ...

  7. python基础学习19,01

    听说python入门很容易,所以想试试对我这个零基础会不会很友好呢.做随笔也顺便督促自己不要轻易放弃学习. #登录接口 print("请输入用户名密码") _username = ...

  8. GO学习-(19) Go语言基础之网络编程

    Go语言基础之网络编程 现在我们几乎每天都在使用互联网,我们前面已经学习了如何编写Go语言程序,但是如何才能让我们的程序通过网络互相通信呢?本章我们就一起来学习下Go语言中的网络编程. 关于网络编程其 ...

  9. iOS 学习 - 19 结构体

    //创建新类型typedef struct { int age; ];//最大字节为 20 }Student; Student value2 = {,*strcpy(value2.name, &quo ...

随机推荐

  1. 各大IT公司 技术博客汇总

    来自:http://www.cnblogs.com/IT-Bear/p/3191423.html 腾讯系列(13)  阿里系列(18)  百度系列(3)  搜狐系列(3)  新浪系列(2)  360系 ...

  2. 百度地图api ak值

    http://api.map.baidu.com/geocoder/v2/?ak=859d16285fd000feec89e9032513f8bb&callback=renderReverse ...

  3. 关于JavaScript中的this

    1.关于this的误会. 误会(1):this指向所在函数本身. 我们常常会对this产生各种误会,比如说我们可能认为this会指向所在函数本身,但实际上并非如此. function foo (){ ...

  4. Visual Studio使用技巧,创建自己的代码片段

    1.代码片段的使用示例 在编写代码中常会使用代码片段来提高我们的编写代码的效率,如:在Visual Studio中编写一个 for(int i = 0; i < length;i++) { } ...

  5. 使用 C# 开发智能手机软件:推箱子(二)

    在上篇文章"使用 C# 开发智能手机软件:推箱子(一)"中.我对推箱子程序作了整体介绍.这次,我先介绍 Common/Fcl.cs 源程序文件.  1 using System; ...

  6. float类型如何转换为string类型

    在一些很大的float类型的地方会用科学记数法表示,这个时候如果想完整记录下来,还是得转字符串,这里书写一个float类型转string类型的方法 <?php function float_to ...

  7. U3D关于message的使用

    Message相关有3条指令: SendMessage ("函数名",参数,SendMessageOptions) //GameObject自身的Script BroadcastM ...

  8. Windows下Nutch的配置

    Nutch是一个开源的.Java实现的搜索引擎.它提供了我们运行自己的搜索引擎所需的全部工具. Nutch可以分为2个部分: 抓取部分crawler 抓取程序抓取页面并把抓取回来的数据做成反向索引 搜 ...

  9. beginUpdates和endUpdates-实现UITableView的动画块

    我们在做UITableView的修改,删除,选择时,需要对UITableView进行一系列的动作操作. 这样,我们就会用到 [tableView beginUpdates]; if (newCount ...

  10. c++11——列表初始化

    1. 使用列表初始化 在c++98/03中,对象的初始化方法有很多种,例如 int ar[3] = {1,2,3}; int arr[] = {1,2,3}; //普通数组 struct A{ int ...