1.ftp 数据采集模块


  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;
}


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值