学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1157

主题

[转载图文] 基于MFC的FTP客户端

发表于 2020-8-1 19:36:20 | 查看: 4500| 回复: 3
                                              最近在看关于C++  FTP编程的东西,写了一个Demo版本的FTP客户端。同样,在开发之前,先弄清原理性的东西。呵呵
相关知识  FTP(File Transfer Protocol,文件传输协议)工作在TCP/IP协议的应用层,在传输层使用的是TCP,用于在网络上控制文件的双向传输。百度百科上有对其详细的介绍。
FTP主要用于校园网、企业网等各种局域网中,FTP也是传输网络资源的首选途经。FTP具有如下特点:
  • 适应异构系统
  FTP把一台主机的文件传输到另一台主机上,而两台主机可能运行不同的操作系统、使用不同的文件结构和不同的字符集,这就要求FTP必须适用于异构系统。FTP通过支持有限数量的文件类型和数据结构来解决易购性。FTP支持四种文件类型:ASCII码文件EBCDIC码文件图像文件(二进制文件)本地文件;FTP支持的数据结构有以下几类:文件结构、记录结构、页结构。FTP主要以如下几种方式传输文件:流方式、块方式、压缩方式
  • 匿名FTP
  通常使用FTP必须先登录,然后输入用户名和密码,从远程主机获得相应的权限后,方可下载或者上传文件。这种方式违背了Internet的开放性,匿名FTP就解决这个问题。用户可以通过匿名FTP连接到远程主机并下载文件,无须成为注册用户。系统管理员建立一个特殊的用户名——anonymous,Internet上的任何人在任何地方都可以使用该用户名。
工作过程  关于FTP的工作过程,网上有一大堆,但是说得都不太清楚,而且比较乱,这篇blog写得比较详细。其实,归结起来,就是4个步骤:启动FTP、建立控制连接、建立数据连接和进行文件传输、关闭FTP。
服务器搭建  微软的windows操作系统已经内置了FTP服务器的功能,只需要进行简单的设置就可以将计算机配置成一台FTP服务器。关于关于windows下FTP网络环境的搭建,这篇文章写得不错。当然,也可以自己开发一个FTP服务器,呵呵,其实也不是很难,后期打算写一个。
技术支持  对于FTP编程,MFC WinInet提供了支持,主要是Internet会话类CInternetSession、连接类CInternetConnection、文件类CInternetFile、文件操作类CFileFind以及通用异常类CInternetException等类。
开发步骤
  • 界面设计
    整个客户端的界面设计如下:
  • 编程实现
    注:在主对话框的头文件内引入需要引入afxinet.h头文件。
    为了简单起见,只实现“匿名”复选框,勾选后触发实现如下:
void CFtpClientDlg::OnNoname()
{
    // TODO: 在此添加控件通知处理程序代码
    int icheck=m_noname.GetCheck();//获得匿名复选框的选择状态
    if (icheck==1)//用户选中匿名复选框
    {
        m_usr.EnableWindow(FALSE);
        m_pwd.EnableWindow(FALSE);
        m_usr.SetWindowText("anonymous");//用户名自动设置为“anonymous”
        m_pwd.SetWindowText("");
        UpdateData();
        if (!ServerIP.IsBlank()&&!strport.IsEmpty())
        {
            m_connect.EnableWindow(TRUE);//连接按钮变为可用
        }
    }
    else{//用户没有按照要求输入,则无法连接
        m_usr.EnableWindow(TRUE);
        m_pwd.EnableWindow(TRUE);
        m_usr.SetWindowText("");
        m_pwd.SetWindowText("");
        m_connect.EnableWindow(FALSE);//连接按钮不可用,禁止用户继续操作
    }
}
  “连接”按钮实现过程如下:
void CFtpClientDlg::OnConnect()
{
    // TODO: 在此添加控件通知处理程序代码
    this->ConnectFtp();//连接FTP服务器
    this->UpdateDir();//显示服务器上的目录和文件夹列表

    ServerIP.EnableWindow(FALSE);
    m_port.EnableWindow(FALSE);
    m_connect.EnableWindow(FALSE);
    m_disconnect.EnableWindow(TRUE);
    m_enterdir.EnableWindow(TRUE);
    m_upload.EnableWindow(TRUE);
    m_download.EnableWindow(TRUE);
    m_delete.EnableWindow(TRUE);
    m_noname.EnableWindow(FALSE);
    m_exit.EnableWindow(FALSE);
}
  其中,ConnectFtp()和UpdateDir()是自定义的两个函数。
