This commit is contained in:
eviwbh 2024-09-12 18:17:01 +08:00
commit c7eda8b25d
16 changed files with 5618 additions and 0 deletions

62
EL/README.md Normal file
View File

@ -0,0 +1,62 @@
# 面试专用贴
面试官你好,该框架非本人开发,仅作学习整理,但是常用,且熟悉函数源码、原理和流程。
# EL框架
EL框架整理了多个大佬的开源框架可以提升开发效率源码方便学习参考
## 使用
拉下来make即可
使用动态链接库时需要把动态链接库地址添加到在linux环境变量中。
## 主要类
```
微秒计时器
class ctimer
目录文件列表
class cdir
写文件的类
class cofile
读取文件的类
class cifile
自旋锁
class spinlock_mutex
日志文件
class clogfile
进程心跳
class cpactive
信号量
class csemp
循环队列
template <class TT, int MaxLength>
class squeue
socket通讯的服务端类
class ctcpserver
socket通讯的客户端类
class ctcpclient
```

58
EL/_cmel.h Normal file
View File

@ -0,0 +1,58 @@
/****************************************************************************************
* _cmel.h
*****************************************************************************************/
#ifndef _cmel_H
#define _cmel_H 1
#include <stdio.h>
#include <utime.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <time.h>
#include <math.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <netdb.h>
#include <locale.h>
#include <dirent.h>
#include <termios.h>
#include <pthread.h>
#include <poll.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/timerfd.h>
#include <sys/signalfd.h>
#include <atomic>
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <map>
#include <unordered_map>
#include <forward_list>
#include <vector>
#include <deque>
#include <memory>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <algorithm>
#include <thread>
#endif

1828
EL/_el.cpp Normal file

File diff suppressed because it is too large Load Diff

824
EL/_el.h Normal file
View File

