VC++实现FTP编程

VC++ 实现 FTP 编程

 

一.概述

TCP/IP 协议是一个四层协议,它由应用层、传输层、网络层和链路层构成。 TCP/IP 协议栈的每一层都由许多协议构成,从而构成了一个协议簇。

应用层主要包括的协议有 Telnet FTP HTTP SMTP/POP3 DNS 等。

传输层主要包括的协议有 TCP UDP

网络层主要包括 IP IP 的附属协议。

数据链路层主要包括的协议有 ARP (地址解析协议)、 RARP 协议、 Ethernet 协议等。

 

 

FTP File Transfer Protocol )协议主要用来在网络上进行文件传输。 FTP 通讯除了有一个默认的端口 21 外,还有其他端口,同城两个端口同时进行数据传输。一个是默认的端口(通常为 21 ),主要进行控制连接,即进行命令协议及服务器端响应码的传输。另一个非标准端口主要进行数据,上传下载文件等。

关于 FTP 协议和 FTP 命令的详细描述,参考《 Visual C 网络通信编程实用案例精选》。

 

实现 FTP 协议,有两种方式,实用 WinInet API 和使用基本 Winsock 。对于一般应用,用 WinInet 效率要高,而且简单。也可以用 Winsock 来编写,这样更加灵活,但是复杂度高且需要对协议非常熟悉。

 

二. VC 开发

在项目中,为了开发效率,使用了 WinInet 的方式。

FTP MFC WinInet 支持的三个 Internet 功能( HTTP, gopher )之一,我们需要先创建一个 CInternetSession 实例和一个 CFtpConnection 对象就可以实现和一个 FTP 服务器的通信。不需要直接创建 CFtpConnection 对象,而是通过调用 CInternetsession::GetFtpConnection 来完成这项工作。它创建 CFtpConnection 对象并返回一个指向该对象的指针。

 

要联接到 FTP 服务器,需要两个步骤,首先必须创建一个 CInternetSession 对象,用类 CInternetSession 创建并初始化一个或几个同时存在的 Internet 会话 (session) ,并描述与代理服务器的连接(如果有必要的话),如果在程序运行期间需要保持与 Internet 的连接,可以创建一个 CInternetsession 对象作为类 CWinApp 的成员。

然后利用 CInternetsession 对象获取 CFtpConnection 对象。 MFC 中的类 CFtpConnection 管理我们与 Internet 服务器的连接,并直接操作服务器上的目录和文件。

1 Ftp 连接类的信息

下面我们简要介绍连接类的信息

1.1 建立连接

CInternetsession 对象

CInternetsession(LPCTSTR pstrAgent, DWORD dwConText, DWORD dwACCESSType, LPCTSTR pstrProxyName, LPCTSTR pstrProxyBypass, DWORD dwFlags);

 

在创建 CInternetSession 对象时调用这个成员函数, CInternetsession 是应用程序第一个要调用的 Internet 函数,它将创始化内部数据结构,以备将来在应用程序中调用。如果 dwFlags 包含 INTERNET_FLAG_ASYNC, 那末从这个句柄派生的所有的句柄,在状态回调例程注冊之前,都会出现异步状态。如果沒有打开 Internet 连接, CInternetsession 就会抛出一个例外, AfxThrowInternetException

 

GetFtpConnection() 函数

CFtpConnection* CIternetsession::GetFtpConnection(LPCTSTR pstrServer, LPCTSTR pstrUserName, LPCTSTR pstrPassword, INTERNET_PORT nPort, BOOL bPassive);

 

调用这个函数建立一个 FTP 连接,并获得一个指向 CFtpConnection 对象的指针, GetFtpConnection 连接到一个 FTP 服务器,创建并返回指向 CFtpConnection 对象的指针,它不在服务器上进行任何操作。如果打算读写文件,必须进行分步操作。关于查找,打开和读写文件的信息需参考 CFtpConnection CFtpFileFind 类。

 

