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服务应用。

最新文章

  1. Vue脚手架工具vue-cli和调试组件vue-devtools
  2. [水煮 ASP.NET Web API2 方法论](3-6)万能路由
  3. CSS3与页面布局学习笔记(二)——盒子模型(Box Model)、边距折叠、内联与块标签、CSSReset
  4. PRCR-1065 Failed to stop resource ora.asm 处理
  5. Position和anchorPoint
  6. Lua函数之一
  7. apache ab压力测试报错(apr_socket_recv: Connection reset by peer (104))
  8. 使用Active MQ在.net和java系统之间通信
  9. hide(1000)跟show(1000)
  10. sql server日期时间转字符串(转)
  11. hibernate学习(二)
  12. ZA7783:MIPI转LVDS/MIPI转RGB888/RGB转LVDS
  13. how to check if you have TURNIN successfully?
  14. php 编程效率(1)
  15. Akka(20): Stream:压力缓冲-Batching backpressure and buffering
  16. Spring标签之Bean @Scope
  17. Jtest的简单使用
  18. XcenServer和XcenCenterter的安装
  19. ubuntu 下安装摄像头驱动
  20. FFMPEG,将字幕“烧进”MP4视频中

热门文章

  1. COOKIE和SESSION关系和区别等
  2. linux 数据盘和系统盘的查看
  3. 【zookeeper】 zookeeper 集群搭建
  4. Java集合----Set集合
  5. CCNet持续集成编译中SVN问题解决
  6. Hacking up an armv7s library
  7. Sublime Text 3配置Minify压缩,格式化css,js,html,json,svg
  8. Python汉英/英汉翻译(百度API/有道API)
  9. go http 文件下载
  10. mysql日期处在某两个时间段之间的between比较