编写FTP客户端程序

maqun / 2025-02-21 / 原文

《网络程序设计》上机实验报告3

编写FTP客户端程序

[实验目的]

利用MFC提供的网络类设计实现FTP客户端程序

利用CInternetSession、CFtpConnection、CFtpFind类实现下面任务之一

1.向FTP服务器上传文件

2.从FTP服务器下载文件

3.列出FTP服务器上的文件(目录)

注意:

1.需要启动FTP服务器,可在本机启动,也可使用统一启动的服务器。

2.理解FTP协议的基本概念,比如连接模式,根据这些去设计实现客户端

[实验要求]

给出实验中主要的程序代码以及运行结果,并把编译、运行过程中出现的问题以及解决方法填入实验报告中,按时上交。

[实验学时] 1学时。

[实验内容]

实验环境

Visual Studio 2022

实验原理

FTP工作原理

文件传送协议FTP(File Transfer Protocol)是TCP/IP体系的一个重要协议,它采用Internet标准文件传输协议FTP的用户界面,向用户提供了一组用来管理计算机之间文件传输的应用程序。FTP是基于客户—服务器(C/S)模型而设计的,在客户端和FTP建立两个TCP连接。

FTP 的独特的优势同时也是与其它客户服务器程序最大的不同点就在于它在两台通信的主机之间使用了两条 TCP 连接,一条是数据连接,用于数据传送;另一条是控制连接,用于传送控制信息(命令和响应),这种将命令和数据分开传送的思想大大提高了 FTP 的效率,而其它客户服务器应用程序一般只有一条 TCP 连接。

FTP基于TCP协议服务,是互联网中进行文件传输的协议,默认使用20、21号两个端口,一个数据端口和一个命令端口,端口20是数据端口,用于文件在客户端和服务器之间传输数据流。端口21是命令端口,用于传输控制流,接受客户端发出的相关FTP命令与参数。

FTP客户端在计算机网络中向FTP服务器发送服务请求,FTP服务器接收与响应FTP客户机的请求,并向FTP客户机提供所需的文件传输服务。根据TCP协议的规定,FTP服务器使用熟知端口号20、21来提供服务,FTP客户机使用临时端口号来发送请求。FTP协议为控制连接与数据连接规定不同的熟知端口号,为控制连接规定的熟知端口号是21,为数据连接规定的熟知端口号为20。FTP协议采用的是。

持续连接的通信方式,它所建立的控制连接的维持时间通常较长。

讲完FTP的工作原理的主要连接方式,接着讲解FTP工作原理中两种数据连接的建立类型:主动模式和被动模式。FTP的主动模式是指客户端从任意一个非特权端口连接FTP服务器的熟知端口,即端口21。FTP服务器在收到命令后从数据端口连接客户端又一临时端口,返回数据;被动模式是指客户端开启两个任意非特权端口提交命令,FTP服务器被动开启任意非特权端口发送命令给客户端,客户端接收命令后从本地端口向FTP服务器发起建立连接的传送数据通道,在这一模式内,命令连接和数据连接都由客户端发起,解决服务器发起到客户的连接的问题。

图 FTP工作原理

实验准备

  • 服务端准备

FTP服务器:

1.运行FTPServer.Exe

2.添加FTP用户

3.设置用户的密码(口令)

4.设置FTP路径:“添加”->“..”->选择一个目录“确定”

5.设置FTP路径的权限

6.启动FTP服务器

  • 客户端准备

VS2022配置MFC环境

  1. 打开Visual Studio Installer

1)点击修改

2)选择使用C++的桌面开发,等待安装完成

2.创建MFC应用

1)创建新项目:

搜索找到MFC应用并点击下一步

2)配置MFC应用程序:

3)高级功能里勾选Windows套接字,生成的类里选去app

4)之后点击右下角“完成”,等待十几秒

3.编写MFC项目

1)在工具箱中找到这次项目会用到的四个组件:

没有工具箱的可以点击vs最上方的视图,在这一栏中找到工具箱。