对这个函数的调用返回一个指向 CFtpConnection 对象的指针。如果调用失败,检查抛出的 CInternetException 对象,就可以确定失败的原因。

 

1.2 远程目录操作

CreateDirectory() 函数

BOOL CreateDirectory( LPCTSTR pstrDirName );

 

Return Value

 

Nonzero if successful; otherwise 0. If the call fails, the Windows functionGetLastError may be called to determine the cause of the error.

 

Parameters

 

pstrDirName

 

A pointer to a string containing the name of the directory to create.

 

Remarks

 

Call this member function to create a directory on the connected server.

 

Use GetCurrentDirectory to determine the current working directory for this connection to the server. Do not assume that the remote system has connected you to the root directory.

 

The pstrDirName parameter can be either a partially or a fully qualified filename relative to the current directory. A backslash (/) or forward slash (/) can be used as the directory separator for either name. CreateDirectory translates the directory name separators to the appropriate characters before they are used.

注意: CreateDir FTP 服务器上创建已经存在的文件夹时会 返回 FALSE ,而且只能创建到当前(根)目录下

 

RemoveDirectory() 函数

BOOL RemoveDirectory( LPCTSTR pstrDirName );

 

Return Value

 

Nonzero if successful; otherwise 0. If the call fails, the Win32 functionGetLastError may be called to determine the cause of the error.

 

Parameters

 

pstrDirName

 

A pointer to a string containing the directory to be removed.

 

Remarks

 

Call this member function to remove the specified directory from the connected server.

 

Use GetCurrentDirectory to determine the server’s current working directory. Do not assume that the remote system has connected you to the root directory.

 

The pstrDirName parameter can be either a partially or fully qualified filename relative to the current directory. A backslash (/) or forward slash (/) can be used as the directory separator for either name. RemoveDirectory translates the directory name separators to the appropriate characters before they are used.

注意: DeleteDir 文件夹中有内容,先删除文件夹中文件,才可以删文件夹,否则返回 FALSE, 删除不存在的文件夹返回 FALSE

1.3 文件上传下载删除

GetFile() 函数

BOOL GetFile(LPCTSTR pstrRemoteFile, LPCTSTR pstrLocalFile, BOOL bFailExists, DWORD dwAttributes, DWORD dwFlags, DWORD dwContext);

 

调用这个成员函数,可以从 FTP 服务器取得文件,并且把文件保存在本地机器上。 GetFile() 函数是一个比较高级的例程,它可以处理所有有关从 FTP 服务器读文件,以及把文件存放在本地机器上的工作。如果 dwFlags FILE_TRANSFER_TYPE_ASCII, 文件数据的传输也会把控制和格式符转化为 Windows 中的等阶符号。默认的传输模式是二进制模式,文件会以和服务器上相同的格式被下载。

 

pstrRemoteFile pstrLocalFile 可以是相对于当前目录的部分文件名,也可以是全文件名,在这两个名字中间,都既可以用反斜杠 (/) 或者正斜杠 (/) 来作为文件名的目录分隔符, GetFile() 在使用前会把目录分隔符转化为适当的字符。

 

可以用自己选择的值来取代 dwContext 默认的值,设置为上下文标识符与 CFtpConnection 对象的定位操作有关,这个操作由 CFtpConnection 中的 CInternetSession 对象创建。返回给 CInternetsession::OnStatusCallBack 的值指出了所标识操作的状态。

 

如果调用成功,函数的返回为非 0, 否则返回 0 ,如果调用失败,可以调用 Win32 函数 GetLastError(), 确认出错的原因。

 

需要注意:本地路径须为绝对路径,远程路径可为相对路径,如 hello/hello.zip ,如果本地文件已经存在,则返回 FALSE

 

 

PutFile() 函数

BOOL PutFile(LPCTSTR pstrLocalFile, LPCTSTR pstrRemoveFile ,DWORD dwFlags, DWORD dwContext);

 

