ftp 数据采集模块主要是使用封装好的 Cftp类写的客户端程序从服务器将数据采集回来。Cftp类是在 ftplib开源库的基础上做的二次封装。
使用 ftp 进行开发前,先对ftp 进行简单的了解。
一、ftp 协议简单介绍
ftp(File Transfer Protocol文件传输协议)是基于TCP/IP 协议的应用层协议,用于文件的传输,包括ftp服务器(或服务端)和ftp客户端。
ftp客户端与服务器创建网络连接,请求登录服务器,登录成功后,就可以进行文件传输,主要包括下载文件和上传文件两种操作。
ftp协议很古老,但是,ftp的应用场景仍非常广泛,这是不争的事实。
在Linux系统中,ftp客户端和ftp服务器是操作系统自带的,但不一定会缺省安装。
1、主动模式和被动模式
ftp有两种模式,分别是port模式(主动模式)和pasv模式(被动模式)。主动模式是客户端先打开一个数据端口传输数据,服务端主动向这个端口发起连接。被动模式是服务端打开一个数据端口传输数据,客户端向这个端口发起连接。连接成功后开始传输文件。
但是无论是主动模式和被动模式,客户端都是在服务端的21端口发送ftp命令的。
1.1、主动模式
客户端给服务端的21端口发命令说:我要输传文件,我已经打开了自己的20端口,您向我的20端口发起TCP连接,我们来传输文件。服务端知道后,就会主动向客户端的20端口发起连接,连接成功后开始传输文件。
主动模式发数据时,是服务端向客户端的端口发起连接。
1.2、被动模式
客户端给服务器端的21端口发命令说:我要传输文件。服务器端知道后打开一个空闲的高端口,然后告诉客户端,我已经打开了某某端口,您向我这个端口发起TCP连接,然后我们用这个端口来传输文件。在被动模式下,不管是ftp命令,还是传输数据,都是由客户端向服务端发起TCP连接。
1.3、从主动模式到被动模式
在很久以前每台电脑都有一个ip地址,ftp只有主动模式,后来出现了共享上网技术,所以也就有了下面的问题。
共享上网就是多台电脑共享一个公网ip去使用internet,例如某个局域网出口的公网ip是210.33.25.108,当内网用户(192.168.1.100)访问外网的ftp服务器时,如果采用主动模式,192.168.1.100告诉了ftp服务器我需要某个文件和我打开了20端口之后,由于共享上网的原因,192.168.1.100在出网关的时候ip已经被转换成了210.33.25.108,所以ftp服务器端收到的消息是210.33.25.108需要某个文件并打开了20端口,ftp服务器就会尝试连接210.33.25.108的20端口,这样当然不会成功。(内网用户ip地址就像是一个小村庄,外网ip地址就像是一个市。)
在主动模式中,ftp的两个端口是相对固定的,如果命令端口是n的话,那数据端口就是n-1,也就是说默认情况下,命令端口是21,数据端口就是20,如果您把ftp服务的端口改成了521,那么数据端口就是520,这样配置防火墙很方便,只需要开通两个端口就可以了。但是,在共享上网的环境中无法使用主动模式。
在被动模式中,默认情况下命令端口是21,数据端口是随机分配的。但是,被动模式中数据端口的范围可以配置,防火墙也可以配置端口范围。
二、ftp 命令
1、登录服务器
方法一:输入ftp 服务器ip地址
,回车后根据提示输入用户名和密码,如下图:
方法二:输入ftp
,用open 服务器ip地址
,连上服务器后再输入用户名和密码,如下图:
方法三:输入ftp -n 服务器ip地址
,再输入user 用户名 密码登录
,如下图:
2、切换工作目录
注意,如果目录名中有特殊符号,如空格,可以用双引号把目录名包含起来。
2.1、查看服务器工作目录
pwd
2.2、切换服务器工作目录
cd 目录名
2.3、切换本地工作目录
lcd 目录名
3、查看服务器上的目录和文件
============================
3.1、列出目录或文件名的详细信息
ls 目录或文件名
dir 目录或文件名
ls和dir都可以用于查看目录和文件信息,常用ls,语法和Linux的ls命令相同。
3.2、仅列出目录和文件名
nlist 目录或文件名 [本地文件名]
1)列出/freecplus目录下的匹配*.h的文件名信息。
2)列出/freecplus目录下的匹配*.h的文件名信息,结果输出到本地的/tmp/freecplus.list文件中。
查看/tmp/freecplus.list内容。
4、下载/上传文件
==================
4.1、文件传输入的模式
ftp的传输模式分为二进制和ASCII码两种模式,二进制模式可以传输任何文件,包括压缩包、可执行程序、图片、视频、音频等,而ASCII模式只能传输.txt、.htm等ascii码文件(文本文件)。在实际开发中,不管什么文件,都用二进制方式传输文件。
1)查看当前的传输模式。
type
2)设定传输模式为二进制。
bin
3)设定传输模式为ASCII。
ascii
示例:
4.2、下载文件
1)下载单个文件。
get/recv 服务器文件名 [本地文件名]
使用说明:
a)下载文件用get和recv都可以。
b)文件名不允许用通配符。
c)服务器文件名和本地文件名可以用绝对路径,如果不写路径,表示当前工作目录。
d)如果本地文件名省略不写,表示把服务器文件下载到本地的当前工作目录,文件名与服务器文件名相同。
2)下载多个文件。
mget 服务器文件1 服务器文件2 服务器文件3 …… 服务器文件n
使用说明:
a)待下载的文件名,可以一一列出来(用空格分隔),也可以用通配符。
b)下载的文件,存放在本地当前工作目录中。
c)下载文件时,会一一提示,如果想关闭都显示信息,先输入prompt命令。
prompt
4.3、上传文件
1)上传单个文件。
put/send 本地文件名 [服务器文件名]
a)上传文件用put和send都可以。
b)文件名不允许用通配符。
c)本地文件名和服务器文件名可以用绝对路径,如果不写路径,表示当前工作目录。
d)如果服务器文件名省略不写,表示把本地文件上传到服务器的当前工作目录,文件名与本地文件名相同。
2)上传多个文件。
mput 本地文件1 本地文件2 本地文件3 …… 本地文件n
使用说明:
a)待上传的文件名,可以一一列出来(用空格分隔),也可以用通配符。
b)上传的文件,存放在服务器当前工作目录中。
c)上传文件时,会一一提示,如果想关闭都显示信息,先输入prompt命令。
prompt
5、其它ftp命令
================
1)重命名服务器上的文件
rename 旧文件名 新文件名
2)删除ftp服务器上单个文件
delete 文件名
3)删除多个文件。
mdelete 文件名1 文件名2 文件名3 …… 文件名n
4)在服务器上创建目录。
mkdir pathname
5)删除服务器上的目录。
rmdir pathname
6)切换传输模式。
passive
7)显示帮助信息。
help [命令名]
显示ftp命令的帮助信息,如果不输入命令名,则显示全ftp命令的帮助信息。
8)退出ftp。
bye
三、使用 ftp 协议开发的客户端程序
ftp客户端程序主要的目的是从服务端下载数据或者上传数据。首先要了解 ftp从服务端获取数据的基本流程。
1.ftp 下载服务器数据的流程
(1)登录 ftp 服务器,用户名和密码
(2)选择主动模式或者被动模式,一般是被动模式
(3)找到要下载数据的路径,也就是要切换服务器的工作目录
(4)切换了工作目录,如果有多种数据类型,匹配自己要找的数据类型,比如说我要下载 .txt 类型,就匹配 .txt 类型文件
(5)执行下载命令下载自己想要的数据
(6)指定存放数据的本地路径
(7)检查是否下载成功,将已经下载的文件的文件名放到本地指定文件中。
所以程序的参数应该包含前面的流程需求:
2.实现增量下载服务器数据的功能
增量采集:采集过的文件不再采集,缓解网络压力。实现这个功能要解决的一个重点是:如何知道哪些文件已经被采集,哪些没有被采集,要将他们区分开来。
(1)这里采用的办法是每次采集数据之前,都要把指定的服务端目录下的文件名,写到客户端的一个文件中,称为要采集文件。
(2)当从服务端下载了一个文件到客户端,就将这个文件名写到客户端的一个文件中,称为已下载文件。
(3)当选择了增量采集模式,就把要采集文件的内容导入程序中的要采集容器中,这个步骤要把文件筛选,就是文件有很多种,只要本次 要下载的文件格式。把已经下载文件内容导入已经下载容器中。
(4)然后比较这两个容器,如果要采集容器中有的文件(其实是文件名和文件最后修改时间),而已经下载容器中没有,就说明本次任务要下载。就把这个文件放到另一个容器中。
3.保证下载文件的完整性措施
凡是涉及到文件传输的步骤,都要考虑到文件传输后的完整性。导致文件不完整的两个常见的现象:(1)服务端还没有写完文件,客户端就已经下载了;(2)传输过程中,由于网络异常等原因导致
详细文章:https://blog.csdn.net/qq_43403759/article/details/115421725
3.1 服务端未写完文件就客户端就下载
这个现象比较少见,但是不排除会出现。解决的办法:
给没写完(正在写)的文件加上没有写完的标识(后缀)。比如说要将一批数据写入aaa.txt,在没有写完之前命名为 aaa.txt.tmp,写完了之后重命名为 aaa.txt。客户端指定了要下载的文件格式,下载文件的时候只下载那些完整的文件(比如说文件后缀名为 .txt),不完整的就不获取(比如说 .tmp 的文件后缀名)。
上面的步骤就已经封装在函数里面了。
3.2 由于传输过程出错导致不完整
从服务端下载文件,从客户端上传文件到服务端。这两个操作在封装的Cftp类中,分别是get() 函数和 put() 函数。
为了保证在传输过程中文件的完整性,在这个两个函数的参数中加了一个参数,get()函数加了核对传输前后时间,put() 函数加了核对传输前后文件大小。
(1)get()
在文件传输之前,获取文件内容的最后修改时间,传输了之后再获取文件的最后的修改时间。对比这两个时间如果不相同,就说明源文件传输完之后被修改了,与传输的目标文件已经不同了,那么我得到这个被修改过后的文件后要丢弃。
为什么不核对大小呢?文件内容改变了大小不一定变,但文件内容的最后修改时间一定变。
(2)put()
这个不用核对时间,因为客户端传输文件给另一端之前都会保证文件是正确的完整的,不会是中间状态的文件,传输的是准备好的文件。所以核对传输前后的文件大小就可以了。
四、封装的 Cftp 类
class Cftp
{
public:
netbuf *m_ftpconn; // ftp连接句柄。
unsigned int m_size; // 文件的大小,单位:字节。
char m_mtime[21]; // 文件的修改时间,格式:yyyymmddhh24miss。
// 以下三个成员变量用于存放login方法登录失败的原因。
bool m_connectfailed; // 连接失败。
bool m_loginfailed; // 登录失败,用户名和密码不正确,或没有登录权限。
bool m_optionfailed; // 设置传输模式失败。
Cftp(); // 类的构造函数。
~Cftp(); // 类的析构函数。
void initdata(); // 初始化m_size和m_mtime成员变量。
// 登录ftp服务器。
// host:ftp服务器ip地址和端口,中间用":"分隔,如"192.168.1.1:21"。
// username:登录ftp服务器用户名。
// password:登录ftp服务器的密码。
// imode:传输模式,FTPLIB_PASSIVE是被动模式,FTPLIB_PORT是主动模式,缺省是被动模式。
bool login(const char *host,const char *username,const char *password,const int imode=FTPLIB_PASSIVE);
// 注销。
bool logout();
// 获取ftp服务器上文件的时间。
// remotefilename:待获取的文件名。
// 返回值:false-失败;true-成功,获取到的文件时间存放在m_mtime成员变量中。
bool mtime(const char *remotefilename);
// 获取ftp服务器上文件的大小。
// remotefilename:待获取的文件名。
// 返回值:false-失败;true-成功,获取到的文件大小存放在m_size成员变量中。
bool size(const char *remotefilename);
// 改变ftp服务器的当前工作目录。
// remotedir:ftp服务器上的目录名。
// 返回值:true-成功;false-失败。
bool chdir(const char *remotedir);
// 在ftp服务器上创建目录。
// remotedir:ftp服务器上待创建的目录名。
// 返回值:true-成功;false-失败。
bool mkdir(const char *remotedir);
// 删除ftp服务器上的目录。
// remotedir:ftp服务器上待删除的目录名。
// 返回值:true-成功;false-失败。
bool rmdir(const char *remotedir);
// 发送NLST命令列出ftp服务器的目录和文件名。
// remotedir:ftp服务器的目录名。
// listfilename:用于保存从服务器返回的目录和文件名列表。
// 返回值:true-成功;false-失败。
// 注意:如果列出的是ftp服务器当前目录,remotedir用"","*","."都可以,但是,不规范的ftp服务器可能有差别。
bool nlist(const char *remotedir,const char *listfilename);
// 从ftp服务器上获取文件。
// remotefilename:待获取ftp服务器上的文件名。
// localfilename:保存到本地的文件名。
// bCheckMTime:文件传输完成后,是否核对远程文件传输前后的时间,保证文件的完整性。
// 返回值:true-成功;false-失败。
// 注意:文件在传输的过程中,采用临时文件命名的方法,即在localfilename后加".tmp",在传输
// 完成后才正式改为localfilename。
bool get(const char *remotefilename,const char *localfilename,const bool bCheckMTime=true);
// 向ftp服务器发送文件。
// localfilename:本地待发送的文件名。
// remotefilename:发送到ftp服务器上的文件名。
// bCheckSize:文件传输完成后,是否核对本地文件和远程文件的大小,保证文件的完整性。
// 返回值:true-成功;false-失败。
// 注意:文件在传输的过程中,采用临时文件命名的方法,即在remotefilename后加".tmp",在传输
// 完成后才正式改为remotefilename。
bool put(const char *localfilename,const char *remotefilename,const bool bCheckSize=true);
// 删除ftp服务器上的文件。
// remotefilename:待删除的ftp服务器上的文件名。
// 返回值:true-成功;false-失败。
bool ftpdelete(const char *remotefilename);
// 重命名ftp服务器上的文件。
// srcremotefilename:ftp服务器上的原文件名。
// dstremotefilename:ftp服务器上的目标文件名。
// 返回值:true-成功;false-失败。
bool ftprename(const char *srcremotefilename,const char *dstremotefilename);
/* 以下三个方法如果理解不了就算了,可以不启用。 */
// 发送LIST命令列出ftp服务器目录中的文件。
// 参数和返回值与nlist方法相同。
bool dir(const char *remotedir,const char *listfilename);
// 向ftp服务器发送site命令。
// command:命令的内容。
// 返回值:true-成功;false-失败。
bool site(const char *command);
// 获取服务器返回信息的最后一条(return a pointer to the last response received)。
char *response();
};
五、ftp 下载客户端
头文件的获取方式:在freecplus框架里面https://www.freecplus.net/index.html
#include "_public.h"
#include "_ftp.h"
struct st_arg
{
char host[51];
int mode;
char username[31];
char password[31];
char localpath[301];
char remotepath[301];
char matchname[301];
int ptype;
char remotepathbak[301];
char listfilename[301];
char okfilename[301];
int timetvl;
} starg;
Cftp ftp;
CLogFile logfile;
// 本程序的业务流程主函数
bool _ftpgetfiles();
vector<struct st_fileinfo> vlistfile,vlistfile1;
vector<struct st_fileinfo> vokfilename,vokfilename1;
// 把nlist方法获取到的list文件加载到vlistfile容器中
bool LoadListFile();
// 把okfilename文件内容加载到vokfilename容器中
bool LoadOKFileName();
// 把vlistfile容器中的文件与vokfilename容器中文件对比,得到两个容器
// 一、在vlistfile中存在,并已经采集成功的文件vokfilename1
// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1
bool CompVector();
// 把vokfilename1容器中的内容先写入okfilename文件中,覆盖之前的旧okfilename文件
bool WriteToOKFileName();
// 如果ptype==1,把采集成功的文件记录追加到okfilename文件中
bool AppendToOKFileName(struct st_fileinfo *stfileinfo);
void EXIT(int sig);
// 显示程序的帮助
void _help(char *argv[]);
// 把xml解析到参数starg结构中
bool _xmltoarg(char *strxmlbuffer);
int main(int argc,char *argv[])
{
if (argc!=3) { _help(argv); return -1; }
// 关闭全部的信号和输入输出
CloseIOAndSignal();
// 处理程序退出的信号
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if (logfile.Open(argv[1],"a+")==false)
{
printf("打开日志文件失败(%s)。\n",argv[1]); return -1;
}
// 把xml解析到参数starg结构中
if (_xmltoarg(argv[2])==false) return -1;
while (true)
{
if (ftp.login(starg.host,starg.username,starg.password,starg.mode)==false)
{
logfile.Write("ftp.login(%s,%s,%s) failed.\n",starg.host,starg.username,starg.password); sleep(10); continue;
}
// logfile.Write("ftp.login ok.\n");
_ftpgetfiles();
ftp.logout();
sleep(starg.timetvl);
}
return 0;
}
void EXIT(int sig)
{
logfile.Write("程序退出,sig=%d\n\n",sig);
exit(0);
}
// 本程序的业务流程主函数
bool _ftpgetfiles()
{
// 进入服务器文件存放的目录
if (ftp.chdir(starg.remotepath)==false)
{
logfile.Write("ftp.chdir(%s) failed.\n",starg.remotepath); return false;
}
// logfile.Write("chdir ok.\n");
// 列出服务器目录文件
if (ftp.nlist(".",starg.listfilename)==false)
{
logfile.Write("ftp.nlist(%s) failed.\n",starg.remotepath); return false;
}
// logfile.Write("nlist ok.\n");
// 把nlist方法获取到的list文件加载到vlistfile容器中
if (LoadListFile()==false)
{
logfile.Write("LoadListFile() failed.\n"); return false;
}
if (starg.ptype==1)
{
// 加载okfilename文件中的内容到容器vokfilename中
LoadOKFileName();
// 把vlistfile容器中的文件与vokfilename容器中文件对比,得到两个容器
// 一、在vlistfile中存在,并已经采集成功的文件vokfilename1
// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1
CompVector();
// 把vokfilename1容器中的内容先写入okfilename文件中,覆盖之前的旧okfilename文件
WriteToOKFileName();
// 把vlistfile1容器中的内容复制到vlistfile容器中
vlistfile.clear(); vlistfile.swap(vlistfile1);
}
// 从服务器上获取新文件或已改动过后的文件
for (int ii=0;ii<vlistfile.size();ii++)
{
char strremotefilename[301],strlocalfilename[301];
SNPRINTF(strlocalfilename,300,"%s/%s",starg.localpath,vlistfile[ii].filename);
SNPRINTF(strremotefilename,300,"%s/%s",starg.remotepath,vlistfile[ii].filename);
logfile.Write("get %s ...",strremotefilename);
// 获取文件
if (ftp.get(strremotefilename,strlocalfilename,true)==false)
{
logfile.WriteEx("failed.\n"); break;
}
logfile.WriteEx("ok.\n");
// 删除文件
if (starg.ptype==2) ftp.ftpdelete(strremotefilename);
// 转存到备份目录
if (starg.ptype==3)
{
char strremotefilenamebak[301];
SNPRINTF(strremotefilenamebak,300,"%s/%s",starg.remotepathbak,vlistfile[ii].filename);
ftp.ftprename(strremotefilename,strremotefilenamebak);
}
// 如果ptype==1,把采集成功的文件记录追加到okfilename文件中
if (starg.ptype==1) AppendToOKFileName(&vlistfile[ii]);
}
return true;
}
// 把nlist方法获取到的list文件加载到vlistfile容器中
bool LoadListFile()
{
vlistfile.clear();
CFile File;
if (File.Open(starg.listfilename,"r") == false)
{
logfile.Write("File.Open(%s) 失败。\n",starg.listfilename); return false;
}
struct st_fileinfo stfileinfo;
while (true)
{
memset(&stfileinfo,0,sizeof(struct st_fileinfo));
if (File.Fgets(stfileinfo.filename,300,true)==false) break;
if (MatchFileName(stfileinfo.filename,starg.matchname)==false) continue;
if (starg.ptype==1)
{
// 获取对方服务器文件时间
if (ftp.mtime(stfileinfo.filename)==false)
{
logfile.Write("ftp.mtime(%s) failed.\n",stfileinfo.filename); return false;
}
strcpy(stfileinfo.mtime,ftp.m_mtime);
}
vlistfile.push_back(stfileinfo);
// logfile.Write("vlistfile filename=%s,mtime=%s\n",stfileinfo.filename,stfileinfo.mtime);
}
return true;
}
// 把okfilename文件内容加载到vokfilename容器中
bool LoadOKFileName()
{
vokfilename.clear();
CFile File;
// 注意:如果程序是第一次采集,okfilename是不存在的,并不是错误,所以也返回true。
if (File.Open(starg.okfilename,"r") == false) return true;
struct st_fileinfo stfileinfo;
char strbuffer[301];
while (true)
{
memset(&stfileinfo,0,sizeof(struct st_fileinfo));
if (File.Fgets(strbuffer,300,true)==false) break;
GetXMLBuffer(strbuffer,"filename",stfileinfo.filename,300);
GetXMLBuffer(strbuffer,"mtime",stfileinfo.mtime,20);
vokfilename.push_back(stfileinfo);
// logfile.Write("vokfilename filename=%s,mtime=%s\n",stfileinfo.filename,stfileinfo.mtime);
}
return true;
}
// 把vlistfile容器中的文件与vokfilename容器中文件对比,得到两个容器
// 一、在vlistfile中存在,并已经采集成功的文件vokfilename1
// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1
bool CompVector()
{
vokfilename1.clear(); vlistfile1.clear();
for (int ii=0;ii<vlistfile.size();ii++)
{
int jj=0;
for (jj=0;jj<vokfilename.size();jj++)
{
if ( (strcmp(vlistfile[ii].filename,vokfilename[jj].filename)==0) &&
(strcmp(vlistfile[ii].mtime,vokfilename[jj].mtime)==0) )
{
vokfilename1.push_back(vlistfile[ii]); break;
}
}
if (jj==vokfilename.size())
{
vlistfile1.push_back(vlistfile[ii]);
}
}
/*
for (int ii=0;ii<vokfilename1.size();ii++)
{
logfile.Write("vokfilename1 filename=%s,mtime=%s\n",vokfilename1[ii].filename,vokfilename1[ii].mtime);
}
for (int ii=0;ii<vlistfile1.size();ii++)
{
logfile.Write("vlistfile1 filename=%s,mtime=%s\n",vlistfile1[ii].filename,vlistfile1[ii].mtime);
}
*/
return true;
}
// 把vokfilename1容器中的内容先写入okfilename文件中,覆盖之前的旧okfilename文件
bool WriteToOKFileName()
{
CFile File;
// 注意,打开文件不要采用缓冲机制
if (File.Open(starg.okfilename,"w",false) == false)
{
logfile.Write("File.Open(%s) failed.\n",starg.okfilename); return false;
}
for (int ii=0;ii<vokfilename1.size();ii++)
{
File.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",vokfilename1[ii].filename,vokfilename1[ii].mtime);
}
return true;
}
// 如果ptype==1,把采集成功的文件记录追加到okfilename文件中
bool AppendToOKFileName(struct st_fileinfo *stfileinfo)
{
CFile File;
// 注意,打开文件不要采用缓冲机制
if (File.Open(starg.okfilename,"a",false) == false)
{
logfile.Write("File.Open(%s) failed.\n",starg.okfilename); return false;
}
File.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",stfileinfo->filename,stfileinfo->mtime);
return true;
}
// 显示程序的帮助
void _help(char *argv[])
{
printf("\n");
printf("Using:/htidc/public/bin/ftpgetfiles logfilename xmlbuffer\n\n");
printf("Sample:/htidc/public/bin/ftpgetfiles /log/shqx/ftpgetfiles_surfdata.log \"<host>172.16.0.15:21</host><port>21</port><mode>1</mode><username>oracle</username><password>te.st1234TES@T</password><localpath>/data/shqx/ftp/surfdata</localpath><remotepath>/data/shqx/sdata/surfdata</remotepath><matchname>SURF_*.TXT,*.DAT</matchname><ptype>1</ptype><remotepathbak></remotepathbak><listfilename>/data/shqx/ftplist/ftpgetfiles_surfdata.list</listfilename><okfilename>/data/shqx/ftplist/ftpgetfiles_surfdata.xml</okfilename><timetvl>30</timetvl>\"\n\n\n");
printf("本程序是数据中心的公共功能模块,用于把远程FTP服务器的文件采集到本地目录。\n");
printf("logfilename是本程序运行的日志文件。\n");
printf("xmlbuffer为文件传输的参数,如下:\n");
printf("<host>118.89.50.198:21</host> 远程服务器的IP和端口。\n");
printf("<mode>1</mode> 传输模式,1-被动模式,2-主动模式,缺省采用被模式。\n");
printf("<username>wucz</username> 远程服务器FTP的用户名。\n");
printf("<password>test1234TEST</password> 远程服务器FTP的密码。\n");
printf("<localpath>/tmp/ftpget</localpath> 本地文件存放的目录。\n");
printf("<remotepath>/tmp/gzrad</remotepath> 远程服务器存放文件的目录。\n");
printf("<matchname>*.GIF</matchname> 待采集文件匹配的文件名,采用大写匹配,"\
"不匹配的文件不会被采集,本字段尽可能设置精确,不允许用*匹配全部的文件。\n");
printf("<ptype>1</ptype> 文件采集成功后,远程服务器文件的处理方式:1-什么也不做;2-删除;3-备份,如果为3,还要指定备份的目录。\n");
printf("<remotepathbak>/tmp/gzradbak</remotepathbak> 文件采集成功后,服务器文件的备份目录,此参数只有当ptype=3时才有效。\n");
printf("<listfilename>/oracle/qxidc/list/ftpgetfiles_surfdata.list</listfilename> 采集前列出服务器文件名的文件。\n");
printf("<okfilename>/oracle/qxidc/list/ftpgetfiles_surfdata.xml</okfilename> 已采集成功文件名清单,此参数只有当ptype=1时有效。\n");
printf("<timetvl>30</timetvl> 采集时间间隔,单位:秒,建议大于10。\n\n");
}
// 把xml解析到参数starg结构中
bool _xmltoarg(char *strxmlbuffer)
{
memset(&starg,0,sizeof(struct st_arg));
GetXMLBuffer(strxmlbuffer,"host",starg.host);
if (strlen(starg.host)==0) { logfile.Write("host is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"mode",&starg.mode);
if ( (starg.mode!=1) && (starg.mode!=2) ) starg.mode=1;
GetXMLBuffer(strxmlbuffer,"username",starg.username);
if (strlen(starg.username)==0) { logfile.Write("username is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"password",starg.password);
if (strlen(starg.password)==0) { logfile.Write("password is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"localpath",starg.localpath);
if (strlen(starg.localpath)==0) { logfile.Write("localpath is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"remotepath",starg.remotepath);
if (strlen(starg.remotepath)==0) { logfile.Write("remotepath is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"matchname",starg.matchname);
if (strlen(starg.matchname)==0) { logfile.Write("matchname is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"ptype",&starg.ptype);
if ( (starg.ptype!=1) && (starg.ptype!=2) && (starg.ptype!=3) ){ logfile.Write("ptype is error.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"remotepathbak",starg.remotepathbak);
if ((starg.ptype==3) && (strlen(starg.remotepathbak)==0) ) { logfile.Write("remotepathbak is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"listfilename",starg.listfilename);
if (strlen(starg.listfilename)==0) { logfile.Write("listfilename is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"okfilename",starg.okfilename);
if ((starg.ptype==1) && (strlen(starg.okfilename)==0)) { logfile.Write("okfilename is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"timetvl",&starg.timetvl);
if (starg.timetvl==0) { logfile.Write("timetvl is null.\n"); return false; }
return true;
}