/***************************************************************************************** * _el.cpp,公共函数和类定义文件。 /*****************************************************************************************/ #include "_el.h" namespace eviwbh { char *deletelchr(char* str, const int cc) { if (str == nullptr) return nullptr; // 如果传进来的是空地址,直接返回,防止程序崩溃。 char* p = str; // 指向字符串的首地址。 while (*p == cc) // 遍历字符串,p将指向左边第一个不是cc的字符。 p++; memmove(str, p, strlen(str) - (p - str)+1); // 把结尾标志0也拷过来。 return str; } string& deletelchr(string &str, const int cc) { auto pos=str.find_first_not_of(cc); // 从字符串的左边查找第一个不是cc的字符的位置。 if (pos!= 0) str.replace(0,pos,""); // 把0-pos之间的字符串替换成空。 return str; } char* deleterchr(char *str,const int cc) { if (str == nullptr) return nullptr; // 如果传进来的是空地址,直接返回,防止程序崩溃。 char* p = str; // 指向字符串的首地址。 char* piscc = 0; // 右边全是字符cc的第一个位置。 while (*p != 0) // 遍历字符串。 { if (*p == cc && piscc == 0) piscc = p; // 记下字符cc的第一个位置。 if (*p != cc) piscc = 0; // 只要当前字符不是cc,清空piscc。 p++; } if (piscc != 0) *piscc = 0; // 把piscc位置的字符置为0,表示字符串已结束。 return str; } string& deleterchr(string &str,const int cc) { auto pos=str.find_last_not_of(cc); // 从字符串的右边查找第一个不是cc的字符的位置。 if (pos!= 0) str.erase(pos+1); // 把pos之后的字符删掉。 return str; } char* deletelrchr(char *str,const int cc) { deletelchr(str,cc); deleterchr(str,cc); return str; } string& deletelrchr(string &str,const int cc) { deletelchr(str,cc); deleterchr(str,cc); return str; } char* toupper(char *str) { if (str == nullptr) return nullptr; char* p = str; // 指向字符串的首地址。 while (*p != 0) // 遍历字符串。 { if ( (*p >= 'a') && (*p <= 'z') ) *p=*p - 32; p++; } return str; } string& toupper(string &str) { for (auto &cc:str) { if ( (cc >= 'a') && (cc <= 'z') ) cc=cc - 32; } return str; } char* tolower(char *str) { if (str == nullptr) return nullptr; char* p = str; // 指向字符串的首地址。 while (*p != 0) // 遍历字符串。 { if ( (*p >= 'A') && (*p <= 'Z') ) *p=*p + 32; p++; } return str; } string& tolower(string &str) { for (auto &cc:str) { if ( (cc >= 'A') && (cc <= 'Z') ) cc=cc + 32; } return str; } bool replacestr(string &str,const string &str1,const string &str2,bool bloop) { // 如果原字符串str或旧的内容str1为空,没有意义,不执行替换。 if ( (str.length() == 0) || (str1.length() == 0) ) return false; // 如果bloop为true并且str2中包函了str1的内容,直接返回,因为会进入死循环,最终导致内存溢出。 if ( (bloop==true) && (str2.find(str1)!=string::npos) ) return false; int pstart=0; // 如果bloop==false,下一次执行替换的开始位置。 int ppos=0; // 本次需要替换的位置。 while (true) { if (bloop == true) ppos=str.find(str1); // 每次从字符串的最左边开始查找子串str1。 else ppos=str.find(str1,pstart); // 从上次执行替换的位置后开始查找子串str1。 if (ppos == string::npos) break; // 如果没有找到子串str1。 str.replace(ppos,str1.length(),str2); // 把str1替换成str2。 if (bloop == false) pstart=ppos+str2.length(); // 下一次执行替换的开始位置往右移动。 } return true; } bool replacestr(char *str,const string &str1,const string &str2,bool bloop) { if (str == nullptr) return false; string strtemp(str); replacestr(strtemp,str1,str2,bloop); strtemp.copy(str,strtemp.length()); str[strtemp.length()]=0; // string的copy函数不会给C风格字符串的结尾加0。 return true; } char* picknumber(const string &src,char *dest,const bool bsigned,const bool bdot) { if (dest==nullptr) return nullptr; // 判断空指针。 string strtemp=picknumber(src,bsigned,bdot); strtemp.copy(dest,strtemp.length()); dest[strtemp.length()]=0; // string的copy函数不会给C风格字符串的结尾加0。 return dest; } string& picknumber(const string &src,string &dest,const bool bsigned,const bool bdot) { // 为了支持src和dest是同一变量的情况,定义str临时变量。 string str; for (char cc:src) { // 判断是否提取符号。 if ( (bsigned==true) && ( (cc == '+') || (cc == '-') )) { str.append(1,cc); continue; } // 判断是否提取小数点。 if ( (bdot==true) && (cc == '.') ) { str.append(1,cc); continue; } // 提取数字。 if (isdigit(cc)) str.append(1,cc); } dest=str; return dest; } string picknumber(const string &src,const bool bsigned,const bool bdot) { string dest; picknumber(src,dest,bsigned,bdot); return dest; } bool matchstr(const string &str,const string &rules) { // 如果匹配规则表达式的内容是空的,返回false。 if (rules.length() == 0) return false; // 如果如果匹配规则表达式的内容是"*",直接返回true。 if (rules == "*") return true; int ii,jj; int pos1,pos2; ccmdstr cmdstr,cmdsubstr; string filename=str; string matchstr=rules; // 把字符串都转换成大写后再来比较 toupper(filename); toupper(matchstr); cmdstr.splittocmd(matchstr,","); for (ii=0;ii=m_cmdstr.size()) return false; // 从xml中截取数据项的内容。 // 视频中是以下代码: // value=m_cmdstr[ii]; // 改为: int itmplen=m_cmdstr[ii].length(); if ( (ilen>0) && (ilen=m_cmdstr.size()) || (value==nullptr) ) return false; if (len>0) memset(value,0,len+1); // 调用者必须保证value的空间足够,否则这里会内存溢出。 if ( (m_cmdstr[ii].length()<=(unsigned int)len) || (len==0) ) { m_cmdstr[ii].copy(value,m_cmdstr[ii].length()); value[m_cmdstr[ii].length()]=0; // string的copy函数不会给C风格字符串的结尾加0。 } else { m_cmdstr[ii].copy(value,len); value[len]=0; } return true; } bool ccmdstr::getvalue(const int ii,int &value) const { if (ii>=m_cmdstr.size()) return false; try { value = stoi(picknumber(m_cmdstr[ii],true)); // stoi有异常,需要处理异常。 } catch(const std::exception& e) { return false; } return true; } bool ccmdstr::getvalue(const int ii,unsigned int &value) const { if (ii>=m_cmdstr.size()) return false; try { value = stoi(picknumber(m_cmdstr[ii])); // stoi有异常,需要处理异常。不提取符号 + - } catch(const std::exception& e) { return false; } return true; } bool ccmdstr::getvalue(const int ii,long &value) const { if (ii>=m_cmdstr.size()) return false; try { value = stol(picknumber(m_cmdstr[ii],true)); // stol有异常,需要处理异常。 } catch(const std::exception& e) { return false; } return true; } bool ccmdstr::getvalue(const int ii,unsigned long &value) const { if (ii>=m_cmdstr.size()) return false; try { value = stoul(picknumber(m_cmdstr[ii])); // stoul有异常,需要处理异常。不提取符号 + - } catch(const std::exception& e) { return false; } return true; } bool ccmdstr::getvalue(const int ii,double &value) const { if (ii>=m_cmdstr.size()) return false; try { value = stod(picknumber(m_cmdstr[ii],true,true)); // stod有异常,需要处理异常。提取符号和小数点。 } catch(const std::exception& e) { return false; } return true; } bool ccmdstr::getvalue(const int ii,float &value) const { if (ii>=m_cmdstr.size()) return false; try { value = stof(picknumber(m_cmdstr[ii],true,true)); // stof有异常,需要处理异常。提取符号和小数点。 } catch(const std::exception& e) { return false; } return true; } bool ccmdstr::getvalue(const int ii,bool &value) const { if (ii>=m_cmdstr.size()) return false; string str=m_cmdstr[ii]; toupper(str); // 转换为大写来判断。 if (str=="TRUE") value=true; else value=false; return true; } ccmdstr::~ccmdstr() { m_cmdstr.clear(); } ostream& operator<<(ostream& out, const ccmdstr& cmdstr) { for (int ii=0;ii"; // 数据项开始的标签。 string end=""; // 数据项结束的标签。 int startp=xmlbuffer.find(start); // 在xml中查找数据项开始的标签的位置。 if (startp==string::npos) return false; int endp=xmlbuffer.find(end); // 在xml中查找数据项结束的标签的位置。 if (endp==string::npos) return false; // 从xml中截取数据项的内容。 // 视频中是以下代码: // value=xmlbuffer.substr(startp+start.length(),endp-startp-start.length()); // 改为: int itmplen=endp-startp-start.length(); if ( (ilen>0) && (ilen0) memset(value,0,len+1); // 调用者必须保证value的空间足够,否则这里会内存溢出。 string str; getxmlbuffer(xmlbuffer,fieldname,str); if ( (str.length()<=(unsigned int)len) || (len==0) ) { str.copy(value,str.length()); value[str.length()]=0; // string的copy函数不会给C风格字符串的结尾加0。 } else { str.copy(value,len); value[len]=0; } return true; } bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,bool &value) { string str; if (getxmlbuffer(xmlbuffer,fieldname,str)==false) return false; toupper(str); // 转换为大写来判断(也可以转换为小写,效果相同)。 if (str=="TRUE") value=true; else value=false; return true; } bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,int &value) { string str; if (getxmlbuffer(xmlbuffer,fieldname,str)==false) return false; try { value = stoi(picknumber(str,true)); // stoi有异常,需要处理异常。 } catch(const std::exception& e) { return false; } return true; } bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,unsigned int &value) { string str; if (getxmlbuffer(xmlbuffer,fieldname,str)==false) return false; try { value = stoi(picknumber(str)); // stoi有异常,需要处理异常。不提取符号 + - } catch(const std::exception& e) { return false; } return true; } bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,long &value) { string str; if (getxmlbuffer(xmlbuffer,fieldname,str)==false) return false; try { value = stol(picknumber(str,true)); // stol有异常,需要处理异常。 } catch(const std::exception& e) { return false; } return true; } bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,unsigned long &value) { string str; if (getxmlbuffer(xmlbuffer,fieldname,str)==false) return false; try { value = stoul(picknumber(str)); // stoul有异常,需要处理异常。不提取符号 + - } catch(const std::exception& e) { return false; } return true; } bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,double &value) { string str; if (getxmlbuffer(xmlbuffer,fieldname,str)==false) return false; try { value = stod(picknumber(str,true,true)); // stod有异常,需要处理异常。提取符号和小数点。 } catch(const std::exception& e) { return false; } return true; } bool getxmlbuffer(const string &xmlbuffer,const string &fieldname,float &value) { string str; if (getxmlbuffer(xmlbuffer,fieldname,str)==false) return false; try { value = stof(picknumber(str,true,true)); // stof有异常,需要处理异常。提取符号和小数点。 } catch(const std::exception& e) { return false; } return true; } // 把整数表示的时间转换为字符串表示的时间。 // ttime:整数表示的时间。 // strtime:字符串表示的时间。 // fmt:输出字符串时间strtime的格式,与ttime函数的fmt参数相同,如果fmt的格式不正确,strtime将为空。 string& timetostr(const time_t ttime,string &strtime,const string &fmt) { //struct tm sttm = *localtime ( &ttime ); // 非线程安全。 struct tm sttm; localtime_r (&ttime,&sttm); // 线程安全。 sttm.tm_year=sttm.tm_year+1900; // tm.tm_year成员要加上1900。 sttm.tm_mon++; // sttm.tm_mon成员是从0开始的,要加1。 // 缺省的时间格式。 if ( (fmt=="") || (fmt=="yyyy-mm-dd hh24:mi:ss") ) { strtime=sformat("%04u-%02u-%02u %02u:%02u:%02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday,\ sttm.tm_hour,sttm.tm_min,sttm.tm_sec); return strtime; } if (fmt=="yyyy-mm-dd hh24:mi") { strtime=sformat("%04u-%02u-%02u %02u:%02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday,\ sttm.tm_hour,sttm.tm_min); return strtime; } if (fmt=="yyyy-mm-dd hh24") { strtime=sformat("%04u-%02u-%02u %02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday,sttm.tm_hour); return strtime; } if (fmt=="yyyy-mm-dd") { strtime=sformat("%04u-%02u-%02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday); return strtime; } if (fmt=="yyyy-mm") { strtime=sformat("%04u-%02u",sttm.tm_year,sttm.tm_mon); return strtime; } if (fmt=="yyyymmddhh24miss") { strtime=sformat("%04u%02u%02u%02u%02u%02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday,\ sttm.tm_hour,sttm.tm_min,sttm.tm_sec); return strtime; } if (fmt=="yyyymmddhh24mi") { strtime=sformat("%04u%02u%02u%02u%02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday,\ sttm.tm_hour,sttm.tm_min); return strtime; } if (fmt=="yyyymmddhh24") { strtime=sformat("%04u%02u%02u%02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday,sttm.tm_hour); return strtime; } if (fmt=="yyyymmdd") { strtime=sformat("%04u%02u%02u",sttm.tm_year,sttm.tm_mon,sttm.tm_mday); return strtime; } if (fmt=="hh24miss") { strtime=sformat("%02u%02u%02u",sttm.tm_hour,sttm.tm_min,sttm.tm_sec); return strtime; } if (fmt=="hh24mi") { strtime=sformat("%02u%02u",sttm.tm_hour,sttm.tm_min); return strtime; } if (fmt=="hh24") { strtime=sformat("%02u",sttm.tm_hour); return strtime; } if (fmt=="mi") { strtime=sformat("%02u",sttm.tm_min); return strtime; } return strtime; } char* timetostr(const time_t ttime,char *strtime,const string &fmt) { if (strtime==nullptr) return nullptr; // 判断空指针。 string str; timetostr(ttime,str,fmt); // 直接调用string& timetostr(const time_t ttime,string &strtime,const string &fmt=""); str.copy(strtime,str.length()); strtime[str.length()]=0; // string的copy函数不会给C风格字符串的结尾加0。 return strtime; } string timetostr1(const time_t ttime,const string &fmt) { string str; timetostr(ttime,str,fmt); // 直接调用string& timetostr(const time_t ttime,string &strtime,const string &fmt=""); return str; } string& ltime(string &strtime,const string &fmt,const int timetvl) { time_t timer; time(&timer ); // 获取系统当前时间。 timer=timer+timetvl; // 加上时间的偏移量。 timetostr(timer,strtime,fmt); // 把整数表示的时间转换为字符串表示的时间。 return strtime; } char* ltime(char *strtime,const string &fmt,const int timetvl) { if (strtime==nullptr) return nullptr; // 判断空指针。 time_t timer; time(&timer ); // 获取系统当前时间。 timer=timer+timetvl; // 加上时间的偏移量。 timetostr(timer,strtime,fmt); // 把整数表示的时间转换为字符串表示的时间。 return strtime; } string ltime1(const string &fmt,const int timetvl) { string strtime; ltime(strtime,fmt,timetvl); // 直接调用string& ltime(string &strtime,const string &fmt="",const int timetvl=0); return strtime; } bool clogfile::open(const string &filename,const ios::openmode mode,const bool bbackup,const bool benbuffer) { // 如果日志文件是打开的状态,先关闭它。 if (fout.is_open()) fout.close(); m_filename=filename; // 日志文件名。 m_mode=mode; // 打开模式。 m_backup=bbackup; // 是否自动备份。 m_enbuffer=benbuffer; // 是否启用文件缓冲区。 newdir(m_filename,true); // 如果日志文件的目录不存在,创建它。 fout.open(m_filename,m_mode); // 打开日志文件。 if (m_enbuffer==false) fout << unitbuf; // 是否启用文件缓冲区。 return fout.is_open(); } bool clogfile::backup() { // 不备份 if (m_backup == false) return true; if (fout.is_open() == false) return false; // 如果当前日志文件的大小超过m_maxsize,备份日志。 if (fout.tellp() > m_maxsize*1024*1024) { m_splock.lock(); // 加锁。 fout.close(); // 关闭当前日志文件。 // 拼接备份日志文件名。 string bak_filename=m_filename+"."+ltime1("yyyymmddhh24miss"); rename(m_filename.c_str(),bak_filename.c_str()); // 把当前日志文件改名为备份日志文件。 fout.open(m_filename,m_mode); // 重新打开当前日志文件。 if (m_enbuffer==false) fout << unitbuf; // 判断是否启动文件缓冲区。 m_splock.unlock(); // 解锁。 return fout.is_open(); } return true; } bool cifile::open(const string &filename,const ios::openmode mode) { // 如果文件是打开的状态,先关闭它。 if (fin.is_open()) fin.close(); m_filename=filename; fin.open(m_filename,mode); return fin.is_open(); } int cifile::read(void *buf,const int bufsize) { // fin.read((char *)buf,bufsize); fin.read(static_cast(buf),bufsize); return fin.gcount(); // 返回读取的字节数。 } bool cifile::closeandremove() { if (fin.is_open()==false) return false; fin.close(); if (remove(m_filename.c_str())!=0) return false; return true; } void cifile::close() { if (fin.is_open()==false) return; fin.close(); } bool cifile::readline(string &buf,const string& endbz) { buf.clear(); // 清空buf。 string strline; // 存放从文件中读取的一行。 while (true) { getline(fin,strline); // 从文件中读取一行。 if (fin.eof()) break; // 如果文件已读完。 buf=buf+strline; // 把读取的内容拼接到buf中。 if (endbz=="") return true; // 如果行没有结尾标志。 else { // 如果行有结尾标志,判断本次是否读到了结尾标志,如果没有,继续读,如果有,返回。 if (buf.find(endbz,buf.length()-endbz.length()) != string::npos) return true; } buf=buf+"\n"; // getline从文件中读取一行的时候,会删除\n,所以,这里要补上\n,因为这个\n不应该被删除。 } return false; } bool cofile::open(const string &filename,const bool btmp,const ios::openmode mode,const bool benbuffer) { // 如果文件是打开的状态,先关闭它。 if (fout.is_open()) fout.close(); m_filename=filename; newdir(m_filename,true); // 如果文件的目录不存在,创建目录。 if (btmp==true) { // 采用临时文件的方案。 m_filenametmp=m_filename+".tmp"; fout.open(m_filenametmp,mode); } else { // 不采用临时文件的方案。 m_filenametmp.clear(); fout.open(m_filename,mode); } // 不启用文件缓冲区。 if (benbuffer==false) fout << unitbuf; return fout.is_open(); } bool cofile::write(void *buf,int bufsize) { if (fout.is_open()==false) return false; // fout.write((char *)buf,bufsize); fout.write(static_cast(buf),bufsize); return fout.good(); } // 关闭文件,并且把临时文件名改为正式文件名。 bool cofile::closeandrename() { if (fout.is_open()==false) return false; fout.close(); // 如果采用了临时文件的方案。 if (m_filenametmp.empty()==false) if (rename(m_filenametmp.c_str(),m_filename.c_str())!=0) return false; return true; } // 关闭文件,删除临时文件。 void cofile::close() { if (fout.is_open()==false) return; fout.close(); // 如果采用了临时文件的方案。 if (m_filenametmp.empty()==false) remove(m_filenametmp.c_str()); } bool newdir(const string &pathorfilename,bool bisfilename) { // /tmp/aaa/bbb/ccc/ddd /tmp /tmp/aaa /tmp/aaa/bbb /tmp/aaa/bbb/ccc // 检查目录是否存在,如果不存在,逐级创建子目录 int pos=1; // 不要从0开始,0是根目录/。 while (true) { int pos1=pathorfilename.find('/',pos); if (pos1==string::npos) break; string strpathname=pathorfilename.substr(0,pos1); // 截取目录。 pos=pos1+1; // 位置后移。 if (access(strpathname.c_str(),F_OK) != 0) // 如果目录不存在,创建它。 { // 0755是八进制,不要写成755。 if (mkdir(strpathname.c_str(),0755) != 0) return false; // 如果目录不存在,创建它。 } } // 如果pathorfilename不是文件,是目录,还需要创建最后一级子目录。 if (bisfilename==false) { if (access(pathorfilename.c_str(),F_OK) != 0) { if (mkdir(pathorfilename.c_str(),0755) != 0) return false; } } return true; } int filesize(const string &filename) { struct stat st_filestat; // 存放文件信息的结构体。 // 获取文件信息,存放在结构体中。 if (stat(filename.c_str(),&st_filestat) < 0) return -1; return st_filestat.st_size; // 返回结构体的文件大小成员。 } bool setmtime(const string &filename,const string &mtime) { struct utimbuf stutimbuf; stutimbuf.actime=stutimbuf.modtime=strtotime(mtime); if (utime(filename.c_str(),&stutimbuf)!=0) return false; return true; } time_t strtotime(const string &strtime) { string strtmp,yyyy,mm,dd,hh,mi,ss; picknumber(strtime,strtmp,false,false); // 把字符串中的数字全部提取出来。 // 2021-12-05 08:30:45 // 2021/12/05 08:30:45 // 20211205083045 if (strtmp.length() != 14) return -1; // 如果时间格式不是yyyymmddhh24miss,说明时间格式不正确。 yyyy=strtmp.substr(0,4); mm=strtmp.substr(4,2); dd=strtmp.substr(6,2); hh=strtmp.substr(8,2); mi=strtmp.substr(10,2); ss=strtmp.substr(12,2); struct tm sttm; try { sttm.tm_year = stoi(yyyy) - 1900; sttm.tm_mon = stoi(mm) - 1; sttm.tm_mday = stoi(dd); sttm.tm_hour = stoi(hh); sttm.tm_min = stoi(mi); sttm.tm_sec = stoi(ss); sttm.tm_isdst = 0; } catch(const std::exception& e) { return -1; } return mktime(&sttm); } bool addtime(const string &in_stime,string &out_stime,const int timetvl,const string &fmt) { time_t timer; // 把字符串表示的时间转换为整数表示的时间,方便运算。 if ( (timer=strtotime(in_stime))==-1) { out_stime=""; return false; } timer=timer+timetvl; // 时间运算。 // 把整数表示的时间转换为字符串表示的时间。 timetostr(timer,out_stime,fmt); return true; } bool addtime(const string &in_stime,char *out_stime,const int timetvl,const string &fmt) { if (out_stime==nullptr) return false; // 判断空指针。 time_t timer; // 把字符串表示的时间转换为整数表示的时间,方便运算。 if ( (timer=strtotime(in_stime))==-1) { strcpy(out_stime,""); return false; } timer=timer+timetvl; // 时间运算。 // 把整数表示的时间转换为字符串表示的时间。 timetostr(timer,out_stime,fmt); return true; } bool filemtime(const string &filename,string &mtime,const string &fmt) { struct stat st_filestat; // 存放文件信息的结构体。 // 获取文件信息,存放在结构体中。 if (stat(filename.c_str(),&st_filestat) < 0) return false; // 把整数表示的时间转换成字符串表示的时间。 timetostr(st_filestat.st_mtime,mtime,fmt); return true; } bool filemtime(const string &filename,char *mtime,const string &fmt) { struct stat st_filestat; // 存放文件信息的结构体。 // 获取文件信息,存放在结构体中。 if (stat(filename.c_str(),&st_filestat) < 0) return false; // 把整数表示的时间转换成字符串表示的时间。 timetostr(st_filestat.st_mtime,mtime,fmt); return true; } void cdir::setfmt(const string &fmt) { m_fmt=fmt; } bool cdir::opendir(const string &dirname,const string &rules,const int maxfiles,const bool bandchild,bool bsort) { m_filelist.clear(); // 清空文件列表容器。 m_pos=0; // 从文件列表中已读取文件的位置归0。 // 如果目录不存在,创建它。 if (newdir(dirname,false) == false) return false; // 打开目录,获取目录中的文件列表,存放在m_filelist容器中。 bool ret=_opendir(dirname,rules,maxfiles,bandchild); if (bsort==true) // 对文件列表排序。 { sort(m_filelist.begin(), m_filelist.end()); } return ret; } // 这是一个递归函数,在opendir()中调用,cdir类的外部不需要调用它。 bool cdir::_opendir(const string &dirname,const string &rules,const int maxfiles,const bool bandchild) { DIR *dir; // 目录指针。 // 打开目录。 if ( (dir=::opendir(dirname.c_str())) == nullptr ) return false; // opendir与库函数重名,需要加:: string strffilename; // 全路径的文件名。 struct dirent *stdir; // 存放从目录中读取的内容。 // 用循环读取目录的内容,将得到目录中的文件名和子目录。 while ((stdir=::readdir(dir)) != 0) // readdir与库函数重名,需要加:: { // 判断容器中的文件数量是否超出maxfiles参数。 if ( m_filelist.size()>=maxfiles ) break; // 文件名以"."打头的文件不处理。.是当前目录,..是上一级目录,其它以.打头的都是特殊目录和文件。 if (stdir->d_name[0]=='.') continue; // 拼接全路径的文件名。 strffilename=dirname+'/'+stdir->d_name; // 如果是目录,处理各级子目录。 if (stdir->d_type==4) { if (bandchild == true) // 打开各级子目录。 { if (_opendir(strffilename,rules,maxfiles,bandchild) == false) // 递归调用_opendir函数。 { closedir(dir); return false; } } } // 如果是普通文件,放入容器中。 if (stdir->d_type==8) { // 把能匹配上的文件放入m_filelist容器中。 if (matchstr(stdir->d_name,rules) == false) continue; m_filelist.push_back(std::move(strffilename)); } } closedir(dir); // 关闭目录。 return true; } bool cdir::readdir() { // 如果已读完,清空容器 if (m_pos >= m_filelist.size()) { m_pos=0; m_filelist.clear(); return false; } // 文件全名,包括路径 m_ffilename=m_filelist[m_pos]; // 从绝对路径的文件名中解析出目录名和文件名。 int pp=m_ffilename.find_last_of("/"); m_dirname=m_ffilename.substr(0,pp); m_filename=m_ffilename.substr(pp+1); // 获取文件的信息。 struct stat st_filestat; stat(m_ffilename.c_str(),&st_filestat); m_filesize=st_filestat.st_size; // 文件大小。 m_mtime=timetostr1(st_filestat.st_mtime,m_fmt); // 文件最后一次被修改的时间。 m_ctime=timetostr1(st_filestat.st_ctime,m_fmt); // 文件生成的时间。 m_atime=timetostr1(st_filestat.st_atime,m_fmt); // 文件最后一次被访问的时间。 m_pos++; // 已读取文件的位置后移。 return true; } cdir::~cdir() { m_filelist.clear(); } bool renamefile(const string &srcfilename,const string &dstfilename) { // 如果原文件不存在,直接返回失败。 if (access(srcfilename.c_str(),R_OK) != 0) return false; // 创建目标文件的目录。 if (newdir(dstfilename,true) == false) return false; // 调用操作系统的库函数rename重命名文件。 mv if (rename(srcfilename.c_str(),dstfilename.c_str()) == 0) return true; return false; } // 忽略关闭全部的信号、关闭全部的IO,缺省只忽略信号,不关IO。 // 不希望后台服务程序被信号打扰,需要什么信号可以在程序中设置。 // 实际上关闭的IO是0、1、2。 void closeioandsignal(bool bcloseio) { int ii=0; for (ii=0;ii<64;ii++) { if (bcloseio==true) close(ii); signal(ii,SIG_IGN); } } bool ctcpclient::connect(const string &ip,const int port) { // 如果已连接到服务端,则断开,这种处理方法没有特别的原因,不要纠结。 if (m_connfd!=-1) { ::close(m_connfd); m_connfd=-1; } // 忽略SIGPIPE信号,防止程序异常退出。 // 如果send到一个disconnected socket上,内核就会发出SIGPIPE信号。这个信号 // 的缺省处理方法是终止进程,大多数时候这都不是我们期望的。我们重新定义这 // 个信号的处理方法,大多数情况是直接屏蔽它。 signal(SIGPIPE,SIG_IGN); m_ip=ip; m_port=port; struct hostent* h; struct sockaddr_in servaddr; if ( (m_connfd = socket(AF_INET,SOCK_STREAM,0) ) < 0) return false; if ( !(h = gethostbyname(m_ip.c_str())) ) { ::close(m_connfd); m_connfd=-1; return false; } memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(m_port); // 指定服务端的通讯端口 memcpy(&servaddr.sin_addr,h->h_addr,h->h_length); if (::connect(m_connfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0) { ::close(m_connfd); m_connfd=-1; return false; } return true; } void ctcpclient::close() { if (m_connfd >= 0) ::close(m_connfd); m_connfd=-1; m_port=0; } ctcpclient::~ctcpclient() { close(); } bool ctcpserver::initserver(const unsigned int port,const int backlog) { // 如果服务端的socket>0,关掉它,这种处理方法没有特别的原因,不要纠结。 if (m_listenfd > 0) { ::close(m_listenfd); m_listenfd=-1; } if ( (m_listenfd = socket(AF_INET,SOCK_STREAM,0))<=0) return false; // 忽略SIGPIPE信号,防止程序异常退出。 // 如果往已关闭的socket继续写数据,会产生SIGPIPE信号,它的缺省行为是终止程序,所以要忽略它。 signal(SIGPIPE,SIG_IGN); // 打开SO_REUSEADDR选项,当服务端连接处于TIME_WAIT状态时可以再次启动服务器, // 否则bind()可能会不成功,报:Address already in use。 int opt = 1; setsockopt(m_listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); memset(&m_servaddr,0,sizeof(m_servaddr)); m_servaddr.sin_family = AF_INET; m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 任意ip地址。 m_servaddr.sin_port = htons(port); if (bind(m_listenfd,(struct sockaddr *)&m_servaddr,sizeof(m_servaddr)) != 0 ) { closelisten(); return false; } if (listen(m_listenfd,backlog) != 0 ) { closelisten(); return false; } return true; } bool ctcpserver::accept() { if (m_listenfd==-1) return false; int m_socklen = sizeof(struct sockaddr_in); if ((m_connfd=::accept(m_listenfd,(struct sockaddr *)&m_clientaddr,(socklen_t*)&m_socklen)) < 0) return false; return true; } char *ctcpserver::getip() { return(inet_ntoa(m_clientaddr.sin_addr)); } bool ctcpserver::read(void *buffer,const int ibuflen,const int itimeout) // 接收二进制数据。 { if (m_connfd==-1) return false; return(tcpread(m_connfd,buffer,ibuflen,itimeout)); } bool ctcpserver::read(string &buffer,const int itimeout) // 接收文本数据。 { if (m_connfd==-1) return false; return(tcpread(m_connfd,buffer,itimeout)); } bool ctcpclient::read(void *buffer,const int ibuflen,const int itimeout) // 接收二进制数据。 { if (m_connfd==-1) return false; return(tcpread(m_connfd,buffer,ibuflen,itimeout)); } bool ctcpclient::read(string &buffer,const int itimeout) // 接收文本数据。 { if (m_connfd==-1) return false; return(tcpread(m_connfd,buffer,itimeout)); } bool ctcpserver::write(const void *buffer,const int ibuflen) // 发送二进制数据。 { if (m_connfd==-1) return false; return(tcpwrite(m_connfd,(char*)buffer,ibuflen)); } bool ctcpserver::write(const string &buffer) { if (m_connfd==-1) return false; return(tcpwrite(m_connfd,buffer)); } bool ctcpclient::write(const void *buffer,const int ibuflen) { if (m_connfd==-1) return false; return(tcpwrite(m_connfd,(char*)buffer,ibuflen)); } bool ctcpclient::write(const string &buffer) { if (m_connfd==-1) return false; return(tcpwrite(m_connfd,buffer)); } void ctcpserver::closelisten() { if (m_listenfd >= 0) { ::close(m_listenfd); m_listenfd=-1; } } void ctcpserver::closeclient() { if (m_connfd >= 0) { ::close(m_connfd); m_connfd=-1; } } ctcpserver::~ctcpserver() { closelisten(); closeclient(); } bool tcpread(const int sockfd,void *buffer,const int ibuflen,const int itimeout) // 接收二进制数据。 { if (sockfd==-1) return false; // 如果itimeout>0,表示需要等待itimeout秒,如果itimeout秒后还没有数据到达,返回false。 if (itimeout>0) { struct pollfd fds; fds.fd=sockfd; fds.events=POLLIN; if ( poll(&fds,1,itimeout*1000) <= 0 ) return false; } // 如果itimeout==-1,表示不等待,立即判断socket的缓冲区中是否有数据,如果没有,返回false。 if (itimeout==-1) { struct pollfd fds; fds.fd=sockfd; fds.events=POLLIN; if ( poll(&fds,1,0) <= 0 ) return false; } // 读取报文内容。 if (readn(sockfd,(char*)buffer,ibuflen) == false) return false; return true; } bool tcpread(const int sockfd,string &buffer,const int itimeout) // 接收文本数据。 { if (sockfd==-1) return false; // 如果itimeout>0,表示等待itimeout秒,如果itimeout秒后接收缓冲区中还没有数据,返回false。 if (itimeout>0) { struct pollfd fds; fds.fd=sockfd; fds.events=POLLIN; if ( poll(&fds,1,itimeout*1000) <= 0 ) return false; } // 如果itimeout==-1,表示不等待,立即判断socket的接收缓冲区中是否有数据,如果没有,返回false。 if (itimeout==-1) { struct pollfd fds; fds.fd=sockfd; fds.events=POLLIN; if ( poll(&fds,1,0) <= 0 ) return false; } int buflen=0; // 先读取报文长度,4个字节。 if (readn(sockfd,(char*)&buflen,4) == false) return false; buffer.resize(buflen); // 设置buffer的大小。 // 再读取报文内容。 if (readn(sockfd,&buffer[0],buflen) == false) return false; return true; } bool tcpwrite(const int sockfd,const void *buffer,const int ibuflen) // 发送二进制数据。 { if (sockfd==-1) return false; if (writen(sockfd,(char*)buffer,ibuflen) == false) return false; return true; } bool tcpwrite(const int sockfd,const string &buffer) // 发送文本数据。 { if (sockfd==-1) return false; int buflen=buffer.size(); // 先发送报头。 if (writen(sockfd,(char*)&buflen,4) == false) return false; // 再发送报文体。 if (writen(sockfd,buffer.c_str(),buflen) == false) return false; return true; } // 从已经准备好的socket中读取数据。 // sockfd:已经准备好的socket连接。 // buffer:接收数据缓冲区的地址。 // n:本次接收数据的字节数。 // 返回值:成功接收到n字节的数据后返回true,socket连接不可用返回false。 bool readn(const int sockfd,char *buffer,const size_t n) { int nleft=n; // 剩余需要读取的字节数。 int idx=0; // 已成功读取的字节数。 int nread; // 每次调用recv()函数读到的字节数。 while(nleft > 0) { if ( (nread=recv(sockfd,buffer+idx,nleft,0)) <= 0) return false; idx=idx+nread; nleft=nleft-nread; } return true; } // 向已经准备好的socket中写入数据。 // sockfd:已经准备好的socket连接。 // buffer:待发送数据缓冲区的地址。 // n:待发送数据的字节数。 // 返回值:成功发送完n字节的数据后返回true,socket连接不可用返回false。 bool writen(const int sockfd,const char *buffer,const size_t n) { int nleft=n; // 剩余需要写入的字节数。 int idx=0; // 已成功写入的字节数。 int nwritten; // 每次调用send()函数写入的字节数。 while(nleft > 0 ) { if ( (nwritten=send(sockfd,buffer+idx,nleft,0)) <= 0) return false; nleft=nleft-nwritten; idx=idx+nwritten; } return true; } bool copyfile(const string &srcfilename,const string &dstfilename) { // 创建目标文件的目录。 if (newdir(dstfilename,true) == false) return false; cifile ifile; cofile ofile; int ifilesize=filesize(srcfilename); int total_bytes=0; int onread=0; char buffer[5000]; if (ifile.open(srcfilename,ios::in|ios::binary)==false) return false; if (ofile.open(dstfilename,ios::out|ios::binary)==false) return false; while (true) { if ((ifilesize-total_bytes) > 5000) onread=5000; else onread=ifilesize-total_bytes; memset(buffer,0,sizeof(buffer)); ifile.read(buffer,onread); ofile.write(buffer,onread); total_bytes = total_bytes + onread; if (total_bytes == ifilesize) break; } ifile.close(); ofile.closeandrename(); // 更改文件的修改时间属性 string strmtime; filemtime(srcfilename,strmtime); setmtime(dstfilename,strmtime); return true; } ctimer::ctimer() { start(); // 计时开始。 } // 计时开始。 void ctimer::start() { memset(&m_start,0,sizeof(struct timeval)); memset(&m_end,0,sizeof(struct timeval)); gettimeofday(&m_start, 0); // 获取当前时间,精确到微秒。 } // 计算已逝去的时间,单位:秒,小数点后面是微秒 // 每调用一次本方法之后,自动调用Start方法重新开始计时。 double ctimer::elapsed() { gettimeofday(&m_end,0); // 获取当前时间作为计时结束的时间,精确到微秒。 string str; str=sformat("%ld.%06ld",m_start.tv_sec,m_start.tv_usec); double dstart=stod(str); // 把计时开始的时间点转换为double。 str=sformat("%ld.%06ld",m_end.tv_sec,m_end.tv_usec); double dend=stod(str); // 把计时结束的时间点转换为double。 start(); // 重新开始计时。 return dend-dstart; } cpactive::cpactive() { m_shmid=0; m_pos=-1; m_shm=0; } // 把当前进程的信息加入共享内存进程组中。 bool cpactive::addpinfo(const int timeout,const string &pname,clogfile *logfile) { if (m_pos!=-1) return true; // 创建/获取共享内存,键值为SHMKEYP,大小为MAXNUMP个st_procinfo结构体的大小。 if ( (m_shmid = shmget((key_t)SHMKEYP, MAXNUMP*sizeof(struct st_procinfo), 0666|IPC_CREAT)) == -1) { if (logfile!=nullptr) logfile->write("创建/获取共享内存(%x)失败。\n",SHMKEYP); else printf("创建/获取共享内存(%x)失败。\n",SHMKEYP); return false; } // 将共享内存连接到当前进程的地址空间。 m_shm=(struct st_procinfo *)shmat(m_shmid, 0, 0); /* struct st_procinfo stprocinfo; // 当前进程心跳信息的结构体。 memset(&stprocinfo,0,sizeof(stprocinfo)); stprocinfo.pid=getpid(); // 当前进程号。 stprocinfo.timeout=timeout; // 超时时间。 stprocinfo.atime=time(0); // 当前时间。 strncpy(stprocinfo.pname,pname.c_str(),50); // 进程名。 */ st_procinfo stprocinfo(getpid(),pname.c_str(),timeout,time(0)); // 当前进程心跳信息的结构体。 // 进程id是循环使用的,如果曾经有一个进程异常退出,没有清理自己的心跳信息, // 它的进程信息将残留在共享内存中,不巧的是,如果当前进程重用了它的id, // 守护进程检查到残留进程的信息时,会向进程id发送退出信号,将误杀当前进程。 // 所以,如果共享内存中已存在当前进程编号,一定是其它进程残留的信息,当前进程应该重用这个位置。 for (int ii=0;iipid==stprocinfo.pid ) { m_pos=ii; break; } } csemp semp; // 用于给共享内存加锁的信号量id。 if (semp.init(SEMKEYP) == false) // 初始化信号量。 { if (logfile!=nullptr) logfile->write("创建/获取信号量(%x)失败。\n",SEMKEYP); else printf("创建/获取信号量(%x)失败。\n",SEMKEYP); return false; } semp.wait(); // 给共享内存上锁。 // 如果m_pos==-1,表示共享内存的进程组中不存在当前进程编号,那就找一个空位置。 if (m_pos==-1) { for (int ii=0;iipid==0 ) { m_pos=ii; break; } } // 如果m_pos==-1,表示没找到空位置,说明共享内存的空间已用完。 if (m_pos==-1) { if (logfile!=0) logfile->write("共享内存空间已用完。\n"); else printf("共享内存空间已用完。\n"); semp.post(); // 解锁。 return false; } // 把当前进程的心跳信息存入共享内存的进程组中。 memcpy(m_shm+m_pos,&stprocinfo,sizeof(struct st_procinfo)); semp.post(); // 解锁。 return true; } // 更新共享内存进程组中当前进程的心跳时间。 bool cpactive::uptatime() { if (m_pos==-1) return false; (m_shm+m_pos)->atime=time(0); return true; } cpactive::~cpactive() { // 把当前进程从共享内存的进程组中移去。 if (m_pos!=-1) memset(m_shm+m_pos,0,sizeof(struct st_procinfo)); // 把共享内存从当前进程中分离。 if (m_shm!=0) shmdt(m_shm); } // 如果信号量已存在,获取信号量;如果信号量不存在,则创建它并初始化为value。 // 如果用于互斥锁,value填1,sem_flg填SEM_UNDO。 // 如果用于生产消费者模型,value填0,sem_flg填0。 bool csemp::init(key_t key,unsigned short value,short sem_flg) { if (m_semid!=-1) return false; // 如果已经初始化了,不必再次初始化。 m_sem_flg=sem_flg; // 信号量的初始化不能直接用semget(key,1,0666|IPC_CREAT) // 因为信号量创建后,初始值是0,如果用于互斥锁,需要把它的初始值设置为1, // 而获取信号量则不需要设置初始值,所以,创建信号量和获取信号量的流程不同。 // 信号量的初始化分三个步骤: // 1)获取信号量,如果成功,函数返回。 // 2)如果失败,则创建信号量。 // 3) 设置信号量的初始值。 // 获取信号量。 if ( (m_semid=semget(key,1,0666)) == -1) { // 如果信号量不存在,创建它。 if (errno==ENOENT) { // 用IPC_EXCL标志确保只有一个进程创建并初始化信号量,其它进程只能获取。 if ( (m_semid=semget(key,1,0666|IPC_CREAT|IPC_EXCL)) == -1) { if (errno==EEXIST) // 如果错误代码是信号量已存在,则再次获取信号量。 { if ( (m_semid=semget(key,1,0666)) == -1) { perror("init 1 semget()"); return false; } return true; } else // 如果是其它错误,返回失败。 { perror("init 2 semget()"); return false; } } // 信号量创建成功后,还需要把它初始化成value。 union semun sem_union; sem_union.val = value; // 设置信号量的初始值。 if (semctl(m_semid,0,SETVAL,sem_union) < 0) { perror("init semctl()"); return false; } } else { perror("init 3 semget()"); return false; } } return true; } // 信号量的P操作(把信号量的值减value),如果信号量的值是0,将阻塞等待,直到信号量的值大于0。 bool csemp::wait(short value) { if (m_semid==-1) return false; struct sembuf sem_b; sem_b.sem_num = 0; // 信号量编号,0代表第一个信号量。 sem_b.sem_op = value; // P操作的value必须小于0。 sem_b.sem_flg = m_sem_flg; if (semop(m_semid,&sem_b,1) == -1) { perror("p semop()"); return false; } return true; } // 信号量的V操作(把信号量的值减value)。 bool csemp::post(short value) { if (m_semid==-1) return false; struct sembuf sem_b; sem_b.sem_num = 0; // 信号量编号,0代表第一个信号量。 sem_b.sem_op = value; // V操作的value必须大于0。 sem_b.sem_flg = m_sem_flg; if (semop(m_semid,&sem_b,1) == -1) { perror("V semop()"); return false; } return true; } // 获取信号量的值,成功返回信号量的值,失败返回-1。 int csemp::getvalue() { return semctl(m_semid,0,GETVAL); } // 销毁信号量。 bool csemp::destroy() { if (m_semid==-1) return false; if (semctl(m_semid,0,IPC_RMID) == -1) { perror("destroy semctl()"); return false; } return true; } csemp::~csemp() { } 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); } } // namespace