调用这个成员函数可以把文件保存到 FTP 服务器。 PutFile() 函数是一个比较高级的例程,它可以处理有关把文件存放到服务器上的工作。只发送数据,或要严格控制文件传输的应用程序,应该调用 OpenFile CInternet::Write 。利用自己选择的值来取代 dwContext 默认的值,设置为上下文标识符,上下文标识符是 CInternetSession 对象创建的 CFtpConnection 对象的特定操作有关,这个值返回给 CInternetsession::OnStateCallBack, 从而把操作的状态通报给它所标识的上下文。

 

如果调用成功,函数的返回为非 0, 否则返回 0 ,如果调用失败,可以调用 Win32 函数 GetLastError(), 确认出错的原因。

 

主要注意:如果重复上传文件,会把服务器上的文件覆盖掉,且可以上传特定文件夹下,如 hello/hello.zip

 

Remove() 函数

BOOL Remove( LPCTSTR pstrFileName );

 

如果调用成功,函数的返回为非 0, 否则返回 0 ,如果调用失败,可以调用 Win32 函数 GetLastError(), 确认出错的原因。

 

pstrFileName

需要删除的服务器上的文件名

Call this member function to delete the specified file from the connected server.

 

The pstrFileName parameter can be either a partially qualified filename relative to the current directory or fully qualified. A backslash (/) or forward slash (/) can be used as the directory separator for either name. The Remove function translates the directory name separators to the appropriate characters before they are used.

注意: Remove 如果删除的文件不存在,则返回 FALSE, 支持相对路径

2. 测试实例

2.1 例一 连接到 FTP 站点

 

建立连接到 ftp.microsoft.com 的程序,它是一个单文档程序。并且连接由视图类的构造函数完成。

 

 

建立单文档程序 ftp

 

ftpview.h 中加入包含 #include < afxinet.h >

 

ftpview.h 中添加如下的成员变量

public

CInternetSession *m_pInetsession;

CFtpConnection *m_pFtpConnection;

 

ftpview.cpp 中的 ftpview 构造函数中加入下面的代码

CFtpView::CFtpView()

{

m_pInetSession=new CInternetsession

(AfxGetAppName(),1,

PRE_CONFIG_INTERNET_ACCESS);

try

{

m_pFtpConnection=m_pInetsession->

GetFtpConnection("FTP.MICROSOFT.COM");

}

catch(CInternetException *pEx)

{

TCHAR szError[1024];

if(pEx->GetErrorMessage(szError,1024))

AfxMessageBox(szError);

else

AfxMessageBox("There was an exception");

pEx->Delete();

m_pFtpConnection=NULL;

}

}

 

ftpview.cpp 中的 ftpview 析构函数中加入下面的代码

CFtpView::~CFtpView()

{

if(m_pFtpConnection!=NULL)

{

m_pFtpConnection->Close();

delete m_pFtpConnection;

}

delete m_pInetsession;

}

 

编译并且执行程序,如果连接出现问题,将会在一个消息框中报告出错消息。

 

2.2 例二 发送文件到 FTP 文件服务器

 

创建一个发送文件到 FTP 文件服务器的程序

 

建立单文档程序 ftpfw, ftpfwview.h 中加入包含 #include < afxinet.h >

 

ftpfwview.h 中添加如下的成员变量

public

bool m_bConnectionAttempted;

int m_nFileStatus;

 

ftpview.cpp 中的 ftpview 构造函数中加入下面的代码

CFtpfwView::CFtpfwView()

{

m_bConnectionAttempted=false;

}

 

使用 ClassWizard 加入新的类 CFtpThread ,该类派生于 CWinThread ftpthread.h 中加入如下变量

public:

static UINT PutFile(LPVOID Status);

 

添加新类成员函数代码

UINT CFtpThread::PutFile(LPVOID Status)

