c++网络编程实战——开发基于ftp协议的文件传输模块(三) 封装自己的ftp客户端

一.前言

在前面我们已经简单介绍过FTP协议的基本概念以及我们如何配置FTP服务和一些常用的ftp命令,这篇文章主要是介绍我们如何基于开源库去封装我们自己的ftp客户端。这篇文章也可以看做一篇介绍如何基于开源库去封装自己工具库的教程。

补充: 在上一篇文章中我犯了一个小错误,就是我们云服务器开放的端口协议是ipv4,但是我在ubuntu上编写ftp的配置文件vsftpd.conf上进行监听的端口所用的协议设成了IPV6,大家要记得修改一下,不然这篇文章下的部分代码会无法运行,大家也可以在看到这篇文章是使用上一篇文章的配置文件,此时我已经对它进行了改正。

二.将开源库编译成静态/动态库

首先 我们要将开源库编译成静态/动态库,项目的链接如下:
ftplib开源库
这里我们只要取用ftplib.cftplib.h即可,由于github访问的问题,后面我会将这个作为资源上传,大家按照需求自行取用。
获取了我们想要的开源库之后我们就要着手编译了,编译的makefile编写如下:

all: libftp.a libftp.solibftp.a:_ftplib.h _ftplib.cgcc -c -o libftp.a _ftplib.clibftp.so:_ftplib.h _ftplib.cgcc -fPIC -shared -o libftp.so _ftplib.cclean:rm -f libftp.a libftp.so

这要我们就可以在我们的代码中去使用这些开源库的代码了。

这里我们简单介绍一下该开源库提供的一些功能函数以及它们功能的介绍:

GLOBALREF void FtpInit(void);  //初始化函数
GLOBALREF char *FtpLastResponse(netbuf *nControl);  //获取上一次响应的函数
GLOBALREF int FtpConnect(const char *host, netbuf **nControl);  //连接函数
GLOBALREF int FtpOptions(int opt, long val, netbuf *nControl); //设置选项函数
GLOBALREF int FtpSetCallback(const FtpCallbackOptions *opt, netbuf *nControl); //设置回调函数
GLOBALREF int FtpClearCallback(netbuf *nControl); //清除回调函数
GLOBALREF int FtpLogin(const char *user, const char *pass, netbuf *nControl); //登录函数
GLOBALREF int FtpAccess(const char *path, int typ, int mode, netbuf *nControl,netbuf **nData); //访问函数
GLOBALREF int FtpRead(void *buf, int max, netbuf *nData); 
GLOBALREF int FtpWrite(const void *buf, int len, netbuf *nData);
GLOBALREF int FtpClose(netbuf *nData);
GLOBALREF int FtpSite(const char *cmd, netbuf *nControl); //site命令
GLOBALREF int FtpSysType(char *buf, int max, netbuf *nControl); //获取系统类型
GLOBALREF int FtpMkdir(const char *path, netbuf *nControl);  //创建文件夹
GLOBALREF int FtpChdir(const char *path, netbuf *nControl); //切换目录
GLOBALREF int FtpCDUp(netbuf *nControl);  //
GLOBALREF int FtpRmdir(const char *path, netbuf *nControl);  //删除文件夹
GLOBALREF int FtpPwd(char *path, int max, netbuf *nControl);  //获取当前路径
GLOBALREF int FtpNlst(const char *output, const char *path, netbuf *nControl); //列出远程服务器指定路径下的文件名
GLOBALREF int FtpDir(const char *output, const char *path, netbuf *nControl);   //列出远程服务器指定路径下的文件名和详细信息
GLOBALREF int FtpSize(const char *path, unsigned int *size, char mode, netbuf *nControl); //查询远程文件的大小
#if defined(__UINT64_MAX)
GLOBALREF int FtpSizeLong(const char *path, fsz_t *size, char mode, netbuf *nControl); 
#endif
GLOBALREF int FtpModDate(const char *path, char *dt, int max, netbuf *nControl); //获取远程文件的最后修改日期和时间。
GLOBALREF int FtpGet(const char *output, const char *path, char mode, netbuf *nControl);//从远程服务器下载文件。
GLOBALREF int FtpPut(const char *input, const char *path, char mode,netbuf *nControl); //上传文件到远程服务器
GLOBALREF int FtpRename(const char *src, const char *dst, netbuf *nControl); //在远程服务器上重命名文件或目录。
GLOBALREF int FtpDelete(const char *fnm, netbuf *nControl); //删除远程服务器上的文件。
GLOBALREF void FtpQuit(netbuf *nControl); //断开连接

