本次实验的工程文件下载:https://gryffinbit.lanzous.com/idK4Hj7kb4b

实验目标

程序能实现基本的 FTP 客户机端功能,能登录 FTP 服务器,显示登录客户机目录下的文件和 目录名,能从该目录中选择下载服务器的文件,也能向服务器上传文件。

实验目的

  1. 学习如何创建一个 Internet 会话,即创建 CInternetSession 对象;
  2. 学习如何建立与 FTP 服务器的连接, 即创建 CFtpConnection 对象;如果连接成功,如何获得当前登录的目录下的文件和目录名称,即 检索一个目录下的文件,并显示文件信息;如何下载文件、上传文件以及关闭连接。

实验环境

Windows10 x64,Visual Studio 2017

实验内容

应用程序的类型是基于对话框的。

对话框中包括三个文本框,分别用于输入 FTP 服务器域名、登录用户名和登录口令;一个列 表框,用来显示 FTP 服务器当前目录的内容,并允许用户从中选择文件下载;四个命令按钮,分 别执行查询、上传、下载和退出的操作。

实验原理(模型)

WinInet 类编程的一般步骤如下。

(1)创建 CInternetSession 类对象,创建并初始化 Internet 会话。

(2)利用 CInternetSession 类的 QueryOption 或 SetOption 成员函数,可以查询或设置该类内 含的 Internet 请求选项,这一步是可选的,不需要可以不做。

(3)创建连接类对象,建立 CInternetSession 对象与网络服务器的连接,也就是应用程序与网 络服务器的连接。只需要分别调用 CInternetSession 类的 GetFtpConnection、GetHttpConnection 或 GetGopherConnection 函数就可以轻松地创建 CFtpConnection 类、CHttpConnection 类或 CGopher Connection 类的对象实例。再使用这些对象实例的成员函数就能完成很多对于网络服务器的操作。 例如,对于 FTP 服务器,可以获知或设置当前目录,下载或上传文件,创建或删除目录,重命名 文件或目录等。

(4)创建文件检索类对象,对服务器进行检索。

(5)如果需要使用异步操作模式,可以重载 CInternetSession 类的 OnStatusCallback 函数,并 启动应用程序,使用状态回调机制,重载相关函数,加入自己的代码。

(6)如果还想更紧密地控制对服务器文件的访问,可以进一步创建文件类对象实例,完成文 件查找或文件读写操作。

(7)创建 CInternetException 类对象实例,处理错误。

(8)关闭各种类,将资源释放给系统。


实验过程

1.使用 MFC AppWizard 创建应用程序框架

应用程序类:CFtpApp,对应的文件是 Ftp.h 和 Ftp.cpp。
对话框类:CFtpDlg,对应的文件是 FtpDlg.h 和 FtpDlg.cpp。

2.为对话框添加控件

3.定义控件的成员变量

4.添加成员变量的初始化代码

在 FtpDlg.cpp 文件的 OnInitDialog( )函数中添加成员变量的初始化代码。对服务器名、登录用户名和登录口令的控件变量赋初值。

// TODO: 在此添加额外的初始化代码
    m_strFtp = _T("");
    m_strName = _T("");
    m_strPwd = _T("");
    m_strPwd = _T("");
    UpdateData(FALSE);
    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE

5.为对话框中的控件对象添加事件响应函数

6.为 CFtpDlg 类添加其他的成员函数

BOOL CFtpDlg::Download(CString strSName, CString strDName)
{
    // TODO: 在此处添加实现代码.

    CInternetSession* pSession;      //定义会话对象变量指针
    CFtpConnection* pConnection;    //定义连接对象变量指针

    pConnection = NULL;

    //创建Internet会话对象
    pSession = new CInternetSession(AfxGetAppName(), 1,
        PRE_CONFIG_INTERNET_ACCESS);

    try
    {
        //建立FTP连接
        pConnection = pSession->GetFtpConnection(m_strFtp,
            m_strName, m_strPwd);
    }
    catch (CInternetException* e)
    {
        //错误处理
        e->Delete();
        pConnection = NULL;
        return FALSE;
    }

    if (pConnection != NULL)
    {
        //下载文件
        if (!pConnection->GetFile(strSName, strDName))
        {
            //下载文件错误
            pConnection->Close();
            delete pConnection;
            delete pSession;
            return FALSE;
        }
    }

    //清除对象
    if (pConnection != NULL)
    {
        pConnection->Close();
        delete pConnection;
    }
    delete pSession;

    return TRUE;
}


