65.9K
CodeProject 正在变化。 阅读更多。
Home

FTP 客户端类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (78投票s)

2004年10月28日

CPOL

4分钟阅读

viewsIcon

879344

downloadIcon

48443

一个封装 FTP 协议的非 MFC 类。

引言

CFTPClient 是一个封装 FTP 协议的类。我试图使其平台无关。为了进行通信,我使用了 David J. Kruglinski 的《Inside Visual C++》中的类 CBlockingSocketCSockAddr 等。这些类只是对 sockets API 的小包装。此外,我使用了 Scott Meyers 的《Effective C++》、《More Effective C++》、《Effective STL》中的智能指针实现。登录序列(带防火墙支持)的实现由 Phil Anderson 在 CodeGuru 上的一篇文章发表。用于解析不同 FTP LIST 响应的代码来自 D. J. Bernstein 的(解析代码)。我只是将 C 代码封装在一个类中。我没有在其他平台上测试过代码,但我认为只需稍作修改,它就能顺利编译并运行。

主要功能包括

  • 不基于 MFC-sockets,
  • 不使用其他 MFC 要素,如 CString(使用 STL),
  • 支持防火墙,
  • 支持续传,
  • 支持文件交换协议 (FXP) - 使用 FTP 直接从一个远程服务器传输数据到另一个服务器(服务器必须支持此功能),
  • 在 Windows 上使用 Visual Studio 2008 测试,
  • 在 Linux (Suse 11.4) 上使用 Qt 测试,
  • 可以通过定义 USE_BOOST_SMART_PTR 或 USE_STD_SMART_PTR 来轻松地将智能指针实现替换为 boost::shared_ptr 或 std::shared_ptr,
  • 可以通过实现接口 "IFileListParser" 来替换解析 LIST 命令输出的解析器,
  • 易于扩展。

该示例展示了使用此类是多么容易。只需几行代码,您就可以记录通信或可视化文件传输。注意:该示例不是一个功能齐全的 FTP 客户端应用程序。该示例应用程序仅适用于 Windows 平台。

背景

文件传输协议 (FTP) 的官方规范是 RFC 959。我的代码中的大部分文档都取自此 RFC。

使用代码

