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. Google的基础设施安全

        Google基础设施在设计时就确保了信息在整个生命周期内都是安全的.这里所说的安全涉及多个层面,包括服务部署.数据存储.进程与进程及外部系统之间的通信,以及运维层面.     物理层      ...

  2. jQuery弹出层效果

    <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta ...

  3. iOS-WKWebView使用

    使用代码:可直接粘贴到自己项目中使用 .h #import "BaseViewController.h" @interface LinkNewsController : BaseV ...

  4. Dubbo注册中心Zookeeper安装步骤

    第一步:安装jdk 第二步:上传zookeeper至Linux 第三步:解压zookeeper安装包(/soft目录是我在根目录下建立的一个用户存放上传安装包的目录),解压命令tar -xvf /so ...

  5. Python 练习题:计算 MAC 地址

    #!/usr/bin/env python #-*- coding:utf-8 -*- ''' 给一个MAC地址加1 ''' mac = '52:54:00:e6:b2:0a' prefix_mac ...

  6. Python MySQLdb 模块

    MySQLdb 是 Python2 连接 MySQL 的一个模块,常见用法如下: [root@localhost ~]$ yum install -y MySQL-python # 安装 MySQLd ...

  7. 也谈谈Unity的transform使用

    一.Transform和transform 我们来详谈Unity的transform使用,这里所说的tansform不是类UnityEngine命名空间下的Transform,而是transform. ...

  8. c语言基础知识要点

    C语言程序的构成 与C++.Java相比,C语言其实很简单,但却非常重要.因为它是C++.Java的基础.不把C语言基础打扎实,很难成为程序员高手. 一.C语言的结构 先通过一个简单的例子,把C语言的 ...

  9. KVC与Runtime结合使用(案例)及其底层原理

    一.KVC 的用法和实践 用法 KVC(Key-value coding)键值编码,顾名思义.额,简单来说,是可以通过对象属性名称(Key)直接给属性值(value)编码(coding)“编码”可以理 ...

  10. mysql使用笔记(一)

    一.安装 使用免安装的版本进行安装: 1. 解压到安装目录 2. 拷贝目录下的 my-default.ini 文件为 my.ini 文件 3. 修改my.ini 文件内容为 [client] port ...