C#Ftp类之FtpWebRoesponse意外报停不响应 [System.Net.Sockets]

学无止境,此路甚长。

最近做项目遇到一个问题,自己是做后台的,但涉及到网络的机会有些少,在这方面也是刚刚起步,在这里记录一下自己的成长,以供日后回望。

问题描述:FtpWebRoesponse接收服务器反馈的时候,一直不相应,其实是因为FtpWebRoesponse拿不到消息,一直苦苦等待,直到超时(TimeOut)之后才恢复正常。

触发原因:在调用自定义的方法时,多次实例化了自定义的FtpServer类,导致程序中存在多个NetworkCredentia网络凭证,服务器却只会验证通过第一个。

解决方法:采用单例模式

为了满足同步服务器的需求,暂时使用微软封装的的System.Net.Sockets命名空间下的的Ftp类,编写一个程序模块去请求服务器(测试的时候用的是视窗自带IIS管理器发布的的Ftp服务)在模块中有两个函数是这么写的(代码什么的可以简略的看):

      

   public classFtpServer

{

       private NetworkCredential networkCredential;

        public string FtpUriString { get; set; }//IP,UserName,PassWord都是属性,可以自己赋值,做测试的时候用的是Ftp的网址,用户,密码

        /// </summary>

        /// 请求

        /// </summary>

        /// <param name="uri"></param>

        /// <param name="requestMethod"></param>

        /// <returns></returns>

        public FtpWebRequest CreateFtpWebRequest(string uri, string requestMethod)

        {

            FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(uri);

            networkCredential = new NetworkCredential(this.UserName, this.PassWord);

            request.Credentials = networkCredential;

            request.KeepAlive = true;

            request.UseBinary = true;

            request.Method = requestMethod;

            return request;

        }

        /// <summary>

        /// 反馈

        /// </summary>

        /// <param name="request"></param>

        /// <returns></returns>

        public FtpWebResponse GetFtpWebResponse(FtpWebRequest request)

        {

            FtpWebResponse response = null;

            try

            {

                response = (FtpWebResponse)request.GetResponse();

                return response;

            }

            catch (WebException)

            {

                return null;

            }

            finally

            { }

        }

 

        /// <summary>

        /// 尝试登陆

        /// </summary>

        /// <param name="loginInfo"></param>

        /// <returns></returns>

        public bool FtpServerConnet()

        {

            this.FtpUriString = "ftp://" + this.FtpServerIP;

            FtpWebRequest request = CreateFtpWebRequest(FtpUriString, WebRequestMethods.Ftp.ListDirectoryDetails);

            FtpWebResponse response = GetFtpWebResponse(request);

            if (response == null) return false;

            return true;

        }

/// <summary>

        /// 获得服务器目标路径详细列表

        /// </summary>

        /// <param name="ftpDirectoryPath"></param>

        /// <returns></returns>

        public string[] GetFtpListDirectoryDetails(string ftpDirectoryPath)

        {

            string uri = GetUriString(ftpDirectoryPath);

            StringBuilder strBuilder = new StringBuilder();

            try

            {

                FtpWebRequest request = CreateFtpWebRequest(uri, WebRequestMethods.Ftp.ListDirectoryDetails);

                if (request == null) return null;

                FtpWebResponse response = GetFtpWebResponse(request);

                Stream stream = response.GetResponseStream();

                StreamReader reader = new StreamReader(stream, Encoding.UTF8);

                string line = reader.ReadLine();

                while (line != null)

                {

                    strBuilder.Append(line);

                    strBuilder.Append("@");

                    line = reader.ReadLine();

                }

                reader.Close();

                response.Close();

                string path = strBuilder.ToString();

                path.Remove(path.LastIndexOf("@"), 1);

                return path.Split('@');

            }

            catch

            {

                return null;

            }

        }

}

      在调用的时候是这样调用的:

在A模块中:

FtpServer ftpClient = new FtpServer();

if (ftpClient .FtpServerConnet())

{

         ftpClient .GetFtpListDirectoryDetails(ftpPath);

}

在B模块中:

FtpServer ftpClient = new FtpServer();

if (ftpClient .FtpServerConnet())

{

         ftpClient .GetFtpListDirectoryDetails(ftpPath);

}

问题来了

假如在A模块运行过后,B模块运行以上代码会卡在GetFtpWebResponse方法里(文字加粗的地方),一直不响应,直到TimeOut(请求超时)

     public FtpWebResponse GetFtpWebResponse(FtpWebRequest request)

        {

            FtpWebResponse response = null;

            try

            {

                response = (FtpWebResponse)request.GetResponse();

                return response;

            }

            catch (WebException)

            {

                return null;

            }

为什么?

因为A和B中都实例化了FtpServer类,在这个类里面有个私有变量

private NetworkCredential networkCredential;

这个东西就是网络凭证,里面存储的是你登录Ftp服务必须的验证信息。

在实例化FtpServer时,会将这个声明一个该变量,实例化一次就拥有一个,这导致的问题是,同一个凭证在程序中出现多次,都以自己实例化的凭证访问Ftp服务器,但他们都是一样的,那Ftp服务器相信谁呢?第一个以此凭证登陆的人,即第一个请求Ftp服务器的模块,之后的都是无法验证。这就导致了在FtpWebResponse在接收服务器回应时,一直得不到反馈,得不到反馈就继续等待,调试的时候就像程序卡死,但是你还能做其他操作,其实不是卡死(这是单线程的弊端),一般在服务器交互中,下载上传都采用多线程的方式,以便继续做其他的事情。

解决方法

在知道问题后我第一个想到的就是C#设计模式中的单例模式(事实证明,学了终会有用到的时候)

1、定义私有变量

2、构造函数私有化

3、获得唯一单例的方法

        private static FtpServer _FtpServer = new FtpServer();

       private FtpServer()

        {

        }

        public static FtpServer GetInstance()

        {

            return _FtpServer;

        }

至此问题便解决了。

众里寻他千百度,暮然回首,就在灯火阑珊处。

猜你喜欢

转载自blog.csdn.net/RicardoMTan/article/details/83793850
今日推荐