ConnectFtp实现如下:
void CFtpClientDlg::ConnectFtp(){
    BYTE nFild[4];
    UpdateData();
    ServerIP.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);
    CString sip;
    sip.Format("%d.%d.%d.%d",nFild[0],nFild[1],nFild[2],nFild[3]);
    if (sip.IsEmpty())
    {
        AfxMessageBox(_T("IP地址为空!"));
        return;
    }
    if (strport.IsEmpty())
    {
        AfxMessageBox(_T("端口号为空!"));
        return;
    }
    if (strusr.IsEmpty())
    {
        return;
    }
    //建立一个Internet会话
    pInternetSession= new CInternetSession("MR",INTERNET_OPEN_TYPE_PRECONFIG);

    try
{
    //利用Internet会话对象pInternetSession打开一个FTP连接
        pFtpConnection=pInternetSession->GetFtpConnection(sip,strusr,strpwd,atoi(strport));
        bconnect=true;
}
catch (CInternetException* pEx)
{
    TCHAR szErr[1024];
    pEx->GetErrorMessage(szErr,1024);
    AfxMessageBox(szErr);
    pEx->Delete();
}

}
  UpdateDir实现如下:
void CFtpClientDlg::UpdateDir(){
    m_lst.ResetContent();
    //读写服务器中的数据,需要创建一个CFtpFileFind的实例
    CFtpFileFind ftpfind(pFtpConnection);
    //找到第一个文件或者文件夹,通过CFtpFileFind::FindFile实现
    BOOL bfind=ftpfind.FindFile(NULL);
    while (bfind)
    {
        bfind=ftpfind.FindNextFile();
        CString strpath;
        if (!ftpfind.IsDirectory())//判断是目录还是文件夹
        {
            strpath=ftpfind.GetFileName();//是文件则读取文件名
            m_lst.AddString(strpath);
        }
        else{
            strpath=ftpfind.GetFilePath();//如果是文件夹则获取相对路径
            m_lst.AddString(strpath);
        }
    }
}
  注:WinInet的CftpFileFind将服务器上的数据(包括文件和文件夹)都看做是一个文件,对读取的内容需要进行判断。为了避免“短连接”的问题,可以采取如下措施:每当执行一个新的操作时,自动执行重新连接服务器和更新资源目录的过程。这样做可以使得用户感受不到服务器曾经断开过。为了保证连接的顺利成功,需要在主对话框的头文件中添加CInternetSession类对象指针和CFtpConnection类对象指针,以及标识连接成功与否的BOOL类型的变量如下:
BOOL bconnect;
CInternetSession *pInternetSession;
CFtpConnection *pFtpConnection;
void ConnectFtp();
void UpdateDir();
  成功登录FTP服务器后,模仿windows操作系统,资源浏览框会自动列出所有的目录和文件,同时,用户可以自由地进入、退出文件夹并浏览各个目录下的资源。
“>>”按钮表示进入选中的文件夹中,添加事件处理程序如下:
void CFtpClientDlg::OnEnterDir()
{
    // TODO: 在此添加控件通知处理程序代码
    CString selfile;
    //获取用户选择的目录名
    m_lst.GetText(m_lst.GetCurSel(),selfile);
    if (!selfile.IsEmpty())
    {
        pFtpConnection->Close();//及时关闭废弃的会话句柄
        this->ConnectFtp();//重新连接,保持与服务器的持续会话
        CString strdir;
        pFtpConnection->GetCurrentDirectory(strdir);//获得原来的工作目录
        strdir+=selfile;//生成新的目录
        pFtpConnection->SetCurrentDirectory(strdir);//改变目录到当前服务目录
        this->UpdateDir();//更新目录列表

        m_goback.EnableWindow(TRUE);
    }
}
  为了使用户灵活地切换目录,需要要有返回的功能,使得用户能够返回上一级目录,”<<”按钮的事件处理过程如下:
void CFtpClientDlg::OnGoBack()
{
    // TODO: 在此添加控件通知处理程序代码
    CString strdir;
    pFtpConnection->GetCurrentDirectory(strdir);
    int pos;
    //用字符串截取的方法获得上一级目录
    pos=strdir.ReverseFind('/');
    strdir=strdir.Left(pos);
    pInternetSession->Close();//关闭废弃的对话
    this->ConnectFtp();//重新连接保持持续会话
    pFtpConnection->SetCurrentDirectory(strdir);
    this->UpdateDir();//更新目录列表
}
  上传按钮的事件处理过程如下:
void CFtpClientDlg::OnUpLoad()
{
    // TODO: 在此添加控件通知处理程序代码
    CString str;
    CString strname;
    //弹出“打开”对话框
    CFileDialog file(true,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"所有文件(*.*)|*.*|",this);
    if (file.DoModal()==IDOK)
    {
        str=file.GetPathName();
        strname=file.GetFileName();
    }
    if (bconnect)
    {
        CString strdir;
        pFtpConnection->GetCurrentDirectory(strdir);
        //上传文件
        BOOL bput=pFtpConnection->PutFile((LPCTSTR)str,(LPCTSTR)strname);
        if (bput)
        {
            pInternetSession->Close();//关闭会话
            this->ConnectFtp();//重新连接保持持续会话
            pFtpConnection->SetCurrentDirectory(strdir);
            this->UpdateDir();//更新目录列表
            AfxMessageBox(_T("上传成功!"));
        }
    }
}
  下载按钮的事件处理过程:
void CFtpClientDlg::OnDownLoad()
{
    // TODO: 在此添加控件通知处理程序代码
    CString selfile;
    m_lst.GetText(m_lst.GetCurSel(),selfile);//获取用户选择要下载的资源名
    if (!selfile.IsEmpty())
    {
        //弹出“另存为”对话框
        CFileDialog file(FALSE,NULL,selfile,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"所有文件(*.*)|*.*|",this);
        if (file.DoModal()==IDOK)
        {
            CString strname;
            strname=file.GetFileName();
            CString strdir;
            pFtpConnection->GetCurrentDirectory(strdir);
            pFtpConnection->GetFile(selfile,strname);//下载文件到选定的本地位置
            pInternetSession->Close();//关闭废弃的对话
            this->ConnectFtp();//保持持续会话
            pFtpConnection->SetCurrentDirectory(strdir);
            this->UpdateDir();//更新目录列表
            AfxMessageBox(_T("下载成功!"));
        }
    }
}
  删除按钮的事件处理过程:
void CFtpClientDlg::OnDelete()
{
    // TODO: 在此添加控件通知处理程序代码
    CString selfile;
    m_lst.GetText(m_lst.GetCurSel(),selfile);//获取用户要删除的资源名
    if (!selfile.IsEmpty())
    {
        //弹出删除警告框
        if (AfxMessageBox("确定要删除这个文件?",4+48)==6)
        {
            pFtpConnection->Remove(selfile);//删除该文件
        }
        CString strdir;
        pFtpConnection->GetCurrentDirectory(strdir);
        pInternetSession->Close();//关闭废弃的会话
        this->ConnectFtp();//保持持续会话
        pFtpConnection->SetCurrentDirectory(strdir);
        this->UpdateDir();//更新目录列表
    }
}
  断开按钮的事件处理过程如下:
void CFtpClientDlg::OnDisconnect()
{
    // TODO: 在此添加控件通知处理程序代码
    pInternetSession->Close();//结束会话
    m_lst.ResetContent();
    m_lst.AddString(_T("连接已经断开!"));

    ServerIP.EnableWindow(true);
    m_port.EnableWindow(true);
    m_connect.EnableWindow(true);
    m_disconnect.EnableWindow(false);
    m_enterdir.EnableWindow(false);
    m_goback.EnableWindow(false);
    m_upload.EnableWindow(false);
    m_download.EnableWindow(false);
    m_delete.EnableWindow(false);
    m_noname.EnableWindow(true);
    m_exit.EnableWindow(true);
}
  注:用户点击断开按钮时,就相当于结束了会话,资源浏览框需要显示“连接已经断开”等状态信息。
同样,在客户端刚刚启动、用户未登陆时,也需要进行一些初始化操作,显示“用户尚未登录”等信息。在主对话框的初始化代码中添加如下:
bconnect=FALSE;
    m_lst.ResetContent();
    m_lst.AddString(_T("服务器尚未连接,无法访问资源!"));

    //界面控制部分
    m_connect.EnableWindow(false);
    m_disconnect.EnableWindow(false);
    m_enterdir.EnableWindow(false);
    m_goback.EnableWindow(false);
    m_upload.EnableWindow(false);
    m_download.EnableWindow(false);
    m_delete.EnableWindow(false);
  下面给个测试例子
初始登陆界面:

注:本人设置的ftp服务器的IP为192.168.205.218,端口为21
点击“连接”按钮后,

点击上传,选择桌面的txt文件,


工程源码
下载
            
温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046

    发表于 2021-12-24 07:04:58
    谢谢分享谢谢分享

      发表于 2022-3-26 21:56:25
      资源很给力,请收下我的膝盖!

        发表于 2022-8-26 20:23:56
        不错 支持支持

        小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

        GMT+8, 2024-4-26 14:36 , Processed in 0.127274 second(s), 54 queries .

        Powered by Discuz! X3.4

        Copyright © 2001-2021, Tencent Cloud.

        快速回复 返回顶部 返回列表