2)将四个Button,一个Edit Control,一个List Box和若干个Static Text拖拽到Dialog文件当中。

3)右键每一个工具,然后点击属性,在属性当中配置一些相关信息

4)添加变量

右键每一个组件然后选择添加变量

更改名称:

5)类向导

右键任意一个组件然后选择类向导

点击这六个对象ID,然后点击添加到处理程序

6)方法

将这些添加到方法,

实验代码

在Dlg.cpp文件中编写函数:

ForConnect函数:

在这个函数中,传入的参数增加一个

CString m_IPadress

CString m_strUser;

CString m_strPass;

m_strPass = "123456";//密码

m_strUser = "user1";//用户名

m_pInetSession = new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS);

m_pFtpConnection = m_pInetSession->GetFtpConnection(m_IPadress, m_strUser, m_strPass);

m_pRemoteFile = new CFtpFileFind(m_pFtpConnection);

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

//

#include "pch.h"

#include "framework.h"

#include "FTPClient.h"

#include "FTPClientDlg.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

// CFTPClientApp

BEGIN_MESSAGE_MAP(CFTPClientApp, CWinApp)

ON_COMMAND(ID_HELP, &CWinApp::OnHelp)

END_MESSAGE_MAP()

// CFTPClientApp 构造

CFTPClientApp::CFTPClientApp()

{

// 支持重新启动管理器

m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;

// TODO: 在此处添加构造代码,

// 将所有重要的初始化放置在 InitInstance 中

}

// 唯一的 CFTPClientApp 对象

CFTPClientApp theApp;

// CFTPClientApp 初始化

BOOL CFTPClientApp::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("应用程序向导生成的本地应用程序"));

CFTPClientDlg 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;

}

// FTPClientDlg.cpp: 实现文件

//

#include "pch.h"

#include "framework.h"

#include "FTPClient.h"

#include "FTPClientDlg.h"

#include "afxdialogex.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#endif

CInternetSession* m_pInetSession;

CFtpConnection* m_pFtpConnection;

CFtpFileFind* m_pRemoteFile;

CString m_pRemoteFileName, m_LocateFileName;

// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx

{

public:

CAboutDlg();

// 对话框数据

#ifdef AFX_DESIGN_TIME

enum { IDD = IDD_ABOUTBOX };

#endif

protected:

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

// 实现

protected:

DECLARE_MESSAGE_MAP()

};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)

{

}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)

END_MESSAGE_MAP()

// CFTPClientDlg 对话框

CFTPClientDlg::CFTPClientDlg(CWnd* pParent /*=nullptr*/)

: CDialogEx(IDD_FTPCLIENT_DIALOG, pParent)

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

void CFTPClientDlg::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

DDX_Control(pDX, IDC_Connect, Connect);

DDX_Control(pDX, IDC_Update, Update);

DDX_Control(pDX, IDC_Download, Download);

DDX_Control(pDX, IDC_Delete, Delete);

DDX_Control(pDX, IDC_LISTBOX, ListBox);

}

BEGIN_MESSAGE_MAP(CFTPClientDlg, CDialogEx)

ON_WM_SYSCOMMAND()

ON_WM_PAINT()

ON_WM_QUERYDRAGICON()

ON_BN_CLICKED(IDC_Connect, &CFTPClientDlg::OnClickedConnect)

ON_BN_CLICKED(IDC_Delete, &CFTPClientDlg::OnClickedDelete)

ON_BN_CLICKED(IDC_Download, &CFTPClientDlg::OnClickedDownload)

ON_BN_CLICKED(IDC_Update, &CFTPClientDlg::OnClickedUpdate)

END_MESSAGE_MAP()

// CFTPClientDlg 消息处理程序

BOOL CFTPClientDlg::OnInitDialog()

