FTP 超时检测 ,C# 函数运行时超时功能改进

最近频繁操作FTP,但是也频繁遇到一个问题,那就是检测的超时问题,虽然有设定超时时间,但是由于其他原因,会造成这个超时时间根本无法随心定义,主要原因如MSDN描述如下:
Timeout 是进行后续同步请求时使用 GetResponse 方法等待响应以及 GetRequestStream 方法等待流所允许的毫秒数。 Timeout 适用于整个请求和响应,不单独对 GetRequestStream 与 GetResponse 方法调用响应。 如果资源在超时期限内未返回,请求将引发 WebException,并将 Status 属性设置为 WebExceptionStatus.Timeout。
Timeout 属性必须在 GetRequestStream 或 GetResponse 方法被调用之前设置。 在调用 GetRequestStream 或 GetResponse 方法之后更改 Timeout 属性不起任何作用
Timeout 属性对使用 BeginGetResponse 或 BeginGetRequestStream 方法生成的异步请求无效。
警告 在异步请求的情况下,客户端应用程序实现其自己的超时机制。 请参考 BeginGetResponse 方法中的示例。
若要指定在读写操作超时之前等待的时间量,请使用 ReadWriteTimeout 属性。
域名系统 (DNS) 查询可能需要 15 秒返回或超时。 如果您的请求包含要求解析的主机名,并且您将 Timeout 设置为小于 15 秒的值,则在 15 秒或更长时间之后才会引发 WebException 以指示您的请求超时。


原有的访问FTP的代码如下:
//创建FtpWebRequest对象
FtpWebRequest ftprequest = (FtpWebRequest)WebRequest.Create("ftp://" + DomainName);
ftprequest.Timeout = 5000;//设定5秒超时
ftprequest.ReadWriteTimeout = 5000;
 
//域名系统 (DNS) 查询可能需要 15 秒返回或超时。 如果您的请求包含需要解析的主机
//名,并将 Timeout 设置为少于 15 秒的值,则在引发 WebException 以指示您的请求
//超时之前,可能需要 15 秒或更多的时间。


这种方案在项目中的体检非常不好,因为凡是超过5秒以上的请求(内网中,5秒数字为估算),基本成功访问的可能性很低了,但是FTP自己的超时机制还是会等待,有时甚至等待20-30秒,结果返回访问失败,这样对于用户来说很是头疼,就连我在测试时也是非常纠结,只能在超过5秒后选择直接关闭DEBUG,但是问题终究需要解决,查阅资料后,发现如下的方式:


ManualResetEvent 类  


发送反馈
通知一个或多个正在等待的线程已发生事件。无法继承此类。
命名空间:   System.Threading
程序集:   mscorlib(在 mscorlib.dll 中) 
ManualResetEvent 允许线程通过发信号互相通信。 通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。
当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态。 此线程可被视为控制 ManualResetEvent。 调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。 当控制线程完成活动时,它调用 Set 以发出等待线程可以继续进行的信号。 并释放所有等待线程。
一旦它被终止, ManualResetEvent 将保持终止状态,直到它被手动重置。 即对 WaitOne 的调用将立即返回。 


以下代码参考自:http://www.51testing.com/html/71/n-836371.html

