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

学无止境,此路甚长。

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

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

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

解决方法:采用单例模式

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

      

public class FtpServer
{
    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;
}

至此问题便解决了。

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

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值