@ -0,0 +1,824 @@
/****************************************************************************************
* _el.h
****************************************************************************************/
#ifndef __EL_H
#define __EL_H 1
#include "_cmel.h"
using namespace std;
namespace eviwbh
{
//////////////////////////////////////////////////////////////////////////
// 字符串操作
// 删除字符串左边指定的字符。
// str待处理的字符串。
// cc需要删除的字符缺省删除空格。
char* deletelchr(char* str, const int cc=' ');
string& deletelchr(string &str, const int cc=' ');
// 删除字符串右边指定的字符。
// str待处理的字符串。
// cc需要删除的字符缺省删除空格。
char* deleterchr(char *str,const int cc=' ');
string& deleterchr(string &str,const int cc=' ');
// 删除字符串左右两边指定的字符。
// str待处理的字符串。
// chr需要删除的字符缺省删除空格。
char* deletelrchr(char *str,const int cc=' ');
string& deletelrchr(string &str,const int cc=' ');
// 把字符串中的小写字母转换成大写,忽略不是字母的字符。
// str待转换的字符串。
char* toupper(char *str);
string& toupper(string &str);
// 把字符串中的大写字母转换成小写,忽略不是字母的字符。
// str待转换的字符串。
char* tolower(char *str);
string& tolower(string &str);
// 字符串替换函数。
// 在字符串str中如果存在字符串str1就替换为字符串str2。
// str待处理的字符串。
// str1旧的内容。
// str2新的内容。
// bloop是否循环执行替换。
// 注意:
// 1、如果str2比str1要长替换后str会变长所以必须保证str有足够的空间否则内存会溢出C++风格字符串不存在这个问题)。
// 2、如果str2中包含了str1的内容且bloop为true这种做法存在逻辑错误replacestr将什么也不做。
// 3、如果str2为空表示删除str中str1的内容。
bool replacestr(char *str ,const string &str1,const string &str2,const bool bloop=false);
bool replacestr(string &str,const string &str1,const string &str2,const bool bloop=false);
// 从一个字符串中提取出数字、符号和小数点,存放到另一个字符串中。
// src原字符串。
// dest目标字符串。
// bsigned是否提取符号+和-true-包括false-不包括。
// bdot是否提取小数点.true-包括false-不包括。
// 注意src和dest可以是同一个变量。
char* picknumber(const string &src,char *dest,const bool bsigned=false,const bool bdot=false);
string& picknumber(const string &src,string &dest,const bool bsigned=false,const bool bdot=false);
string picknumber(const string &src,const bool bsigned=false,const bool bdot=false);
// 正则表达式,判断一个字符串是否匹配另一个字符串。
// str需要判断的字符串是精确表示的如文件名"_public.cpp"。
// rules匹配规则的表达式用星号"*"代表任意字符,多个表达式之间用半角的逗号分隔,如"*.h,*.cpp"。
// 注意1str参数不需要支持"*"rules参数支持"*"2函数在判断str是否匹配rules的时候会忽略字母的大小写。
bool matchstr(const string &str,const string &rules);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// ccmdstr类用于拆分有分隔符的字符串。
// 字符串的格式为字段内容1+分隔符+字段内容2+分隔符+字段内容3+分隔符+...+字段内容n。
// 例如:"messi,10,striker,30,1.72,68.5,Barcelona",这是足球运动员梅西的资料。
// 包括:姓名、球衣号码、场上位置、年龄、身高、体重和效力的俱乐部,字段之间用半角的逗号分隔。
class ccmdstr
{
private:
vector<string> m_cmdstr; // 存放拆分后的字段内容。
ccmdstr(const ccmdstr &) = delete; // 禁用拷贝构造函数。
ccmdstr &operator=(const ccmdstr &) = delete; // 禁用赋值函数。
public:
ccmdstr() { } // 构造函数。
ccmdstr(const string &buffer,const string &sepstr,const bool bdelspace=false);
const string& operator[](int ii) const // 重载[]运算符可以像访问数组一样访问m_cmdstr成员。
{
return m_cmdstr[ii];
}
// 把字符串拆分到m_cmdstr容器中。
// buffer待拆分的字符串。
// sepstrbuffer中采用的分隔符注意sepstr参数的数据类型不是字符是字符串如","、" "、"|"、"~!~"。
// bdelspace拆分后是否删除字段内容前后的空格true-删除false-不删除,缺省不删除。
void splittocmd(const string &buffer,const string &sepstr,const bool bdelspace=false);
// 获取拆分后字段的个数即m_cmdstr容器的大小。
int size() const { return m_cmdstr.size(); }
int cmdcount() const { return m_cmdstr.size(); } // 兼容以前的项目。
// 从m_cmdstr容器获取字段内容。
// ii字段的顺序号类似数组的下标从0开始。
// value传入变量的地址用于存放字段内容。
// 返回值true-成功如果ii的取值超出了m_cmdstr容器的大小返回失败。
bool getvalue(const int ii,string &value,const int ilen=0) const; // C++风格字符串。视频中没有第三个参数,加上第三个参数更好。
bool getvalue(const int ii,char *value,const int ilen=0) const; // C风格字符串ilen缺省值为0-全部长度。
bool getvalue(const int ii,int &value) const; // int整数。
bool getvalue(const int ii,unsigned int &value) const; // unsigned int整数。
bool getvalue(const int ii,long &value) const; // long整数。
bool getvalue(const int ii,unsigned long &value) const; // unsigned long整数。
bool getvalue(const int ii,double &value) const; // 双精度double。
bool getvalue(const int ii,float &value) const; // 单精度float。
bool getvalue(const int ii,bool &value) const; // bool型。
~ccmdstr(); // 析构函数。
};
// 重载<<运算符输出ccmdstr::m_cmdstr中的内容方便调试。
ostream& operator<<(ostream& out, const ccmdstr& cc);
///////////////////////////////////// /////////////////////////////////////
///////////////////////////////////// /////////////////////////////////////
// 解析xml格式字符串的函数族。
// xml格式的字符串的内容如下
// <filename>/tmp/_public.h</filename><mtime>2020-01-01 12:20:35</mtime><size>18348</size>
// <filename>/tmp/_public.cpp</filename><mtime>2020-01-01 10:10:15</mtime><size>50945</size>
// xmlbuffer待解析的xml格式字符串。
// fieldname字段的标签名。
// value传入变量的地址用于存放字段内容支持bool、int、insigned int、long、
// unsigned long、double和char[]。
// 注意当value参数的数据类型为char []时必须保证value数组的内存足够否则可能发生内存溢出的问题
// 也可以用ilen参数限定获取字段内容的长度ilen的缺省值为0表示不限长度。
// 返回值true-成功如果fieldname参数指定的标签名不存在返回失败。
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,string &value,const int ilen=0); // 视频中没有第三个参数,加上第三个参数更好。
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,char *value,const int ilen=0);
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,bool &value);
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,int &value);
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,unsigned int &value);
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,long &value);
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,unsigned long &value);
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,double &value);
bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,float &value);
///////////////////////////////////// /////////////////////////////////////
// C++格式化输出函数模板。
template< typename... Args >
bool sformat(string &str,const char* fmt, Args... args )
{
int len = snprintf( nullptr, 0, fmt, args... ); // 得到格式化输出后字符串的总长度。
if (len < 0) return false; // 如果调用snprintf失败返回-1。
if (len == 0) { str.clear(); return true; } // 如果调用snprintf返回0表示格式化输出的内容为空。
str.resize(len); // 为string分配内存。
snprintf(&str[0], len + 1, fmt, args... ); // linux平台第二个参数是len+1windows平台是len。
return true;
}
template< typename... Args >
string sformat(const char* fmt, Args... args )
{
string str;
int len = snprintf( nullptr, 0, fmt, args... ); // 得到格式化后字符串的长度。
if (len < 0) return str; // 如果调用snprintf失败返回-1。
if (len == 0) return str; // 如果调用snprintf返回0表示格式化输出的内容为空。;
str.resize(len); // 为string分配内存。
snprintf(&str[0], len + 1, fmt, args... ); // linux平台第二个参数是len+1windows平台是len。
return str;
}
///////////////////////////////////// /////////////////////////////////////
// 时间操作的若干函数。
/*
strtime
timetvl03030-3030
fmtfmt每部分的含义yyyy-mm-dd-hh24-mi-ss-
"yyyy-mm-dd hh24:mi:ss"
"yyyy-mm-dd hh24:mi:ss"
"yyyymmddhh24miss"
"yyyy-mm-dd"
"yyyymmdd"
"hh24:mi:ss"
"hh24miss"
"hh24:mi"
"hh24mi"
"hh24"
"mi"
1hh24hh
2timetostr()
3fmt与上述格式都匹配strtime的内容将为空
40
*/
string& ltime(string &strtime,const string &fmt="",const int timetvl=0);
char * ltime(char *strtime ,const string &fmt="",const int timetvl=0);
// 为了避免重载的岐义增加ltime1()函数。
string ltime1(const string &fmt="",const int timetvl=0);
// 把整数表示的时间转换为字符串表示的时间。
// ttime整数表示的时间。
// strtime字符串表示的时间。
// fmt输出字符串时间strtime的格式与ltime()函数的fmt参数相同如果fmt的格式不正确strtime将为空。
string& timetostr(const time_t ttime,string &strtime,const string &fmt="");
char* timetostr(const time_t ttime,char *strtime ,const string &fmt="");
// 为了避免重载的岐义增加timetostr1()函数。
string timetostr1(const time_t ttime,const string &fmt="");
// 把字符串表示的时间转换为整数表示的时间。
// strtime字符串表示的时间格式不限但一定要包括yyyymmddhh24miss一个都不能少顺序也不能变。
// 返回值整数表示的时间如果strtime的格式不正确返回-1。
time_t strtotime(const string &strtime);
// 把字符串表示的时间加上一个偏移的秒数后得到一个新的字符串表示的时间。
// in_stime输入的字符串格式的时间格式不限但一定要包括yyyymmddhh24miss一个都不能少顺序也不能变。
// out_stime输出的字符串格式的时间。
// timetvl需要偏移的秒数正数往后偏移负数往前偏移。
// fmt输出字符串时间out_stime的格式与ltime()函数的fmt参数相同。
// 注意in_stime和out_stime参数可以是同一个变量的地址如果调用失败out_stime的内容会清空。
// 返回值true-成功false-失败如果返回失败可以认为是in_stime的格式不正确。
bool addtime(const string &in_stime,char *out_stime ,const int timetvl,const string &fmt="");
bool addtime(const string &in_stime,string &out_stime,const int timetvl,const string &fmt="");
///////////////////////////////////// /////////////////////////////////////
///////////////////////////////////// /////////////////////////////////////
// 这是一个精确到微秒的计时器。
class ctimer
{
private:
struct timeval m_start; // 计时开始的时间点。
struct timeval m_end; // 计时结束的时间点。
public:
ctimer(); // 构造函数中会调用start方法。
void start(); // 开始计时。
// 计算已逝去的时间,单位:秒,小数点后面是微秒。
// 每调用一次本方法之后自动调用start方法重新开始计时。
double elapsed();
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// 根据绝对路径的文件名或目录名逐级的创建目录。
// pathorfilename绝对路径的文件名或目录名。
// bisfilename指定pathorfilename的类型true-pathorfilename是文件名否则是目录名缺省值为true。
// 返回值true-成功false-失败,如果返回失败,原因有大概有三种情况:
// 1权限不足2pathorfilename参数不是合法的文件名或目录名3磁盘空间不足。
bool newdir(const string &pathorfilename,bool bisfilename=true);
///////////////////////////////////// /////////////////////////////////////
// 文件操作相关的函数
// 重命名文件类似Linux系统的mv命令。
// srcfilename原文件名建议采用绝对路径的文件名。
// dstfilename目标文件名建议采用绝对路径的文件名。
// 返回值true-成功false-失败,失败的主要原因是权限不足或磁盘空间不够,如果原文件和目标文件不在同一个磁盘分区,重命名也可能失败。
// 注意在重命名文件之前会自动创建dstfilename参数中包含的目录。
// 在应用开发中可以用renamefile()函数代替rename()库函数。
bool renamefile(const string &srcfilename,const string &dstfilename);
// 复制文件类似Linux系统的cp命令。
// srcfilename原文件名建议采用绝对路径的文件名。
// dstfilename目标文件名建议采用绝对路径的文件名。
// 返回值true-成功false-失败,失败的主要原因是权限不足或磁盘空间不够。
// 注意:
// 1在复制文件之前会自动创建dstfilename参数中的目录名。
// 2复制文件的过程中采用临时文件命名的方法复制完成后再改名为dstfilename避免中间状态的文件被读取。
// 3复制后的文件的时间与原文件相同这一点与Linux系统cp命令不同。
bool copyfile(const string &srcfilename,const string &dstfilename);
// 获取文件的大小。
// filename待获取的文件名建议采用绝对路径的文件名。
// 返回值:如果文件不存在或没有访问权限,返回-1成功返回文件的大小单位是字节。
int filesize(const string &filename);
// 获取文件的时间。
// filename待获取的文件名建议采用绝对路径的文件名。
// mtime用于存放文件的时间即stat结构体的st_mtime。
// fmt设置时间的输出格式与ltime()函数相同,但缺省是"yyyymmddhh24miss"。
// 返回值如果文件不存在或没有访问权限返回false成功返回true。
bool filemtime(const string &filename,char *mtime ,const string &fmt="yyyymmddhh24miss");
bool filemtime(const string &filename,string &mtime,const string &fmt="yyyymmddhh24miss");
// 重置文件的修改时间属性。
// filename待重置的文件名建议采用绝对路径的文件名。
// mtime字符串表示的时间格式不限但一定要包括yyyymmddhh24miss一个都不能少顺序也不能变。
// 返回值true-成功false-失败失败的原因保存在errno中。
bool setmtime(const string &filename,const string &mtime);
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////// /////////////////////////////////////
// 获取某目录及其子目录中的文件列表的类。
class cdir
{
private:
vector<string> m_filelist; // 存放文件列表的容器(绝对路径的文件名)。
int m_pos; // 从文件列表m_filelist中已读取文件的位置。
string m_fmt; // 文件时间格式,缺省"yyyymmddhh24miss"。
cdir(const cdir &) = delete; // 禁用拷贝构造函数。
cdir &operator=(const cdir &) = delete; // 禁用赋值函数。
public:
// /project/public/_public.h
string m_dirname; // 目录名,例如:/project/public
string m_filename; // 文件名不包括目录名例如_public.h
string m_ffilename; // 绝对路径的文件,例如:/project/public/_public.h
int m_filesize; // 文件的大小,单位:字节。
string m_mtime; // 文件最后一次被修改的时间即stat结构体的st_mtime成员。
string m_ctime; // 文件生成的时间即stat结构体的st_ctime成员。
string m_atime; // 文件最后一次被访问的时间即stat结构体的st_atime成员。
cdir():m_pos(0),m_fmt("yyyymmddhh24miss") {} // 构造函数。
// 设置文件时间的格式,支持"yyyy-mm-dd hh24:mi:ss"和"yyyymmddhh24miss"两种,缺省是后者。
void setfmt(const string &fmt);
// 打开目录获取目录中文件的列表存放在m_filelist容器中。
// dirname目录名采用绝对路径如/tmp/root。
// rules文件名的匹配规则不匹配的文件将被忽略。
// maxfiles本次获取文件的最大数量缺省值为10000个如果文件太多可能消耗太多的内存。
// bandchild是否打开各级子目录缺省值为false-不打开子目录。
// bsort是否按文件名排序缺省值为false-不排序。
// 返回值true-成功false-失败。
bool opendir(const string &dirname,const string &rules,const int maxfiles=10000,const bool bandchild=false,bool bsort=false);
private:
// 这是一个递归函数被opendir()的调用在cdir类的外部不需要调用它。
bool _opendir(const string &dirname,const string &rules,const int maxfiles,const bool bandchild);
public:
// 从m_filelist容器中获取一条记录文件名同时获取该文件的大小、修改时间等信息。
// 调用opendir方法时m_filelist容器被清空m_pos归零每调用一次readdir方法m_pos加1。
// 当m_pos小于m_filelist.size()返回true否则返回false。
bool readdir();
unsigned int size() { return m_filelist.size(); }
~cdir(); // 析构函数。
};
///////////////////////////////////// /////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// 写文件的类。
class cofile // class out file
{
private:
ofstream fout; // 写入文件的对象。
string m_filename; // 文件名,建议采用绝对路径。
string m_filenametmp; // 临时文件名在m_filename后面加".tmp"。
public:
cofile() {}
bool isopen() const { return fout.is_open(); } // 文件是否已打开。
// 打开文件。
// filename待打开的文件名。
// btmp是否采用临时文件的方案。
// mode打开文件的模式。
// benbuffer是否启用文件缓冲区。
bool open(const string &filename,const bool btmp=true,const ios::openmode mode=ios::out,const bool benbuffer=true);
// 把数据以文本的方式格式化输出到文件。
template< typename... Args >
bool writeline(const char* fmt, Args... args)
{
if (fout.is_open()==false) return false;
fout << sformat(fmt,args...);
return fout.good();
}
// 重载<<运算符,把数据以文本的方式输出到文件。
// 注意:换行只能用\n不能用endl。
template<typename T>
cofile& operator<<(const T &value)
{
fout << value; return *this;
}
// 把二进制数据写入文件。
bool write(void *buf,int bufsize);
// 关闭文件,并且把临时文件名改为正式文件名。
bool closeandrename();
// 关闭文件,如果有临时文件,则删除它。
void close();
~cofile() { close(); };
};
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// 读取文件的类。
class cifile // class in file
{
private:
ifstream fin; // 读取文件的对象。
string m_filename; // 文件名,建议采用绝对路径。
public:
cifile() {}
// 判断文件是否已打开。
bool isopen() const { return fin.is_open(); }
// 打开文件。
// filename待打开的文件名。
// mode打开文件的模式。
bool open(const string &filename,const ios::openmode mode=ios::in);
// 以行的方式读取文本文件endbz指定行的结尾标志缺省为空没有结尾标志。
bool readline(string &buf,const string& endbz="");
// 读取二进制文件,返回实际读取到的字节数。
int read(void *buf,const int bufsize);
// 关闭并删除文件。
bool closeandremove();
// 只关闭文件。
void close();
~cifile() { close(); }
};
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// 自旋锁。
class spinlock_mutex
{
private:
atomic_flag flag;
spinlock_mutex(const spinlock_mutex&) = delete;
spinlock_mutex& operator=(const spinlock_mutex) = delete;
public:
spinlock_mutex()
{
flag.clear();
}
void lock() // 加锁。
{
while (flag.test_and_set())
;
}
void unlock() // 解锁。
{
flag.clear();
}
};
///////////////////////////////////// /////////////////////////////////////
// 日志文件。
class clogfile
{
ofstream fout; // 日志文件对象。
string m_filename; // 日志文件名,建议采用绝对路径。
ios::openmode m_mode; // 日志文件的打开模式。
bool m_backup; // 是否自动切换日志。
int m_maxsize; // 当日志文件的大小超过本参数时,自动切换日志。
bool m_enbuffer; // 是否启用文件缓冲区。
spinlock_mutex m_splock; // 自旋锁,用于多线程程序中给写日志的操作加锁。
public:
// 构造函数日志文件的大小缺省100M。
clogfile(int maxsize=100):m_maxsize(maxsize){}
// 打开日志文件。
// filename日志文件名建议采用绝对路径如果文件名中的目录不存在就先创建目录。
// openmode日志文件的打开模式缺省值是ios::app。
// bbackup是否自动切换备份true-切换false-不切换在多进程的服务程序中如果多个进程共用一个日志文件bbackup必须为false。
// benbuffer是否启用文件缓冲机制true-启用false-不启用,如果启用缓冲区,那么写进日志文件中的内容不会立即写入文件,缺省是不启用。
// 注意,在多进程的程序中,多个进程往同一日志文件写入大量的日志时,可能会出现小混乱,但是,多线程不会。
// 1多个进程往同一日志文件写入大量的日志时可能会出现小混乱这个问题并不严重可以容忍
// 2只有同时写大量日志时才会出现混乱在实际开发中这种情况不多见。
// 3如果业务无法容忍可以用信号量加锁。
bool open(const string &filename,const ios::openmode mode=ios::app,const bool bbackup=true,const bool benbuffer=false);
// 把日志内容以文本的方式格式化输出到日志文件,并且,在日志内容前面写入时间。
template< typename... Args >
bool write(const char* fmt, Args... args)
{
if (fout.is_open()==false) return false;
backup(); // 判断是否需要切换日志文件。
m_splock.lock(); // 加锁。
fout << ltime1() << " " << sformat(fmt,args...); // 把当前时间和日志内容写入日志文件。
m_splock.unlock(); // 解锁。
return fout.good();
}
// 重载<<运算符,把日志内容以文本的方式输出到日志文件,不会在日志内容前面写时间。
// 注意:内容换行用\n不能用endl。
template<typename T>
clogfile& operator<<(const T &value)
{
m_splock.lock();
fout << value;
m_splock.unlock();
return *this;
}
private:
// 如果日志文件的大小超过m_maxsize的值就把当前的日志文件名改为历史日志文件名再创建新的当前日志文件。
// 备份后的文件会在日志文件名后加上日期时间,如/tmp/log/filetodb.log.20200101123025。
// 注意,在多进程的程序中,日志文件不可切换,多线的程序中,日志文件可以切换。
bool backup();
public:
void close() { fout.close(); }
~clogfile() { close(); };
};
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////
// 以下是socket通讯的函数和类
// socket通讯的客户端类
class ctcpclient
{
private:
int m_connfd; // 客户端的socket.
string m_ip; // 服务端的ip地址。
int m_port; // 服务端通讯的端口。
public:
ctcpclient(): m_connfd(-1),m_port(0) { } // 构造函数。
// 向服务端发起连接请求。
// ip服务端的ip地址。
// port服务端通讯的端口。
// 返回值true-成功false-失败。
bool connect(const string &ip,const int port);
// 接收对端发送过来的数据。
// buffer存放接收数据缓冲区。
// ibuflen: 打算接收数据的大小。
// itimeout等待数据的超时时间-1-不等待0-无限等待;>0-等待的秒数。
// 返回值true-成功false-失败失败有两种情况1等待超时2socket连接已不可用。
bool read(string &buffer,const int itimeout=0); // 接收文本数据。
bool read(void *buffer,const int ibuflen,const int itimeout=0); // 接收二进制数据。
// 向对端发送数据。
// buffer待发送数据缓冲区。
// ibuflen待发送数据的大小。
// 返回值true-成功false-失败如果失败表示socket连接已不可用。
bool write(const string &buffer); // 发送文本数据。
bool write(const void *buffer,const int ibuflen); // 发送二进制数据。
// 断开与服务端的连接
void close();
~ctcpclient(); // 析构函数自动关闭socket释放资源。
};
// socket通讯的服务端类
class ctcpserver
{
private:
int m_socklen; // 结构体struct sockaddr_in的大小。
struct sockaddr_in m_clientaddr; // 客户端的地址信息。
struct sockaddr_in m_servaddr; // 服务端的地址信息。
int m_listenfd; // 服务端用于监听的socket。
int m_connfd; // 客户端连接上来的socket。
public:
ctcpserver():m_listenfd(-1),m_connfd(-1) {} // 构造函数。
// 服务端初始化。
// port指定服务端用于监听的端口。
// 返回值true-成功false-失败一般情况下只要port设置正确没有被占用初始化都会成功。
bool initserver(const unsigned int port,const int backlog=5);
// 从已连接队列中获取一个客户端连接,如果已连接队列为空,将阻塞等待。
// 返回值true-成功的获取了一个客户端连接false-失败如果accept失败可以重新accept。
bool accept();
// 获取客户端的ip地址。
// 返回值客户端的ip地址如"192.168.1.100"。
char *getip();
// 接收对端发送过来的数据。
// buffer存放接收数据的缓冲区。
// ibuflen: 打算接收数据的大小。
// itimeout等待数据的超时时间-1-不等待0-无限等待;>0-等待的秒数。
// 返回值true-成功false-失败失败有两种情况1等待超时2socket连接已不可用。
bool read(string &buffer,const int itimeout=0); // 接收文本数据。
bool read(void *buffer,const int ibuflen,const int itimeout=0); // 接收二进制数据。
// 向对端发送数据。
// buffer待发送数据缓冲区。
// ibuflen待发送数据的大小。
// 返回值true-成功false-失败如果失败表示socket连接已不可用。
bool write(const string &buffer); // 发送文本数据。
bool write(const void *buffer,const int ibuflen); // 发送二进制数据。
// 关闭监听的socket即m_listenfd常用于多进程服务程序的子进程代码中。
void closelisten();
// 关闭客户端的socket即m_connfd常用于多进程服务程序的父进程代码中。
void closeclient();
~ctcpserver(); // 析构函数自动关闭socket释放资源。
};
// 接收socket的对端发送过来的数据。
// sockfd可用的socket连接。
// buffer接收数据缓冲区的地址。
// ibuflen本次成功接收数据的字节数。
// itimeout读取数据超时的时间单位-1-不等待0-无限等待;>0-等待的秒数。
// 返回值true-成功false-失败失败有两种情况1等待超时2socket连接已不可用。
bool tcpread(const int sockfd,string &buffer,const int itimeout=0); // 读取文本数据。
bool tcpread(const int sockfd,void *buffer,const int ibuflen,const int itimeout=0); // 读取二进制数据。
// 向socket的对端发送数据。
// sockfd可用的socket连接。
// buffer待发送数据缓冲区的地址。
// ibuflen待发送数据的字节数。
// 返回值true-成功false-失败如果失败表示socket连接已不可用。
bool tcpwrite(const int sockfd,const string &buffer); // 写入文本数据。
bool tcpwrite(const int sockfd,const void *buffer,const int ibuflen); // 写入二进制数据。
// 从已经准备好的socket中读取数据。
// sockfd已经准备好的socket连接。
// buffer存放数据的地址。
// n本次打算读取数据的字节数。
// 返回值成功接收到n字节的数据后返回truesocket连接不可用返回false。
bool readn(const int sockfd,char *buffer,const size_t n);
// 向已经准备好的socket中写入数据。
// sockfd已经准备好的socket连接。
// buffer待写入数据的地址。
// n待写入数据的字节数。
// 返回值成功写入完n字节的数据后返回truesocket连接不可用返回false。
bool writen(const int sockfd,const char *buffer,const size_t n);
// 以上是socket通讯的函数和类
///////////////////////////////////// /////////////////////////////////////
// 忽略关闭全部的信号、关闭全部的IO缺省只忽略信号不关IO。
void closeioandsignal(bool bcloseio=false);
// 循环队列。
template <class TT, int MaxLength>
class squeue
{
private:
bool m_inited; // 队列被初始化标志true-已初始化false-未初始化。
TT m_data[MaxLength]; // 用数组存储循环队列中的元素。
int m_head; // 队列的头指针。
int m_tail; // 队列的尾指针,指向队尾元素。
int m_length; // 队列的实际长度。
squeue(const squeue &) = delete; // 禁用拷贝构造函数。
squeue &operator=(const squeue &) = delete; // 禁用赋值函数。
public:
squeue() { init(); } // 构造函数。
// 循环队列的初始化操作。
// 注意:如果用于共享内存的队列,不会调用构造函数,必须调用此函数初始化。
void init()
{
if (m_inited!=true) // 循环队列的初始化只能执行一次。
{
m_head=0; // 头指针。
m_tail=MaxLength-1; // 为了方便写代码,初始化时,尾指针指向队列的最后一个位置。
m_length=0; // 队列的实际长度。
memset(m_data,0,sizeof(m_data)); // 数组元素清零。
m_inited=true;
}
}
// 元素入队返回值false-失败true-成功。
bool push(const TT &ee)
{
if (full() == true)
{
cout << "循环队列已满,入队失败。\n"; return false;
}
// 先移动队尾指针,然后再拷贝数据。
m_tail=(m_tail+1)%MaxLength; // 队尾指针后移。
m_data[m_tail]=ee;
m_length++;
return true;
}
// 求循环队列的长度,返回值:>=0-队列中元素的个数。
int size()
{
return m_length;
}
// 判断循环队列是否为空返回值true-空false-非空。
bool empty()
{
if (m_length == 0) return true;
return false;
}
// 判断循环队列是否已满返回值true-已满false-未满。
bool full()
{
if (m_length == MaxLength) return true;
return false;
}
// 查看队头元素的值,元素不出队。
TT& front()
{
return m_data[m_head];
}
// 元素出队返回值false-失败true-成功。
bool pop()
{
if (empty() == true) return false;
m_head=(m_head+1)%MaxLength; // 队列头指针后移。
m_length--;
return true;
}
// 显示循环队列中全部的元素。
// 这是一个临时的用于调试的函数队列中元素的数据类型支持cout输出才可用。
void printqueue()
{
for (int ii = 0; ii < size(); ii++)
{
cout << "m_data[" << (m_head+ii)%MaxLength << "],value=" \
<< m_data[(m_head+ii)%MaxLength] << endl;
}
}
};
// 信号量。
class csemp
{
private:
union semun // 用于信号量操作的共同体。
{
int val;
struct semid_ds *buf;
unsigned short *arry;
};
int m_semid; // 信号量id描述符
// 如果把sem_flg设置为SEM_UNDO操作系统将跟踪进程对信号量的修改情况
// 在全部修改过信号量的进程(正常或异常)终止后,操作系统将把信号量恢复为初始值。
// 如果信号量用于互斥锁设置为SEM_UNDO。
// 如果信号量用于生产消费者模型设置为0。
short m_sem_flg;
csemp(const csemp &) = delete; // 禁用拷贝构造函数。
csemp &operator=(const csemp &) = delete; // 禁用赋值函数。
public:
csemp():m_semid(-1){}
// 如果信号量已存在获取信号量如果信号量不存在则创建它并初始化为value。
// 如果用于互斥锁value填1sem_flg填SEM_UNDO。
// 如果用于生产消费者模型value填0sem_flg填0。
bool init(key_t key,unsigned short value=1,short sem_flg=SEM_UNDO);
bool wait(short value=-1); // 信号量的P操作如果信号量的值是0将阻塞等待直到信号量的值大于0。
bool post(short value=1); // 信号量的V操作。
int getvalue(); // 获取信号量的值,成功返回信号量的值,失败返回-1。
bool destroy(); // 销毁信号量。
~csemp();
};
// 进程心跳信息的结构体。
struct st_procinfo
{
int pid=0; // 进程id。
char pname[51]={0}; // 进程名称,可以为空。
int timeout=0; // 超时时间,单位:秒。
time_t atime=0; // 最后一次心跳的时间,用整数表示。
st_procinfo() = default; // 有了自定义的构造函数,编译器将不提供默认构造函数,所以启用默认构造函数。
st_procinfo(const int in_pid,const string & in_pname,const int in_timeout, const time_t in_atime)
:pid(in_pid),timeout(in_timeout),atime(in_atime) { strncpy(pname,in_pname.c_str(),50); }
};
// 以下几个宏用于进程的心跳。
#define MAXNUMP 1000 // 最大的进程数量。
#define SHMKEYP 0x5095 // 共享内存的key。
#define SEMKEYP 0x5095 // 信号量的key。
// 查看共享内存: ipcs -m
// 删除共享内存: ipcrm -m shmid
// 查看信号量: ipcs -s
// 删除信号量: ipcrm sem semid
// 进程心跳操作类。
class cpactive
{
private:
int m_shmid; // 共享内存的id。
int m_pos; // 当前进程在共享内存进程组中的位置。
st_procinfo *m_shm; // 指向共享内存的地址空间。
public:
cpactive(); // 初始化成员变量。
// 把当前进程的信息加入共享内存进程组中。
bool addpinfo(const int timeout,const string &pname="",clogfile *logfile=nullptr);
// 更新共享内存进程组中当前进程的心跳时间。
bool uptatime();
~cpactive(); // 从共享内存中删除当前进程的心跳记录。
};
}
#endif