参考别人的代码,鉴于我在项目中需要使用的功能,修改封装如下:

 
	/// <summary>
    /// 函数运行中超时控制类
    /// </summary>
    public class FuncTimeout
    {
        /// <summary> 
        /// 信号量 
        /// </summary> 
        private ManualResetEvent manu = new ManualResetEvent(false);
        /// <summary>
        /// 是否接受到信号 
        /// </summary> 
        private bool isGetSignal;
        /// <summary> 
        /// 设置超时时间 
        /// </summary> 
        private int timeout;


        public delegate bool EventNeedRun(ITimeOutPara paras);
        /// <summary> 
        /// 要调用的方法的一个委托 
        /// </summary> 
        private EventNeedRun FunctionNeedRun;
        /// <summary> 
        /// 构造函数,传入超时的时间以及运行的方法 
        /// </summary> 
        /// <param name="_action"></param> 
        /// <param name="_timeout"></param> 
        public FuncTimeout(EventNeedRun _action, int _timeout)
        {
            FunctionNeedRun = _action;
            timeout = _timeout;
        }
        /// <summary> 
        /// 回调函数 
        /// </summary> 
        /// <param name="ar"></param> 
        public void MyAsyncCallback(IAsyncResult ar)
        {
            //isGetSignal为false,表示异步方法其实已经超出设置的时间,此时不再需要执行回调方法。 
            if (isGetSignal == false)
            {
                Console.WriteLine("放弃执行回调函数");
                Thread.CurrentThread.Abort();
            }
            else
            {
                Console.WriteLine("调用回调函数");
            }
        }
        /// <summary> 
        /// 调用函数 
        /// </summary> 
        /// <param name="param1"></param> 
        public bool doAction(ITimeOutPara paras)
        {
            EventNeedRun WhatTodo = CombineActionAndManuset;
            //通过BeginInvoke方法,在线程池上异步的执行方法。 
            var r = WhatTodo.BeginInvoke(paras, MyAsyncCallback, null);
            //设置阻塞,如果上述的BeginInvoke方法在timeout之前运行完毕,则manu会收到信号。此时isGetSignal为true。 
            //如果timeout时间内,还未收到信号,即异步方法还未运行完毕,则isGetSignal为false。 
            isGetSignal = manu.WaitOne(timeout * 1000);


            if (isGetSignal == true)
            {
                Console.WriteLine("函数运行完毕,收到设置信号,异步执行未超时");
                return true;
            }
            else
            {
                Console.WriteLine("没有收到设置信号,异步执行超时");
                return false;
            }
        }
        /// <summary> 
        /// 把要传进来的方法,和 manu.Set()的方法合并到一个方法体。 
        /// action方法运行完毕后,设置信号量,以取消阻塞。 
        /// </summary> 
        /// <param name="num"></param> 
        private bool CombineActionAndManuset(ITimeOutPara paras)
        {
            bool pass = FunctionNeedRun(paras);
            manu.Set();
            return pass;
        }
    }


    /// <summary>
    /// 可以借助这个接口进行异步方法的参数传入和传出。
    /// </summary>
    public interface ITimeOutPara
    {
        /// <summary>
        /// 参数集合,顺序不能乱
        /// </summary>
        List<object> Parameters { set; get; }
    }


检查FTP连通性的参数类如下:

	/// <summary>
    /// 检查FTP连通性,参数
    /// </summary>
    public class CheckFtpPatermeters : ITimeOutPara
    {
        public List<object> Parameters { set; get; }

        public CheckFtpPatermeters(string DomainName, string FtpUserName, string FtpUserPwd)
        {
            Parameters = new List<object>();
            Parameters.Add(DomainName);
            Parameters.Add(FtpUserName);
            Parameters.Add(FtpUserPwd);
        }
    }

FTP连通性检查方法:

 
	/// <summary>
        /// 异步调用FTP验证
        /// </summary>
        /// <param name="DomainName"></param>
        /// <param name="FtpUserName"></param>
        /// <param name="FtpUserPwd"></param>
        /// <param name="asyncEventTure"></param>
        /// <param name="asyncEventFalse"></param>
        public void CheckFtp(string DomainName, string FtpUserName, string FtpUserPwd
            , EventHandler asyncEventTure, EventHandler asyncEventFalse, int timeout)
        {
            bool ResultValue = true;
            CheckFtpPatermeters paras = new CheckFtpPatermeters(DomainName, FtpUserName, FtpUserPwd);
            try
            {
                FuncTimeout time = new FuncTimeout(asyncCheckFtp, timeout);
                ResultValue = time.doAction(paras);
            }
            catch
            {
                ResultValue = false;
            }
            if (paras.Parameters.Count ==4 && paras.Parameters[3].ToString().Equals("true"))
                asyncEventTure(null, EventArgs.Empty);
            else
                asyncEventFalse(null, EventArgs.Empty);

        }


外部的调用方式:

 
	class Program
    {
        static void Main(string[] args)
        {
            FtpHelper FtpInstance = new FtpHelper();
            string FtpSer = "1.1.1.1";
            string FtpUsr = "xxxx";
            string FtpPass = "xxxx";
             
            FtpInstance.CheckFtp(FtpSer, FtpUsr, FtpPass, returnTrue, returnFalse,8);

            Console.ReadLine();
        }


        public static void returnTrue(object sender,EventArgs e)
        {
            Console.WriteLine("returnTrue");
        }

        public static void returnFalse(object sender, EventArgs e)
        {
            Console.WriteLine("returnFalse");
        }
    }

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值