{

CDialogEx::OnInitDialog();

// 将“关于...”菜单项添加到系统菜单中。

// IDM_ABOUTBOX 必须在系统命令范围内。

ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

ASSERT(IDM_ABOUTBOX < 0xF000);

CMenu* pSysMenu = GetSystemMenu(FALSE);

if (pSysMenu != nullptr)

{

BOOL bNameValid;

CString strAboutMenu;

bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);

ASSERT(bNameValid);

if (!strAboutMenu.IsEmpty())

{

pSysMenu->AppendMenu(MF_SEPARATOR);

pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);

}

}

// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动

// 执行此操作

SetIcon(m_hIcon, TRUE); // 设置大图标

SetIcon(m_hIcon, FALSE); // 设置小图标

// TODO: 在此添加额外的初始化代码

return TRUE; // 除非将焦点设置到控件,否则返回 TRUE

}

void CFTPClientDlg::OnSysCommand(UINT nID, LPARAM lParam)

{

if ((nID & 0xFFF0) == IDM_ABOUTBOX)

{

CAboutDlg dlgAbout;

dlgAbout.DoModal();

}

else

{

CDialogEx::OnSysCommand(nID, lParam);

}

}

// 如果向对话框添加最小化按钮,则需要下面的代码

// 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序,

// 这将由框架自动完成。

void CFTPClientDlg::OnPaint()

{

if (IsIconic())

{

CPaintDC dc(this); // 用于绘制的设备上下文

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

// 使图标在工作区矩形中居中

int cxIcon = GetSystemMetrics(SM_CXICON);

int cyIcon = GetSystemMetrics(SM_CYICON);

CRect rect;

GetClientRect(&rect);

int x = (rect.Width() - cxIcon + 1) / 2;

int y = (rect.Height() - cyIcon + 1) / 2;

// 绘制图标

dc.DrawIcon(x, y, m_hIcon);

}

else

{

CDialogEx::OnPaint();

}

}

//当用户拖动最小化窗口时系统调用此函数取得光标

//显示。

HCURSOR CFTPClientDlg::OnQueryDragIcon()

{

return static_cast<HCURSOR>(m_hIcon);

}

void CFTPClientDlg::OnClickedConnect()

{

// TODO: 在此添加控件通知处理程序代码

// TODO: 在此添加控件通知处理程序代码

CString Address;//声明变量

GetDlgItemText(IDC_IP_INPUT, Address);//获得输入框所输入的地址 复制给Address

OnConnect(Address);//调用OnConnect函数 把IP地址赋值

this->UpdateList();//更新列表

}

void CFTPClientDlg::OnClickedDelete()

{

// TODO: 在此添加控件通知处理程序代码

// TODO: 在此添加控件通知处理程序代码

this->OnDelete();

this->UpdateList();

}

void CFTPClientDlg::OnClickedDownload()

{

// TODO: 在此添加控件通知处理程序代码

// TODO: 在此添加控件通知处理程序代码

this->OnDownload();

this->UpdateList();

}

void CFTPClientDlg::OnClickedUpdate()

{

// TODO: 在此添加控件通知处理程序代码

// TODO: 在此添加控件通知处理程序代码

this->OnUpdate();

this->UpdateList();

}

void CFTPClientDlg::OnDownload()

{

// TODO: 在此处添加实现代码.

// TODO: 在此处添加实现代码.

CString selfile;

ListBox.GetText(ListBox.GetCurSel(), selfile);//获得想要下载资源名

if (!selfile.IsEmpty())

{

//弹出另存为对话框

CFileDialog file(FALSE, NULL, selfile, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, L"所有文件(*.*)|*.*|", this);

if (file.DoModal() == IDOK)

{

CString strname;

strname = file.GetPathName();

CString strdir;

m_pFtpConnection->GetCurrentDirectory(strdir);

m_pFtpConnection->GetFile(selfile, strname);//下载文件到的本地位置

m_pInetSession->Close();

this->OnClickedConnect();

m_pFtpConnection->SetCurrentDirectory(strdir);

this->UpdateList();

AfxMessageBox(_T("下载成功!"));

}

}

}