232
EL/_ftp.cpp Normal file
View File

@ -0,0 +1,232 @@
/****************************************************************************************/
/* 程序名_ftp.cpp此程序是开发框架的ftp客户端工具的类的定义文件。 */
/****************************************************************************************/
#include "_ftp.h"
namespace eviwbh
{
cftpclient::cftpclient()
{
m_ftpconn=0;
initdata();
FtpInit();
m_connectfailed=false;
m_loginfailed=false;
m_optionfailed=false;
}
cftpclient::~cftpclient()
{
logout();
}
void cftpclient::initdata()
{
m_size=0;
m_mtime.clear();
}
bool cftpclient::login(const string &host,const string &username,const string &password,const int imode)
{
if (m_ftpconn != 0) { FtpQuit(m_ftpconn); m_ftpconn=0; }
m_connectfailed=m_loginfailed=m_optionfailed=false;
if (FtpConnect(host.c_str(),&m_ftpconn) == false) { m_connectfailed=true; return false; }
if (FtpLogin(username.c_str(),password.c_str(),m_ftpconn) == false) { m_loginfailed=true; return false; }
if (FtpOptions(FTPLIB_CONNMODE,(long)imode,m_ftpconn) == false) { m_optionfailed=true; return false; }
return true;
}
bool cftpclient::logout()
{
if (m_ftpconn == 0) return false;
FtpQuit(m_ftpconn);
m_ftpconn=0;
return true;
}
bool cftpclient::get(const string &remotefilename,const string &localfilename,const bool bcheckmtime)
{
if (m_ftpconn == 0) return false;
// 创建本地文件目录。
newdir(localfilename);
// 生成本地文件的临时文件名。
string strlocalfilenametmp=localfilename+".tmp";
// 获取远程服务器的文件的时间。
if (mtime(remotefilename) == false) return false;
// 取文件。
if (FtpGet(strlocalfilenametmp.c_str(),remotefilename.c_str(),FTPLIB_IMAGE,m_ftpconn) == false) return false;
// 判断文件下载前和下载后的时间,如果时间不同,表示在文件传输的过程中已发生了变化,返回失败。
if (bcheckmtime==true)
{
string strmtime=m_mtime;
if (mtime(remotefilename) == false) return false;
if (m_mtime!=strmtime) return false;
}
// 重置文件时间。
setmtime(strlocalfilenametmp,m_mtime);
// 改为正式的文件。
if (rename(strlocalfilenametmp.c_str(),localfilename.c_str()) != 0) return false;
// 获取文件的大小。
m_size=filesize(localfilename);
return true;
}
bool cftpclient::mtime(const string &remotefilename)
{
if (m_ftpconn == 0) return false;
m_mtime.clear();
string strmtime;
strmtime.resize(14);
if (FtpModDate(remotefilename.c_str(),&strmtime[0],14,m_ftpconn) == false) return false;
// 把UTC时间转换为本地时间。
addtime(strmtime,m_mtime,0+8*60*60,"yyyymmddhh24miss");
return true;
}
bool cftpclient::size(const string &remotefilename)
{
if (m_ftpconn == 0) return false;
m_size=0;
if (FtpSize(remotefilename.c_str(),&m_size,FTPLIB_IMAGE,m_ftpconn) == false) return false;
return true;
}
bool cftpclient::chdir(const string &remotedir)
{
if (m_ftpconn == 0) return false;
if (FtpChdir(remotedir.c_str(),m_ftpconn) == false) return false;
return true;
}
bool cftpclient::mkdir(const string &remotedir)
{
if (m_ftpconn == 0) return false;
if (FtpMkdir(remotedir.c_str(),m_ftpconn) == false) return false;
return true;
}
bool cftpclient::rmdir(const string &remotedir)
{
if (m_ftpconn == 0) return false;
if (FtpRmdir(remotedir.c_str(),m_ftpconn) == false) return false;
return true;
}
bool cftpclient::nlist(const string &remotedir,const string &listfilename)
{
if (m_ftpconn == 0) return false;
newdir(listfilename.c_str()); // 创建本地list文件目录
if (FtpNlst(listfilename.c_str(),remotedir.c_str(),m_ftpconn) == false) return false;
return true;
}
bool cftpclient::put(const string &localfilename,const string &remotefilename,const bool bchecksize)
{
if (m_ftpconn == 0) return false;
// 生成服务器文件的临时文件名。
string strremotefilenametmp=remotefilename+".tmp";
string filetime1,filetime2;
filemtime(localfilename,filetime1); // 获取上传文件之前的时间。
// 发送文件。
if (FtpPut(localfilename.c_str(),strremotefilenametmp.c_str(),FTPLIB_IMAGE,m_ftpconn) == false) return false;
filemtime(localfilename,filetime2); // 获取上传文件之后的时间。
// 如果文件上传前后的时间不一致,说明本地有修改文件,放弃本次上传。
if (filetime1!=filetime2) { ftpdelete(strremotefilenametmp); return false; }
// 重命名文件。
if (FtpRename(strremotefilenametmp.c_str(),remotefilename.c_str(),m_ftpconn) == false) return false;
// 判断已上传的文件的大小与本地文件是否相同,确保上传成功。
// 一般来说,不会出现文件大小不一致的情况,如果有,应该是服务器方的原因,不太好处理。
if (bchecksize==true)
{
if (size(remotefilename) == false) return false;
if (m_size != filesize(localfilename)) { ftpdelete(remotefilename); return false; }
}
return true;
}
bool cftpclient::ftpdelete(const string &remotefilename)
{
if (m_ftpconn == 0) return false;
if (FtpDelete(remotefilename.c_str(),m_ftpconn) == false) return false;
return true;
}
bool cftpclient::ftprename(const string &srcremotefilename,const string &dstremotefilename)
{
if (m_ftpconn == 0) return false;
if (FtpRename(srcremotefilename.c_str(),dstremotefilename.c_str(),m_ftpconn) == false) return false;
return true;
}
bool cftpclient::site(const string &command)
{
if (m_ftpconn == 0) return false;
if (FtpSite(command.c_str(),m_ftpconn) == false) return false;
return true;
}
char *cftpclient::response()
{
if (m_ftpconn == 0) return 0;
return FtpLastResponse(m_ftpconn);
}
} // end namespace idc

