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,一经查实,立即删除!

相关文章

Python 和 Boto3 生成 Amazon S3 对象的 HTTPS URL

在使用 Amazon S3 存储服务时,我们经常需要获取存储桶中对象的 HTTPS URL。这篇博文将详细介绍如何使用 Python 和 Boto3 库来实现这一功能。 背景 Amazon S3(Simple Storage Service)是一种广泛使用的云存储服务。在许多场景中,我们需要获取 S3 存储桶中对象的公开访问 …

353_C++_Boost.Asio库来处理异步操作

逐行解析这段C++代码。该代码使用了Boost.Asio库来处理异步操作,并且使用了智能指针和线程相关的一些内容。 #include <boost/asio.hpp> #include <memory>这两行代码包含了Boost.Asio库和标准库中的智能指针std::shared_ptr的头文件。 /**********************…

[ABC279B] LOOKUP 题解

题目传送门 \color{orangered}\text{题目传送门} 题目传送门 题意 给出两个字符串 A , B A,B A,B&#xff0c;问 B B B 是否是 A A A 的子串。 分析 调用find函数&#xff0c; 用来查找 B B B 是否在 A A A 中出现过。 因为题目上说字符串的长度不大于100&#xff0c…

【前端学习】CSS三大特性

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

Java整理12

1、前端工程化 概念&#xff1a;使用软件工程的方法来单独解决前端开发流程中模块化、组件化、规范化、自动化的问题&#xff0c;提高效率降低成本。 ECMA6Script&#xff08;VUE3的基础&#xff09; let&#xff1a;&#xff08;1&#xff09;let不能重复声明&#xff08;2&a…

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;移动平均是通过创建整个数据集中不同选择的一系列平均值来分析数据点的计算。 …

React 常见的报错及解决方法

1、Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons&#xff08;无效的钩子调用。钩子只能在函数组件的内部调用。这可能是由于以下原因之一&#xff09; 原因&#x…

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

&#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的底层是红黑树…

YOLOv8目标检测网络评估指标介绍

本章主要介绍一下AP的计算方法, 其中会穿插介绍TP、TN、FP、FN、Precision和Recall等概念。 1.类别、置信度和IoU 先来了解下类别、置信度和IoU,这三者的定义很重要,因为在后面的计算过程中会借助这三者的值来计算AP值。 类别是指模型预测的类别概率中的最大的一个类别,置…

openFeign配置okhttp

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

设计模式实战:媒体播放器的设计与实现

问题描述 设计一个媒体播放器系统,用户可以播放、暂停、停止和切换媒体。系统需要支持多种媒体格式(如音频和视频),并允许在播放过程中应用不同的效果(如音量调节、均衡器等)。 设计分析 状态模式 状态模式允许对象在其内部状态改变时改变其行为。媒体播放器需要在不…

AI学习指南机器学习篇-半监督聚类的优缺点

AI学习指南机器学习篇-半监督聚类的优缺点 引言 半监督聚类是机器学习领域中的一个重要概念&#xff0c;它结合了监督学习和无监督学习的优点&#xff0c;可以应用于许多领域&#xff0c;例如文本分类、图像分类和社交网络分析等。然而&#xff0c;半监督聚类算法也存在着一些…

react配置代理的3中方法

1.使用create-react-app的代理配置 可以在项目根目录下的package.json文件中添加proxy字段来配置代理&#xff1a; {..."proxy": "http://localhost:5000" } //注意&#xff1a;比如当前端口是3000&#xff0c;先在当前端口3000中找对应路径内容&#xff…

c17 新特性 字面量,变量,函数,隐藏转换等

导论 c17新特性引入了许多新的语法&#xff0c;这些语法特性更加清晰&#xff0c;不像传统语法&#xff0c;语义飘忽不定&#xff0c;比如‘a’你根本不知道是宽字符还是UTF-8 字符。以及测试i i&#xff0c;最后结果到底是多少。这种问题很大情况是根据编译器的优化进行猜测&a…