BOOL CFtpDlg::Upload(CString strSName, CString strDName)
{
    // TODO: 在此处添加实现代码.
    CInternetSession* pSession;
    CFtpConnection* pConnection;

    pConnection = NULL;

    //创建Internet会话
    pSession = new CInternetSession(AfxGetAppName(), 1,
        PRE_CONFIG_INTERNET_ACCESS);

    try
    {
        //建立FTP连接
        pConnection = pSession->GetFtpConnection(m_strFtp,
            m_strName, m_strPwd);
    }
    catch (CInternetException* e)
    {
        //错误处理
        e->Delete();
        pConnection = NULL;
        return FALSE;
    }

    if (pConnection != NULL)
    {
        //上传文件
        if (!pConnection->PutFile(strSName, strDName))
        {
            //上传文件错误
            pConnection->Close();
            delete pConnection;
            delete pSession;
            return FALSE;
        }
    }

    //清除对象
    if (pConnection != NULL)
    {
        pConnection->Close();
        delete pConnection;
    }

    delete pSession;
    return TRUE;
}

7.手工添加包含语句

在 CFtpDlg 类的 FtpDlg.cpp 文件中添加对于 Afxinet.h 的包含命令,来获得对于 MFC WinInet 类的支持。

8.添加事件函数和成员函数的代码

FTP.h


// Ftp.h: PROJECT_NAME 应用程序的主头文件
//

#pragma once

#ifndef __AFXWIN_H__
    #error "include 'pch.h' before including this file for PCH"
#endif

#include "resource.h"        // 主符号


// CFtpApp:
// 有关此类的实现,请参阅 Ftp.cpp
//

class CFtpApp : public CWinApp
{
public:
    CFtpApp();

// 重写
public:
    virtual BOOL InitInstance();

// 实现

    DECLARE_MESSAGE_MAP()
};

extern CFtpApp theApp;

FTP.cpp


// Ftp.cpp: 定义应用程序的类行为。
//

#include "pch.h"
#include "framework.h"
#include "Ftp.h"
#include "FtpDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CFtpApp

BEGIN_MESSAGE_MAP(CFtpApp, CWinApp)
    ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()


// CFtpApp 构造

CFtpApp::CFtpApp()
{
    // 支持重新启动管理器
    m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;

    // TODO: 在此处添加构造代码,
    // 将所有重要的初始化放置在 InitInstance 中
}


// 唯一的 CFtpApp 对象

CFtpApp theApp;


// CFtpApp 初始化

BOOL CFtpApp::InitInstance()
{
    // 如果一个运行在 Windows XP 上的应用程序清单指定要
    // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
    //则需要 InitCommonControlsEx()。  否则,将无法创建窗口。
    INITCOMMONCONTROLSEX InitCtrls;
    InitCtrls.dwSize = sizeof(InitCtrls);
    // 将它设置为包括所有要在应用程序中使用的
    // 公共控件类。
    InitCtrls.dwICC = ICC_WIN95_CLASSES;
    InitCommonControlsEx(&InitCtrls);

    CWinApp::InitInstance();


    AfxEnableControlContainer();

    // 创建 shell 管理器,以防对话框包含
    // 任何 shell 树视图控件或 shell 列表视图控件。
    CShellManager *pShellManager = new CShellManager;

    // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
    CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));

    // 标准初始化
    // 如果未使用这些功能并希望减小
    // 最终可执行文件的大小,则应移除下列
    // 不需要的特定初始化例程
    // 更改用于存储设置的注册表项
    // TODO: 应适当修改该字符串,
    // 例如修改为公司或组织名
    SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

    CFtpDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
        // TODO: 在此放置处理何时用
        //  “确定”来关闭对话框的代码
    }
    else if (nResponse == IDCANCEL)
    {
        // TODO: 在此放置处理何时用
        //  “取消”来关闭对话框的代码
    }
    else if (nResponse == -1)
    {
        TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
        TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
    }

    // 删除上面创建的 shell 管理器。
    if (pShellManager != nullptr)
    {
        delete pShellManager;
    }