116
EL/_ftp.h Normal file
View File

@ -0,0 +1,116 @@
/****************************************************************************************/
/* 程序名_ftp.h此程序是开发框架的ftp客户端工具的类的声明文件。 */
/****************************************************************************************/
#ifndef __FTP_H
#define __FTP_H
#include "_el.h"
#include "ftplib.h"
namespace eviwbh
{
class cftpclient
{
private:
netbuf *m_ftpconn; // ftp连接句柄。
public:
unsigned int m_size; // 文件的大小,单位:字节。
string m_mtime; // 文件的修改时间格式yyyymmddhh24miss。
// 以下三个成员变量用于存放login方法登录失败的原因。
bool m_connectfailed; // 如果网络连接失败该成员的值为true。
bool m_loginfailed; // 如果登录失败用户名和密码不正确或没有登录权限该成员的值为true。
bool m_optionfailed; // 如果设置传输模式失败该成员变量的值为true。
cftpclient(); // 类的构造函数。
~cftpclient(); // 类的析构函数。
cftpclient(const cftpclient&) = delete;
cftpclient& operator=(const cftpclient) = delete;
void initdata(); // 初始化m_size和m_mtime成员变量。
// 登录ftp服务器。
// hostftp服务器ip地址和端口中间用":"分隔,如"192.168.1.1:21"。
// username登录ftp服务器用户名。
// password登录ftp服务器的密码。
// imode传输模式1-FTPLIB_PASSIVE是被动模式2-FTPLIB_PORT是主动模式缺省是被动模式。
bool login(const string &host,const string &username,const string &password,const int imode=FTPLIB_PASSIVE);
// 注销。
bool logout();
// 获取ftp服务器上文件的时间。
// remotefilename待获取的文件名。
// 返回值false-失败true-成功获取到的文件时间存放在m_mtime成员变量中。
bool mtime(const string &remotefilename);
// 获取ftp服务器上文件的大小。
// remotefilename待获取的文件名。
// 返回值false-失败true-成功获取到的文件大小存放在m_size成员变量中。
bool size(const string &remotefilename);
// 改变ftp服务器的当前工作目录。
// remotedirftp服务器上的目录名。
// 返回值true-成功false-失败。
bool chdir(const string &remotedir);
// 在ftp服务器上创建目录。
// remotedirftp服务器上待创建的目录名。
// 返回值true-成功false-失败。
bool mkdir(const string &remotedir);
// 删除ftp服务器上的目录。
// remotedirftp服务器上待删除的目录名。
// 返回值true-成功如果权限不足、目录不存在或目录不为空会返回false。
bool rmdir(const string &remotedir);
// 发送NLST命令列出ftp服务器目录中的子目录名和文件名。
// remotedirftp服务器的目录名。
// listfilename用于保存从服务器返回的目录和文件名列表。
// 返回值true-成功false-失败。
// 注意如果列出的是ftp服务器当前目录remotedir用"","*","."都可以但是不规范的ftp服务器可能有差别。
bool nlist(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 bcheckmtime=true);
// 向ftp服务器发送文件。
// localfilename本地待发送的文件名。
// remotefilename发送到ftp服务器上的文件名。
// bchecksize文件传输完成后是否核对本地文件和远程文件的大小保证文件的完整性。
// 返回值true-成功false-失败。
// 注意文件在传输的过程中采用临时文件命名的方法即在remotefilename后加".tmp",在传输
// 完成后才正式改为remotefilename。
bool put(const string &localfilename,const string &remotefilename,const bool bchecksize=true);
// 删除ftp服务器上的文件。
// remotefilename待删除的ftp服务器上的文件名。
// 返回值true-成功false-失败。
bool ftpdelete(const string &remotefilename);
// 重命名ftp服务器上的文件。
// srcremotefilenameftp服务器上的原文件名。
// dstremotefilenameftp服务器上的目标文件名。
// 返回值true-成功false-失败。
bool ftprename(const string &srcremotefilename,const string &dstremotefilename);
// 向ftp服务器发送site命令。
// command命令的内容。
// 返回值true-成功false-失败。
bool site(const string &command);
// 获取服务器返回信息的最后一条(return a pointer to the last response received)。
char *response();
};
} // end namespace idc
#endif

