ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MFC FTP客户端开发实战:CInternetSession、CFtpConnection与CFtpFileFind详解

MFC FTP客户端开发实战:CInternetSession、CFtpConnection与CFtpFileFind详解 简介这份《FTP客户端程序设计》课程设计报告面向网络工程等专业的学生与网络编程初学者围绕基于MFC对话框的FTP客户端开发展开帮助读者理解FTP协议交互与Windows网络应用构建的完整流程。资源包内含1个doc文档压缩包约151KB以课程设计报告形式呈现涵盖题目要求、系统概要设计、详细设计及控件、变量、函数清单等模块。报告具体讲解了CInternetSession、CFtpConnection、CFtpFileFind三个核心类的用法并给出OnQuery、OnDownLoad、OnUpLoad等函数的实现思路涉及登录服务器、检索目录、下载与上传文件、事件驱动编程及资源释放等关键环节。读者可借此掌握FTP连接建立、目录遍历、文件传输与错误处理的实现方法获得一份可直接参考的课程设计范例与网络编程入门素材。目前已有140人学习。1. 从一份课设报告说起MFC 写 FTP 客户端到底能跑通什么很多人第一次接触网络编程不是从 socket 裸写开始而是从一份课程设计报告里翻出一个能跑的 MFC 对话框程序。这份《FTP客户端程序设计》就是典型基于对话框的 MFC 工程三个编辑框收服务器域名、用户名、口令一个列表框显示远端目录四个按钮负责查询、上传、下载、退出。它不炫技但把 FTP 客户端最核心的四件事——建会话、连服务器、列目录、传文件——全串了一遍。如果你正在找一份能直接编译、能对着抄事件响应、能理解CInternetSession/CFtpConnection/CFtpFileFind三者关系的参考资料这份文档的价值不在“代码多”而在“流程完整且每一步都有对应函数”。它适合两类人一是刚学完 MFC 控件绑定、想找个真实网络场景练手的学生二是需要快速回忆 WinINet 封装类怎么用、不想翻 MSDN 的老手。下面按“资源是什么 → 怎么用 → 坑在哪”的顺序拆开讲。2. 拆解 MFC 的 FTP 三件套CInternetSession、CFtpConnection、CFtpFileFind2.1 为什么是这三个类而不是直接调 WinINet 或 socketMFC 对 WinINet 做了一层薄封装FTP 相关的主力就是三个类。CInternetSession管会话生命周期和全局配置CFtpConnection管一条具体的 FTP 控制连接CFtpFileFind管目录遍历。它们的关系是层层依赖没有 Session 就拿不到 Connection没有 Connection 就构造不了 FileFind。选这套方案的理由很实际。直接用 WinINet 的InternetOpen/InternetConnect/FtpFindFirstFile也能做但错误处理要自己写InternetGetLastResponseInfo字符串和句柄管理繁琐。用 socket 从零实现 FTP 协议则要处理控制连接与数据连接的分离、被动模式协商、换行符转换对课设量级来说过重。MFC 这三个类把“建连—操作—清理”的模板固定下来代码结构清晰适合作为教学和快速原型。代价也要说清楚这套封装绑定 Windows 平台依赖 WinINet不支持 SFTP对被动模式的控制粒度较粗。如果你的目标是跨平台或需要 FTPS这份资源只能作为理解流程的起点不能直接当生产代码用。2.2 查询目录OnQuery 与 ListContent 的配合查询按钮的响应函数OnQuery本身很薄它只做两件事把编辑框内容同步到成员变量清空列表框然后调用ListContent。真正的连接和遍历逻辑都在ListContent里。这种拆分值得学——界面刷新和网络操作分开后面加日志或加进度条时不用动按钮逻辑。void CFTP_ClientDlg::OnQuery() { UpdateData(); // 把编辑框内容写入 m_dname / m_user / m_psw while (m_List.GetCount() ! 0) m_List.DeleteString(0); // 清空列表框避免上次结果残留 ListContent(); // 真正的连接与目录遍历 }UpdateData()默认参数为TRUE方向是控件到变量。如果哪天发现变量里还是旧值先检查是不是在DoDataExchange里漏绑了 DDX 映射。清空列表框用while循环而不是ResetContent()效果一样但报告里这么写更直观地展示了“逐条删除”的过程。ListContent是核心。它先new一个CInternetSession再用GetFtpConnection尝试连接连接失败会抛CInternetException必须用try/catch接住并Delete()异常对象否则内存泄漏。连接成功后构造CFtpFileFind用FindFile(*)找第一个条目然后循环FindNextFile。void CFTP_ClientDlg::ListContent() { CInternetSession *psession; CFtpConnection *pconnection NULL; CFtpFileFind *pfilefind NULL; CString filename; BOOL bcontinue; psession new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS); try { pconnection psession-GetFtpConnection(m_dname, m_user, m_psw); } catch (CInternetException *e) { e-Delete(); pconnection NULL; } if (pconnection ! NULL) { m_btn2.EnableWindow(); // 连接成功才允许上传 pfilefind new CFtpFileFind(pconnection); bcontinue pfilefind-FindFile(*); // 查找当前目录任意文件 while (bcontinue) { filename pfilefind-GetFileName(); if (pfilefind-IsDirectory()) filename [ filename ]; // 目录加方括号标记 m_List.AddString(filename); bcontinue pfilefind-FindNextFile(); } pfilefind-Close(); delete pfilefind; pconnection-Close(); delete pconnection; } delete psession; }参数说明CInternetSession构造函数第一个参数是应用名第二个参数是上下文标识第三个PRE_CONFIG_INTERNET_ACCESS表示使用预配置的访问方式。GetFtpConnection三个参数依次是服务器地址、用户名、口令端口默认 21。FindFile(*)的通配符决定列出哪些条目改成*.txt就只列文本文件。这里有个容易忽略的点IsDirectory()必须在GetFileName()之后、FindNextFile()之前调用因为它依赖当前找到的条目状态。顺序反了会拿到错误的属性。2.3 下载与上传GetFile / PutFile 的对称结构下载和上传的代码几乎是对称的理解一个另一个自然通。列表框选中变化触发OnSelchangeList1它禁用输入框和查询、上传按钮激活下载按钮。这个状态机设计是为了防止用户在下载过程中改服务器地址或重复查询。void CFTP_ClientDlg::OnSelchangeList1() { m_EDIT1.EnableWindow(FALSE); m_EDIT2.EnableWindow(FALSE); m_EDIT3.EnableWindow(FALSE); m_btn1.EnableWindow(FALSE); // 查询 m_btn2.EnableWindow(FALSE); // 上传 m_btn3.EnableWindow(); // 下载 }右键点击对话框触发OnRButtonDown恢复所有输入控件和查询、上传按钮禁用下载按钮。这相当于一个“取消选择”的后悔药。下载按钮的OnDOWNLOAD先取列表框当前选中项判断首字符是不是[是则说明选中的是目录弹提示拒绝下载。否则弹CFileDialog让用户选保存路径再调GetFile。BOOL CFTP_ClientDlg::GetFile(CString fname, CString pname) { CInternetSession *psession; CFtpConnection *pconnection NULL; psession new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS); try { pconnection psession-GetFtpConnection(m_dname, m_user, m_psw); } catch (CInternetException *e) { e-Delete(); return FALSE; } if (pconnection ! NULL) { if (!pconnection-GetFile(fname, pname)) { pconnection-Close(); delete pconnection; delete psession; return FALSE; } pconnection-Close(); delete pconnection; } delete psession; return TRUE; }GetFile第一个参数是远端文件名第二个是本地保存路径。注意它每次下载都重新建会话、重连服务器这是报告里明确说的“为了简单清楚”。上传的PutFile结构完全一样只是把GetFile换成PutFile参数顺序是本地路径在前、远端文件名在后。提示GetFile和PutFile的返回值是BOOL但 WinINet 底层失败时不一定抛异常所以判断返回值比只靠catch更可靠。3. 从零复现对话框控件绑定与事件映射的完整步骤3.1 控件 ID 与变量绑定清单复现的第一步不是写代码而是把对话框资源画对。报告里给的控件清单可以直接照抄但有几个 ID 在原文里重复了三个按钮都写成IDC_BUTTON1实际在 Visual Studio 资源编辑器里必须改成不同的 ID否则 ClassWizard 无法区分事件。下面是我整理后的可用版本控件建议 ID变量名变量类型说明Edit BoxIDC_EDIT1m_dnameCString服务器域名Edit BoxIDC_EDIT2m_userCString用户名Edit BoxIDC_EDIT3m_pswCString口令List BoxIDC_LIST1m_ListCListBox远端目录列表ButtonIDC_BTN_QUERYm_btn1CButton查询ButtonIDC_BTN_UPLOADm_btn2CButton上传ButtonIDC_BTN_DOWNLOADm_btn3CButton下载ButtonIDCANCEL——退出绑定变量时编辑框选 Value 类别绑CString列表框和按钮选 Control 类别绑对应控件类。Value 类别的变量才能配合UpdateData()自动同步。3.2 事件映射BN_CLICKED 与 LBN_SELCHANGE 怎么加在对话框类的消息映射里需要手动或通过向导添加以下条目。如果向导生成的 ID 和上面不一致以资源编辑器里的实际 ID 为准。BEGIN_MESSAGE_MAP(CFTP_ClientDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BTN_QUERY, OnQuery) ON_BN_CLICKED(IDC_BTN_UPLOAD, OnUPLOAD) ON_BN_CLICKED(IDC_BTN_DOWNLOAD, OnDOWNLOAD) ON_LBN_SELCHANGE(IDC_LIST1, OnSelchangeList1) ON_WM_RBUTTONDOWN() END_MESSAGE_MAP()ON_BN_CLICKED对应按钮点击ON_LBN_SELCHANGE对应列表框选中项变化ON_WM_RBUTTONDOWN对应对话框右键。这三个宏是整份程序事件驱动的骨架缺一个对应功能就失效。3.3 头文件与预编译配置stdafx.h里必须包含afxinet.h否则CInternetSession等类找不到声明。报告附录里已经写了这一行但很多人复制代码时只复制 cpp 忘了头文件编译报一堆未定义类型。// stdafx.h 关键包含 #include afxwin.h #include afxext.h #include afxinet.h // CInternetSession / CFtpConnection / CFtpFileFind #include afxdlgs.h // CFileDialog另外InitInstance里要调用AfxSocketInit()否则网络相关功能初始化失败。报告里的FTP_Client.cpp已经包含这一步返回FALSE时会弹IDP_SOCKETS_INIT_FAILED。3.4 编译与运行前的检查顺序按这个顺序排查能省很多时间先确认afxinet.h已包含再确认AfxSocketInit返回真然后确认三个按钮 ID 不重复最后确认DoDataExchange里三个编辑框的 DDX 映射都存在。这四步过了程序基本能连上服务器。连不上时先看catch块有没有被触发如果异常被吞掉没有任何提示就在e-Delete()前加一句e-GetErrorMessage把错误码打出来。4. 避坑与排查连接失败、目录误判、资源泄漏的五个血泪经验4.1 现象点击查询没反应也不报错原因通常是GetFtpConnection抛了异常但catch块只做了Delete()没有提示或者服务器地址填了ftp://前缀导致解析失败。解决在catch里临时加AfxMessageBox输出e-m_dwError同时确认域名只写主机名或 IP不要带协议头。4.2 现象目录项被当成文件下载下载后是空文件原因在OnDOWNLOAD里判断目录用的是fname.GetAt(0) ! [但如果ListContent里IsDirectory()判断失效目录名没加方括号就会被当成文件。解决确保IsDirectory()在GetFileName()之后立即调用并且FindFile返回真之后才进入循环。4.3 现象连续查询几次后程序变慢或崩溃原因是CFtpFileFind对象在循环结束后没有Close()就delete或者CInternetSession在异常路径下没被释放。解决把pfilefind-Close()放在delete之前异常分支里也要保证psession被delete。报告里的代码在正常路径下是完整的但异常路径下psession的释放容易被漏掉。4.4 现象上传成功但服务器上文件大小为 0原因是PutFile的本地路径传成了文件名而不是完整路径或者文件被其他进程占用。解决CFileDialog取GetPathName()而不是GetFileName()作为第一个参数上传前确认文件可读。4.5 现象中文文件名显示乱码或下载失败原因是 FTP 控制连接默认使用 ASCII 编码中文文件名在GetFileName返回时可能已经丢失。解决这份课设级别代码没有处理编码转换遇到中文名建议先用英文名测试确认流程通了再考虑加WideCharToMultiByte转换。这是这套封装的边界不是代码写错了。5. 进阶技巧把“每次重连”改成一次会话复用以及验证清单报告里明确说查询、上传、下载各自独立建会话是为了简单。实际用起来连续下载多个文件时每次重连会明显变慢。一个不改动整体结构的优化是把CInternetSession和CFtpConnection提升为对话框类的成员指针在OnQuery成功连接后保留下载和上传直接复用退出时统一释放。// 在 CFTP_ClientDlg.h 中增加成员 CInternetSession *m_pSession; CFtpConnection *m_pConnection; // 在 OnQuery 连接成功后赋值 m_pSession psession; m_pConnection pconnection; // 下载时不再 new直接用 m_pConnection-GetFile(...) // 退出按钮或析构函数里统一清理 if (m_pConnection) { m_pConnection-Close(); delete m_pConnection; } if (m_pSession) { delete m_pSession; }这样改的好处是减少握手开销代价是要处理连接失效的情况——服务器可能主动断开复用前最好用m_pConnection-GetFile的返回值判断失败则重建。另一个技巧是给列表框加排序m_List.AddString之后调m_List.SetCurSel(0)让用户一眼看到第一个条目。验证这份资源是否真的跑通我一般走这个清单检查项预期结果失败时看哪里编译无未定义类型0 errorstdafx.h 是否含 afxinet.h查询按钮列出目录列表框出现文件名catch 块是否吞异常目录带方括号[dirname]IsDirectory 调用顺序下载非目录文件本地出现文件GetFile 返回值上传本地文件服务器出现文件PutFile 参数顺序右键恢复界面输入框可编辑OnRButtonDown 映射从那以后我每次拿到这类课设代码都强制先跑一遍“查询—下载—上传—右键恢复”四步再去看它有没有做异常路径的资源释放。这份报告的价值在于流程完整、函数职责清晰适合作为 MFC 网络编程的第一块跳板。希望帮到你。本文还有配套的精品资源点击获取
返回列表