下面就要开始我们基于上述开源库的客户端的封装了。

3.ftp客户端的封装

在开始对我们封装代码具体讲解之前我们先看一下整体的框架(也就是.h文件):

//_ftp.h
// 此程序是开发框架的ftp客户端工具的类的声明文件。
#ifndef __FTP_H
#define __FTP_H#include "./_public.h"
#include "_ftplib.h"namespace idc
{class cftpclient{private:netbuf *m_ftpcoon; // ftp服务的连接句柄public:unsigned int m_msize; // 文件的大小string m_mtime;       // 最后一次进行文件修改的时间// 下面是用来存放ftp登录可能出现的问题bool m_connetfailed;                                // 网络连接失败bool m_loginfailed;                                 // 登录失败(用户名/密码错误或是没有登录权限)bool m_optionfailed;                                // 设置传输模式失败cftpclient();                                       // 构造函数~cftpclient();                                      // 析构函数cftpclient(const cftpclient &) = delete;            // 禁止拷贝构造函数cftpclient &operator=(const cftpclient &) = delete; // 禁止赋值操作符void initdata();                                    // 初始化数据// 登录ftp服务器// host:ftp服务器的ip地址与端口号,如:127.0.0.1:21// username:用户名// password:密码// fmode:传输模式,默认为被动模式bool login(const string &host, const string &username, const string &password, const int fmode =FTPLIB_PASSIVE);// 断开连接bool loginout();// 获取ftp服务器上文件的时间 返回值:false-失败;true-成功,获取到的文件大小存放在m_mtime成员变量中。bool mtime(const string &filename);// 获取ftp服务器上文件的大小//  返回值:false-失败;true-成功,获取到的文件大小存放在m_size成员变量中。bool size(const string &filename);// 修改ftp服务器的工作目录// 返回值: false-失败;true-成功bool chdir(const string &dir);// 在ftp服务器上创建目录// 返回值: false-失败;true-成功bool mkdir(const string &dir);// 删除ftp服务器上的目录// 返回值: false-失败;true-成功bool rmdir(const string &dir);// 发送NLST命令列出ftp服务器目录中的子目录名和文件名。// remotedir:ftp服务器的目录名。// listfilename:用于保存从服务器返回的目录和文件名列表。// 返回值:true-成功;false-失败。// 注意:如果列出的是ftp服务器当前目录,remotedir用"","*","."都可以,但是,不规范的ftp服务器可能有差别。bool nlst(const string &remotedir, const string &listfilename);// 从ftp服务器上获取文件。// remotefilename:待获取ftp服务器上的文件名。// localfilename:保存到本地的文件名。// bcheckmtime:文件传输完成后,是否核对远程文件传输前后的时间,保证文件的完整性。// 返回值:true-成功;false-失败。// 注意:文件在传输的过程中,采用临时文件命名的方法,即在localfilename后加".tmp",在传输// 完成后才正式改为localfilename。bool get(const string &remotefilename, const string &localfilename,const bool bcheckname = true);// 向ftp服务器发送文件。// localfilename:本地待发送的文件名。// remotefilename:发送到ftp服务器上的文件名。// bchecksize:文件传输完成后,是否核对本地文件和远程文件的大小,保证文件的完整性。// 返回值:true-成功;false-失败。// 注意:文件在传输的过程中,采用临时文件命名的方法,即在remotefilename后加".tmp",在传输// 完成后才正式改为remotefilename。bool put(const string &localfilename, const string &remotefilename,const bool bcheckname = true);//删除ftp服务器上的文件//返回值:true-成功;false-失败。bool deletefile(const string &remotefilename);//重命名ftp服务器上的文件//返回值:true-成功;false-失败。bool rename(const string &oldfilename, const string &newfilename);//向ftp服务器发送site命令//command:命令的具体内容//返回值:true-成功;false-失败。bool site(const string &command);//获取服务器返回信息的最后一条char* response();};
};#endif // __FTP_H

我们可以看到主要还是怼一些我们常用的ftp命令在ftplib.c的基础上对其进行了封装,接下来我们再来看一下它的具体实现

//_ftp.cpp
#include "_ftp.h"namespace idc{cftpclient::cftpclient(){m_ftpcoon=0;m_loginfailed=0;m_optionfailed=0;m_connetfailed=0;initdata();}cftpclient::~cftpclient(){loginout();}void cftpclient::initdata(){m_msize=0;m_mtime.clear();}bool cftpclient::login(const string& host,const string& username,const string& password,const int fmode){if(m_ftpcoon!=0){FtpQuit(m_ftpcoon);m_ftpcoon=0;}m_loginfailed=m_optionfailed=m_connetfailed=0;if(FtpConnect(host.c_str(),&m_ftpcoon)==0)  {m_connetfailed=1;return false;}if(FtpLogin(username.c_str(),password.c_str(),m_ftpcoon)==0) {m_loginfailed=1;return false;}if(FtpOptions(FTPLIB_CONNMODE,long(fmode),m_ftpcoon)==0)  {m_optionfailed=1;return false;}return true;}bool cftpclient::loginout(){if(m_ftpcoon==0){return false;}FtpQuit(m_ftpcoon);m_ftpcoon=0;return true;}bool cftpclient::mtime(const string& filename){if(m_ftpcoon==0)  return false;m_mtime.clear();string mtimestr;mtimestr.resize(14);if(FtpModDate(filename.c_str(),&mtimestr[0],14,m_ftpcoon)==0) return false;//将UTC时间转换为实际时间if(addtime(mtimestr,m_mtime,0,"yyyymmddhh24miss")==0) return false;return true;}bool cftpclient::size(const string& filename){if(m_ftpcoon==0)  return false;m_msize=0;if(FtpSize(filename.c_str(),&m_msize,FTPLIB_IDLETIME,m_ftpcoon)==0) return false;return true;}bool cftpclient::chdir(const string& dirname){if (m_ftpcoon==0)  return false;if(FtpChdir(dirname.c_str(),m_ftpcoon)==0) return false;return true;}bool cftpclient::mkdir(const string& dirname){if (m_ftpcoon==0)  return false;if(FtpMkdir(dirname.c_str(),m_ftpcoon)==0) return false;return true;}bool cftpclient::rmdir(const string& dirname){if (m_ftpcoon==0)  return false;if(FtpRmdir(dirname.c_str(),m_ftpcoon)==0) return false;return true;}bool cftpclient::nlst(const string& remotedir,const string& filenamelist){if (m_ftpcoon==0)  return false;newdir(filenamelist.c_str());if(FtpNlst(filenamelist.c_str(),remotedir.c_str(),m_ftpcoon)==0) return false;return true;}bool cftpclient::get(const string &remotefilename, const string &localfilename, bool bcheckname){if (m_ftpcoon==0) return false;newdir(localfilename);string tmpfile=localfilename+".tmp";//获取远程服务器文件的时间if(mtime(remotefilename)==0) return false;//下载文件到本地的临时文件中if(FtpGet(remotefilename.c_str(),tmpfile.c_str(),FTPLIB_IMAGE,m_ftpcoon)==0) return false;//对比文件下载前和下载后的时间,如果不同,说明文件发生了改变,终止if(bcheckname){string strtime=m_mtime;if(mtime(remotefilename)==false) return false;if(strtime!=m_mtime) return false;}//更新文件信息setmtime(remotefilename,m_mtime);//将临时文件重命名为正式文件if(rename(tmpfile.c_str(),localfilename.c_str())==0) return false;size(remotefilename);return true;}bool cftpclient::put(const string &localfilename, const string &remotefilename,const bool bcheckname){if (m_ftpcoon==0) return false;newdir(remotefilename);string tmpfile=remotefilename+".tmp";string filetime1,filetime2;//获取本地文件的时间filetime1=filemtime(localfilename,filetime1); //上传文件到远程服务器的临时文件中if(FtpPut(localfilename.c_str(),tmpfile.c_str(),FTPLIB_IMAGE,m_ftpcoon)==0) return false;//对比文件上传前和上传后的时间,如果不同,说明文件发生了改变,终止if(bcheckname){if(filemtime(remotefilename,filetime2)==false) return false;if(filetime2!=filetime1) return false;}//将临时文件重命名为正式文件if(rename(tmpfile.c_str(),remotefilename.c_str())==0) return false;if(size(localfilename)==0) return false;return true;}bool cftpclient::deletefile(const string &remotefilename){if (m_ftpcoon==0) return false;if(FtpDelete(remotefilename.c_str(),m_ftpcoon)==0) return false;return true;}bool cftpclient::rename(const string &oldfilename, const string &newfilename){if (m_ftpcoon==0) return false;if(FtpRename(oldfilename.c_str(),newfilename.c_str(),m_ftpcoon)==0) return false;return true;}bool cftpclient::site(const string &command){if (m_ftpcoon==0) return false;if(FtpSite(command.c_str(),m_ftpcoon)==0) return false;return true;}char* cftpclient::response(){if(m_ftpcoon==0) return nullptr;return FtpLastResponse(m_ftpcoon);}
};