1455
EL/ftplib.c Normal file

File diff suppressed because it is too large Load Diff

120
EL/ftplib.h Normal file
View File

@ -0,0 +1,120 @@
/***************************************************************************/
/* */
/* ftplib.h - header file for callable ftp access routines */
/* Copyright (C) 1996-2001, 2013, 2016 Thomas Pfau, tfpfau@gmail.com */
/* 1407 Thomas Ave, North Brunswick, NJ, 08902 */
/* */
/* This library is free software. You can redistribute it and/or */
/* modify it under the terms of the Artistic License 2.0. */
/* */
/* This library is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* Artistic License 2.0 for more details. */
/* */
/* See the file LICENSE or */
/* http://www.perlfoundation.org/artistic_license_2_0 */
/* */
/***************************************************************************/
#if !defined(__FTPLIB_H)
#define __FTPLIB_H
#if defined(__unix__) || defined(VMS)
#define GLOBALDEF
#define GLOBALREF extern
#elif defined(_WIN32)
#if defined BUILDING_LIBRARY
#define GLOBALDEF __declspec(dllexport)
#define GLOBALREF __declspec(dllexport)
#else
#define GLOBALREF __declspec(dllimport)
#endif
#endif
#include <limits.h>
#include <inttypes.h>
/* FtpAccess() type codes */
#define FTPLIB_DIR 1
#define FTPLIB_DIR_VERBOSE 2
#define FTPLIB_FILE_READ 3
#define FTPLIB_FILE_WRITE 4
/* FtpAccess() mode codes */
#define FTPLIB_ASCII 'A'
#define FTPLIB_IMAGE 'I'
#define FTPLIB_TEXT FTPLIB_ASCII
#define FTPLIB_BINARY FTPLIB_IMAGE
/* connection modes */
#define FTPLIB_PASSIVE 1
#define FTPLIB_PORT 2
/* connection option names */
#define FTPLIB_CONNMODE 1
#define FTPLIB_CALLBACK 2
#define FTPLIB_IDLETIME 3
#define FTPLIB_CALLBACKARG 4
#define FTPLIB_CALLBACKBYTES 5
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__UINT64_MAX)
typedef uint64_t fsz_t;
#else
typedef uint32_t fsz_t;
#endif
typedef struct NetBuf netbuf;
typedef int (*FtpCallback)(netbuf *nControl, fsz_t xfered, void *arg);
typedef struct FtpCallbackOptions {
FtpCallback cbFunc; /* function to call */
void *cbArg; /* argument to pass to function */
unsigned int bytesXferred; /* callback if this number of bytes transferred */
unsigned int idleTime; /* callback if this many milliseconds have elapsed */
} FtpCallbackOptions;
GLOBALREF int ftplib_debug;
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);
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);
#ifdef __cplusplus
};
#endif
#endif /* __FTPLIB_H */

16
EL/makefile Normal file
View File

@ -0,0 +1,16 @@
all: lib_el.a lib_el.so libftp.a libftp.so
lib_el.a:_el.h _el.cpp
g++ -c -o lib_el.a _el.cpp
lib_el.so:_el.h _el.cpp
g++ -fPIC -shared -o lib_el.so _el.cpp
libftp.a:ftplib.h ftplib.c
gcc -c -o libftp.a ftplib.c
libftp.so:ftplib.h ftplib.c
gcc -fPIC -shared -o libftp.so ftplib.c
clean:
rm -f lib_el.a lib_el.so libftp.a libftp.so

0
README.md Normal file
View File

37
download.cpp Normal file
View File

