1、使用到的库文件
库文件下载:
https://download.csdn.net/download/weixin_45715405/85228391?spm=1001.2014.3001.5501
2、测试库文件基本功能
#include "_ftp.h"
Cftp ftp;
int main(int argc,char* argv[])
{
// 连接ftp服务器,ip地址,用户名,密码,失败返回false
if(ftp.login("172.16.244.2","oracle","oracle")==false)
{
printf("ftp.login(172.16.244.2) failed\n");
return -1;
}
printf("ftp.login(172.16.244.2) ok.\n");
// 连接成功之后
// 获取ftp服务器aa这个文件的时间,会将时间存储在类的成员变量中m_mtime
if(ftp.mtime("/oracle/ftp/aa")==false)
{
printf("ftp.mtime failed.\n");
return -1;
}
printf("ftp.mtime ok mtime=%s.\n",ftp.m_mtime);
// 获取文件大小
if(ftp.size("/oracle/ftp/aa")==false)
{
printf("ftp size failed.\n");
return -1;
}
printf("ftp.size size:%d.\n",ftp.m_size);
// 列出服务器oracle目录的文件,到/root/bb.list 这个文件中
if(ftp.nlist("/oracle","/root/bb.lis")==false)
{
printf("ftp.nlist failed.\n");
return -1;
}
printf("ftp.nlist ok.\n");
// 将本地aa.bak这个文件上传到服务器上,
// 第三个参数默认是true表示下载 本地和服务器上的文件判断时间是否相等,保证文件的完整性。
if(ftp.get("/oracle/ftp/aa","/root/aa.bak",true)==false)
{
printf("ftp.get() failed.\n");
return -1;
}
printf("ftp.get() ok.\n");
// 上传文件,
// 第三个参数true表示:上传最后一个参数检查判断大小是否相等,保证文件的完整性。
if(ftp.put("/root/cc","/oracle/ftp/cc.bak",true)==false)
{
printf("ftp.put() failed.\n");
return -1;
}
printf("ftp.put() ok.\n");
// 重命名,服务器上的aa文件,修改为aa.abk
if(ftp.ftprename("/oracle/ftp/aa","/oracle/ftp/aa.bak")==false)
{
printf("ftprename faile.\n");
return -1;
}
printf("ftprename ok.\n");
ftp.logout();
return 0;
}
编译程序,根据自己的存放位置,指定头文件和库所在路径
g++ -g -o ftpclient ftpclient.cpp /root/project_2022-4-12/project/public/_ftp.cpp /root/project_2022-4-12/project/public/_public.cpp -I /root/project_2022-4-12/project/public -L /root/project_2022-4-12/project/public/ -lftp -lm -lc
可能会报错,找不到库文件,需要配置
Linux下 静态库和动态库 制作使用
3、FTP协议文件传输系统(文件下载)
#include "_ftp.h"
void EXIT(int sig);
CLogFile logfile;
Cftp ftp;
struct st_arg
{
char host[31]; // 远程服务器的ip和端口
int mode; // 传输某事 1-被动某事,2-主动模式,缺省采用被动模式
char username[31]; // 远程服务器ftp的用户名
char password[31]; // 远程服务器ftp的密码
char remotepath[301]; // 远程服务器存放文件的目录
char localpath[301]; // 本地文件存放的目录
char matchname[301]; // 待下载文件匹配的规则
char listfilename[301]; // 下载前列出服务器文件名的文件
int ptype; // 文件下载成功后,远程服务器文件的处理方式:1-什么都不做,2-删除,3-备份
char remotepathbak[301]; //文件下载成功后,服务器文件的备份目录
char okfilename[301]; // 已下载成功文件名清单
bool checkmtime; // 是否需要检查服务端文件的时间,true需要,false不需要,缺省是false
int timeout; // 进程心跳的超时时间
char pname[51]; // 进程名,建议用ftpgetfiles_后缀的方式
}starg;
struct st_fileinfo
{
char filename[301]; // 文件名
char mtime[21]; // 文件时间
};
// "<host>172.16.244.2</host><mode>1</mode><username>oracle</username><password>oracle</password><localpath>/tmp/bak</localpath><remotepath>/tmp/surfdata</remotepath><matchname>SURF_ZH*.XML,SURF_ZH*.CSV</matchname><listfilename>/log/ftplist/ftpget.list</listfilename><ptype>1</ptype><remotepathbak>/tmp/surbak</remotepathbak><okfilename>/tmp/ftplist/surfdata.xml</okfilename><checkmtime>true</checkmtime><timeout>80</timeout><pname>ftpgetfiles_surfdata</pname>"
bool _xmltoarg(char* strxmlbuffer); // 把xml解析道参数starg结构中
bool _ftpgetfiles(); // 下载文件函数
bool LoadListFile(); // 把ftp.nlist()获取文件名加载到容器中
// 加载okfilename文件中的内容到容器vlistfile1中
bool LoadOKFile();
// 比较vlistfile2和vlistfile1,得到vlistfile3和vlistfile4
bool CompVector();
// 把容器vlistfile3中的内容写入okfilename文件,覆盖之前的旧okfilename文件
bool WriteToOKFile();
bool AppendToOKFile(struct st_fileinfo* strfilename);
vector<struct st_fileinfo> vlistfile1; // 已下载的文件,从okfilename中加载
vector<struct st_fileinfo> vlistfile2; // 存放下载前列出服务器文件名的窗口
vector<struct st_fileinfo> vlistfile3; // 本次不需要下载的文件容器
vector<struct st_fileinfo> vlistfile4; // 本次需要下载的文件容器
// 进程心跳
CPActive PActive;
int main(int argc,char* argv[])
{
if(argc<3)
{
printf("param not enough.\n");
return -1;
}
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
if(logfile.Open(argv[1],"a+")==false)
{
printf("logfile.open(%s) failed.\n",argv[1]);
return -1;
}
if(_xmltoarg(argv[2]) == false) return -1;
PActive.AddPInfo(starg.timeout,starg.pname); // 把进程的心跳信息写入共享内存
// 登录ftp服务器
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);
return -1;
}
logfile.Write("ftp.login(%s,%s,%s) ok.\n",starg.host,starg.username,starg.password);
if(_ftpgetfiles()==false)
{
logfile.Write("_ftpgetfiles() failed.\n");
return -1;
}
ftp.logout();
return 0;
}
// 加载okfilename文件中的内容到容器vlistfile1中
bool LoadOKFile()
{
vlistfile1.clear();
CFile file;
// 注意:如果程序是第一次下载,okfilename是不存在的,并不是错误,所以返回true
if(file.Open(starg.okfilename,"r")==false) return true;
struct st_fileinfo stfileinfo;
char strbuffer[501];
while(true)
{
memset(&stfileinfo,0,sizeof(struct st_fileinfo));
memset(strbuffer,0,sizeof(strbuffer));
if(file.Fgets(strbuffer,300,true)==false) break;
GetXMLBuffer(strbuffer,"filename",stfileinfo.filename);
GetXMLBuffer(strbuffer,"mtime",stfileinfo.mtime);
vlistfile1.push_back(stfileinfo);
}
return true;
}
// 比较vlistfile2和vlistfile1,得到vlistfile3和vlistfile4
bool CompVector()
{
vlistfile3.clear();
vlistfile4.clear();
// 把容器2中文件每个文件在容器1中找一遍,找到了 放入容器3中,没找到放入容器4中
int i,j;
for(i=0;i<vlistfile2.size();i++)
{
for(j=0;j<vlistfile1.size();j++)
{
if((strcmp(vlistfile2[i].filename,vlistfile1[j].filename)==0)&&
(strcmp(vlistfile2[i].mtime,vlistfile1[j].mtime)==0))
{
vlistfile3.push_back(vlistfile2[i]);
break;
}
}
if(j==vlistfile1.size()) vlistfile4.push_back(vlistfile2[i]);
}
}
// 把容器vlistfile3中的内容写入okfilename文件,覆盖之前的旧okfilename文件
bool WriteToOKFile()
{
CFile file;
if(file.Open(starg.okfilename,"w")==false)
{
logfile.Write("file.open(%s) failed.\n",starg.okfilename); return false;
}
for(int i=0;i<vlistfile3.size();i++)
file.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",vlistfile3[i].filename,vlistfile3[i].mtime);
}
bool AppendToOKFile(struct st_fileinfo* strfilename)
{
CFile file;
if(file.Open(starg.okfilename,"a")==false)
{
logfile.Write("file.open(%s) failed.\n",starg.okfilename);
return false;
}
file.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",strfilename->filename,strfilename->mtime);
return true;
}
bool LoadListFile()
{
vlistfile2.clear();
CFile File;
if(File.Open(starg.listfilename,"r")==false)
{
logfile.Write("File.Open(%s) failed.\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(MatchStr(stfileinfo.filename,starg.matchname)==false) continue;
if((starg.ptype==1)&&(starg.checkmtime==true))
{
// 获取ftp服务端文件时间
if(ftp.mtime(stfileinfo.filename)==false)
{
logfile.Write("ftp.mtime(%s) failed .\n",stfileinfo.filename);
return false;
}
strcpy(stfileinfo.mtime,ftp.m_mtime);
}
vlistfile2.push_back(stfileinfo);
}
return true;
}
bool _ftpgetfiles()
{
if(ftp.chdir(starg.remotepath)==false)
{
logfile.Write("ftp.chdir(%s) failed.\n",starg.remotepath);
return false;
}
// 调用nlist()方法列出服务器目录的文件
if(ftp.nlist(".",starg.listfilename)==false)
{
logfile.Write("ftp.nlist(%s) failed.\n",starg.listfilename);
return false;
}
PActive.UptATime();
if(LoadListFile()==false)
{
logfile.Write("LoadListFile() failed.\n");
return false;
}
PActive.UptATime();
if(starg.ptype == 1)
{
// 加载okfilename文件中的内容到容器vlistfile1中
LoadOKFile();
// 比较vlistfile2和vlistfile1,得到vlistfile3和vlistfile4
CompVector();
// 把容器vlistfile3中的内容写入okfilename文件,覆盖之前的旧okfilename文件
WriteToOKFile();
// 把vlistfile4中的内容复制到vlistfile2中
vlistfile2.clear();
vlistfile2.swap(vlistfile4);
}
PActive.UptATime(); // 更新进程心跳信息
// 遍历容器vlistfile
char strremotefilename[301],strlocalfilename[301];
for(int i=0;i<vlistfile2.size();i++)
{
SNPRINTF(strremotefilename,sizeof(strremotefilename),300,"%s/%s",starg.remotepath,vlistfile2[i].filename);
SNPRINTF(strlocalfilename,sizeof(strlocalfilename),300,"%s/%s",starg.localpath,vlistfile2[i].filename);
// 调用ftp.get()方法从服务器下载
logfile.Write("get %s ...",strremotefilename);
if(ftp.get(strremotefilename,strlocalfilename)==false)
{
logfile.WriteEx("failed.\n");
return false;
}
logfile.WriteEx("ok.\n");
PActive.UptATime(); // 更新进程心跳信息
// 本地已经下载,就不下载,本地没有就下载
// 算法,通过四个容器实现,
// v1 已下载,v2加载服务器目录表,v3已下载(第一二个容器都有的,放入),v4代下载(1没有2的放入)
// 如果2,下载完之后,删除服务器上的文件
if(starg.ptype==1)
{
// 把成功下载后的文件追加到okfile文件中
AppendToOKFile(&vlistfile2[i]);
}
if(starg.ptype == 2)
{
if(ftp.ftpdelete(strremotefilename)==false)
{
logfile.Write("ftp.ftpdelete(%s) failed.\n",strremotefilename);
return false;
}
}
// 如果是3,下载完之后,需要备份一下
if(starg.ptype == 3)
{
char strremotefilenamebak[301];
SNPRINTF(strremotefilenamebak,sizeof(strremotefilenamebak),300,"%s/%s",starg.remotepathbak,vlistfile2[i].filename);
//printf("%s--%s\n",strremotefilename,strremotefilenamebak);
if(ftp.ftprename(strremotefilename,strremotefilenamebak)==false)
{
logfile.Write("ftp.ftorename(%s,%s) failed.\n",strremotefilename,strremotefilenamebak);
return false;
}
}
}
return true;
}
bool _xmltoarg(char* strxmlbuffer)
{
// 解析xml,得到程序运行的参数
memset(&starg,0,sizeof(struct st_arg));
GetXMLBuffer(strxmlbuffer,"host",starg.host,30); // 远程服务器ip和端口
if(strlen(starg.host) == 0){logfile.Write("host is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"mode",&starg.mode); // 传输模式, 1-被动,2-主动
if(starg.mode!=2) starg.mode = 1;
GetXMLBuffer(strxmlbuffer,"username",starg.username,30); // 远程服务器ftp用户名
if(strlen(starg.username) == 0){logfile.Write("username is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"password",starg.password,30); // 远程服务器ftp密码
if(strlen(starg.password) == 0){logfile.Write("password is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"remotepath",starg.remotepath,30); // 远程服务器存放文件的目录
if(strlen(starg.remotepath)==0){logfile.Write("remotepath is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"localpath",starg.localpath,30); // 本地文件存放的目录
if(strlen(starg.localpath)==0){logfile.Write("localpath is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"matchname",starg.matchname,100); // 待下载文件匹配的规则
if(strlen(starg.matchname)==0){logfile.Write("matchname is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"listfilename",starg.listfilename,300); // 本地文件存放的目录
if(strlen(starg.listfilename)==0){logfile.Write("listfilename is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"ptype",&starg.ptype); // 文件下载后服务器文件处理
if((starg.ptype!=1) && (starg.ptype!=2) && (starg.ptype!=3)){logfile.Write("ptype si error.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"remotepathbak",starg.remotepathbak,300); // 下载前列列出服务器文件名
if((starg.ptype==3) && (strlen(starg.remotepathbak)==0))
{logfile.Write("remotepathbak is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"okfilename",starg.okfilename,300); // 下载前列列出服务器文件名
if((starg.ptype==1) && (strlen(starg.okfilename)==0))
{logfile.Write("okfilename is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"checkmtime",&starg.checkmtime);
GetXMLBuffer(strxmlbuffer,"timeout",&starg.timeout);
if(starg.timeout==0){logfile.Write("timeout is null.\n"); return false;}
GetXMLBuffer(strxmlbuffer,"pname",starg.pname,50);
if(strlen(starg.pname)==0){logfile.Write("pname is null.\n"); return false;}
return true;
}
void EXIT(int sig)
{
printf("程序退出,sig=%d\n",sig);
exit(0);
}
程序需要十几个启动参数,所以我们采用xml格式来构建这个参数,不容易出错
程序主要流程:
1、解析xml格式的启动参数,并把它存储到struct st_arg结构体中,遇到写入日子系统中
2、连接到ftp服务器
3、调用自定义函数_ftpgetfiles()
这个函数主要功能是:
3.1、切换到指定目录(启动参数给的)
3.2、使用ftp类中nlist函数,将目录中的文件写入到指定文件中(启动参数给的)
3.3、将存放服务器目录的文件内容用结构体st_fileinfo存储,并存放到容器中(由LoadListFile函数完成)
3.4、遍历存储内容的容器(st_fileinfo),进行文件下载
大体流程就是这么多,但是,下载文件会分为三种情况:
1、下载完文件之后对服务端文件进行备份(备份目录,启动参数会准备)。
2、下载完文件之后删除服务端文件。
3、因为每次启动都会将服务端文件全部下载一遍,但是我们的需求是只下载,新添加和修改过后的文件,没有修改的没必要下载。
第三种情况具体实现:
1、我们需要准备四个容器
容器1:存放已经下载的文件
容器2:存放列举出当前服务器的文件
容器3:存放本次不需要下载的文件
容器4:存放本次需要下载的文件
具体流程:
1、容器1里面的内容,是通过读取,已下载的文件(这个文件是启动参数指定的)
2、比较容器1,和容器2,要是容器2里面的内容容器1也有就放到容器3,如果没有就放到容器4
3、把容器3的内容覆盖到已下载的文件(这个文件是启动参数指定的)
4、下载容器4中的内容
编译运行
编写makefile文件
all:execl checkproc gzipfiles deletefiles ftpgetfiles
PUBINCL = -I/root/project_2022-4-12/project/public
PUBCPP = /root/project_2022-4-12/project/public/_public.cpp
FTPCPP = /root/project_2022-4-12/project/public/_ftp.cpp
FTPLIB = -L /root/project_2022-4-12/project/public/ -lftp
CFLAGS = -g
ftpgetfiles:ftpgetfiles.cpp
g++ $(CFLAGS) -o ftpgetfiles ftpgetfiles.cpp $(PUBINCL) $(PUBCPP) $(FTPCPP) $(FTPLIB) -lm -lc
clean:
rm -rf ftpgetfiles
4、FTP协议文件传输系统(文件上传)
和文件下载功能一样,只是将下载的函数换成了上传
#include "_public.h"
#include "_ftp.h"
// 程序运行参数的结构体。
struct st_arg
{
char host[31]; // 远程服务端的IP和端口。
int mode; // 传输模式,1-被动模式,2-主动模式,缺省采用被动模式。
char username[31]; // 远程服务端ftp的用户名。
char password[31]; // 远程服务端ftp的密码。
char remotepath[301]; // 远程服务端存放文件的目录。
char localpath[301]; // 本地文件存放的目录。
char matchname[101]; // 待上传文件匹配的规则。
int ptype; // 上传后客户端文件的处理方式:1-什么也不做;2-删除;3-备份。
char localpathbak[301]; // 上传后客户端文件的备份目录。
char okfilename[301]; // 已上传成功文件名清单。
int timeout; // 进程心跳的超时时间。
char pname[51]; // 进程名,建议用"ftpputfiles_后缀"的方式。
} starg;
// 文件信息的结构体。
struct st_fileinfo
{
char filename[301]; // 文件名。
char mtime[21]; // 文件时间。
};
vector<struct st_fileinfo> vlistfile1; // 已上传成功文件名的容器,从okfilename中加载。
vector<struct st_fileinfo> vlistfile2; // 上传前列出客户端文件名的容器,从nlist文件中加载。
vector<struct st_fileinfo> vlistfile3; // 本次不需要上传的文件的容器。
vector<struct st_fileinfo> vlistfile4; // 本次需要上传的文件的容器。
// 加载okfilename文件中的内容到容器vlistfile1中。
bool LoadOKFile();
// 比较vlistfile2和vlistfile1,得到vlistfile3和vlistfile4。
bool CompVector();
// 把容器vlistfile3中的内容写入okfilename文件,覆盖之前的旧okfilename文件。
bool WriteToOKFile();
// 如果ptype==1,把上传成功的文件记录追加到okfilename文件中。
bool AppendToOKFile(struct st_fileinfo *stfileinfo);
// 把localpath目录下的文件加载到vlistfile2容器中。
bool LoadLocalFile();
CLogFile logfile;
Cftp ftp;
// 程序退出和信号2、15的处理函数。
void EXIT(int sig);
void _help();
// 把xml解析到参数starg结构中。
bool _xmltoarg(char *strxmlbuffer);
// 上传文件功能的主函数。
bool _ftpputfiles();
CPActive PActive; // 进程心跳。
int main(int argc,char *argv[])
{
if (argc!=3) { _help(); return -1; }
// 关闭全部的信号和输入输出。
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程。
// 但请不要用 "kill -9 +进程号" 强行终止。
CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
// 打开日志文件。
if (logfile.Open(argv[1],"a+")==false)
{
printf("打开日志文件失败(%s)。\n",argv[1]); return -1;
}
// 解析xml,得到程序运行的参数。
if (_xmltoarg(argv[2])==false) return -1;
PActive.AddPInfo(starg.timeout,starg.pname); // 把进程的心跳信息写入共享内存。
// 登录ftp服务端。
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); return -1;
}
// logfile.Write("ftp.login ok.\n"); // 正式运行后,可以注释这行代码。
_ftpputfiles();
ftp.logout();
return 0;
}
// 上传文件功能的主函数。
bool _ftpputfiles()
{
// 把localpath目录下的文件加载到vlistfile2容器中。
if (LoadLocalFile()==false)
{
logfile.Write("LoadLocalFile() failed.\n"); return false;
}
PActive.UptATime(); // 更新进程的心跳。
if (starg.ptype==1)
{
// 加载okfilename文件中的内容到容器vlistfile1中。
LoadOKFile();
// 比较vlistfile2和vlistfile1,得到vlistfile3和vlistfile4。
CompVector();
// 把容器vlistfile3中的内容写入okfilename文件,覆盖之前的旧okfilename文件。
WriteToOKFile();
// 把vlistfile4中的内容复制到vlistfile2中。
vlistfile2.clear(); vlistfile2.swap(vlistfile4);
}
PActive.UptATime(); // 更新进程的心跳。
char strremotefilename[301],strlocalfilename[301];
// 遍历容器vlistfile2。
for (int ii=0;ii<vlistfile2.size();ii++)
{
SNPRINTF(strremotefilename,sizeof(strremotefilename),300,"%s/%s",starg.remotepath,vlistfile2[ii].filename);
SNPRINTF(strlocalfilename,sizeof(strlocalfilename),300,"%s/%s",starg.localpath,vlistfile2[ii].filename);
logfile.Write("put %s ...",strlocalfilename);
// 调用ftp.put()方法把文件上传到服务端,第三个参数填true的目的是确保文件上传成功,对方不可抵赖。
if (ftp.put(strlocalfilename,strremotefilename,true)==false)
{
logfile.WriteEx("failed.\n"); return false;
}
logfile.WriteEx("ok.\n");
PActive.UptATime(); // 更新进程的心跳。
// 如果ptype==1,把上传成功的文件记录追加到okfilename文件中。
if (starg.ptype==1) AppendToOKFile(&vlistfile2[ii]);
// 删除文件。
if (starg.ptype==2)
{
if (REMOVE(strlocalfilename)==false)
{
logfile.Write("REMOVE(%s) failed.\n",strlocalfilename); return false;
}
}
// 转存到备份目录。
if (starg.ptype==3)
{
char strlocalfilenamebak[301];
SNPRINTF(strlocalfilenamebak,sizeof(strlocalfilenamebak),300,"%s/%s",starg.localpathbak,vlistfile2[ii].filename);
if (RENAME(strlocalfilename,strlocalfilenamebak)==false)
{
logfile.Write("RENAME(%s,%s) failed.\n",strlocalfilename,strlocalfilenamebak); return false;
}
}
}
return true;
}
void EXIT(int sig)
{
printf("程序退出,sig=%d\n\n",sig);
exit(0);
}
void _help()
{
printf("\n");
printf("Using:/project/tools1/bin/ftpputfiles logfilename xmlbuffer\n\n");
printf("Sample:/project/tools1/bin/procctl 30 /project/tools1/bin/ftpputfiles /log/idc/ftpputfiles_surfdata.log \"<host>127.0.0.1:21</host><mode>1</mode><username>wucz</username><password>wuczpwd</password><localpath>/tmp/idc/surfdata</localpath><remotepath>/tmp/ftpputest</remotepath><matchname>SURF_ZH*.JSON</matchname><ptype>1</ptype><localpathbak>/tmp/idc/surfdatabak</localpathbak><okfilename>/idcdata/ftplist/ftpputfiles_surfdata.xml</okfilename><timeout>80</timeout><pname>ftpputfiles_surfdata</pname>\"\n\n\n");
printf("本程序是通用的功能模块,用于把本地目录中的文件上传到远程的ftp服务器。\n");
printf("logfilename是本程序运行的日志文件。\n");
printf("xmlbuffer为文件上传的参数,如下:\n");
printf("<host>127.0.0.1:21</host> 远程服务端的IP和端口。\n");
printf("<mode>1</mode> 传输模式,1-被动模式,2-主动模式,缺省采用被动模式。\n");
printf("<username>wucz</username> 远程服务端ftp的用户名。\n");
printf("<password>wuczpwd</password> 远程服务端ftp的密码。\n");
printf("<remotepath>/tmp/ftpputest</remotepath> 远程服务端存放文件的目录。\n");
printf("<localpath>/tmp/idc/surfdata</localpath> 本地文件存放的目录。\n");
printf("<matchname>SURF_ZH*.JSON</matchname> 待上传文件匹配的规则。"\
"不匹配的文件不会被上传,本字段尽可能设置精确,不建议用*匹配全部的文件。\n");
printf("<ptype>1</ptype> 文件上传成功后,本地文件的处理方式:1-什么也不做;2-删除;3-备份,如果为3,还要指定备份的目录。\n");
printf("<localpathbak>/tmp/idc/surfdatabak</localpathbak> 文件上传成功后,本地文件的备份目录,此参数只有当ptype=3时才有效。\n");
printf("<okfilename>/idcdata/ftplist/ftpputfiles_surfdata.xml</okfilename> 已上传成功文件名清单,此参数只有当ptype=1时才有效。\n");
printf("<timeout>80</timeout> 上传文件超时时间,单位:秒,视文件大小和网络带宽而定。\n");
printf("<pname>ftpputfiles_surfdata</pname> 进程名,尽可能采用易懂的、与其它进程不同的名称,方便故障排查。\n\n\n");
}
// 把xml解析到参数starg结构中。
bool _xmltoarg(char *strxmlbuffer)
{
memset(&starg,0,sizeof(struct st_arg));
GetXMLBuffer(strxmlbuffer,"host",starg.host,30); // 远程服务端的IP和端口。
if (strlen(starg.host)==0)
{ logfile.Write("host is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"mode",&starg.mode); // 传输模式,1-被动模式,2-主动模式,缺省采用被动模式。
if (starg.mode!=2) starg.mode=1;
GetXMLBuffer(strxmlbuffer,"username",starg.username,30); // 远程服务端ftp的用户名。
if (strlen(starg.username)==0)
{ logfile.Write("username is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"password",starg.password,30); // 远程服务端ftp的密码。
if (strlen(starg.password)==0)
{ logfile.Write("password is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"remotepath",starg.remotepath,300); // 远程服务端存放文件的目录。
if (strlen(starg.remotepath)==0)
{ logfile.Write("remotepath is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"localpath",starg.localpath,300); // 本地文件存放的目录。
if (strlen(starg.localpath)==0)
{ logfile.Write("localpath is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"matchname",starg.matchname,100); // 待上传文件匹配的规则。
if (strlen(starg.matchname)==0)
{ logfile.Write("matchname is null.\n"); return false; }
// 上传后客户端文件的处理方式:1-什么也不做;2-删除;3-备份。
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,"localpathbak",starg.localpathbak,300); // 上传后客户端文件的备份目录。
if ( (starg.ptype==3) && (strlen(starg.localpathbak)==0) )
{ logfile.Write("localpathbak is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"okfilename",starg.okfilename,300); // 已上传成功文件名清单。
if ( (starg.ptype==1) && (strlen(starg.okfilename)==0) )
{ logfile.Write("okfilename is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"timeout",&starg.timeout); // 进程心跳的超时时间。
if (starg.timeout==0) { logfile.Write("timeout is null.\n"); return false; }
GetXMLBuffer(strxmlbuffer,"pname",starg.pname,50); // 进程名。
if (strlen(starg.pname)==0) { logfile.Write("pname is null.\n"); return false; }
return true;
}
// 把localpath目录下的文件加载到vlistfile2容器中。
bool LoadLocalFile()
{
vlistfile2.clear();
CDir Dir;
Dir.SetDateFMT("yyyymmddhh24miss");
// 不包括子目录。
// 注意,如果本地目录下的总文件数超过10000,增量上传文件功能将有问题。
// 建议用deletefiles程序及时清理本地目录中的历史文件。
if (Dir.OpenDir(starg.localpath,starg.matchname)==false)
{
logfile.Write("Dir.OpenDir(%s) 失败。\n",starg.localpath); return false;
}
struct st_fileinfo stfileinfo;
while (true)
{
memset(&stfileinfo,0,sizeof(struct st_fileinfo));
if (Dir.ReadDir()==false) break;
strcpy(stfileinfo.filename,Dir.m_FileName); // 文件名,不包括目录名。
strcpy(stfileinfo.mtime,Dir.m_ModifyTime); // 文件时间。
vlistfile2.push_back(stfileinfo);
}
return true;
}
// 加载okfilename文件中的内容到容器vlistfile1中。
bool LoadOKFile()
{
vlistfile1.clear();
CFile File;
// 注意:如果程序是第一次上传,okfilename是不存在的,并不是错误,所以也返回true。
if ( (File.Open(starg.okfilename,"r"))==false ) return true;
char strbuffer[501];
struct st_fileinfo stfileinfo;
while (true)
{
memset(&stfileinfo,0,sizeof(struct st_fileinfo));
if (File.Fgets(strbuffer,300,true)==false) break;
GetXMLBuffer(strbuffer,"filename",stfileinfo.filename);
GetXMLBuffer(strbuffer,"mtime",stfileinfo.mtime);
vlistfile1.push_back(stfileinfo);
}
return true;
}
// 比较vlistfile2和vlistfile1,得到vlistfile3和vlistfile4。
bool CompVector()
{
vlistfile3.clear(); vlistfile4.clear();
int ii,jj;
// 遍历vlistfile2。
for (ii=0;ii<vlistfile2.size();ii++)
{
// 在vlistfile1中查找vlistfile2[ii]的记录。
for (jj=0;jj<vlistfile1.size();jj++)
{
// 如果找到了,把记录放入vlistfile3。
if ( (strcmp(vlistfile2[ii].filename,vlistfile1[jj].filename)==0) &&
(strcmp(vlistfile2[ii].mtime,vlistfile1[jj].mtime)==0) )
{
vlistfile3.push_back(vlistfile2[ii]); break;
}
}
// 如果没有找到,把记录放入vlistfile4。
if (jj==vlistfile1.size()) vlistfile4.push_back(vlistfile2[ii]);
}
return true;
}
// 把容器vlistfile3中的内容写入okfilename文件,覆盖之前的旧okfilename文件。
bool WriteToOKFile()
{
CFile File;
if (File.Open(starg.okfilename,"w")==false)
{
logfile.Write("File.Open(%s) failed.\n",starg.okfilename); return false;
}
for (int ii=0;ii<vlistfile3.size();ii++)
File.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",vlistfile3[ii].filename,vlistfile3[ii].mtime);
return true;
}
// 如果ptype==1,把上传成功的文件记录追加到okfilename文件中。
bool AppendToOKFile(struct st_fileinfo *stfileinfo)
{
CFile File;
if (File.Open(starg.okfilename,"a")==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;
}