void CFTPClientDlg::OnDelete()

{

// TODO: 在此处添加实现代码.

// TODO: 在此处添加实现代码.

CString selfile;

ListBox.GetText(ListBox.GetCurSel(), selfile);//获取用户要删除的资源名

if (!selfile.IsEmpty())

{

if (AfxMessageBox(L"确定要删除这个文件?", 4 + 48) == 6)

{

m_pFtpConnection->Remove(selfile);

}

CString strdir;

m_pFtpConnection->GetCurrentDirectory(strdir);

m_pInetSession->Close();

this->OnClickedConnect();

m_pFtpConnection->SetCurrentDirectory(strdir);

this->UpdateList();

}

}

void CFTPClientDlg::OnConnect(CString m_IPaddress)

{

// TODO: 在此处添加实现代码.

// TODO: 在此处添加实现代码.

m_pInetSession = new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS);

m_pFtpConnection = m_pInetSession->GetFtpConnection(m_IPaddress);

m_pRemoteFile = new CFtpFileFind(m_pFtpConnection);

}

void CFTPClientDlg::OnUpdate()

{

// TODO: 在此处添加实现代码.

// TODO: 在此处添加实现代码.

CString str;

CString strname;

//弹出“打开”对话框

CFileDialog file(true, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, L"所有文件(*.*)|*.*|", this);

if (file.DoModal() == IDOK)

{

str = file.GetPathName();

strname = file.GetFileName();

}

CString strdir;

m_pFtpConnection->GetCurrentDirectory(strdir);

//上传文件

BOOL bput = m_pFtpConnection->PutFile((LPCTSTR)str, (LPCTSTR)strname);

if (bput)

{

m_pInetSession->Close();//关闭会话

this->OnClickedConnect();//重新连接保持持续会话

m_pFtpConnection->SetCurrentDirectory(strdir);

this->UpdateList();//更新目录列表

AfxMessageBox(_T("上传成功!"));

}

}

void CFTPClientDlg::UpdateList()

{

// TODO: 在此处添加实现代码.

// TODO: 在此处添加实现代码.

ListBox.ResetContent();//自己声明的列表的变量名

//创建一个CFtpFileFind实例

CFtpFileFind ftpfind(m_pFtpConnection);//建立连接的实例

CString strdirpath;

m_pFtpConnection->GetCurrentDirectory(strdirpath);

//找到第一个文件或者文件夹

BOOL bfind = ftpfind.FindFile(strdirpath);

while (bfind)

{

bfind = ftpfind.FindNextFile();

CString strpath;

if (ftpfind.IsDots())

continue;

if (!ftpfind.IsDirectory()) //判断是目录还是文件

{

strpath = ftpfind.GetFileName(); //文件则读取文件名

ListBox.AddString(strpath);

}

else

{

strpath = ftpfind.GetFilePath();

ListBox.AddString(strpath);

}

}

}

五、实验结果

    1. 连接

    1. 上传

    1. 下载

    1. 删除

[实验总结]

本次实验的目的是利用MFC提供的网络类设计并实现FTP客户端程序。设计一个用户界面,包括一些控件,如IP地址、连接、上传、下载、删除等按钮,以及一个列表框用于显示服务器上的文件和目录。

编写一个函数,用于更新列表框,利用CFtpConnection对象的GetCurrentDirectory和SetCurrentDirectory方法,获取和设置当前的工作目录,然后利用CFileFind类,遍历当前目录下的所有文件和子目录,将它们添加到列表框中。

编写一些事件处理函数,用于响应用户的操作,如连接、断开、上传、下载、删除等,利用CFtpConnection对象的PutFile、GetFile、Remove和Close方法,实现文件的上传、下载、删除和断开连接。

通过本次实验,我们深入了解了FTP客户端程序的设计和实现。我们学会了使用MFC提供的网络类来实现FTP操作,包括上传文件、下载文件和列出文件(目录)。同时,我们也加深了对FTP协议的理解,包括连接模式等基本概念。