@ -0,0 +1,37 @@
/*
* cftpclient类下载文件
*/
#include "_ftp.h"
using namespace eviwbh;
int main(int argc,char *argv[])
{
cftpclient ftp;
// 登录远程ftp服务器请改为你自己服务器的ip地址。
if (ftp.login("192.168.150.128:21","wucz","oracle") == false)
{
printf("ftp.login(192.168.150.128:21,wucz/oracle) failed.\n"); return -1;
}
// 把服务器上的/home/wucz/tmp/demo51.cpp下载到本地存为/tmp/test/demo51.cpp。
// 如果本地的/tmp/test目录不存在就创建它。
if (ftp.get("/home/wucz/tmp/demo51.cpp","/tmp/test/demo51.cpp")==false)
{
printf("ftp.get() failed.\n"); return -1;
}
printf("get /home/wucz/tmp/demo51.cpp ok.\n");
/*
// 删除服务上的/home/wucz/tmp/demo51.cpp文件。
if (ftp.ftpdelete("/home/wucz/tmp/demo51.cpp")==false) { printf("ftp.ftpdelete() failed.\n"); return -1; }
printf("delete /home/wucz/tmp/demo51.cpp ok.\n");
// 删除服务器上的/home/wucz/tmp目录如果目录非空删除将失败。
if (ftp.rmdir("/home/wucz/tmp")==false) { printf("ftp.rmdir() failed.\n"); return -1; }
*/
}

416
ftpgetfiles.cpp Normal file
View File

@ -0,0 +1,416 @@
#include "_el.h"
#include "_ftp.h"
using namespace eviwbh;
// 程序退出和信号2、15的处理函数。
void EXIT(int sig);
// 程序运行参数的结构体。
struct st_arg
{
char host[31]; // 远程服务端的IP和端口。
int mode; // 传输模式1-被动模式2-主动模式,缺省采用被动模式。
char username[31]; // 远程服务端ftp的用户名。
char password[31]; // 远程服务端ftp的密码。
char remotepath[256]; // 远程服务端存放文件的目录。
char localpath[256]; // 本地文件存放的目录。
char matchname[256]; // 待下载文件匹配的规则。
int ptype; // 下载后服务端文件的处理方式1-什么也不做2-删除3-备份。
char remotepathbak[256]; // 下载后服务端文件的备份目录。
char okfilename[256]; // 已下载成功文件信息存放的文件。
bool checkmtime; // 是否需要检查服务端文件的时间true-需要false-不需要缺省为false。
int timeout; // 进程心跳超时的时间。
char pname[51]; // 进程名,建议用"ftpgetfiles_后缀"的方式。
} starg;
bool _xmltoarg(const char *strxmlbuffer); // 把xml解析到参数starg结构中。
clogfile logfile; // 日志文件对象。
cftpclient ftp; // 创建ftp客户端对象。
cpactive pactive; // 进程心跳的对象。
void _help(); // 显示帮助文档。
struct st_fileinfo // 文件信息的结构体。
{
string filename; // 文件名。
string mtime; // 文件时间。
st_fileinfo()=default;
st_fileinfo(const string &in_filename,const string &in_mtime):filename(in_filename),mtime(in_mtime) {}
void clear() { filename.clear(); mtime.clear(); }
};
map<string,string> mfromok; // 容器一存放已下载成功文件从starg.okfilename参数指定的文件中加载。
list<struct st_fileinfo> vfromnlist; // 容器二下载前列出服务端文件名的容器从nlist文件中加载。
list<struct st_fileinfo> vtook; // 容器三:本次不需要下载的文件的容器。
list<struct st_fileinfo> vdownload; // 容器四:本次需要下载的文件的容器。
bool loadokfile(); // 加载starg.okfilename文件中的内容到容器vfromok中。
bool loadlistfile(); // 把ftpclient.nlist()方法获取到的list文件加载到容器vfromnlist中。
bool compmap(); // 比较vfromnlist和vfromok得到vtook和vdownload。
bool writetookfile(); // 把容器vtook中的数据写入starg.okfilename文件覆盖之前的旧starg.okfilename文件。
bool appendtookfile(struct st_fileinfo &stfileinfo); // 把下载成功的文件记录追加到starg.okfilename文件中。
int main(int argc,char *argv[])
{
// 第一步计划:从服务器某个目录中下载文件,可以指定文件名匹配的规则。
if (argc!=3) { _help(); return -1; }
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程。
// 但请不要用 "kill -9 +进程号" 强行终止。
// closeioandsignal(true); // 关闭0、1、2和忽略全部的信号在调试阶段这行代码可以不启用。
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
// 打开日志文件。
if (logfile.open(argv[1])==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%s\n",starg.host,starg.username,starg.password,ftp.response()); return -1;
}
// logfile.write("ftp.login ok.\n");
// 进入ftp服务器存放文件的目录。
if (ftp.chdir(starg.remotepath)==false)
{
logfile.write("ftp.chdir(%s) failed.\n%s\n",starg.remotepath,ftp.response()); return -1;
}
// 调用ftpclient.nlist()方法列出服务器目录中的文件名,保存在本地文件中。
if (ftp.nlist(".",sformat("/tmp/nlist/ftpgetfiles_%d.nlist",getpid())) == false)
{
logfile.write("ftp.nlist(%s) failed.\n%s\n",starg.remotepath,ftp.response()); return -1;
}
// logfile.write("nlist(%s) ok.\n",sformat("/tmp/nlist/ftpgetfiles_%d.nlist",getpid()).c_str());
pactive.uptatime(); // 更新进程的心跳。
// 把ftpclient.nlist()方法获取到的list文件加载到容器vfromnlist中。
if (loadlistfile()==false)
{
logfile.write("loadlistfile() failed.\n"); return -1;
}
if (starg.ptype==1)
{
// 加载starg.okfilename文件中的数据到容器vfromok中。
loadokfile();
// 比较vfromnlist和vfromok得到vtook和vdownload。
compmap();
// 把容器vtook中的数据写入starg.okfilename文件覆盖之前的旧starg.okfilename文件。
writetookfile();
}
else
vfromnlist.swap(vdownload); // 为了统一文件下载的代码,把容器二和容器四交换。
pactive.uptatime(); // 更新进程的心跳。
string strremotefilename,strlocalfilename;
// 遍历vdownload容器。
for (auto & aa : vdownload)
{
sformat(strremotefilename,"%s/%s",starg.remotepath,aa.filename.c_str()); // 拼接服务端全路径的文件名。
sformat(strlocalfilename,"%s/%s",starg.localpath,aa.filename.c_str()); // 拼接本地全路径的文件名。
logfile.write("get %s ...",strremotefilename.c_str());
// 调用ftpclient.get()方法下载文件。
if (ftp.get(strremotefilename,strlocalfilename,starg.checkmtime)==false)
{
logfile << "failed.\n" << ftp.response() << "\n"; return -1;
}
logfile << "ok.\n";
pactive.uptatime(); // 更新进程的心跳。
// 如果ptype==1把下载成功的文件记录追加到starg.okfilename文件中。
if (starg.ptype==1) appendtookfile(aa);
// ptype==2删除服务端的文件。
if (starg.ptype==2)
{
if (ftp.ftpdelete(strremotefilename)==false)
{
logfile.write("ftp.ftpdelete(%s) failed.\n%s\n",strremotefilename.c_str(),ftp.response()); return -1;
}
}
// ptype==3把服务端的文件移动到备份目录。
if (starg.ptype==3)
{
string strremotefilenamebak=sformat("%s/%s",starg.remotepathbak,aa.filename.c_str()); // 生成全路径的备份文件名。
if (ftp.ftprename(strremotefilename,strremotefilenamebak)==false)
{
logfile.write("ftp.ftprename(%s,%s) failed.\n%s\n",strremotefilename.c_str(),strremotefilenamebak.c_str(),ftp.response()); return -1;
}
}
}
return 0;
}
void _help() // 显示帮助文档。
{
printf("\n");
printf("Using:/project/tools/bin/ftpgetfiles logfilename xmlbuffer\n\n");
//printf("Sample:/project/tools/bin/procctl 30 /project/tools/bin/ftpgetfiles /log/idc/ftpgetfiles_surfdata.log " \
// "\"<host>192.168.150.128:21</host><mode>1</mode>"\
// "<username>wucz</username><password>oracle</password>"\
// "<remotepath>/tmp/idc/surfdata</remotepath><localpath>/idcdata/surfdata</localpath>"\
// "<matchname>SURF_ZH*.XML,SURF_ZH*.CSV</matchname>"\
// "<ptype>3</ptype><remotepathbak>/tmp/idc/surfdatabak</remotepathbak>\"\n\n");
printf("Sample:/project/tools/bin/procctl 30 /project/tools/bin/ftpgetfiles /log/idc/ftpgetfiles_test.log " \
"\"<host>192.168.150.128:21</host><mode>1</mode>"\
"<username>wucz</username><password>oracle</password>"\
"<remotepath>/tmp/ftp/server</remotepath><localpath>/tmp/ftp/client</localpath>"\
"<matchname>*.TXT</matchname>"\
"<ptype>1</ptype><okfilename>/idcdata/ftplist/ftpgetfiles_test.xml</okfilename>"\
"<checkmtime>true</checkmtime>"\
"<timeout>30</timeout><pname>ftpgetfiles_test</pname>\"\n\n\n");
printf("本程序是通用的功能模块用于把远程ftp服务端的文件下载到本地目录。\n");
printf("logfilename是本程序运行的日志文件。\n");
printf("xmlbuffer为文件下载的参数如下\n");
printf("<host>192.168.150.128:21</host> 远程服务端的IP和端口。\n");
printf("<mode>1</mode> 传输模式1-被动模式2-主动模式,缺省采用被动模式。\n");
printf("<username>wucz</username> 远程服务端ftp的用户名。\n");
printf("<password>oraccle</password> 远程服务端ftp的密码。\n");
printf("<remotepath>/tmp/idc/surfdata</remotepath> 远程服务端存放文件的目录。\n");
printf("<localpath>/idcdata/surfdata</localpath> 本地文件存放的目录。\n");
printf("<matchname>SURF_ZH*.XML,SURF_ZH*.CSV</matchname> 待下载文件匹配的规则。"\
"不匹配的文件不会被下载,本字段尽可能设置精确,不建议用*匹配全部的文件。\n");
printf("<ptype>1</ptype> 文件下载成功后,远程服务端文件的处理方式:"\
"1-什么也不做2-删除3-备份如果为3还要指定备份的目录。\n");
printf("<remotepathbak>/tmp/idc/surfdatabak</remotepathbak> 文件下载成功后,服务端文件的备份目录,"\
"此参数只有当ptype=3时才有效。\n");
printf("<okfilename>/idcdata/ftplist/ftpgetfiles_test.xml</okfilename> 已下载成功文件名清单,"\
"此参数只有当ptype=1时才有效。\n");
printf("<checkmtime>true</checkmtime> 是否需要检查服务端文件的时间true-需要false-不需要,"\
"此参数只有当ptype=1时才有效缺省为false。\n");
printf("<timeout>30</timeout> 下载文件超时时间,单位:秒,视文件大小和网络带宽而定。\n");
printf("<pname>ftpgetfiles_test</pname> 进程名,尽可能采用易懂的、与其它进程不同的名称,方便故障排查。\n\n\n");
}
// 把xml解析到参数starg结构中。
bool _xmltoarg(const 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,255); // 远程服务端存放文件的目录。
if (strlen(starg.remotepath)==0)
{ logfile.write("remotepath is null.\n"); return false; }
getxmlbuffer(strxmlbuffer,"localpath",starg.localpath,255); // 本地文件存放的目录。
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; }
// 下载后服务端文件的备份目录。
if (starg.ptype==3)
{
getxmlbuffer(strxmlbuffer,"remotepathbak",starg.remotepathbak,255);
if (strlen(starg.remotepathbak)==0) { logfile.write("remotepathbak is null.\n"); return false; }
}
// 增量下载文件。
if (starg.ptype==1)
{
getxmlbuffer(strxmlbuffer,"okfilename",starg.okfilename,255); // 已下载成功文件名清单。
if ( strlen(starg.okfilename)==0 ) { logfile.write("okfilename is null.\n"); return false; }
// 是否需要检查服务端文件的时间true-需要false-不需要此参数只有当ptype=1时才有效缺省为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\n",sig);
exit(0);
}
// 把ftp.nlist()方法获取到的list文件加载到容器vfromnlist中。
bool loadlistfile()
{
vfromnlist.clear();
cifile ifile;
if (ifile.open(sformat("/tmp/nlist/ftpgetfiles_%d.nlist",getpid()))==false)
{
logfile.write("ifile.open(%s) 失败。\n",sformat("/tmp/nlist/ftpgetfiles_%d.nlist",getpid())); return false;
}
string strfilename;
while (true)
{
if (ifile.readline(strfilename)==false) break;
if (matchstr(strfilename,starg.matchname)==false) continue;
if ( (starg.ptype==1) && (starg.checkmtime==true) )
{
// 获取ftp服务端文件时间。
if (ftp.mtime(strfilename)==false)
{
logfile.write("ftp.mtime(%s) failed.\n",strfilename.c_str()); return false;
}
}
vfromnlist.emplace_back(strfilename,ftp.m_mtime);
}
ifile.closeandremove();
//for (auto &aa:vfromnlist)
// logfile.write("filename=%s,mtime=%s\n",aa.filename.c_str(),aa.mtime.c_str());
return true;
}
// 加载starg.okfilename文件中的内容到容器vfromok中。
bool loadokfile()
{
if (starg.ptype!=1) return true;
mfromok.clear();
cifile ifile;
// 注意如果程序是第一次运行starg.okfilename是不存在的并不是错误所以也返回true。
if ( (ifile.open(starg.okfilename))==false ) return true;
string strbuffer;
struct st_fileinfo stfileinfo;
while (true)
{
stfileinfo.clear();
if (ifile.readline(strbuffer)==false) break;
getxmlbuffer(strbuffer,"filename",stfileinfo.filename);
getxmlbuffer(strbuffer,"mtime",stfileinfo.mtime);
mfromok[stfileinfo.filename]=stfileinfo.mtime;
}
//for (auto &aa:mfromok)
// logfile.write("filename=%s,mtime=%s\n",aa.first.c_str(),aa.second.c_str());
return true;
}
// 比较vfromnlist和vfromok得到vtook和vdownload。
bool compmap()
{
vtook.clear();
vdownload.clear();
// 遍历vfromnlist。
for (auto &aa:vfromnlist)
{
auto it=mfromok.find(aa.filename); // 在容器一中用文件名查找。
if (it !=mfromok.end())
{ // 如果找到了,再判断文件时间。
if (starg.checkmtime==true)
{
// 如果时间也相同,不需要下载,否则需要重新下载。
if (it->second==aa.mtime) vtook.push_back(aa); // 文件时间没有变化,不需要下载。
else vdownload.push_back(aa); // 需要重新下载。
}
else
{
vtook.push_back(aa); // 不需要重新下载。
}
}
else
{ // 如果没有找到把记录放入vdownload容器。
vdownload.push_back(aa);
}
}
return true;
}
// 把容器vtook中的内容写入starg.okfilename文件覆盖之前的旧starg.okfilename文件。
bool writetookfile()
{
cofile ofile;
if (ofile.open(starg.okfilename)==false)
{
logfile.write("file.open(%s) failed.\n",starg.okfilename); return false;
}
for (auto &aa:vtook)
ofile.writeline("<filename>%s</filename><mtime>%s</mtime>\n",aa.filename.c_str(),aa.mtime.c_str());
ofile.closeandrename();
return true;
}
// 把下载成功的文件记录追加到starg.okfilename文件中。
bool appendtookfile(struct st_fileinfo &stfileinfo)
{
cofile ofile;
// 以追加的方式打开文件注意第二个参数一定要填false。
if (ofile.open(starg.okfilename,false,ios::app)==false)
{
logfile.write("file.open(%s) failed.\n",starg.okfilename); return false;
}
ofile.writeline("<filename>%s</filename><mtime>%s</mtime>\n",stfileinfo.filename.c_str(),stfileinfo.mtime.c_str());
return true;
}