其实绝大多数的函数都不是需要过多的解释,因为主要就是对ftplib.c这个库的调用,主要要注意的是get函数与put函数,这里我们来讲一下:

-get函数

        bool cftpclient::get(const string &remotefilename, const string &localfilename, bool bcheckname){if (m_ftpcoon==0) return false;newdir(localfilename);string tmpfile=localfilename+".tmp";//获取远程服务器文件的时间if(mtime(remotefilename)==0) return false;//下载文件到本地的临时文件中if(FtpGet(remotefilename.c_str(),tmpfile.c_str(),FTPLIB_IMAGE,m_ftpcoon)==0) return false;//对比文件下载前和下载后的时间,如果不同,说明文件发生了改变,终止if(bcheckname){string strtime=m_mtime;if(mtime(remotefilename)==false) return false;if(strtime!=m_mtime) return false;}//更新文件信息setmtime(remotefilename,m_mtime);//将临时文件重命名为正式文件if(rename(tmpfile.c_str(),localfilename.c_str())==0) return false;size(remotefilename);return true;}

get函数是从ftp服务器中下载文件,这里我们的实现思路是:

  1. 检查是否连接服务器
  2. 在本地创建文件
  3. 创建临时文件接收文件
  4. 检查文件下载是否有误
  5. 将临时文件改为正式文件

put函数是从ftp服务器中下载文件,这里我们的实现思路是:

  1. 检查是否连接服务器
  2. 在服务器创建文件
  3. 创建临时文件接收文件
  4. 检查文件下载是否有误
  5. 将临时文件改为正式文件

拓展

在什么的文件中有一些函数是我自己封装的,这里就不一一展示,我和大家大概介绍一下它们的功能,大家可以尝试自己去实现一下:

  • addtime:将电脑获取的UTC时间改成指定格式的时间
  • newdir:创建指定文件/文件夹
  • setmtime:谁知文件的修改信息
  • filemtime:获取文件的最后一次修改时间

最后我还写了一个简单的测试文件,大家也可以试试或者自己写一个测试样例去试试:

#include "./_ftp.h"
using namespace idc;int main(int argc,char *argv[])
{cftpclient ftp;if(ftp.login(argv[1],argv[2],argv[3])==false){perror("ftp.login() failed.");return -1;}cout<<"ftp.login() success."<<endl;if(ftp.nlst("/public/","/tmp/list/tmp.list")==false){perror("ftp.dir() failed.");return -1;}cout<<"ftp.nlst() success."<<endl;// 在ftp服务器上创建/home/wucz/tmp,注意,如果目录已存在,会返回失败。if (ftp.mkdir("/home")==false) { printf("ftp.mkdir() failed.\n"); return -1; }// 把ftp服务器上的工作目录切换到/home/wucz/tmpif (ftp.chdir("/home")==false) { printf("ftp.chdir() failed.\n"); return -1; }return 0;
}

结语

经过上面的步骤我们就基于开源库实现了一个我们自己的ftp客户端,这篇文章一方面是写如何封装ftp客户端,另外一方面也是想介绍如何基于开源库去封装自己工具库,希望大家可以有所收获,下篇见!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/875666.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【前端学习】CSS三大特性

CSS三大特性 CSS的三大特性是为了化简代码、定位问题并且解决问题 继承性 继承性特点&#xff1a; 子级默认继承父级的文字控制属性。注意&#xff1a;如果标签自己有样式则生效自己的样式&#xff0c;不继承。 <!DOCTYPE html> <html lang"en"><…

windows USB 设备驱动开发- WinUSB 简介

WinUSB 是 Windows 随附的 USB 设备的通用驱动程序。WinUSB 包括&#xff1a; 内核模式驱动程序 (Winusb.sys)&#xff1b;公开 winusb.h 中所述的 WinUSB 函数的用户模式动态链接库 (Winusb.dll)。 借助这些函数&#xff0c;你可以使用用户模式软件管理 USB 设备&#xff1b;…

12 位运算符

位运算符只能用于整数&#xff0c;其内部执行过程为&#xff1a;首先将整数转换为二进制数&#xff0c;然后右对齐&#xff0c;必要时左侧补0&#xff0c;按位进行运算&#xff0c;最后再把计算结果转换为十进制数字返回。 ① 左移&#xff1a;高位丢弃&#xff0c;低位补0&…

Exponential Moving Average (EMA) in Stable Diffusion

1.Moving Average in Stable Diffusion (SMA&EMA) 1.Moving average 2.移动平均值 3.How We Trained Stable Diffusion for Less than $50k (Part 3) Moving Average 在统计学中&#xff0c;移动平均是通过创建整个数据集中不同选择的一系列平均值来分析数据点的计算。 …

数据结构与算法-插入排序

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、插入排…

unity ui toolkit的使用

UIToolkitExamples (github)样例 GitHub - ikewada/UIToolkitExamples: チュートリアル動画「使ってみようUI Toolkit」のためのサンプルプロジェクトです官网 Unity - Manual: UI Toolkit视频教程 使用 UI Toolkit - 上集_哔哩哔哩_bilibili 使用 UI Toolkit - 下集_哔哩哔哩_…

Java | Leetcode Java题解之第283题移动零

题目&#xff1a; 题解&#xff1a; class Solution {public void moveZeroes(int[] nums) {int n nums.length, left 0, right 0;while (right < n) {if (nums[right] ! 0) {swap(nums, left, right);left;}right;}}public void swap(int[] nums, int left, int right)…

赋能未来教育,3DCAT助力深圳鹏程技师学院打造5G+XR实训室

随着国家对教育行业的重视&#xff0c;实训室建设已成为推动教育现代化的关键。《教育信息化2.0行动计划》、《职业教育示范性虚拟仿真实训基地建设指南》等政策文件&#xff0c;明确指出了加强虚拟仿真实训教学环境建设的重要性。 在这一大背景下&#xff0c;教育行业对于实训…

初识C++ · AVL树(1)

目录 前言&#xff1a; 1 AVL树的创建 2 部分成员函数 2.1 查找 2.2 中序遍历 2.3 插入 2.4 左旋转 2.5右旋转 前言&#xff1a; 上文&#xff0c;上上文提到了map set&#xff0c;二叉搜索树&#xff0c;其实都是为了近两文做铺垫的&#xff0c;虽然map的底层是红黑树…

openFeign配置okhttp

原来的项目出现了性能问题&#xff0c;老大不知道怎么的&#xff0c;让我改openFeign线程池为okhttp&#xff0c;说原生的不支持线程池性能比较差。 原openFeign配置文章地址 一、pom文件 <dependency><groupId>org.springframework.cloud</groupId><arti…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的跳格子游戏(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

手写spring简易版本,让你更好理解spring源码

首先我们要模拟spring&#xff0c;先搞配置文件&#xff0c;并配置bean 创建我们需要的类&#xff0c;beandefito&#xff0c;这个类是用来装解析后的bean&#xff0c;主要三个字段&#xff0c;id&#xff0c;class&#xff0c;scop&#xff0c;对应xml配置的属性 package org…

第二讲:NJ网络配置

Ethernet/IP网络拓扑结构 一. NJ EtherNet/IP 1、网络端口位置 NJ的CPU上面有两个RJ45的网络接口,其中一个是EtherNet/IP网络端口(另一个是EtherCAT的网络端口) 2、网络作用 如图所示,EtherNet/IP网络既可以做控制器与控制器之间的通信,也可以实现与上位机系统的对接通…

MySQL --- 表的操作

在对表进行操作时&#xff0c;需要先选定操作的表所在的数据库&#xff0c;即先执行 use 数据库名; 一、创建表 create table 表名( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎 ; 说明&#xff1a…

从零入门 AI for Science(AI+药物) #Datawhale AI 夏令营

使用平台 我的Notebook 魔搭社区 https://modelscope.cn/my/mynotebook/preset 主要操作 运行实例&#xff0c;如果有时长尽量选择方式二&#xff08;以下操作基于方式二的实例实现&#xff09; 创建文件夹&#xff0c;并重命名为 2.3siRNA 上传两个文件 到文件夹&#…

BGP路由反射器

原理概述 缺省情况下&#xff0c;路由器从它的一个 IBGP对等体那里接收到的路由条目不会被该路由器再传递给其他IBGP对等体&#xff0c;这个原则称为BGP水平分割原则&#xff0c;该原则的根本作用是防止 AS内部的BGP路由环路。因此&#xff0c;在AS内部&#xff0c;一般需要每台…

【Android】数据存储方案——文件存储、SharedPreferences、SQLite数据库用法总结

文章目录 文件存储存储到文件读取文件 SharedPreferences存储存储获取SharedPreferences对象Context 类的 getSharedPreferences() 方法Activity 类的 getPreferences() 方法PreferenceManager 类中的 getDefaultSharedPreferences() 方法 示例 读取记住密码的功能 SQLite数据库…

4.Java Web开发模式(javaBean+servlet+MVC)

Java Web开发模式 一、Java Web开发模式 1.javaBean简介 JavaBeans是Java中一种特殊的类&#xff0c;可以将多个对象封装到一个对象&#xff08;bean&#xff09;中。特点是可序列化&#xff0c;提供无参构造器&#xff0c;提供getter方法和setter方法访问对象的属性。名称中…

JAVA代码审计JAVA0基础学习(需要WEB基础知识)DAY2

JAVA 在 SQL执行当中 分为3种写法&#xff1a; JDBC注入分析 Mybatis注入分析 Hibernate注入分析 JDBC 模式不安全JAVA代码示例部分特征 定义了一个 sql 参数 直接让用户填入id的内容 一个最简单的SQL语句就被执行了 使用安全语句却并没有被执行 Mybatis&#xff1a; #…

【MetaGPT系列】【MetaGPT完全实践宝典——多智能体实践】

目录 前言一、智能体1-1、Agent概述1-2、Agent与ChatGPT的区别 二、多智能体框架MetaGPT2-1、安装&配置2-2、使用已有的Agent&#xff08;ProductManager&#xff09;2-3、多智能体系统介绍2-4、多智能体案例分析2-4-1、构建智能体团队2-4-2、动作/行为 定义2-4-3、角色/智…