有很多类。但大多数只是简单的“数据类型”。其中最重要的类如下:

  • CFTPClient
    应用程序的核心。它接受一个 CLogonInfo 对象。处理与 FTP 服务器的完整通信,例如:
    • 获取目录列表,
    • 下载/上传文件,
    • 删除目录/文件,
    • 遍历目录树,
    • 被动模式,
    • ...
  • CLogonInfo
    用于登录信息的简单数据结构,例如主机、用户名、密码、防火墙等。
  • CFTPClient::IFileListParser
    用于定义解析类接口,该接口可以设置在 CFTPClient 类中,用于解析 LIST 命令的输出。

  • CFTPClient::ITransferNotification
    此接口的实现可以在 Download 和 Upload 方法中使用,用于控制正在下载/上传的字节流。例如,这可用于仅将文件下载到内存而不是本地文件(参见类 COutputStream)。

  • CFTPClient::CNotification
    通知机制的基类。派生自 CFTPClient::CNotifaction 的类可以作为观察者附加到 CFTPClient 类。CFTPClient 对象会通知所有附加的观察者关于各种操作(请参阅示例应用程序)。

    void TestFTP()
    {
       nsFTP::CFTPClient ftpClient;
       nsFTP::CLogonInfo logonInfo(_T("localhost"), 21, _T("anonymous"),
                                               _T("<a href="mailto:anonymous@user.com">anonymous@user.com"));
    
       // connect to server
       ftpClient.Login(logonInfo);
    
       // get directory listing
       nsFTP::TFTPFileStatusShPtrVec list;
       ftpClient.List(_T("/"), list);
    
       // iterate listing
       for( nsFTP::TFTPFileStatusShPtrVec::iterator it=list.begin();
                                                it!=list.end(); ++it )
           TRACE(_T("\n%s"), (*it)->Name().c_str());
    
       // do file operations
       ftpClient.DownloadFile(_T("/pub/test.txt"), _T("c:\\temp\\test.txt"));
    
       ftpClient.UploadFile(_T("c:\\temp\\test.txt"), _T("/upload/test.txt"));
    
       ftpClient.Rename(_T("/upload/test.txt"), _T("/upload/NewName.txt"));
    
       ftpClient.Delete(_T("/upload/NewName.txt"));
    
       // disconnect
       ftpClient.Logout();
    }
    
    void TestFXP()
    {
       nsFTP::CFTPClient ftpClientSource;
       nsFTP::CLogonInfo logonInfoSource(_T("sourceftpserver"), 21, _T("anonymous"),
                                                           _T("<a href="mailto:anonymous@user.com">anonymous@user.com"));
    
       nsFTP::CFTPClient ftpClientTarget;
       nsFTP::CLogonInfo logonInfoTarget(_T("targetftpserver"), 21, _T("anonymous"),
                                                           _T("<a href="mailto:anonymous@user.com">anonymous@user.com"));
    
       // connect to server
       ftpClientSource.Login(logonInfoSource);
       ftpClientTarget.Login(logonInfoTarget);
    
    
       // do file operations
       nsFTP::CFTPClient::TransferFile(ftpClientSource, _T("/file.txt"),
                                       ftpClientTarget, _T("/newFile.txt"));
    
    
       // disconnect
       ftpClientTarget.Logout();
       ftpClientSource.Logout();
    }
    
    
    void TestDownloadAsciiFileIntoTextBuffer()
    {
       nsFTP::CFTPClient ftpClientSource;
       nsFTP::CLogonInfo logonInfoSource(_T("sourceftpserver"), 21, _T("anonymous"),
                                                           _T("<a href="mailto:anonymous@user.com">anonymous@user.com</a>"));
    
       // connect to server
       ftpClientSource.Login(logonInfoSource);
    
       nsFTP::COutputStream outputStream(_T("\r\n"), _T("Example"));
    
       // do file operations
       ftpClientSource.DownloadFile(_T("/file.txt"), outputStream,
                                    nsFTP::CRepresentation(nsFTP::CType::ASCII()));
    
       tstring output = outputStream.GetBuffer();
    
       // disconnect
       ftpClientSource.Logout();
    }
    
    

历史

  • 2004-10-25 - 首次公开发布。
  • 2005-12-04 - 版本 1.1
    • 一些接口已更改(例如 CNotification)。
    • 已删除 OpenPassiveDataConnection 中的错误:在数据连接建立之前调用了 SendCommand
    • 已删除 GetSingleResponseLine 中的错误
      • 如果响应行不以 CRLF 结尾,则会出现无限循环。
      • 必须将 std:string->find 的返回值与 npos 进行比较。
    • 现在可以在 Unicode 下运行。
    • 流已删除。
    • 不再需要显式分离观察者。
    • ExecuteDatachannelCommand 现在接受一个 ITransferNotification 对象。通过这种概念,无需将接收到的文件写入文件。例如,字节可以仅写入内存或其他 TCP 流。
    • 添加了阻塞套接字接口(IBlockingSocket)。因此,可以更换套接字实现,例如用于编写单元测试(通过模拟 FTP 通信的特定场景)。
    • 用一个类替换了与应答代码相关的魔术数字。
    • 添加了新示例。一个使用 Bloodshed Dev-C++ 创建的控制台应用程序。这是一个小型应用程序,用于演示在非 Microsoft 环境中使用这些类。
  • 2012-12-02 - 版本 2.0
    • 修复了一些错误
    • 引入了更多“数据类型”以实现更安全的接口
    • 修改了代码,使其也可以在 Linux 下运行
    • 支持文件交换协议 (FXP)

接下来要做什么

  • 带 Linux GNU-C++ 的示例应用程序。
  • FTP 客户端类的新功能(例如:递归复制和删除)。
  • 单元测试。
© . All rights reserved.