367
ftpputfiles.cpp Normal file
View File

@ -0,0 +1,367 @@
#include "_el.h"
#include "_ftp.h"
using namespace eviwbh;
// 程序运行参数的结构体。
struct st_arg
{
char host[31]; // 远程服务端的IP和端口。
int mode; // 传输模式1-被动模式2-主动模式,缺省采用被动模式。
char username[31]; // 远程服务端ftp的用户名。
char password[31]; // 远程服务端ftp的密码。
char remotepath[256]; // 远程服务端存放文件的目录。
char localpath[256]; // 本地文件存放的目录。
char matchname[101]; // 待上传文件匹配的规则。
int ptype; // 上传后客户端文件的处理方式1-什么也不做2-删除3-备份。
char localpathbak[256]; // 上传后客户端文件的备份目录。
char okfilename[256]; // 已上传成功文件名清单。
int timeout; // 进程心跳的超时时间。
char pname[51]; // 进程名,建议用"ftpputfiles_后缀"的方式。
} starg;
// 文件信息的结构体。
struct st_fileinfo
{
string filename;
string mtime;
st_fileinfo()=default;
st_fileinfo(const string &in_filename,const string &in_mtime):filename(in_filename),mtime(in_mtime) {}
void clear() { filename.clear(); mtime.clear(); }
};
map<string,string> mfromok; // 已上传成功文件从starg.okfilename中加载。
list<struct st_fileinfo> vfromdir; // 客户端目录中的文件名。
list<struct st_fileinfo> vtook; // 本次不需要上传的文件。
list<struct st_fileinfo> vupload; // 本次需要上传的文件。
clogfile logfile; // 日志文件对象。
cftpclient ftp; // 创建ftp客户端对象。
cpactive pactive; // 进程心跳的对象。
// 把starg.localpath目录下的文件列表加载到vfromdir容器中。
bool loadlocalfile();
// 加载starg.okfilename文件中的内容到容器vfromok中。
bool loadokfile();
bool compmap(); // 比较vfromdir和mfromok得到vtook和vupload。
// 把容器vtook中的内容写入starg.okfilename文件覆盖之前的旧starg.okfilename文件。
bool writetookfile();
// 如果ptype==1把上传成功的文件记录追加到starg.okfilename文件中。
bool appendtookfile(struct st_fileinfo &stfileinfo);
// 程序退出和信号2、15的处理函数。
void EXIT(int sig);
void _help();
// 把xml解析到参数starg结构中。
bool _xmltoarg(char *strxmlbuffer);
int main(int argc,char *argv[])
{
if (argc!=3) { _help(); return -1; }
// 关闭全部的信号和输入输出。
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程。
// 但请不要用 "kill -9 +进程号" 强行终止。
//closeioandsignal(true);
signal(SIGINT,EXIT); signal(SIGTERM,EXIT);
// 打开日志文件。
if (logfile.open(argv[1])==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%s\n",starg.host,starg.username,starg.password,ftp.response()); return -1;
}
// logfile.write("ftp.login ok.\n"); // 正式运行后,可以注释这行代码。
// 把starg.localpath目录下的文件列表加载到vfromdir容器中。
if (loadlocalfile()==false)
{
logfile.write("loadlocalfile() failed.\n"); return -1;
}
pactive.uptatime(); // 更新进程的心跳。
if (starg.ptype==1)
{
loadokfile();
// 比较vfromdir和vfromok得到vtook和vupload。
compmap();
// 把容器vtook中的内容写入starg.okfilename文件覆盖之前的旧starg.okfilename文件。
writetookfile();
}
else
vfromdir.swap(vupload);
pactive.uptatime(); // 更新进程的心跳。
string strremotefilename,strlocalfilename;
// 遍历容器vupload。
for (auto &aa:vupload)
{
sformat(strremotefilename,"%s/%s",starg.remotepath,aa.filename.c_str());
sformat(strlocalfilename,"%s/%s",starg.localpath,aa.filename.c_str());
logfile.write("put %s ...",strlocalfilename.c_str());
// 调用ftp.put()方法把文件上传到服务端第三个参数填true的目的是确保文件上传成功对方不可抵赖。
if (ftp.put(strlocalfilename,strremotefilename,true)==false)
{
logfile << "failed.\n" << ftp.response() << "\n"; return -1;
}
logfile << "ok.\n";
pactive.uptatime(); // 更新进程的心跳。
// 如果ptype==1把上传成功的文件记录追加到starg.okfilename文件中。
if (starg.ptype==1) appendtookfile(aa);
// 删除文件。
if (starg.ptype==2)
{
if (remove(strlocalfilename.c_str())!=0)
{
logfile.write("remove(%s) failed.\n",strlocalfilename.c_str()); return -1;
}
}
// 转存到备份目录。
if (starg.ptype==3)
{
string strlocalfilenamebak=sformat("%s/%s",starg.localpathbak,aa.filename.c_str());
if (renamefile(strlocalfilename,strlocalfilenamebak)==false)
{
logfile.write("renamefile(%s,%s) failed.\n",strlocalfilename.c_str(),strlocalfilenamebak.c_str()); return -1;
}
}
}
return 0;
}
void EXIT(int sig)
{
printf("程序退出sig=%d\n\n",sig);
exit(0);
}
void _help()
{
printf("\n");
printf("Using:/project/tools/bin/ftpputfiles logfilename xmlbuffer\n\n");
printf("Sample:/project/tools/bin/procctl 30 /project/tools/bin/ftpputfiles /log/idc/ftpputfiles_surfdata.log "\
"\"<host>127.0.0.1:21</host><mode>1</mode><username>wucz</username><password>oracle</password>"\
"<localpath>/tmp/idc/surfdata</localpath><remotepath>/idcdata/surfdata</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,255); // 远程服务端存放文件的目录。
if (strlen(starg.remotepath)==0)
{ logfile.write("remotepath is null.\n"); return false; }
getxmlbuffer(strxmlbuffer,"localpath",starg.localpath,255); // 本地文件存放的目录。
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; }
if (starg.ptype==3)
{
getxmlbuffer(strxmlbuffer,"localpathbak",starg.localpathbak,255); // 上传后客户端文件的备份目录。
if (strlen(starg.localpathbak)==0) { logfile.write("localpathbak is null.\n"); return false; }
}
if (starg.ptype==1)
{
getxmlbuffer(strxmlbuffer,"okfilename",starg.okfilename,255); // 已上传成功文件名清单。
if (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;
}
// 把starg.localpath目录下的文件列表加载到vfromdir容器中。
bool loadlocalfile()
{
vfromdir.clear();
cdir dir;
// 不包括子目录。
if (dir.opendir(starg.localpath,starg.matchname)==false)
{
logfile.write("dir.opendir(%s) 失败。\n",starg.localpath); return false;
}
while (true)
{
if (dir.readdir()==false) break;
vfromdir.emplace_back(dir.m_filename,dir.m_mtime);
}
return true;
}
// 加载starg.okfilename文件中的内容到容器mfromok中。
bool loadokfile()
{
mfromok.clear();
cifile ifile;
// 注意如果程序是第一次上传starg.okfilename是不存在的并不是错误所以也返回true。
if ( (ifile.open(starg.okfilename))==false ) return true;
string strbuffer;
struct st_fileinfo stfileinfo;
while (true)
{
stfileinfo.clear();
if (ifile.readline(strbuffer)==false) break;
getxmlbuffer(strbuffer,"filename",stfileinfo.filename);
getxmlbuffer(strbuffer,"mtime",stfileinfo.mtime);
mfromok[stfileinfo.filename]=stfileinfo.mtime;
}
//for (auto &aa:mfromok)
// logfile.write("filename=%s,mtime=%s\n",aa.first.c_str(),aa.second.c_str());
return true;
}
bool compmap()
{
vtook.clear();
vupload.clear();
// 遍历vfromdir。
for (auto &aa:vfromdir)
{
auto it=mfromok.find(aa.filename);
if (it !=mfromok.end())
{
// 找到了,如果时间也相同,不需要上传,否则需要重新上传。
if (it->second==aa.mtime) vtook.push_back(aa);
else vupload.push_back(aa);
}
else
{ // 如果没有找到把记录放入vupload容器。
vupload.push_back(aa);
}
}
return true;
}
// 把容器vtook中的内容写入starg.okfilename文件覆盖之前的旧starg.okfilename文件。
bool writetookfile()
{
cofile ofile;
if (ofile.open(starg.okfilename)==false)
{
logfile.write("file.open(%s) failed.\n",starg.okfilename); return false;
}
for (auto &aa:vtook)
ofile.writeline("<filename>%s</filename><mtime>%s</mtime>\n",aa.filename.c_str(),aa.mtime.c_str());
ofile.closeandrename();
return true;
}
bool appendtookfile(struct st_fileinfo &stfileinfo)
{
cofile ofile;
// 以追加的方式打开文件注意第二个参数一定要填false。
if (ofile.open(starg.okfilename,false,ios::app)==false)
{
logfile.write("file.open(%s) failed.\n",starg.okfilename); return false;
}
ofile.writeline("<filename>%s</filename><mtime>%s</mtime>\n",stfileinfo.filename.c_str(),stfileinfo.mtime.c_str());
return true;
}

13
makefile Normal file
View File

@ -0,0 +1,13 @@
all: query upload download
query:query.cpp
g++ -g -o query query.cpp -I/home/eviwbh/FTP/EL /home/eviwbh/FTP/EL/_el.cpp /home/eviwbh/FTP/EL/_ftp.cpp /home/eviwbh/FTP/EL/libftp.a
upload:upload.cpp
g++ -g -o upload upload.cpp -I/home/eviwbh/FTP/EL /home/eviwbh/FTP/EL/_el.cpp /home/eviwbh/FTP/EL/_ftp.cpp /home/eviwbh/FTP/EL/libftp.a
download:download.cpp
g++ -g -o download download.cpp -I/home/eviwbh/FTP/EL /home/eviwbh/FTP/EL/_el.cpp /home/eviwbh/FTP/EL/_ftp.cpp /home/eviwbh/FTP/EL/libftp.a
clean:
rm -f query upload download

41
query.cpp Normal file
View File

@ -0,0 +1,41 @@
/*
* cftpclient类获取ftp服务器上的文件列表
*/
#include "_ftp.h"
using namespace eviwbh;
int main(int argc,char *argv[])
{
cftpclient ftp;
// 登录远程ftp服务器请改为你自己服务器的ip地址。
if (ftp.login("127.0.0.1:21","eviwbh","password") == false)
{
printf("ftp.login(127.0.0.1:21,eviwbh/password) failed.\n"); return -1;
}
// 获取服务器上/project/public/*.h文件列表保存在本地的/tmp/list/tmp.list文件中。
// 如果/tmp/list目录不存在会自动创建它。
if (ftp.nlist("/project/public/*.h","/tmp/list/tmp.list")==false)
{
printf("ftp.nlist() failed.\n"); return -1;
}
cout << "ret=" << ftp.response() << endl;
cifile ifile; // 采用开发框架的cifile类来操作list文件。
string strFileName;
ifile.open("/tmp/list/tmp.list"); // 打开list文件。
while(true) // 获取每个文件的时间和大小。
{
if (ifile.readline(strFileName)==false) break;
ftp.mtime(strFileName); // 获取文件时间。
ftp.size(strFileName); // 获取文件大小。
printf("filename=%s,mtime=%s,size=%d\n",strFileName.c_str(),ftp.m_mtime.c_str(),ftp.m_size);
}
}

33
upload.cpp Normal file
View File

@ -0,0 +1,33 @@
/*
* cftpclient类上传文件
*/
#include "_ftp.h"
using namespace eviwbh;
int main(int argc,char *argv[])
{
cftpclient ftp;
// 登录远程ftp服务器请改为你自己服务器的ip地址。
if (ftp.login("192.168.150.128:21","wucz","oracle") == false)
{
printf("ftp.login(192.168.150.128:21,wucz/oracle) failed.\n"); return -1;
}
// 在ftp服务器上创建/home/wucz/tmp注意如果目录已存在会返回失败。
if (ftp.mkdir("/home/wucz/tmp")==false) { printf("ftp.mkdir() failed.\n"); return -1; }
// 把ftp服务器上的工作目录切换到/home/wucz/tmp
if (ftp.chdir("/home/wucz/tmp")==false) { printf("ftp.chdir() failed.\n"); return -1; }
// 把本地的demo51.cpp上传到ftp服务器的当前工作目录。
if (ftp.put("demo51.cpp","demo51.cpp")==true)
printf("put demo51.cpp ok.\n");
else
printf("put demo51.cpp failed.\n");
// 如果不调用chdir切换工作目录以下代码采用全路径上传文件。
// ftp.put("/project/public/demo/demo51.cpp","/home/wucz/tmp/demo51.cpp");
}