#if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)
    ControlBarCleanUp();
#endif

    // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
    //  而不是启动应用程序的消息泵。
    return FALSE;
}

FtpDlg.h


// FtpDlg.h: 头文件
//

#pragma once


// CFtpDlg 对话框
class CFtpDlg : public CDialogEx
{
// 构造
public:
    CFtpDlg(CWnd* pParent = nullptr);    // 标准构造函数

// 对话框数据
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_FTP_DIALOG };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持


// 实现
protected:
    HICON m_hIcon;

    // 生成的消息映射函数
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    CStatic m_staFtp;
    CStatic m_staName;
    CStatic m_staPwd;
    CEdit m_editFtp;
    CString m_strFtp;
    CEdit m_editName;
    CString m_strName;
    CEdit m_editPwd;
    CString m_strPwd;
    CButton m_btnQuery;
    CButton m_btnDownload;
    CButton m_btnUpload;
    CListBox m_listFile;
    afx_msg void OnQuery();
    afx_msg void OnDownload();
    afx_msg void OnUpload();
    afx_msg void OnSelchangeListFile();
    BOOL Download(CString strSName, CString strDName);
    BOOL Upload(CString strSName, CString strDName);
};

ftpDlg.cpp

BOOL CFtpDlg::Download(CString strSName, CString strDName)
{
    // TODO: 在此处添加实现代码.

    CInternetSession* pSession;      //定义会话对象变量指针
    CFtpConnection* pConnection;    //定义连接对象变量指针

    pConnection = NULL;

    //创建Internet会话对象
    pSession = new CInternetSession(AfxGetAppName(), 1,
        PRE_CONFIG_INTERNET_ACCESS);

    try
    {
        //建立FTP连接
        pConnection = pSession->GetFtpConnection(m_strFtp,
            m_strName, m_strPwd);
    }
    catch (CInternetException* e)
    {
        //错误处理
        e->Delete();
        pConnection = NULL;
        return FALSE;
    }

    if (pConnection != NULL)
    {
        //下载文件
        if (!pConnection->GetFile(strSName, strDName))
        {
            //下载文件错误
            pConnection->Close();
            delete pConnection;
            delete pSession;
            return FALSE;
        }
    }

    //清除对象
    if (pConnection != NULL)
    {
        pConnection->Close();
        delete pConnection;
    }
    delete pSession;

    return TRUE;
}


BOOL CFtpDlg::Upload(CString strSName, CString strDName)
{
    // TODO: 在此处添加实现代码.
    CInternetSession* pSession;
    CFtpConnection* pConnection;

    pConnection = NULL;

    //创建Internet会话
    pSession = new CInternetSession(AfxGetAppName(), 1,
        PRE_CONFIG_INTERNET_ACCESS);

    try
    {
        //建立FTP连接
        pConnection = pSession->GetFtpConnection(m_strFtp,
            m_strName, m_strPwd);
    }
    catch (CInternetException* e)
    {
        //错误处理
        e->Delete();
        pConnection = NULL;
        return FALSE;
    }

    if (pConnection != NULL)
    {
        //上传文件
        if (!pConnection->PutFile(strSName, strDName))
        {
            //上传文件错误
            pConnection->Close();
            delete pConnection;
            delete pSession;
            return FALSE;
        }
    }

    //清除对象
    if (pConnection != NULL)
    {
        pConnection->Close();
        delete pConnection;
    }

    delete pSession;
    return TRUE;
}

9.进行测试

  1. 需要先在本机开启FTP服务器

    filezilla sever开启FTP服务。

    下载链接:

    https://gryffinbit.lanzous.com/i5ISij3kh6f

    安装后,点击edit,setting进行基本配置。14147为默认端口

  2. 在filezilla设置用户端。

    添加一个用户。

  3. 在服务器域名文本框中输入“localhost”,保持登录用户名和口令与刚刚创建的用户一致,单击“查询”按钮,用户将获得 FTP 服务器的默认目录下的文件名和目录名。

实验界面结果展示

界面显示:

下载文件:

实验过程中遇到的问题

字符问题,在字符串前面,加上_T

strFileName = _T("[" )+ strFileName + _T("]");

评论