{

int *pnFileStatus;

CInternetSession *pInetsession;

CFtpConnection *pFtpConnection=NULL;

pnFileStatus=(int *)Status;

*pnFileStatus=0;

pInetsession=new CInternetsession(AfxGetAppName(),1,

PRE_CONFIG_INTERNET_ACCESS);

try

{

pFtpConnection=pInetsession->

GetFtpConnection("192.34.45.0");

}

catch(CInternetException *pEx)

{

pEx->Delete();

pFtpConnection=NULL;

*pnFileStatus=-1;

goto BallOut;

}

*pnFileStatus =1;

pFtpConnection->Remove("test.txt");

if(!pFtpConnection->PutFile

("test.txt","test.txt"))

*pnFileStatus=-2;

else

*pnFileStatus=2;

BallOut:

if(pFtpConnection!=NULL)

{

pFtpConnection->Close();

delete pFtpConnection;

}

delete pInetsession;

AfxEndThread(0);

return false;

}

 

编辑 ftpfwview.cpp 中的 OnDraw() 函数

void CFtpfwView::OnDraw(CDC* pDC)

{

CFtpfwDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if(!m_bConnectAttempted)

{

m_bConnectAttempted=TRUE;

AfxBeginThread((AFX_THREADPROC)

CFtpThread::PutFile,&m_nFileStatus);

}

}

 

编译并且执行程序,在连接和传输的过程中,应用程序仍然可以作自己的工作,这是因为传输的过程发生在线程中。

3 .封装 FTPTransfer

3.1 头文件

///

//  FTPTransfer.h

//  interface of the FTPTransfer module

//  Created on:      24-July-2010

//  Original author:   Andrew Zhang

///

 

#ifndef _FTPTRANSFER_H_

#define _FTPTRANSFER_H_

#include <afxinet.h> // for ftp api functions

 

class FTPTransfer

{

public:

       FTPTransfer();

       ~FTPTransfer();

      

       BOOL Login();

       void Logout();

      

       BOOL CreateRemoteDir(LPCTSTR pstrDirName);

       BOOL DeleteRemoteDir(LPCTSTR pstrDirName);

      

       BOOL Upload(LPCTSTR pstrLocalFile, LPCTSTR pstrRemoteFile);

       BOOL Download(LPCTSTR pstrRemoteFile, LPCTSTR pstrLocalFile);

      

       BOOL DeleteRemoteFile(LPCTSTR pstrFileName);

      

       BOOL UploadAll();

       CString GetLastError();

      

protected:

private:

       void Config();

       CString m_csServer;

       CString m_csUsername;

       CString m_csPassword;

       unsigned int           m_nPort;

      

       CInternetSession *m_pSession;

       CFtpConnection *m_pConn;

      

       static CString lastError;

      

public:

       // 需要传输的文件夹

       CStringArray m_astrAllDirName;

       CString m_csLocalDir;  // 本地图像所在路径,需要外界赋值。

};

 

#endif

 

3.2 设置配置项

可以配置服务器的 IP Port ,用户名,密码等

配置项样例:

[FTPTransfer]

FTPServer = 192.168.29.253

Username = andrew

Password = zhang

Port = 21

;Ftp 服务器的 IP 账户 密码

 

3.3 使用说明

FTPTransfer transfer;

transfer.m_csLocalDir = url + strImgNo.c_str();

transfer.m_astrAllDirName.Add(csImgNo);

try

{

       if (!transfer.UploadAll())

       {

              CString strlog(_T("[threadOperationTransfer]: "));

              ServiceLog.write_log("[threadOperationTransfer]: ERROR! Upload error.");

              CString csError = transfer.GetLastError();

              ServiceLog.write_log((LPCWSTR)(strlog+ csError));

              return -1; // FTP Error;

       }

}

catch (...)

{

              CString strlog(_T("[threadOperationTransfer]: "));

              ServiceLog.write_log("[threadOperationTransfer]: ERROR! upload except:");

              CString csError = transfer.GetLastError();

              ServiceLog.write_log((LPCWSTR)(strlog+ csError));

              return -1;

}

三.总结

通过以上的程序我们可以明白 FTP 的工作原理,因为基于应用,解释的还比较浅显。另外上传和下载需要比较久的时间,可以考虑设计多线程的方式来实现,这样不至于程序阻塞。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值