土法炼制带Timeout属性的WebClient

       位于System.Net名称空间里边的WebClient,其实很好用,下载和上传等常规动作都可以完成,是居家旅行的佳品。可最近在使用的时候,才发现这个宝贝竟然没有Timeout属性,这实在让人苦恼。google了一下,发现一些人已经很早就发出这个疑问了,可各位大拿的答案也总是那么的Cool: 用WebRequest和WebResponse代替WebClient吧!     这固然是个解决的方法,但直接使用这两个底层对象操纵的话,代码要复杂好多,那些DownloadData,DownloadFile,OpenRead,OpenRead....等等函数都没得用了,好生可惜。

        实在不甘心,那就为WebClient对象添加一个Timeout属性吧。首先我想到的是从WebClient中继承一个新的对象出来,这个想法持续了两秒,看到public sealed class WebClient : System.ComponentModel.Component 这句碑文就嘎然而止了。然而sealed如何抵挡得住Reflector的洪流呢,干脆我把WebClient整个类反编译了出来,好在也不大,才24K。

       由于WebClient是.NET Framework里边的类,逆向工程后必需做一点点整容。首先名称空间不能再属于

System.NET了,随便改一个别的名称空间名字。然后编译一看,呵呵,出现了10多个错误,其实主要是调用.NET Framework内部资源,因为现在它下海了,再不能享受这些Internal的资源了。(反向.NET FW内置的对象都会遇到这样唏嘘的问题)改也非常简单,比如这个函数:

private void CopyHeadersTo(WebRequest request)
  {
   if ((this.m_headers != null) && (request is HttpWebRequest))
   {
    string text1 = this.m_headers["Accept"];
    string text2 = this.m_headers["Connection"];
    string text3 = this.m_headers["Content-Type"];
    string text4 = this.m_headers["Expect"];
    string text5 = this.m_headers["Referer"];
    string text6 = this.m_headers["User-Agent"];       
    
//    this.m_headers.RemoveInternal("Accept");              //Reflector
//    this.m_headers.RemoveInternal("Connection");
//    this.m_headers.RemoveInternal("Content-Type");
//    this.m_headers.RemoveInternal("Expect");
//    this.m_headers.RemoveInternal("Referer");
//    this.m_headers.RemoveInternal("User-Agent");      //Reflector
    this.m_headers.Remove("Accept");                              //手工修改
    this.m_headers.Remove("Connection");
    this.m_headers.Remove("Content-Type");
    this.m_headers.Remove("Expect");
    this.m_headers.Remove("Referer");
    this.m_headers.Remove("User-Agent");                      //手工修改 

    request.Headers = this.m_headers;
    if ((text1 != null) && (text1.Length > 0))
    {
     ((HttpWebRequest) request).Accept = text1;
    }
    if ((text2 != null) && (text2.Length > 0))
    {
     ((HttpWebRequest) request).Connection = text2;
    }
    if ((text3 != null) && (text3.Length > 0))
    {
     ((HttpWebRequest) request).ContentType = text3;
    }
    if ((text4 != null) && (text4.Length > 0))
    {
     ((HttpWebRequest) request).Expect = text4;
    }
    if ((text5 != null) && (text5.Length > 0))
    {
     ((HttpWebRequest) request).Referer = text5;
    }
    if ((text6 != null) && (text6.Length > 0))
    {
     ((HttpWebRequest) request).UserAgent = text6;
    }
   }
  }

改完这个函数后,只剩下几个内部资源的错误了,比如:

try
   {
   ...

   }
   catch (Exception exception1)
   {
    if (!(exception1 is WebException) && !(exception1 is SecurityException))
    {
     //throw new WebException(SR.GetString("net_webclient"), exception1);   //Reflector
     throw new WebException("net_webclient", exception1);                               //手动更改
    }
    throw;
   }

不要使用SR对象就万事OK。

这样错误都搞掉了,WebClient已经翻版出来了,添加Timeout就很容易了吧,如下几句:

private const int noTimeout = -1;

  /// <summary>
  /// 超时毫秒数,默认负1
  /// </summary>
  private int timeout = XWebClient.noTimeout;

  /// <summary>
  /// 超时毫秒数
  /// </summary>
  public int Timeout
  {
   get
   {
    return this.timeout;
   }
   set
   {
    this.timeout = value;
   }
  }

  /// <summary>
  /// 带超时设置的WebClient
  /// </summary>
  /// <param name="timeout">超时毫秒数</param>
  public XWebClient(int timeout)
  {
   this.timeout = timeout;
  }

  /// <summary>
  /// 创建带超时设置的WebRequest对象
  /// </summary>
  /// <param name="uri"></param>
  /// <returns></returns>
  private WebRequest createTimeoutWebRequest(Uri uri)
  {
   WebRequest wreq = WebRequest.Create(uri);
   if (this.timeout != XWebClient.noTimeout)  //加上超时设置
    wreq.Timeout = this.timeout;
   return wreq;
  }

加了之后,我们要在整个类中查找WebRequest.Create字符串,把原来的代码替换成调用createTimeoutWebRequest函数的,我们就完成任务啦。比如下面:

 public byte[] DownloadData(string address)
  {
   byte[] buffer1;
   try
   {
    this.m_responseHeaders = null;
    WebRequest request1 = this.createTimeoutWebRequest(this.GetUri(address));     //加上超时设置
    request1.Credentials = this.Credentials;
    this.CopyHeadersTo(request1);
    WebResponse response1 = request1.GetResponse();
    this.m_responseHeaders = response1.Headers;
    buffer1 = this.ResponseAsBytes(response1);
   }
   catch (Exception exception1)
   {
    if (!(exception1 is WebException) && !(exception1 is SecurityException))
    {
     //throw new WebException(SR.GetString("net_webclient"), exception1);
     throw new WebException("net_webclient", exception1);
    }
    throw;
   }
   return buffer1;
  }

        到此,我们就基本达到目标了,经过测试,暂时没发现什么后遗症。当然,这样做也是有代价的,比如没有了原本函数对象的XML Comment了,不过能用就好。有兴趣甚至可以把超时做成一个event委托出去,在多线程使用场景中可能更方便:)

       最后BTW,看了看vs2005 CTP beta 2,里边的WebClient好像仍然没有Timeout属性的

       涂鸦之作,见笑。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值