【C++1】函数重载,类和对象,引用,string类,vector容器,类继承和多态,/socket,进程信号,public,ooci
文章目录
- 1.函数重载:writetofile(),C++true和false,C0和非0
- 2.类和对象:vprintf
- 2.1 构造函数:对成员变量初始化
- 2.2 析构函数:一个类只有一个,不允许被重载
- 3.引用:C中&取地址,C++中&引用。引用就像起别名,typedef,宏define。对引用的操作与对变量直接操作一样
- 4.string类:string str,str=,str.c_str()
- 5.vector容器:vector与string类一样属于STL
- 6.类继承和多态:class派生类名:public基类名
- 7.socket:send/recv
- 7.1 简单文件传输:CTcpClient,CTcpServer
- 7.2 文件下载模块:不建议将tcpgetfile.cpp,tcpfileserver.cpp反过来,虽然全双工但会出现连不上服务器被上网行为审计系统拦截
- 7.3 高性能网络服务:多线程+数据库连接池(多线程每启一个线程都要连数据库耗资源)
- 8.进程:fork(),ps -ef (同-aux) | more
- 9.信号:signal(, EXIT),jps
- 9.1 捕捉信号:ctrl+c:2
- 9.2 捕捉信号:kill -9:9
- 9.3 捕捉信号:kill:15
- 9.4 程序后台运行两种方法:&:ctrl+c无法中止,用killall book1或kill 进程号。if (fork()>0)return 0 父进程退出
- 10._public.h
- 11._public.cpp
- 12._cmpublic.h
- 13._ooci.h
- 14._ooci.cpp
1.函数重载:writetofile(),C++true和false,C0和非0
C++动态内存分配
:在C语言中,动态分配内存用malloc()函数,释放内存用free()函数。C++中new和delete。C++函数重载
:C中不允许函数同名如下:
以上为C写法,下面为C++函数重载写法。函数重载规则:1.函数名必须同
+2.参数列表必须不同
。C++是如何做到函数重载的:C++代码在编译时会根据参数列表对函数进行重命名。
2.类和对象:vprintf
上面完整,下面两行中下行是上行改进,效果一样,但没有涉及类和对象。
上面完整,下面结构体升级为类。
下面为三种show函数重载实现,如下字符串理论上可定义为char name[10],但在函数里字符串也只能传地址
,所以只能定义为char * name
。char name不行,char类型是单个字符,调用时直接给字符串值。
下面为三种Show调用。
2.1 构造函数:对成员变量初始化
CFile是类,CFile()是函数。
如下两个构造函数(该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作),属于成员函数。
2.2 析构函数:一个类只有一个,不允许被重载
3.引用:C中&取地址,C++中&引用。引用就像起别名,typedef,宏define。对引用的操作与对变量直接操作一样
引用的声明方法:类型标识符 &引用名=目标变量名;如int a; int &ra=a;
定义了引用ra,它是变量a的引用即别名。引用可以用const修饰,表示只读,用这种方式声明的引用,不能通过引用对目标变量的值进行修改。
4.string类:string str,str=,str.c_str()
C中以0结尾的字符数组表示字符串(定义后大小不可变),C++中string随着存放字符长度自动伸缩,不用担心内存溢出
。string类是一个模板类,位于std命名空间,如果不加using namespace std;就要用std::string str
。
string特性描述函数:int size()返回当前字符串大小
,int length()返回当前字符串的长度
,void clear()清空字符串
。string本质是一个类,通过动态分配内存实现对字符串的存储,string对象用于存放字符的内存地址是变化的。也就是地址存放的下
就不再重新分配,存放不下
就重新分配地址。
5.vector容器:vector与string类一样属于STL
容器的使用:1.存放整数
访问容器中元素可以像数组形式一样。
2.存放字符串
。
3.存放结构体
,4.存放类:
存放字符串中,string就是类。
vector其他成员函数:1.定位的函数
2.增加元素的函数
3.删除元素的函数
4.判断容器的大小
bool empty():判断容器是否为空
int size():返回容器中元素的个数
5.作业题:封装随机数
//此程序用于生成一组随机数, 指定数组范围和是否重复
#include"_public.h"class CRand
{
public:CRand();~CRand();vector <int> m_val; //m_val容器 bool checkexit(const int aryyval, const int aryysize); // 用于检查是否为重复数据,aryyval为重复的值,这函数不单用,用于Rand成员函数里void Rand(const int minvalue,const int maxvalue,bool brep=true, const int nog=5); //brep为是否允许重复; 默认为允许重复,nog指定生成多少个随机数
};//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
void CRand::Rand(const int minvalue,const int maxvalue,bool brep,const int nog)
{int len = maxvalue-minvalue;int ii=0, itmp=0, jtmp=0; // ii生成第几个,jtmp实际生成共多少个,itmp生成的值m_val.clear();if(brep==true) // 允许重复{jtmp = nog;for(ii=0;ii<jtmp;ii++){itmp = rand()%(len+1)+minvalue; // (0~len)+minvalue,itmp就是min~max之间的值,不是len长度m_val.push_back(itmp); }return; //return是函数直接返回, 也就是结束该函数。//要跳出循环用break, if代码段是不能用break跳出的, 在一个函数内任意位置调用return, 直接退出Rand函数,下面代码不执行。}jtmp = nog; // 以下为不允许重复 ,因为没进入if(brep==true)if (nog>len) jtmp = len + 1; // 比如5-1=4,但1到5可以生成5个,所以如果nog大于len的话就取len+1个,前提不允许重复。while(1) {if (jtmp == m_val.size()) break; //生成满了跳出循环itmp = rand()%(len+1)+minvalue;if (ii==0) // 生成第一个不用管checkexit重不重复{m_val.push_back(itmp);ii++;continue;} if (checkexit(itmp,ii) == false) continue; // checkexit为false则不允许重复m_val.push_back(itmp); ii++;}return;
}//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
bool CRand::checkexit(const int aryyval, const int aryysize) // aryyval重复的值,aryysize允许多少个重复
{for (int ii=0; ii<aryysize; ii++){if (aryyval == m_val[ii]) return false;}return true;
}CRand::~CRand()
{m_val.clear();
}CRand::CRand()
{struct timeval begin;gettimeofday(&begin, 0);srand(begin.tv_usec);
}//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
int main() //如何用CRand这个类
{CRand CrtRand;CrtRand.Rand(0, 10, false); // 若false为true允许重复,不管范围多少取nog个for(int ii=0;ii<CrtRand.m_val.size();ii++){printf("%d\n",CrtRand.m_val[ii]);} return 0;
}
6.类继承和多态:class派生类名:public基类名
如下子类可直接用父类属性和方法。
类多态:子类必重写父类纯虚函数
7.socket:send/recv
服务端:
客户端:
1.send函数。
2.recv函数。
1.socket函数
7.1 简单文件传输:CTcpClient,CTcpServer
// 本程序演示采用CTcpClient类,实现socket通讯的客户端和文件传输,demo13.cpp
#include "_public.h"
bool SendFile(int sockfd,char *filename,int filesize); //把文件的内容发送给服务端
int main(int argc,char *argv[])
{if (argc != 4){printf("\n");printf("Using:./demo13 ip port filename\n\n");printf("Example:./demo13 118.89.50.198 5010 test1.jpg\n\n");printf("本程序演示采用CTcpClient类,实现socket通讯的客户端和文件传输。\n\n");return -1;} if (access(argv[3],R_OK) != 0) //判断文件是否存{printf("file %s not exist.\n",argv[3]); return -1;}int uFileSize=0;char strMTime[20],strRecvBuffer[1024],strSendBuffer[1024];memset(strMTime,0,sizeof(strMTime)); //获取文件的时间和大小FileMTime(argv[3],strMTime); uFileSize=FileSize(argv[3]); //获取文件的大小// 把文件的信息封装成一个xml报文,发送给服务端memset(strSendBuffer,0,sizeof(strSendBuffer));snprintf(strSendBuffer,100,"<filename>%s</filename><mtime>%s</mtime><size>%lu</size>",argv[3],strMTime,uFileSize);CTcpClient TcpClient;//1111111111111111111111111111111111111111111111111111.向服务器发起连接if (TcpClient.ConnectToServer(argv[1],atoi(argv[2])) == false){printf("TcpClient.ConnectToServer(%s,%d) failed.\n",argv[1],atoi(argv[2])); return -1;}//1111111111111111111111111111111111111111112.把文件信息的xml发送给服务端,并没有接收服务端回应,没必要,减少tcp交互次数if (TcpClient.Write(strSendBuffer)==false){printf("TcpClient.Write() failed.\n"); return -1;}printf("send xml:%s\n",strSendBuffer);printf("send file ...");//111111111111111111111111111111111111111111111111111113.把文件的内容发送给服务端if (SendFile(TcpClient.m_sockfd,argv[3],uFileSize)==false){printf("SendFile(%s) failed.\n",argv[3]); return -1;} memset(strRecvBuffer,0,sizeof(strRecvBuffer));//1111111111111111111111111111111111111111111111111114.接收服务端返回的回应报文if (TcpClient.Read(strRecvBuffer)==false){printf("TcpClient.Read() failed.\n"); return -1;}if (strcmp(strRecvBuffer,"ok")==0)printf("ok.\n");elseprintf("failed.\n");return 0;
}//111111111111111111111111111111111111111111111111113.把文件的内容发送给服务端
bool SendFile(int sockfd,char *filename,int filesize)
{int bytes=0;int total_bytes=0;int onread=0;char buffer[1000];FILE *fp=NULL;if ( (fp=fopen(filename,"rb")) == NULL ) {printf("fopen(%s) failed.\n",filename); return false;}while (true){memset(buffer,0,sizeof(buffer));if ((filesize-total_bytes) > 1000) onread=1000; //一次读1000个字节else onread=filesize-total_bytes;bytes=fread(buffer,1,onread,fp); if (bytes > 0){if (Writen(sockfd,buffer,bytes) == false){printf("Writen() failed.\n"); fclose(fp); fp=NULL; return false;}}total_bytes = total_bytes + bytes;if ((int)total_bytes == filesize) break;}fclose(fp);return true;
}
// 本程序演示采用CTcpServer类,实现socket通讯的服务端和文件传输,demo14.cpp
#include "_public.h"
bool RecvFile(char *strRecvBuffer,int sockfd,char *strfilename); //接收文件的内容
int main(int argc,char *argv[])
{if (argc != 3){printf("\n");printf("Using:./demo14 port filename\n\n");printf("Example:./demo14 5010 test2.jpg\n\n"); //test2.jpg重新命名printf("本程序演示采用CTcpServer类,实现socket通讯的服务端和文件传输。\n\n");return -1;}CTcpServer TcpServer;
//1111111111111111111111111111111111111111111111111111.服务端初始化if (TcpServer.InitServer(atoi(argv[1])) == false){printf("TcpServer.InitServer(%s) failed.\n",argv[1]); return -1;}//1111111111111111111111111111111111111111111111111112.等待客户端的连接if (TcpServer.Accept() == false){printf("TcpServer.Accept() failed.\n"); return -1;}//11111111111111111111111111111111111111111113.读取客户端的报文,等时间是20秒char strRecvBuffer[1024],strSendBuffer[1024];memset(strRecvBuffer,0,sizeof(strRecvBuffer));if (TcpServer.Read(strRecvBuffer,20)==false) {printf("TcpServer.Read() failed.\n"); return -1;}printf("recv:%s\n",strRecvBuffer);printf("recv file ...");//111111111111111111111111111111111111111111114.接收文件的内容memset(strSendBuffer,0,sizeof(strSendBuffer));if (RecvFile(strRecvBuffer,TcpServer.m_connfd,argv[2])==true){strcpy(strSendBuffer,"ok");printf("ok.\n");}else{strcpy(strSendBuffer,"failed");printf("failed.\n");}//1111111111111111111111111111111111111111111111111115.接收ok后,向客户端返回响应内容if (TcpServer.Write(strSendBuffer)==false) {printf("TcpServer.Write() failed.\n"); return -1;}printf("send:%s\n",strSendBuffer);return 0;
}//1111111111111111111111111111111111111111111111111114.接收文件的内容
bool RecvFile(char *strRecvBuffer,int sockfd,char *strfilename)
{int ufilesize=0;char strmtime[20]; memset(strmtime,0,sizeof(strmtime));// 获取待接收的文件的时间和大小GetXMLBuffer(strRecvBuffer,"mtime",strmtime);GetXMLBuffer(strRecvBuffer,"size",&ufilesize);FILE *fp=NULL;if ( (fp=fopen(strfilename,"wb")) ==NULL){printf("create %s failed.\n",strfilename); return false;}int total_bytes=0;int onread=0;char buffer[1000];while (true){memset(buffer,0,sizeof(buffer));if ((ufilesize-total_bytes) > 1000) onread=1000; //根据文件大小知道文件接下来读取多少内容else onread=ufilesize-total_bytes;if (Readn(sockfd,buffer,onread) == false){printf("Readn() failed.\n"); fclose(fp); fp=NULL; return false;}fwrite(buffer,1,onread,fp); //一次读1个字节读onread次total_bytes = total_bytes + onread;if ((int)total_bytes == ufilesize) break;}fclose(fp);// 读完后重置文件原始的时间,不是本地接收生成的时间UTime(strfilename,strmtime);return true;
}
如下传二进制文件。
7.2 文件下载模块:不建议将tcpgetfile.cpp,tcpfileserver.cpp反过来,虽然全双工但会出现连不上服务器被上网行为审计系统拦截
// 这是一个通用的功能模块,采用TCP协议获取文件的 客户端tcpgetfile.cpp
#include "_public.h"
struct st_arg
{char ip[31]; // 服务器端的IP地址。int port; // 服务器端的端口。int ptype; // 文件获取成功后文件的处理方式:1-保留文件;2-删除文件;3-移动到备份目录。char clientpath[301]; // 本地文件存放的根目录。char srvpath[301]; // 服务端文件存放的根目录。char srvpathbak[301]; // 文件成功获取后,服务端文件备份的根目录,当ptype==3时有效。bool andchild; // 是否获取srvpath目录下各级子目录的文件,true-是;false-否。char matchname[301]; // 待获取文件名的匹配方式,如"*.TXT,*.XML",注意用大写。char okfilename[301]; // 已获取成功文件名清单。listfilename不需要了,服务端返回的报文直接放容器里了int timetvl; // 扫描本地目录文件的时间间隔,单位:秒。
} starg;
char strRecvBuffer[TCPBUFLEN+10]; // 接收报文的缓冲区
char strSendBuffer[TCPBUFLEN+10]; // 发送报文的缓冲区
vector<struct st_fileinfo> vlistfile,vlistfile1;
vector<struct st_fileinfo> vokfilename,vokfilename1;
bool LoadListFile(); // 把服务端srvpath目录下的文件加载到vlistfile容器中
bool LoadOKFileName(); // 把okfilename文件内容加载到vokfilename容器中
// 把vlistfile容器中的文件与vokfilename容器中文件对比,得到两个容器
// 一、在vlistfile中存在,并已经采集成功的文件vokfilename1
// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1
bool CompVector();
bool WriteToOKFileName(); // 把vokfilename1容器中的内容先写入okfilename文件中,覆盖之前的旧okfilename文件
bool AppendToOKFileName(struct st_fileinfo *stfileinfo); // 如果ptype==1,把采集成功的文件记录追加到okfilename文件中
CTcpClient TcpClient;
CLogFile logfile;
bool _tcpgetfiles();
void EXIT(int sig);
void _help(char *argv[]);
bool _xmltoarg(char *strxmlbuffer); // 把xml解析到参数starg结构中
bool ClientLogin(const char *argv); // 登录服务器
bool ActiveTest(); // 向服务端发送心跳报文
bool _tcpgetfiles(); // 实现文件获取的功能int main(int argc,char *argv[])
{if (argc!=3) { _help(argv); return -1; }CloseIOAndSignal();signal(SIGINT,EXIT); signal(SIGTERM,EXIT);if (logfile.Open(argv[1],"a+")==false){printf("打开日志文件失败(%s)。\n",argv[1]); return -1;}if (_xmltoarg(argv[2])==false) return -1; //把xml解析到参数starg结构中while (true){ ClientLogin(argv[2]); // 向服务器发起连接并登录// 实现文件获取的功能,_tcpgetfiles()出现通讯故障没有关socket,_tcpgetfiles函数返回后vlistfile容器是不空的//循环到了ClientLogin这里判断登录,ClientLogin里不判断socket有没有问题不会去重新登录,又到_tcpgetfiles死循环_tcpgetfiles();if (vlistfile.size()==0){ ActiveTest(); // 向服务端发送心跳报文sleep(starg.timetvl);}}return 0;
}void EXIT(int sig)
{logfile.Write("程序退出,sig=%d\n\n",sig);TcpClient.Close();exit(0);
}//111111111111111111111111111111111111111111111111111显示程序的帮助
void _help(char *argv[])
{printf("\n");printf("Using:/htidc/public/bin/tcpgetfiles logfilename xmlbuffer\n\n");printf("Sample:/htidc/public/bin/tcpgetfiles /log/shqx/tcpgetfiles_surfdata.log \"<ip>172.16.0.15</ip><port>5010</port><ptype>1</ptype><clientpath>/data/shqx/sdata/surfdata</clientpath><srvpath>/data/shqx/tcp/surfdata</srvpath><srvpathbak>/data/shqx/tcp/surfdatabak</srvpathbak><andchild>true</andchild><matchname>SURF_*.TXT,*.DAT</matchname><okfilename>/data/shqx/tcplist/tcpgetfiles_surfdata.xml</okfilename><timetvl>10</timetvl>\"\n\n\n");printf("这是一个通用的功能模块,采用TCP协议获取文件的客户端。\n");printf("logfilename 本程序运行的日志文件。\n");printf("xmlbuffer 本程序运行的参数,如下:\n");printf("ip 服务器端的IP地址。\n");printf("port 服务器端的端口。\n");printf("clientpath 客户端文件存放的根目录。\n");printf("srvpath 服务端文件存放的根目录。\n");printf("ptype 文件获取成功后服务端文件的处理方式:1-保留文件;2-删除文件;3-移动到备份目录。\n");printf("srvpathbak 文件成功获取后,服务端文件备份的根目录,当ptype==3时有效,缺省为空。\n");printf("andchild 是否获取srvpath目录下各级子目录的文件,true-是;false-否,缺省为false。\n");printf("matchname 待获取文件名的匹配方式,如\"*.TXT,*.XML\",注意用大写。\n");printf("okfilename 已获取成功文件名清单,缺省为空。\n");printf("timetvl 扫描本地目录文件的时间间隔,单位:秒,取值在1-50之间。\n\n\n");
}//1111111111111111111111111111111111111111111111把xml解析到参数starg结构中
bool _xmltoarg(char *strxmlbuffer)
{memset(&starg,0,sizeof(struct st_arg));GetXMLBuffer(strxmlbuffer,"ip",starg.ip);if (strlen(starg.ip)==0) { logfile.Write("ip is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"port",&starg.port);if ( starg.port==0) { logfile.Write("port is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"ptype",&starg.ptype);if ((starg.ptype!=1)&&(starg.ptype!=2)&&(starg.ptype!=3) ) { logfile.Write("ptype not in (1,2,3).\n"); return false; }GetXMLBuffer(strxmlbuffer,"clientpath",starg.clientpath);if (strlen(starg.clientpath)==0) { logfile.Write("clientpath is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"srvpathbak",starg.srvpathbak);if ((starg.ptype==3)&&(strlen(starg.srvpathbak)==0)) { logfile.Write("srvpathbak is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"srvpath",starg.srvpath);if (strlen(starg.srvpath)==0) { logfile.Write("srvpath is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"andchild",&starg.andchild);GetXMLBuffer(strxmlbuffer,"matchname",starg.matchname);if (strlen(starg.matchname)==0) { logfile.Write("matchname is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"okfilename",starg.okfilename);if ((starg.ptype==1)&&(strlen(starg.okfilename)==0)) { logfile.Write("okfilename is null.\n"); return false; }GetXMLBuffer(strxmlbuffer,"timetvl",&starg.timetvl);if (starg.timetvl==0) { logfile.Write("timetvl is null.\n"); return false; }if (starg.timetvl>50) starg.timetvl=50;return true;
}//1111111111111111111111111111111111111111111111111111111登录服务器
bool ClientLogin(const char *argv)
{if (TcpClient.m_sockfd>0) return true;int ii=0;while (true){if (ii++>0) sleep(20); // 第一次进入循环不休眠// 向服务器发起连接if (TcpClient.ConnectToServer(starg.ip,starg.port) == false){logfile.Write("TcpClient.ConnectToServer(%s,%d) failed.\n",starg.ip,starg.port); continue;}memset(strRecvBuffer,0,sizeof(strRecvBuffer));memset(strSendBuffer,0,sizeof(strSendBuffer));strcpy(strSendBuffer,argv); strcat(strSendBuffer,"<clienttype>2</clienttype>");// logfile.Write("1 strSendBuffer=%s\n",strSendBuffer); // xxxxxxif (TcpClient.Write(strSendBuffer) == false){logfile.Write("1 TcpClient.Write() failed.\n"); continue;}if (TcpClient.Read(strRecvBuffer,20) == false){logfile.Write("1 TcpClient.Read() failed.\n"); continue;}// logfile.Write("1 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxxbreak;}logfile.Write("login(%s,%d) ok.\n",starg.ip,starg.port);return true;
}//11111111111111111111111111111111111111111111111111向服务端发送心跳报文
bool ActiveTest()
{memset(strRecvBuffer,0,sizeof(strRecvBuffer));memset(strSendBuffer,0,sizeof(strSendBuffer));strcpy(strSendBuffer,"<activetest>ok</activetest>");// logfile.Write("2 strSendBuffer=%s\n",strSendBuffer); // xxxxxxif (TcpClient.Write(strSendBuffer) == false){logfile.Write("2 TcpClient.Write() failed.\n"); TcpClient.Close(); return false;}if (TcpClient.Read(strRecvBuffer,20) == false){logfile.Write("2 TcpClient.Read() failed.\n"); TcpClient.Close(); return false;}// logfile.Write("2 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxxif (strcmp(strRecvBuffer,"ok") != 0) { TcpClient.Close(); return false; }return true;
}//111111111111111111111111111111111111111111111111111实现文件获取的功能
bool _tcpgetfiles()
{// 把服务端srvpath目录下的文件加载到vlistfile容器中if (LoadListFile()==false){logfile.Write("LoadListFile() failed.\n"); TcpClient.Close(); return false;}if (starg.ptype==1){// 加载okfilename文件中的内容到容器vokfilename中LoadOKFileName();// 把vlistfile容器中的文件与vokfilename容器中文件对比,得到两个容器// 一、在vlistfile中存在,并已经采集成功的文件vokfilename1// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1CompVector(); WriteToOKFileName(); // 把vokfilename1容器中的内容先写入okfilename文件中,覆盖之前的旧okfilename文件 vlistfile.clear(); vlistfile.swap(vlistfile1); // 把vlistfile1容器中的内容复制到vlistfile容器中}for (int ii=0;ii<vlistfile.size();ii++) // 从服务端逐个获取新文件或已改动过的文件{ memset(strSendBuffer,0,sizeof(strSendBuffer)); // 向服务端发送将获取(下载)的文件信息sprintf(strSendBuffer,"<filename>%s</filename><filesize>%d</filesize><mtime>%s</mtime>",vlistfile[ii].filename,vlistfile[ii].filesize,vlistfile[ii].mtime);// logfile.Write("3 strSendBuffer=%s\n",strSendBuffer); // xxxxxx if (TcpClient.Write(strSendBuffer) == false){logfile.Write("3 TcpClient.Write() failed.\n"); TcpClient.Close(); return false;}// 文件信息已知道,此报文有些多余,但是为了兼容SendFile和RecvFile函数,对性能不会有影响。if (TcpClient.Read(strRecvBuffer) == false){logfile.Write("3 TcpClient.Read() failed.\n"); TcpClient.Close(); return false;}// logfile.Write("3 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxx // 把文件名中的clientpath替换成srvpath,要小心第三个参数struct st_fileinfo stfileinfo;memset(&stfileinfo,0,sizeof(struct st_fileinfo));strcpy(stfileinfo.filename,vlistfile[ii].filename);strcpy(stfileinfo.mtime,vlistfile[ii].mtime);stfileinfo.filesize=vlistfile[ii].filesize;UpdateStr(stfileinfo.filename,starg.srvpath,starg.clientpath);logfile.Write("get %s ...",stfileinfo.filename);// ptype=1是增量传输,对服务端来说什么都不干,保留oklistfile是客户端的事 if (RecvFile(&logfile,TcpClient.m_sockfd,&stfileinfo)== false) // 接收文件的内容{logfile.Write("RecvFile() failed.\n"); TcpClient.Close(); return false;}logfile.WriteEx("ok.\n");// 如果ptype==1,把采集成功的文件记录追加到okfilename文件中if (starg.ptype==1) AppendToOKFileName(&vlistfile[ii]);}return true;
}//11111111111111111111111111111111111111111把服务端srvpath目录下的文件加载到vlistfile容器中
bool LoadListFile()
{vlistfile.clear();memset(strSendBuffer,0,sizeof(strSendBuffer));strcpy(strSendBuffer,"<list>"); //向服务端发<list>,就像向ftp服务端发nlist命令一样// logfile.Write("4 strSendBuffer=%s\n",strSendBuffer); // xxxxxx if (TcpClient.Write(strSendBuffer) == false){logfile.Write("4 TcpClient.Write() failed.\n"); return false;}memset(strRecvBuffer,0,sizeof(strRecvBuffer));if (TcpClient.Read(strRecvBuffer,20) == false){logfile.Write("4 TcpClient.Read() failed.\n"); return false;}// logfile.Write("4 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxx// Read到的报文就是文件总数int totalfile=0; GetXMLBuffer(strRecvBuffer,"totalfile",&totalfile);struct st_fileinfo stfileinfo;for (int ii=0;ii<totalfile;ii++) //利用循环接收文件清单报文,解析出来放入vlistfile容器里{memset(&stfileinfo,0,sizeof(struct st_fileinfo));memset(strRecvBuffer,0,sizeof(strRecvBuffer));if (TcpClient.Read(strRecvBuffer,20) == false){logfile.Write("5 TcpClient.Read() failed.\n"); return false;}// logfile.Write("5 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxxGetXMLBuffer(strRecvBuffer,"filename",stfileinfo.filename);GetXMLBuffer(strRecvBuffer,"filesize",&stfileinfo.filesize);GetXMLBuffer(strRecvBuffer,"mtime",stfileinfo.mtime); vlistfile.push_back(stfileinfo);// logfile.Write("vlistfile filename=%s,mtime=%s\n",stfileinfo.filename,stfileinfo.mtime);}return true;
}//11111111111111111111111111111111111111111111111把okfilename文件内容加载到vokfilename容器中
bool LoadOKFileName()
{vokfilename.clear();CFile File;// 注意:如果程序是第一次采集,okfilename是不存在的,并不是错误,所以也返回true。if (File.Open(starg.okfilename,"r") == false) return true;struct st_fileinfo stfileinfo;char strbuffer[301];while (true){memset(&stfileinfo,0,sizeof(struct st_fileinfo));if (File.Fgets(strbuffer,300,true)==false) break;GetXMLBuffer(strbuffer,"filename",stfileinfo.filename,300);GetXMLBuffer(strbuffer,"mtime",stfileinfo.mtime,20);vokfilename.push_back(stfileinfo);// logfile.Write("vokfilename filename=%s,mtime=%s\n",stfileinfo.filename,stfileinfo.mtime);}return true;
}//11111111111111111111111111111把vlistfile容器中的文件与vokfilename容器中文件对比,得到两个容器
// 一、在vlistfile中存在,并已经采集成功的文件vokfilename1
// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1
bool CompVector()
{vokfilename1.clear(); vlistfile1.clear();for (int ii=0;ii<vlistfile.size();ii++){int jj=0;for (jj=0;jj<vokfilename.size();jj++){if ( (strcmp(vlistfile[ii].filename,vokfilename[jj].filename)==0) &&(strcmp(vlistfile[ii].mtime,vokfilename[jj].mtime)==0) ){vokfilename1.push_back(vlistfile[ii]); break;}}if (jj==vokfilename.size()){vlistfile1.push_back(vlistfile[ii]);}}/*for (int ii=0;ii<vokfilename1.size();ii++){logfile.Write("vokfilename1 filename=%s,mtime=%s\n",vokfilename1[ii].filename,vokfilename1[ii].mtime);}for (int ii=0;ii<vlistfile1.size();ii++){logfile.Write("vlistfile1 filename=%s,mtime=%s\n",vlistfile1[ii].filename,vlistfile1[ii].mtime);}*/return true;
}//111111111111111把vokfilename1容器中的内容先写入okfilename文件中,覆盖之前的旧okfilename文件
bool WriteToOKFileName()
{CFile File;if (File.Open(starg.okfilename,"w",false) == false) // 注意,打开文件不要采用缓冲机制{logfile.Write("File.Open(%s) failed.\n",starg.okfilename); return false;}for (int ii=0;ii<vokfilename1.size();ii++){File.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",vokfilename1[ii].filename,vokfilename1[ii].mtime);}return true;
}//1111111111111111111111如果ptype==1,把采集成功的文件记录追加到okfilename文件中
bool AppendToOKFileName(struct st_fileinfo *stfileinfo)
{CFile File;if (File.Open(starg.okfilename,"a",false) == false){logfile.Write("File.Open(%s) failed.\n",starg.okfilename); return false;}File.Fprintf("<filename>%s</filename><mtime>%s</mtime>\n",stfileinfo->filename,stfileinfo->mtime);return true;
}
//这是一个通用的功能模块,采用TCP协议实现文件传输的服务端,tcpfileserver.cpp多线程。
#include "_public.h"
struct st_arg
{int clienttype;char ip[31]; // 服务器端的IP地址。int port; // 服务器端的端口。int ptype; // 文件发送成功后文件的处理方式:1-保留文件;2-移动到备份目录;3-删除文件。char clientpath[301]; // 本地文件存放的根目录。char clientpathbak[301]; // 文件成功发送后,本地文件备份的根目录,当ptype==2时有效。char srvpath[301]; // 服务端文件存放的根目录。char srvpathbak[301]; // 文件成功接收后,服务端文件备份的根目录,当ptype==2时有效。bool andchild; // 是否发送clientpath目录下各级子目录的文件,true-是;false-否。char matchname[301]; // 待发送文件名的匹配方式,如"*.TXT,*.XML",注意用大写。char okfilename[301]; // 已发送成功文件名清单。int timetvl; // 扫描本地目录文件的时间间隔,单位:秒。
};
bool _xmltoarg(char *strxmlbuffer,struct st_arg *starg); //把xml解析到参数starg结构中
CLogFile logfile;
bool ClientLogin(int clientfd,struct st_arg *starg); // 等待登录
bool ListFile(int clientfd,struct st_arg *starg); // 列出srvpath目录下文件的清单,返回给客户端。
void EXIT(int sig); // 程序退出时调用的函数
void *pth_main(void *arg); // 与客户端通信线程的主函数
bool RecvFilesMain(int clientfd,struct st_arg *starg); // 接收文件主函数
bool SendFilesMain(int clientfd,struct st_arg *starg); // 发送文件主函数
vector<int> vclientfd; // 存放客户端已连接的socket的容器
void AddClient(int clientfd); // 把客户端新的socket加入vclientfd容器中
void RemoveClient(int clientfd); // 关闭客户端的socket并从vclientfd容器中删除,int main(int argc,char *argv[])
{if (argc != 3){printf("\n");printf("Using:/htidc/public/bin/tcpfileserver1 logfilename port\n");printf("Example:/htidc/public/bin/tcpfileserver1 /log/shqx/tcpfileserver1.log 5010\n\n");printf("本程序是一个公共功能模块,采用TCP/IP传输文件的服务端。\n");printf("本程序采用的是多线程的服务端,多进程的服务端程序是tcpfileserver.cpp。\n");printf("logfilename 日志文件名。\n");printf("port 用于传输文件的TCP端口。\n");return -1;}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);// 打开程序运行日志,这是一个多进程程序,日志不能自动切换if (logfile.Open(argv[1],"a+",false) == false){printf("logfile.Open(%s) failed.\n",argv[1]); return -1;}logfile.Write("fileserver started(%s).\n",argv[2]);CTcpServer TcpServer; //定义为局部变量if (TcpServer.InitServer(atoi(argv[2])) == false){logfile.Write("TcpServer.InitServer(%s) failed.\n",argv[2]); return -1;}AddClient(TcpServer.m_listenfd); //保存服务端的listenfd到vclientfdwhile (true){ if (TcpServer.Accept() == false) //等待客户端的连接{logfile.Write("TcpServer.Accept() failed.\n"); continue;}pthread_t pthid; //客户端连上后创建一线程,下面将socket参数传进去,与新连接上来的客户端通信// int4字节,long8字节,*指针8字节,TcpServer.m_connfd定义的是整数intif (pthread_create(&pthid,NULL,pth_main,(void*)(long)TcpServer.m_connfd)!=0){ //主线程等子线程结束才行logfile.Write("创建线程失败,程序退出。n"); close(TcpServer.m_connfd); EXIT(-1);}logfile.Write("%s is connected.\n",TcpServer.GetIP()); AddClient(TcpServer.m_connfd); //保存每个客户端的socket到vclientfd}return 0;
}//11111111111111111111111111111111111111111111111111111111111111
void EXIT(int sig)
{signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);if (sig>0) signal(sig,SIG_IGN);logfile.Write("tcpfileserver1 exit,sig=%d...\n",sig);// 关闭vclientfd容器中全部的socket,释放出资源for (int ii=0;ii<vclientfd.size();ii++){close(vclientfd[ii]);}exit(0);
}//11111111111111111111111111111111111111111111111111111111等待登录
bool ClientLogin(int clientfd,struct st_arg *starg)
{int ibuflen=0;char strRecvBuffer[TCPBUFLEN+10]; // 接收报文的缓冲区char strSendBuffer[TCPBUFLEN+10]; // 发送报文的缓冲区memset(strRecvBuffer,0,sizeof(strRecvBuffer));memset(strSendBuffer,0,sizeof(strSendBuffer));//以前用TcpServer.Read,现在改为TcpRead,对于线程里没有TcpServer这个对象了//TcpServer.Read里也是调用TcpReadif (TcpRead(clientfd,strRecvBuffer,&ibuflen,20) == false){logfile.Write("1 TcpRead() failed.\n"); return false;}// logfile.Write("1 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxxGetXMLBuffer(strRecvBuffer,"clienttype",&starg->clienttype);if ( (starg->clienttype==1) || (starg->clienttype==2) )strcpy(strSendBuffer,"ok");elsestrcpy(strSendBuffer,"failed");// logfile.Write("1 strSendBuffer=%s\n",strSendBuffer); // xxxxxxif (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("1 TcpWrite() failed.\n"); return false;}logfile.Write("login %s(clienttype=%d).\n",strSendBuffer,starg->clienttype);if (strcmp(strSendBuffer,"failed") == 0) return false;// 把参数解析出来_xmltoarg(strRecvBuffer,starg);return true;
}//11111111111111111111111111111111111111111111111111111接收文件主函数
bool RecvFilesMain(int clientfd,struct st_arg *starg)
{int ibuflen=0;char strRecvBuffer[TCPBUFLEN+10]; // 接收报文的缓冲区char strSendBuffer[TCPBUFLEN+10]; // 发送报文的缓冲区while (true){memset(strRecvBuffer,0,sizeof(strRecvBuffer));memset(strSendBuffer,0,sizeof(strSendBuffer));if (TcpRead(clientfd,strRecvBuffer,&ibuflen,80) == false){logfile.Write("TcpRead() failed.\n"); return false;}// logfile.Write("2 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxx// 处理心跳报文if (strstr(strRecvBuffer,"activetest")!=0){strcpy(strSendBuffer,"ok");// logfile.Write("2 strSendBuffer=%s\n",strSendBuffer); // xxxxxxif (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("2 TcpWrite() failed.\n"); return false;}continue;}struct st_fileinfo stfileinfo;memset(&stfileinfo,0,sizeof(struct st_fileinfo));// 获取待接收的文件的时间和大小GetXMLBuffer(strRecvBuffer,"filename",stfileinfo.filename);GetXMLBuffer(strRecvBuffer,"filesize",&stfileinfo.filesize);GetXMLBuffer(strRecvBuffer,"mtime",stfileinfo.mtime);// 把文件名中的clientpath替换成srvpath,要小心第三个参数UpdateStr(stfileinfo.filename,starg->clientpath,starg->srvpath,false);// 接收文件的内容if (RecvFile(&logfile,clientfd,&stfileinfo)== false){logfile.Write("RecvFile() failed.\n"); return false;}logfile.Write("recv %s ok.\n",stfileinfo.filename);}return true;
}//11111111111111111111111111111111111111111111111111111111发送文件主函数
bool SendFilesMain(int clientfd,struct st_arg *starg)
{int ibuflen=0;char strRecvBuffer[TCPBUFLEN+10]; // 接收报文的缓冲区char strSendBuffer[TCPBUFLEN+10]; // 发送报文的缓冲区while (true){memset(strRecvBuffer,0,sizeof(strRecvBuffer));if (TcpRead(clientfd,strRecvBuffer,&ibuflen,80) == false){logfile.Write("TcpRead() failed.\n"); return false;}// logfile.Write("3 strRecvBuffer=%s\n",strRecvBuffer); // xxxxxx// 处理心跳报文if (strstr(strRecvBuffer,"activetest")!=0){memset(strSendBuffer,0,sizeof(strSendBuffer));strcpy(strSendBuffer,"ok");// logfile.Write("3 strSendBuffer=%s\n",strSendBuffer); // xxxxxxif (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("3 TcpWrite() failed.\n"); return false;}continue;}// 处理获取文件列表报文if (strcmp(strRecvBuffer,"<list>")==0){if (ListFile(clientfd,starg)==false){logfile.Write("ListFile() failed.\n"); return false;}continue;}// 取文件报文if (strncmp(strRecvBuffer,"<filename>",10)==0){// 获取待接收的文件的时间和大小struct st_fileinfo stfileinfo;memset(&stfileinfo,0,sizeof(struct st_fileinfo));GetXMLBuffer(strRecvBuffer,"filename",stfileinfo.filename);GetXMLBuffer(strRecvBuffer,"filesize",&stfileinfo.filesize);GetXMLBuffer(strRecvBuffer,"mtime",stfileinfo.mtime);// 把文件发送给客户端if (SendFile(&logfile,clientfd,&stfileinfo)==false) return false;logfile.Write("put %s ...ok.\n",stfileinfo.filename);// 删除服务端的文件if (starg->ptype==2) REMOVE(stfileinfo.filename);// 备份服务端的文件if (starg->ptype==3) {char strfilenamebak[301];memset(strfilenamebak,0,sizeof(strfilenamebak));strcpy(strfilenamebak,stfileinfo.filename);UpdateStr(strfilenamebak,starg->srvpath,starg->srvpathbak,false); // 要小心第三个参数if (RENAME(stfileinfo.filename,strfilenamebak)==false){logfile.Write("RENAME %s to %s failed.\n",stfileinfo.filename,strfilenamebak); return false;}}}}return true;
}//11111111111111111111111111111111111111111111111111111把xml解析到参数starg结构中
bool _xmltoarg(char *strxmlbuffer,struct st_arg *starg)
{GetXMLBuffer(strxmlbuffer,"ip",starg->ip);GetXMLBuffer(strxmlbuffer,"port",&starg->port);GetXMLBuffer(strxmlbuffer,"ptype",&starg->ptype);GetXMLBuffer(strxmlbuffer,"clientpath",starg->clientpath);GetXMLBuffer(strxmlbuffer,"clientpathbak",starg->clientpathbak);GetXMLBuffer(strxmlbuffer,"srvpath",starg->srvpath);GetXMLBuffer(strxmlbuffer,"srvpathbak",starg->srvpathbak);GetXMLBuffer(strxmlbuffer,"andchild",&starg->andchild);GetXMLBuffer(strxmlbuffer,"matchname",starg->matchname);GetXMLBuffer(strxmlbuffer,"okfilename",starg->okfilename);GetXMLBuffer(strxmlbuffer,"timetvl",&starg->timetvl);return true;
}//1111111111111111111111111111111111111111111111列出srvpath目录下文件的清单,返回给客户端。
bool ListFile(int clientfd,struct st_arg *starg)
{int ibuflen=0;char strRecvBuffer[TCPBUFLEN+10]; // 接收报文的缓冲区char strSendBuffer[TCPBUFLEN+10]; // 发送报文的缓冲区CDir Dir;// 注意,如果目录下的总文件数超过50000,增量发送文件功能将有问题if (Dir.OpenDir(starg->srvpath,starg->matchname,50000,starg->andchild,false)==false){logfile.Write("Dir.OpenDir(%s) 失败。\n",starg->srvpath); return false;}// 先把文件总数返回给客户端memset(strSendBuffer,0,sizeof(strSendBuffer));sprintf(strSendBuffer,"<totalfile>%d</totalfile>",Dir.m_vFileName.size());// logfile.Write("4 strSendBuffer=%s\n",strSendBuffer); // xxxxxxif (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("4 TcpWrite() failed.\n"); return false;}// 把文件信息一条条的返回给客户端while (true){if (Dir.ReadDir()==false) break;memset(strSendBuffer,0,sizeof(strSendBuffer));sprintf(strSendBuffer,"<filename>%s</filename><mtime>%s</mtime><filesize>%d</filesize>",Dir.m_FullFileName,Dir.m_ModifyTime,Dir.m_FileSize);// logfile.Write("5 strSendBuffer=%s\n",strSendBuffer); // xxxxxxif (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("5 TcpWrite() failed.\n"); return false;}}return true;
}//111111111111111111111111111111111111111111111111111111111与客户端通信线程的主函数
void *pth_main(void *arg)
{int clientfd=(long) arg; // arg参数为新客户端的socketpthread_detach(pthread_self());struct st_arg starg;memset(&starg,0,sizeof(struct st_arg));// 等待客户端的登录if (ClientLogin(clientfd,&starg) == false) { RemoveClient(clientfd); pthread_exit(0); }// 接收文件主函数if (starg.clienttype==1) {if (RecvFilesMain(clientfd,&starg) == false) { RemoveClient(clientfd); pthread_exit(0); }}// 发送文件主函数if (starg.clienttype==2) {if (SendFilesMain(clientfd,&starg) == false) { RemoveClient(clientfd); pthread_exit(0); }}RemoveClient(clientfd); pthread_exit(0);
}//11111111111111111111111111111111111111111111把客户端新的socket加入vclientfd容器中
void AddClient(int clientfd)
{vclientfd.push_back(clientfd);
}//11111111111111111111111111111111111111111关闭客户端的socket并从vclientfd容器中删除
void RemoveClient(int clientfd)
{for (int ii=0;ii<vclientfd.size();ii++){if (vclientfd[ii]==clientfd) { close(clientfd); vclientfd.erase(vclientfd.begin()+ii); return; }}
}
7.3 高性能网络服务:多线程+数据库连接池(多线程每启一个线程都要连数据库耗资源)
如下开始APP服务端设计,客户端就是手机app软件。第一次客户端将手机编号
传给服务端,服务端将站点信息
传给客户端。
短连接
:客户端即用户点击按钮一次建立一次socket连接请求,处理完一个就断开
。响应慢:建立一次socket连接费时间,服务端fork一个进程也要时间,之后和数据库连接也要时间。
长连接
:客户端与服务端socket一直连接着进行数据通信,没有数据通信时用心跳(之前文件传输都用的是长连接)
,用户关了app,连接才断开。费服务端资源
:长连接连上后,数据库连接和进程都已准备好,一直通信完才断开。响应快
:用户看到数据越快越好控制在1秒内。如下项目组织(shtqapp是一个独立的项目)。
如下第一行是上面创建用户sql,pdm文件是数据结构设计。
// client.cpp,模拟tcp手机客户端,客户端用短链接还是长连接由客户端自己安排
#include "_freecplus.h"
CTcpClient TcpClient;
char strSendBuffer[301],strRecvBuffer[301];
bool biz10000(); // 心跳
bool biz10001(); // 新用户登录:只传个设备编号id,服务端把城市站点信息传给客户端,手机利用定位匹配int main(int argc,char *argv[])
{//if (TcpClient.ConnectToServer("127.0.0.1",5015)==false) { printf("conn failed.\n"); return -1; }if (TcpClient.ConnectToServer("172.16.0.15",5015)==false) { printf("conn failed.\n"); return -1; }//if (TcpClient.ConnectToServer("118.89.50.198",5015)==false) { printf("conn failed.\n"); return -1; }if (biz10000()==false) return 0; // 心跳CTimer Timer;if (biz10001()==false) return 0; // 新用户登录 printf("biz10001=%lf\n",Timer.Elapsed());sleep(1); return 0;
}bool biz10000()
{memset(strSendBuffer,0,sizeof(strSendBuffer));memset(strRecvBuffer,0,sizeof(strRecvBuffer));strcpy(strSendBuffer,"<bizid>10000</bizid>");//printf("send=%s=\n",strSendBuffer);if (TcpClient.Write(strSendBuffer)==false) { printf("send failed.\n"); return false; }if (TcpClient.Read(strRecvBuffer,20)==false) { printf("recv failed.\n"); return false; }//printf("recv=%s=\n",strRecvBuffer);return true;
}bool biz10001()
{memset(strSendBuffer,0,sizeof(strSendBuffer)); memset(strRecvBuffer,0,sizeof(strRecvBuffer));// 如下请求报文strcpy(strSendBuffer,"<bizid>10001</bizid><userid>52:54:00:83:0f:c1</userid><ttytype>1</ttytype><lat>20.234518</lat><lon>115.90832</lon><height>150.5</height>");//printf("send=%s=\n",strSendBuffer);if (TcpClient.Write(strSendBuffer)==false) { printf("send failed.\n"); return false; }//如下用一个循环接收全部的站点信息while (1){memset(strRecvBuffer,0,sizeof(strRecvBuffer));if (TcpClient.Read(strRecvBuffer,20)==false) { printf("recv failed.\n"); return false; }// printf("recv=%s=\n",strRecvBuffer); //手机端没数据库,手机软件真正处理方法把数据保存到xml文件里if (strcmp(strRecvBuffer,"ok")==0) break; //接收到ok的话表示数据处理完了}return true;
}
数据库连接池的设计可用一个参数去控制连接池的总大小,比如这连接池里有10个connection连接就需要10把锁。在sqlstatement每次想使用数据库连接时就会从10个已创建好的connection看看哪个没锁就拿1个过来用。
//上海天气APP软件服务端主程序。shtqappserver.cpp多线程方式,采用连接池。
#include "_freecplus.h"
#include "_ooci.h"
#define MAXCONNS 10 // 数据库连接池的大小。
pthread_mutex_t mutex[MAXCONNS]; // 锁数组。
connection conns[MAXCONNS]; // 数据库连接数组。
bool initconns(); // 初始数据库连接池。
connection *getconns(); // 从连接池中获取一个数据库连接。
bool freeconns(connection *in_conn); // 释放数据库连接。struct st_biz // 业务请求
{int bizid; // 业务代码char userid[51]; // 设备IDint ttytype; // 用户的设备类型,0-未知;1-IOS;2-Andriod,2-鸿蒙。int usertype; // 用户分类,0-未知;1-普通用户;2-气象志愿者;3-内部用户。double lon;double lat;double height;char obtid[11];char xmlbuffer[1001];
};
void xmltobiz(char *strxmlbuffer,struct st_biz *stbiz);
CTcpServer TcpServer;
CLogFile logfile;
void EXIT(int sig); // 程序退出时调用的函数
void *pth_main(void *arg); // 与客户端通信线程的主函数
bool biz10000(int clientfd); // 心跳业务
bool biz10001(struct st_biz *stbiz,int clientfd); // 新用户登录业务
bool biz10002(struct st_biz *stbiz,int clientfd); // 获取天气实况
bool InsertUSERLOG(struct st_biz *stbiz,connection *conn); // 插入用户请求日志表
vector<int> vclientfd; // 存放客户端已连接的socket的容器
void AddClient(int clientfd); // 把客户端新的socket加入vclientfd容器中
void RemoveClient(int clientfd); // 关闭客户端的socket并从vclientfd容器中删除,int main(int argc,char *argv[])
{if (argc != 3){printf("\n");printf("Using:/htidc/shtqapp1/bin/shtqappserver1 logfilename port\n");printf("Example:/htidc/shtqapp1/bin/shtqappserver1 /log/shtqapp/shtqappserver1.log 5015\n\n");printf("本程序是上海天气APP软件的服务端。\n");printf("logfilename 日志文件名。\n");printf("port 用于传输文件的TCP端口。\n");return -1;}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);// 打开程序运行日志,这是一个多进程程序,日志不能自动切换if (logfile.Open(argv[1],"a+",false) == false){printf("logfile.Open(%s) failed.\n",argv[1]); return -1;}logfile.Write("shtqappserver started(%s).\n",argv[2]);if (TcpServer.InitServer(atoi(argv[2])) == false){logfile.Write("TcpServer.InitServer(%s) failed.\n",argv[2]); EXIT(-1);}// 保存服务端的listenfd到vclientfdAddClient(TcpServer.m_listenfd);if (initconns()==false) // 初始化数据库连接池。{logfile.Write("initconns() failed.\n"); EXIT(-1);}while (true){if (TcpServer.Accept() == false) // 等待客户端的连接{logfile.Write("TcpServer.Accept() failed.\n"); continue;}pthread_t pthid; // 创建一线程,与新连接上来的客户端通信if (pthread_create(&pthid,NULL,pth_main,(void*)(long)TcpServer.m_connfd)!=0){logfile.Write("创建线程失败,程序退出。n"); close(TcpServer.m_connfd); EXIT(-1);}logfile.Write("%s is connected.\n",TcpServer.GetIP()); AddClient(TcpServer.m_connfd); // 保存每个客户端的socket到vclientfd}return 0;
}void EXIT(int sig) // 退出时调用的函数
{signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);if (sig>0) signal(sig,SIG_IGN);logfile.Write("tcpfileserver1 exit,sig=%d...\n",sig);// 关闭vclientfd容器中全部的socketfor (int ii=0;ii<vclientfd.size();ii++){close(vclientfd[ii]);}for (int ii=0;ii<MAXCONNS;ii++){logfile.Write("disconnect and pthread_mutex_destroy.\n");conns[ii].disconnect();pthread_mutex_destroy(&mutex[ii]);}exit(0);
}//11111111111111111111111111111111111111111与客户端通信线程的主函数
void *pth_main(void *arg)
{int clientfd=(long) arg; // arg参数为新客户端的socket。pthread_detach(pthread_self());struct st_biz stbiz;int ibuflen=0;char strRecvBuffer[1024]; // 接收报文的缓冲区while (true){memset(strRecvBuffer,0,sizeof(strRecvBuffer));// 接收客户端的业务请求报文,如果返回false,认为是客户端退出或网络原因,不写错误日志if (TcpRead(clientfd,strRecvBuffer,&ibuflen,50) == false){// logfile.Write("TcpRead() failed.\n"); break;}logfile.Write("strRecvBuffer=%s\n",strRecvBuffer); // xxxxxx// 把参数解析出来xmltobiz(strRecvBuffer,&stbiz);if (stbiz.bizid==10000) // 心跳报文{if (biz10000(clientfd)==true) continue;else break;}// 新用户登录 if (stbiz.bizid==10001) {if (biz10001(&stbiz,clientfd)==true) continue;else break;}// 获取天气实况if (stbiz.bizid==10002) {if (biz10002(&stbiz,clientfd)==true) continue;else break;}// 体力活logfile.Write("非法报文%s\n",strRecvBuffer); break;}RemoveClient(clientfd);pthread_exit(0);
}//111111111111111111111111111111111111111111把xml解析到参数starg结构中
void xmltobiz(char *strxmlbuffer,struct st_biz *stbiz)
{memset(stbiz,0,sizeof(struct st_biz));// 业务代码GetXMLBuffer(strxmlbuffer,"bizid",&stbiz->bizid);// logfile.Write("bizid=%d\n",stbiz->bizid);// 用户设备IDGetXMLBuffer(strxmlbuffer,"userid",stbiz->userid,50);// logfile.Write("userid=%s\n",stbiz->userid);GetXMLBuffer(strxmlbuffer,"obtid",stbiz->obtid,10);// logfile.Write("obtid=%s\n",stbiz->obtid);GetXMLBuffer(strxmlbuffer,"lat",&stbiz->lat);// logfile.Write("lat=%lf\n",stbiz->lat);GetXMLBuffer(strxmlbuffer,"lon",&stbiz->lon);// logfile.Write("lon=%lf\n",stbiz->lon);GetXMLBuffer(strxmlbuffer,"height",&stbiz->height);// logfile.Write("height=%lf\n",stbiz->height);strncpy(stbiz->xmlbuffer,strxmlbuffer,1000);return;
}//1111111111111111111111111111111111111111心跳业务
bool biz10000(int clientfd)
{char strSendBuffer[1024]; // 发送报文的缓冲区memset(strSendBuffer,0,sizeof(strSendBuffer));strcpy(strSendBuffer,"ok");if (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("biz10000 TcpWrite() failed.\n"); return false;}return true;
}//11111111111111111111111111111111111111111111新用户登录
bool biz10001(struct st_biz *stbiz,int clientfd)
{CTimer Timer;char strSendBuffer[1024]; // 发送报文的缓冲区 connection *conn=getconns(); // 获取一个数据库连接。// 插入用户基本信息表T_USERINFOsqlstatement stmt(conn);stmt.prepare("insert into T_USERINFO(userid,downtime,ttytype,keyid) values(:1,sysdate,:2,SEQ_USERINFO.nextval)");stmt.bindin(1, stbiz->userid,50);stmt.bindin(2,&stbiz->ttytype);if (stmt.execute() != 0){if (stmt.m_cda.rc!=1){logfile.Write("insert T_USERINFO failed.\n%s\n%s\n",stmt.m_cda.message,stmt.m_sql); freeconns(conn); return false;}}logfile.Write("insert T_USERINFO =%lf\n",Timer.Elapsed());// 插入用户请求日志表if (InsertUSERLOG(stbiz,conn)==false) { freeconns(conn); return false; }logfile.Write("insert T_USERLOG =%lf\n",Timer.Elapsed());char strobtid[6],strobtname[31],strlon[11],strlat[11];stmt.prepare("select obtid,obtname,lon,lat from T_OBTCODE where rsts=1 and rownum<=30");stmt.bindout(1,strobtid,5);stmt.bindout(2,strobtname,30);stmt.bindout(3,strlon,10);stmt.bindout(4,strlat,10);if (stmt.execute() != 0){logfile.Write("select T_OBTCODE failed.\n%s\n%s\n",stmt.m_cda.message,stmt.m_sql); freeconns(conn); return false;}while (true){memset(strobtid,0,sizeof(strobtid)); memset(strobtname,0,sizeof(strobtname));memset(strlon,0,sizeof(strlon)); memset(strlat,0,sizeof(strlat));memset(strSendBuffer,0,sizeof(strSendBuffer));if (stmt.next()!=0) break;sprintf(strSendBuffer,"<obtid>%s</obtid><obtname>%s</obtname><lon>%s</lon><lat>%s</lat><endl/>",strobtid,strobtname,strlon,strlat);if (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("biz10001 TcpWrite() failed.\n"); freeconns(conn); return false;}}logfile.Write("select =%lf\n",Timer.Elapsed());strcpy(strSendBuffer,"ok"); //最后发送一个okif (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("biz10001 TcpWrite() failed.\n"); freeconns(conn); return false;}freeconns(conn);return true;
}//11111111111111111111111111111111111111111111111插入用户请求日志表
bool InsertUSERLOG(struct st_biz *stbiz,connection *conn)
{sqlstatement stmt(conn);stmt.prepare("insert into T_USERLOG(logid,userid,atime,bizid,obtid,lon,lat,height,xmlbuffer) values(SEQ_USERLOG.nextval,:1,sysdate,:2,:3,:4,:5,:6,:7)");stmt.bindin(1, stbiz->userid,50);stmt.bindin(2,&stbiz->bizid);stmt.bindin(3, stbiz->obtid,10);stmt.bindin(4,&stbiz->lon);stmt.bindin(5,&stbiz->lat);stmt.bindin(6,&stbiz->height);stmt.bindin(7, stbiz->xmlbuffer,10000);if (stmt.execute() != 0){logfile.Write("insert T_USERLOG failed.\n%s\n%s\n",stmt.m_cda.message,stmt.m_sql); return false;}return true;
}//1111111111111111111111111111111111111111获取天气实况
bool biz10002(struct st_biz *stbiz,int clientfd)
{return true;
}//1111111111111111111111111111111111111把客户端新的socket加入vclientfd容器中
void AddClient(int clientfd)
{vclientfd.push_back(clientfd);
}//111111111111111111111111111111111111关闭客户端的socket并从vclientfd容器中删除
void RemoveClient(int clientfd)
{for (int ii=0;ii<vclientfd.size();ii++){if (vclientfd[ii]==clientfd) { close(clientfd); vclientfd.erase(vclientfd.begin()+ii); return; }}
}//111111111111111111111111111111111初始数据库连接池:连接好数据库,初始化锁
bool initconns()
{for (int ii=0;ii<MAXCONNS;ii++){logfile.Write("%d,connecttodb and pthread_mutex_init.\n",ii);// 连接数据库if (conns[ii].connecttodb("shtqapp/pwdidc@snorcl11g_198","Simplified Chinese_China.ZHS16GBK",true)!=0){logfile.Write("conns[%d].connettodb() failed.\n",ii); return false;}pthread_mutex_init(&mutex[ii],0); // 创建锁}return true;
}//11111111111111111111111111111111111111111获得连接池
connection *getconns()
{// for (int jj=0;jj<1000;jj++)while (true){for (int ii=0;ii<MAXCONNS;ii++){if (pthread_mutex_trylock(&mutex[ii])==0) {// logfile.Write("jj=%d,ii=%d\n",jj,ii);logfile.Write("get conns is %d.\n",ii);return &conns[ii]; }} usleep(10000);}
}//1111111111111111111111111111111111111111释放连接池
bool freeconns(connection *in_conn)
{for (int ii=0;ii<MAXCONNS;ii++){if (in_conn==&conns[ii]) {logfile.Write("free conn %d.\n",ii);pthread_mutex_unlock(&mutex[ii]); in_conn=0; return true;}}return false; //理论上不会来到这里,除非连接池搞乱了
}
心跳报文业务不需要连接池,先开启服务端。
8.进程:fork(),ps -ef (同-aux) | more
一个进程(正在内存中运行的程序)在内存里有三部分数据:代码段(相同),堆栈段+数据段(不同)
。getpid库函数功能是获取进程编号,该函数没有参数,返回值是进程的编号(相同程序在不同时间执行,进程编号不同)。
进程应用:并发:把socket服务端改为多进程,每次accept到一个客户端连接后,生成一个子进程,让子进程负责与这个客户端通讯。父进程继续accept客户端连接
。以下在服务端中,在CTcpServer类中增加两个成员函数。
僵尸进程
:一个进程执行了exit系统调用退出时会向父进程发送SIGCHLD信号,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态,如进程ID、终止状态等等)的进程,ps显示有< default >。总结:仔细处理子进程死后的信息,如果不想处理就需要交给系统处理。
9.信号:signal(, EXIT),jps
进程间通信方式:1.管道
:ls | grep 1。
2.消息队列
:内核创建的一个消息队列,os中多个进程都能操作这个消息队列,可以往里面发送消息,可以接收消息。类似socket。
3.共享内存
:每个进程访问内存时,有一个虚拟内存地址和物理内存地址的一个映射,一般两个进程的虚拟内存地址可以一样,但映射的物理内存地址
一般不一样。共享内存
就是将它们映射的物理内存地址也变一样,这时两个进程能同时访问一块相同的物理内存,于是借助这块物理内存实现通信。
4.套接字
:常见,访问数据库进程和数据库进程本身,这两个进程间通信就是通过3306号端口建立起的tcp套接字。本机访问mysql不走tcp的套接字,而是走linux底层套接字。
5.信号量/灯
:类似一个计数器,控制多个进程对一个共享资源的访问,起到控制数量的锁机制。
6.信号
:一个进程可向另一个进程发送一个信号,进程可处理这个信号。如下列出所有信号,linux中信号大多数是把另一个进程杀死,干脆把这个指令叫kill了,如下64种死法。 如下是键盘中断ctrl+c,是当前shell向tail -f这个进程发送一个信号值为2的2)SIGINT
的信号。
9.1 捕捉信号:ctrl+c:2
如下在死循环之前注册下信号的处理,如下让ctrl+c无效。
点击Build后就会将类编译出来。
如上ctrl+c无法停止,只能用kill,jps查看进程pid。
9.2 捕捉信号:kill -9:9
9.3 捕捉信号:kill:15
将如上KILL换成TERM即SIGTERM,重新build project。win下信号比linux下信号少很多,将当前程序打包成jar包传到linux。用如下命令行打包。
如下用压缩软件打开1.jar。
如下:后有一个空格,最后一行是空行。
9.4 程序后台运行两种方法:&:ctrl+c无法中止,用killall book1或kill 进程号。if (fork()>0)return 0 父进程退出
信号作用:服务程序在后台运行,如果想终止它,杀了它不是个好办法,因为没有释放资源。如果能向程序发一个信号,程序收到这个信号后调用一个函数,在函数中编写释放资源代码,程序就可以安全体面退出。
下面 EXIT函数就是自定义函数,TcpServer设为全局变量因为EXIT函数要访问它并关闭socket。
10._public.h
#ifndef _PUBLIC_H
#define _PUBLIC_H 1
#include "_cmpublic.h"
//全路径文件名,大小,时间的结构体
struct st_fileinfo
{char filename[301];int filesize;char mtime[21];
};//1111111111111111111.读取某目录下的全部的文件
class CDir
{
public:char m_DirName[301]; // 目录名char m_FileName[301]; // 文件名,不包括目录名char m_FullFileName[301]; // 文件全名,包括目录名int m_FileSize; // 文件的大小char m_ModifyTime[21]; // 文件最后一次被修改的时间char m_CreateTime[21]; // 文件生成的时间char m_AccessTime[21]; // 文件最后一次被访问的时间int m_uPOS; // 已读取m_vFileName容器的位置vector<string> m_vFileName; // 存放OpenDir方法获取到的文件列表CDir();// 变量初始化void initdata();// 设置日期时间的格式,支持"yyyy-mm-dd hh24:mi:ss"和"yyyymmddhh24miss"两种格式,缺省是前者char m_DateFMT[21];void SetDateFMT(const char *in_DateFMT);// 打开目录,获取文件名信息,存放于m_vFileName容器中// in_DirName,待打开的目录名// in_MatchStr,待获取文件名的匹配规则// in_MaxCount,获取文件的最大数量// bAndChild,是否打开各级子目录// bSort,是否对结果集进行排序bool OpenDir(const char *in_DirName,const char *in_MatchStr,const unsigned int in_MaxCount=10000,const bool bAndChild=false,bool bSort=false);// 这是一个递归函数,用于OpenDir()的调用。bool _OpenDir(const char *in_DirName,const char *in_MatchStr,const unsigned int in_MaxCount,const bool bAndChild);// 逐个读取目录中的文件信息bool ReadDir();~CDir();
};//111111111111111111112.STRCPY、STRNCPY、STRCAT、STRNCAT四个函数,弥补库函数的缺陷
// 以后可以用这四个函数取代strcpy、strncpy、strcat、strncat
// 函数的第二个参数是第一个参数dest有效长度,即占用内存的字节数-1。
// 该系列函数解决三个问题:1)变量初始化;2)内存溢出;3)修复strncpy的缺陷。
char *STRCPY(char* dest,const size_t destlen,const char* src);
char *STRNCPY(char* dest,const size_t destlen,const char* src,size_t n);
char *STRCAT(char* dest,const size_t destlen,const char* src);
char *STRNCAT(char* dest,const size_t destlen,const char* src,size_t n);int SNPRINTF(char *str, size_t size, const char *fmt, ...);// 把整数的时间转换为字符串格式的时间,格式如:"2019-02-08 12:05:08",如果转换成功函数返回0,失败返回-1,函数的声明如下:
int timetostr(const time_t ti,char *strtime);// 把字符串格式的时间转换为整数的时间,函数的声明如下:
int strtotime(const char *strtime,time_t *ti);// 从文件文件中读取一行
// strEndStr是一行数据的结束标志,如果为空,则以换行符"\n"为结束标志。
// 本函数不会删除行的结束标志
bool FGETS(const FILE *fp,char *strBuffer,const int ReadSize,const char *strEndStr=0);//11111111111111111111113.文件操作类声明
class CFile
{
private:FILE *m_fp; // 文件指针bool m_bEnBuffer; // 是否启用缓冲区,true-启用;false-不启用char m_filename[301]; // 文件名char m_filenametmp[301]; // 临时文件名public:CFile(); // 类的构造函数~CFile(); // 类的析构函数bool IsOpened(); // 判断文件是否已打开// 打开文件,参数与fopen相同,打开成功true,失败返回false bool Open(const char *filename,const char *openmode,bool bEnBuffer=true);// 关闭文件指针,并删除文件bool CloseAndRemove();// 专为改名而创建文件,参数与fopen相同,打开成功true,失败返回false // 函数将会创建filename后加.tmp的临时文件,调用CloseAndRename()后才把临时文件改名为正式文件bool OpenForRename(const char *filename,const char *openmode,bool bEnBuffer=true);// 关闭文件并改名bool CloseAndRename();// 调用fprintf向文件写入数据void Fprintf(const char *fmt, ... );// 调用fgets从文件中读取一行,bDelCRT=true删除换行符,false不删除,缺省为falsebool Fgets(char *strBuffer,const int ReadSize,bool bDelCRT=false);// 从文件文件中读取一行// strEndStr是一行数据的结束标志,如果为空,则以换行符"\n"为结束标志。// 与Fgets不同,本函数不删除结束标志bool FFGETS(char *strBuffer,const int ReadSize,const char *strEndStr=0);// 调用fread从文件中读取数据size_t Fread(void *ptr,size_t size);// 调用fwrite向文件中写数据size_t Fwrite(const void *ptr,size_t size);// 关闭文件指针,如果存在临时文件,就删除它。void Close();
};//1111111111111111111111111111114.拆分字符串的类
// 字符串的格式为:内容1+分隔字符串+内容2+分隔字符串+内容3
// 如:num~!~name~!~address,分隔符为"~!~"
class CCmdStr
{
public:vector<string> m_vCmdStr; // 存放拆分后的字段内容。CCmdStr();// 拆分字符串到容器中void SplitToCmd(const string in_string,const char *in_sep,const bool bdeletespace=true);int CmdCount();// 获取字段的值,取每个字段的值inum从0开始bool GetValue(const int inum,char *in_return);bool GetValue(const int inum,char *in_return,const int in_len);bool GetValue(const int inum,int *in_return);bool GetValue(const int inum,long *in_return);bool GetValue(const int inum,double *in_return);~CCmdStr();
};// 删除字符串左边指定的字符
void DeleteLChar(char *in_string,const char in_char);// 删除字符串右边指定的字符
void DeleteRChar(char *in_string,const char in_char);// 删除字符串两边指定的字符
void DeleteLRChar(char *in_string,const char in_char);//11111111111111111115.取操作系统的时间
/*out_stime是输出结果in_interval是偏移常量,单位是秒返回的格式由fmt决定,fmt目前的取值如下,如果需要,可以增加:yyyy-mm-dd hh:mi:ss,此格式是缺省格式,长度为19yyyymmddhhmissyyyy-mm-ddyyyymmddhh:mi:sshhmisshh:mihhmihhmi
*/
void LocalTime(char *out_stime,const char *in_fmt=0,const int in_interval=0);//日志文件操作类
#define MAXLOGFSIZE 100 // 日志文件切换的大小
// 日志文件操作类
class CLogFile
{
public:FILE *m_tracefp; // 日志文件指针char m_filename[301]; // 日志文件全名char m_openmode[11]; // 日志文件的打开方式bool m_bBackup; // 日志文件超出MAXLOGFSIZE,是否自动备份bool m_bEnBuffer; // 写入日志时,是否启用操作系统的缓冲机制CLogFile();// filename日志文件名// openmode打开文件的方式,操作日志文件的权限,同打开文件函数(fopen)使用方法一致,一般采用"a+"// bBackup,true-备份,false-不备份,在多进程的服务程序中,如果多个进行共用一个日志文件,bBackup必须为false// bEnBuffer:true-启用缓冲区,false-不启用缓冲区,如果启用缓冲区,那么写进日志文件中的内容不会立即写入文件// 注意,bEnBuffer参数的值如果设置为true,可能会导致日志文件内容不完整。bool Open(const char *in_filename,const char *in_openmode,bool bBackup=true,bool bEnBuffer=false);// 如果日志文件大于100M,就备份它// 备份后的文件会在源文件名后加上日期时间// 注意,在多进程程序中,日志文件不可切换,多线程程序中,日志文件可切换。bool BackupLogFile();// 写日志文件,它是个可变参数的方法,同printf函数。// Write()方法会写入时间,WriteEx()方法不写时间。bool Write(const char *fmt,...);bool WriteEx(const char *fmt,...);// 关闭日志文件void Close();~CLogFile();
};// 关闭全部的信号和输入输出
void CloseIOAndSignal();// 用某文件或目录的全路径中的目录创建目录,以及该目录下的各级子目录
// pathorfilename 目录名或文件名
// bisfilename true-pathorfilename是文件名,否则是目录名
bool MKDIR(const char *pathorfilename,bool bisfilename=true);// 删除文件,如果删除失败,会尝试in_times次
bool REMOVE(const char *in_filename,const int in_times=3);// 把in_srcfilename改名为in_dstfilename,如果改名失败,会尝试in_times次
bool RENAME(const char *in_srcfilename,const char *in_dstfilename,const int in_times=3);// 把某一个文件复制到另一个文件
bool COPY(const char *srcfilename,const char *dstfilename);// 调用fopen函数打开文件,如果文件名中包含的目录不存在,就创建目录
FILE *FOPEN(const char *filename,const char *mode);// 获取文件的大小,返回字节数
int FileSize(const char *in_FullFileName);// 获取文件的时间,即modtime
void FileMTime(const char *in_FullFileName,char *out_ModTime);// 更改文件的修改时间属性,mtime指定了时间,格式不限,只要包括了yyyy,mm,dd,hh24,mi,ss即可。
int UTime(const char *filename,const char *mtime);// 把字符串格式的时间转换为time_t
// stime为输入的时间,格式不限,但一定要包括yyyymmddhh24miss
time_t UTCTime(const char *stime);// 从一个字符串中提取数字、符号和小数点,并判断是否是一个合法的数
// 如果不合法,将返回空字符串
// bWithSign==true表示包括符号,bWithDOT==true表示包括圆点
void PickNumber(const char *strSrc,char *strDst,const bool bWithSign,const bool bWithDOT);// 判断字符串中的负号和圆点是否合法
bool JudgeSignDOT(const char *strSrc,const char *strBZ);/*把一个字符串表格的时间加上一个偏移量,得到偏移后的时间in_stime是传入的时间,任意格式,但是一定要包括yyyymmddhh24miss,是否有分隔符没有关系。把yyyy-mm-dd hh24:mi:ss偏移in_interval秒传出的格式由fmt决定,fmt目前的取值如下,如果需要,可以增加:yyyy-mm-dd hh24:mi:ss(此格式是缺省格式)yyyymmddhh24missyyyymmddhh24missyyyy-mm-ddyyyymmddhh24:mi:sshh24misshh24:mihh24mi返回值:0-成功,-1-失败。
*/
int AddTime(const char *in_stime,char *out_stime,const int in_interval,const char *in_fmt=0);//1111111111111111111111116.以下是XML格式字符串的相关操作函数和类
// 操作XMLBuffer的函数
// in_XMLBuffer,XML格式的字符串,如下:
// <filename>/tmp/readme.txt</filename><mtime>2018-01-01 12:20:35</mtime><size>10241</size>
// in_FieldName,字段的标签名
// out_Value,获取内容存放的变量的指针
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,bool *out_Value);
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,int *out_Value);
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,unsigned int *out_Value);
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,long *out_Value);
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,unsigned long *out_Value);
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,double *out_Value);
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,char *out_Value,const int in_StrLen=0);//11111111111111111111111117.判断文件名是否匹配in_MatchStr指定的规则
// in_FileName文件名
// in_MatchStr规则表达式,如"*.txt,*.xml",中间用逗号分隔
bool MatchFileName(const string in_FileName,const string in_MatchStr);// 把小写转换成大写,忽略不是字母的字符
void ToUpper(char *str);
void ToUpper(string &str);// 把大写转换成小写,忽略不是字母的字符
void ToLower(char *str);
void ToLower(string &str);// 字符串替换函数
// 把in_string中的in_str1替换为in_str2
// bLoop是否循环执行替换
// 注意
// 1、如果in_str2比in_str1要长,替换后in_string会变长,所以必须保证in_string有足够的长度, 否则内存会溢出
// 2、如果in_str2中包函了in_str1的内容,且bLoop为true,就会进入死循环,最终导致内存溢出
void UpdateStr(char *in_string,const char *in_str1,const char *in_str2,bool bLoop=true);//111111111111111111111118.以下是TCP/IP通讯的函数和类
// socket通信的客户端类
class CTcpClient
{
public:int m_sockfd; // 客户端的socket.char m_ip[21]; // 服务端的ip地址。int m_port; // 与服务端通信的端口。bool m_state; // 与服务端的socket连接状态。bool m_btimeout; // 调用Read和Write方法时,失败的原因是否是超时:true-未超时,false-已超时。int m_buflen; // 调用Read方法后,接收到的报文的大小,单位:字节。CTcpClient(); // 构造函数。// 向服务端发起连接请求。// ip:服务端的ip地址。// port:服务端监听的端口。// 返回值:true-成功;false-失败。bool ConnectToServer(const char *ip,const int port);// 接收服务端发送过来的数据。// buffer:接收数据缓冲区的地址,数据的长度存放在m_buflen成员变量中。// itimeout:等待数据的超时时间,单位:秒,缺省值是0-无限等待。// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时,成员变量m_btimeout的值被设置为true;2)socket连接已不可用。bool Read(char *buffer,const int itimeout=0);// 向服务端发送数据。// buffer:待发送数据缓冲区的地址。// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。bool Write(const char *buffer,const int ibuflen=0);// 断开与服务端的连接void Close();~CTcpClient(); // 析构函数自动关闭socket,释放资源。
};// socket通信的服务端类
class CTcpServer
{
private:int m_socklen; // 结构体struct sockaddr_in的大小。struct sockaddr_in m_clientaddr; // 客户端的地址信息。struct sockaddr_in m_servaddr; // 服务端的地址信息。
public:int m_listenfd; // 服务端用于监听的socket。int m_connfd; // 客户端连接上来的socket。bool m_btimeout; // 调用Read和Write方法时,失败的原因是否是超时:true-未超时,false-已超时。int m_buflen; // 调用Read方法后,接收到的报文的大小,单位:字节。CTcpServer(); // 构造函数。// 服务端初始化。// port:指定服务端用于监听的端口。// 返回值:true-成功;false-失败,一般情况下,只要port设置正确,没有被占用,初始化都会成功。bool InitServer(const unsigned int port); // 阻塞等待客户端的连接请求。// 返回值:true-有新的客户端已连接上来,false-失败,Accept被中断,如果Accept失败,可以重新Accept。bool Accept();// 获取客户端的ip地址。// 返回值:客户端的ip地址,如"192.168.1.100"。char *GetIP();// 接收客户端发送过来的数据。// buffer:接收数据缓冲区的地址,数据的长度存放在m_buflen成员变量中。// itimeout:等待数据的超时时间,单位:秒,缺省值是0-无限等待。// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时,成员变量m_btimeout的值被设置为true;2)socket连接已不可用。bool Read(char *buffer,const int itimeout);// 向客户端发送数据。// buffer:待发送数据缓冲区的地址。// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。bool Write(const char *buffer,const int ibuflen=0);// 关闭监听的socket,即m_listenfd,常用于多进程服务程序的子进程代码中。void CloseListen();// 关闭客户端的socket,即m_connfd,常用于多进程服务程序的父进程代码中。void CloseClient();~CTcpServer(); // 析构函数自动关闭socket,释放资源。
};// 接收socket的对端发送过来的数据。
// sockfd:可用的socket连接。
// buffer:接收数据缓冲区的地址。
// ibuflen:本次成功接收数据的字节数。
// itimeout:接收等待超时的时间,单位:秒,缺省值是0-无限等待。
// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时;2)socket连接已不可用。
bool TcpRead(const int sockfd,char *buffer,int *ibuflen,const int itimeout=0);// 向socket的对端发送数据。
// sockfd:可用的socket连接。
// buffer:待发送数据缓冲区的地址。
// ibuflen:待发送数据的字节数,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。
// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。
bool TcpWrite(const int sockfd,const char *buffer,const int ibuflen=0);// 从已经准备好的socket中读取数据。
// sockfd:已经准备好的socket连接。
// buffer:接收数据缓冲区的地址。
// n:本次接收数据的字节数。
// 返回值:成功接收到n字节的数据后返回true,socket连接不可用返回false。
bool Readn(const int sockfd,char *buffer,const size_t n);// 向已经准备好的socket中写入数据。
// sockfd:已经准备好的socket连接。
// buffer:待发送数据缓冲区的地址。
// n:待发送数据的字节数。
// 返回值:成功发送完n字节的数据后返回true,socket连接不可用返回false。
bool Writen(const int sockfd,const char *buffer,const size_t n);//111111111111111111119.这是一个精确到微秒的计时器
class CTimer
{
public:struct timeval m_start,m_end;CTimer();// 开始计时void Start();// 计算已逝去的时间,单位:秒,小数点后面是微秒double Elapsed();
};#endif
11._public.cpp
#include "_public.h" int SNPRINTF(char *str, size_t size, const char *fmt, ...)
{memset(str,0,size+1);va_list arg;va_start( arg, fmt );vsnprintf( str,size, fmt, arg );va_end( arg );
}char *STRCPY(char* dest,const size_t destlen,const char* src)
{memset(dest,0,destlen+1); if (strlen(src)>destlen) strncpy(dest,src,destlen);else strcpy(dest,src);return dest;
}char *STRNCPY(char* dest,const size_t destlen,const char* src,size_t n)
{memset(dest,0,destlen+1); if (n>destlen) strncpy(dest,src,destlen);else strncpy(dest,src,n);return dest;
}char *STRCAT(char* dest,const size_t destlen,const char* src)
{memset(dest+strlen(dest),0,destlen-strlen(dest)+1); int left=destlen-strlen(dest);int len=0;if (strlen(src)>left) len=left;else len=strlen(src);strncat(dest,src,len);return dest;
}char *STRNCAT(char* dest,const size_t destlen,const char* src,size_t n)
{memset(dest+strlen(dest),0,destlen-strlen(dest)+1); int left=destlen-strlen(dest);int len=0;if (n>left) len=left;else len=n;strncat(dest,src,len);return dest;
}// 把整数的时间转换为字符串格式的时间,格式如:"2019-02-08 12:05:08",如果转换成功函数返回0,失败返回-1,函数的声明如下:
int timetostr(const time_t ti,char *strtime)
{struct tm *sttm; if ( (sttm=localtime(&ti))==0 ) return -1;sprintf(strtime,"%d-%02d-%02d %02d:%02d:%02d",\sttm->tm_year+1900,sttm->tm_mon+1,sttm->tm_mday,sttm->tm_hour,sttm->tm_min,sttm->tm_sec);return 0;
}// 把字符串格式的时间转换为整数的时间,函数的声明如下:
int strtotime(const char *strtime,time_t *ti)
{char strtmp[11];//"2019-02-08 12:05:08"struct tm sttm; memset(strtmp,0,sizeof(strtmp));strncpy(strtmp,strtime,4);sttm.tm_year=atoi(strtmp)-1900;memset(strtmp,0,sizeof(strtmp));strncpy(strtmp,strtime+5,2);sttm.tm_mon=atoi(strtmp)-1;memset(strtmp,0,sizeof(strtmp));strncpy(strtmp,strtime+8,2);sttm.tm_mday=atoi(strtmp);memset(strtmp,0,sizeof(strtmp));strncpy(strtmp,strtime+11,2);sttm.tm_hour=atoi(strtmp);memset(strtmp,0,sizeof(strtmp));strncpy(strtmp,strtime+14,2);sttm.tm_min=atoi(strtmp);memset(strtmp,0,sizeof(strtmp));strncpy(strtmp,strtime+17,2);sttm.tm_sec=atoi(strtmp);*ti=mktime(&sttm);return *ti;
}CFile::CFile() // 类的构造函数
{m_fp=0;m_bEnBuffer=true;memset(m_filename,0,sizeof(m_filename));memset(m_filenametmp,0,sizeof(m_filenametmp));
}// 关闭文件指针
void CFile::Close()
{if (m_fp==0) return;fclose(m_fp); // 关闭文件指针m_fp=0;memset(m_filename,0,sizeof(m_filename));// 如果存在临时文件,就删除它。if (strlen(m_filenametmp)!=0) remove(m_filenametmp);memset(m_filenametmp,0,sizeof(m_filenametmp));
}// 判断文件是否已打开
bool CFile::IsOpened()
{if (m_fp==0) return false;return true;
}// 关闭文件指针,并删除文件
bool CFile::CloseAndRemove()
{if (m_fp==0) return true;fclose(m_fp); // 关闭文件指针m_fp=0;if (remove(m_filename) != 0) { memset(m_filename,0,sizeof(m_filename)); return false; }memset(m_filename,0,sizeof(m_filename));return true;
}CFile::~CFile() // 类的析构函数
{Close();
}// 打开文件,参数与FOPEN相同,打开成功true,失败返回false
bool CFile::Open(const char *filename,const char *openmode,bool bEnBuffer)
{Close();if ( (m_fp=FOPEN(filename,openmode)) == 0 ) return false;memset(m_filename,0,sizeof(m_filename));strncpy(m_filename,filename,300);m_bEnBuffer=bEnBuffer;return true;
}// 专为改名而打开文件,参数与fopen相同,打开成功true,失败返回false
bool CFile::OpenForRename(const char *filename,const char *openmode,bool bEnBuffer)
{Close();memset(m_filename,0,sizeof(m_filename));strncpy(m_filename,filename,300);memset(m_filenametmp,0,sizeof(m_filenametmp));SNPRINTF(m_filenametmp,300,"%s.tmp",m_filename);if ( (m_fp=FOPEN(m_filenametmp,openmode)) == 0 ) return false;m_bEnBuffer=bEnBuffer;return true;
}// 关闭文件并改名
bool CFile::CloseAndRename()
{if (m_fp==0) return false;fclose(m_fp); // 关闭文件指针m_fp=0;if (rename(m_filenametmp,m_filename) != 0){remove(m_filenametmp);memset(m_filename,0,sizeof(m_filename));memset(m_filenametmp,0,sizeof(m_filenametmp));return false;}memset(m_filename,0,sizeof(m_filename));memset(m_filenametmp,0,sizeof(m_filenametmp));return true;
}// 调用fprintf向文件写入数据
void CFile::Fprintf(const char *fmt, ... )
{if ( m_fp == 0 ) return;va_list arg;va_start( arg, fmt );vfprintf( m_fp, fmt, arg );va_end( arg );if ( m_bEnBuffer == false ) fflush(m_fp);
}// 调用fgets从文件中读取一行,bDelCRT=true删除换行符,false不删除,缺省为false
bool CFile::Fgets(char *strBuffer,const int ReadSize,bool bDelCRT)
{if ( m_fp == 0 ) return false;memset(strBuffer,0,ReadSize+1);if (fgets(strBuffer,ReadSize,m_fp) == 0) return false;if (bDelCRT==true){DeleteRChar(strBuffer,'\n'); DeleteRChar(strBuffer,'\r');}return true;
}// 从文件文件中读取一行
// strEndStr是一行数据的结束标志,如果为空,则以换行符"\n"为结束标志。
// 与Fgets不同,本函数不删除结束标志
bool CFile::FFGETS(char *strBuffer,const int ReadSize,const char *strEndStr)
{return FGETS(m_fp,strBuffer,ReadSize,strEndStr);
}// 调用fread从文件中读取数据。
size_t CFile::Fread(void *ptr, size_t size)
{if ( m_fp == 0 ) return -1;return fread(ptr,1,size,m_fp);
}// 调用fwrite向文件中写数据
size_t CFile::Fwrite(const void *ptr, size_t size )
{if ( m_fp == 0 ) return -1;size_t tt=fwrite(ptr,1,size,m_fp);if ( m_bEnBuffer == false ) fflush(m_fp);return tt;
}// 从文件文件中读取一行
// strEndStr是一行数据的结束标志,如果为空,则以换行符"\n"为结束标志。
// 本函数不会删除行的结束标志
bool FGETS(const FILE *fp,char *strBuffer,const int ReadSize,const char *strEndStr)
{char strLine[ReadSize+1];memset(strLine,0,sizeof(strLine));while (true){memset(strLine,0,ReadSize+1);if (fgets(strLine,ReadSize,(FILE *)fp) == 0) break;// 防止strBuffer溢出if ( (strlen(strBuffer)+strlen(strLine)) >= (unsigned int)ReadSize ) break;strcat(strBuffer,strLine);if (strEndStr == 0) return true;if (strstr(strLine,strEndStr)!= 0) return true;}return false;
}CCmdStr::CCmdStr()
{m_vCmdStr.clear();
}void CCmdStr::SplitToCmd(const string in_string,const char *in_sep,const bool bdeletespace)
{// 清除所有的旧数据m_vCmdStr.clear();int iPOS=0;string srcstr,substr;srcstr=in_string;char str[2048];while ( (iPOS=srcstr.find(in_sep)) >= 0){substr=srcstr.substr(0,iPOS);if (bdeletespace == true){memset(str,0,sizeof(str));strncpy(str,substr.c_str(),2000);DeleteLRChar(str,' ');substr=str;}m_vCmdStr.push_back(substr);iPOS=iPOS+strlen(in_sep);srcstr=srcstr.substr(iPOS,srcstr.size()-iPOS);}substr=srcstr;if (bdeletespace == true){memset(str,0,sizeof(str));strncpy(str,substr.c_str(),2000);DeleteLRChar(str,' ');substr=str;}m_vCmdStr.push_back(substr);return;
}int CCmdStr::CmdCount()
{return m_vCmdStr.size();
}bool CCmdStr::GetValue(const int inum,char *in_return)
{if (inum >= m_vCmdStr.size()) return false;strcpy(in_return,m_vCmdStr[inum].c_str());return true;
}bool CCmdStr::GetValue(const int inum,char *in_return,const int in_len)
{memset(in_return,0,in_len+1);if (inum >= m_vCmdStr.size()) return false;if (m_vCmdStr[inum].length() > (unsigned int)in_len){strncpy(in_return,m_vCmdStr[inum].c_str(),in_len);}else{strcpy(in_return,m_vCmdStr[inum].c_str());}return true;
}bool CCmdStr::GetValue(const int inum,int *in_return)
{(*in_return) = 0;if (inum >= m_vCmdStr.size()) return false;(*in_return) = atoi(m_vCmdStr[inum].c_str());return true;
}bool CCmdStr::GetValue(const int inum,long *in_return)
{(*in_return) = 0;if (inum >= m_vCmdStr.size()) return false;(*in_return) = atol(m_vCmdStr[inum].c_str());return true;
}bool CCmdStr::GetValue(const int inum,double *in_return)
{(*in_return) = 0;if (inum >= m_vCmdStr.size()) return false;(*in_return) = (double)atof(m_vCmdStr[inum].c_str());return true;
}CCmdStr::~CCmdStr()
{m_vCmdStr.clear();
}/* 删除字符串左边指定的字符 */
void DeleteLChar(char *in_string,const char in_char)
{if (in_string == 0) return;if (strlen(in_string) == 0) return;char strTemp[strlen(in_string)+1];int iTemp=0;memset(strTemp,0,sizeof(strTemp));strcpy(strTemp,in_string);while ( strTemp[iTemp] == in_char ) iTemp++;memset(in_string,0,strlen(in_string)+1);strcpy(in_string,strTemp+iTemp);return;
}/* 删除字符串右边指定的字符 */
void DeleteRChar(char *in_string,const char in_char)
{if (in_string == 0) return;int istrlen = strlen(in_string);while (istrlen>0){if (in_string[istrlen-1] != in_char) break;in_string[istrlen-1]=0;istrlen--;}
}/* 删除字符串两边指定的字符 */
void DeleteLRChar(char *in_string,const char in_char)
{DeleteLChar(in_string,in_char);DeleteRChar(in_string,in_char);
}/*取操作系统的时间out_stime是输出结果in_interval是偏移常量,单位是秒返回的格式由fmt决定,fmt目前的取值如下,如果需要,可以增加:yyyy-mm-dd hh24:mi:ss,此格式是缺省格式yyyymmddhh24missyyyy-mm-ddyyyymmddhh24:mi:sshh24misshh24:mihh24mihh24mi
*/
void LocalTime(char *out_stime,const char *in_fmt,const int in_interval)
{if (in_fmt == 0) memset(out_stime,0,19+1);else memset(out_stime,0,strlen(in_fmt)+1); time_t timer;struct tm nowtimer;time( &timer ); timer=timer+in_interval;nowtimer = *localtime ( &timer ); nowtimer.tm_mon++;if (in_fmt==0){snprintf(out_stime,20,"%04u-%02u-%02u %02u:%02u:%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);return;}if (strcmp(in_fmt,"yyyy-mm-dd hh24:mi:ss") == 0){snprintf(out_stime,20,"%04u-%02u-%02u %02u:%02u:%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);return;}if (strcmp(in_fmt,"yyyy-mm-dd hh24:mi") == 0){snprintf(out_stime,17,"%04u-%02u-%02u %02u:%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min);return;}if (strcmp(in_fmt,"yyyy-mm-dd hh24") == 0){snprintf(out_stime,14,"%04u-%02u-%02u %02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour);return;}if (strcmp(in_fmt,"yyyy-mm-dd") == 0){snprintf(out_stime,11,"%04u-%02u-%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday); return;}if (strcmp(in_fmt,"yyyy-mm") == 0){snprintf(out_stime,8,"%04u-%02u",nowtimer.tm_year+1900,nowtimer.tm_mon); return;}if (strcmp(in_fmt,"yyyymmddhh24miss") == 0){snprintf(out_stime,15,"%04u%02u%02u%02u%02u%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);return;}if (strcmp(in_fmt,"yyyymmddhh24mi") == 0){snprintf(out_stime,13,"%04u%02u%02u%02u%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min);return;}if (strcmp(in_fmt,"yyyymmddhh24") == 0){snprintf(out_stime,11,"%04u%02u%02u%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour);return;}if (strcmp(in_fmt,"yyyymmdd") == 0){snprintf(out_stime,9,"%04u%02u%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday); return;}if (strcmp(in_fmt,"hh24miss") == 0){snprintf(out_stime,7,"%02u%02u%02u",nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec); return;}if (strcmp(in_fmt,"hh24mi") == 0){snprintf(out_stime,5,"%02u%02u",nowtimer.tm_hour,nowtimer.tm_min); return;}if (strcmp(in_fmt,"hh24") == 0){snprintf(out_stime,3,"%02u",nowtimer.tm_hour); return;}if (strcmp(in_fmt,"mi") == 0){snprintf(out_stime,3,"%02u",nowtimer.tm_min); return;}
}CLogFile::CLogFile()
{m_tracefp = 0;memset(m_filename,0,sizeof(m_filename));memset(m_openmode,0,sizeof(m_openmode));m_bBackup=true;m_bEnBuffer=false; //日志文件一般不启用缓冲区,可以立即看到
}CLogFile::~CLogFile()
{Close();
}void CLogFile::Close()
{if (m_tracefp != 0){fclose(m_tracefp); m_tracefp=0;}
}// filename日志文件名
// openmode打开文件的方式,操作日志文件的权限,同打开文件函数(FOPEN)使用方法一致
// bBackup,true-备份,false-不备份,在多进程的服务程序中,如果多个进行共用一个日志文件,bBackup必须为false
// bEnBuffer:true-启用缓冲区,false-不启用缓冲区,如果启用缓冲区,那么写进日志文件中的内容不会立即写入文件是
bool CLogFile::Open(const char *in_filename,const char *in_openmode,bool bBackup,bool bEnBuffer)
{if (m_tracefp != 0) { fclose(m_tracefp); m_tracefp=0; }m_bEnBuffer=bEnBuffer;memset(m_filename,0,sizeof(m_filename));strcpy(m_filename,in_filename);memset(m_openmode,0,sizeof(m_openmode));strcpy(m_openmode,in_openmode);if ((m_tracefp=FOPEN(m_filename,m_openmode)) == NULL) return false;m_bBackup=bBackup;return true;
}// 如果日志文件大于MAXLOGFSIZE,就备份它
bool CLogFile::BackupLogFile()
{// 不备份if (m_bBackup == false) return true;if (m_tracefp == 0) return true;fseek(m_tracefp,0L,2); //定位到文件最后,我们打开日志文件用a或a+这种方式,一打开任何时候文件指针都是在文件最后,这行代码可省if (ftell(m_tracefp) > MAXLOGFSIZE*1024*1024) // 看看文件有多大{fclose(m_tracefp); m_tracefp=0;char strLocalTime[21];memset(strLocalTime,0,sizeof(strLocalTime));LocalTime(strLocalTime,"yyyymmddhhmiss");char bak_filename[301];memset(bak_filename,0,sizeof(bak_filename));snprintf(bak_filename,300,"%s.%s",m_filename,strLocalTime);rename(m_filename,bak_filename);
printf("rename %s m_filename ok\n",bak_filename);if ((m_tracefp=FOPEN(m_filename,m_openmode)) == NULL) return false;}return true;
}bool CLogFile::Write(const char *fmt,...)
{if (BackupLogFile() == false) return false;char strtime[20]; LocalTime(strtime);va_list ap;va_start(ap,fmt);if (m_tracefp == 0) // m_tracefp日志文件指针{fprintf(stdout,"%s ",strtime); // stdout:C语言缺省的标准输出,strtime:输出时间vfprintf(stdout,fmt,ap); // ap:输出内容if (m_bEnBuffer==false) fflush(stdout);}else{fprintf(m_tracefp,"%s ",strtime);vfprintf(m_tracefp,fmt,ap);if (m_bEnBuffer==false) fflush(m_tracefp);}va_end(ap);return true;
}bool CLogFile::WriteEx(const char *fmt,...)
{va_list ap;va_start(ap,fmt);if (m_tracefp == 0){vfprintf(stdout,fmt,ap);if (m_bEnBuffer==false) fflush(stdout);}else{vfprintf(m_tracefp,fmt,ap);if (m_bEnBuffer==false) fflush(m_tracefp);}va_end(ap);return true;
}// 关闭全部的信号和输入输出
void CloseIOAndSignal()
{int ii=0;for (ii=0;ii<50;ii++){signal(ii,SIG_IGN); close(ii);}
}// 用某文件或目录的全路径中的目录创建目录,以级该目录下的各级子目录
bool MKDIR(const char *filename,bool bisfilename)
{// 检查目录是否存在,如果不存在,逐级创建子目录char strPathName[301];for (int ii=1; ii<strlen(filename);ii++){if (filename[ii] != '/') continue;memset(strPathName,0,sizeof(strPathName));strncpy(strPathName,filename,ii);if (access(strPathName,F_OK) == 0) continue;if (mkdir(strPathName,00755) != 0) return false;}if (bisfilename==false){if (access(filename,F_OK) != 0){if (mkdir(filename,00755) != 0) return false;}}return true;
}// 调用fopen函数打开文件,如果文件名中包含的目录不存在,就创建目录
FILE *FOPEN(const char *filename,const char *mode)
{if (MKDIR(filename) == false) return NULL;return fopen(filename,mode);
}// 获取文件的大小,返回字节数
int FileSize(const char *in_FullFileName)
{struct stat st_filestat;if (stat(in_FullFileName,&st_filestat) < 0) return -1;return st_filestat.st_size;
}// 更改文件的修改时间属性
int UTime(const char *filename,const char *mtime)
{struct utimbuf stutimbuf;stutimbuf.actime=stutimbuf.modtime=UTCTime(mtime);return utime(filename,&stutimbuf);
}// 把字符串格式的时间转换为time_t
// stime为输入的时间,格式不限,但一定要包括yyyymmddhh24miss
time_t UTCTime(const char *stime)
{char strtime[21],yyyy[5],mm[3],dd[3],hh[3],mi[3],ss[3];memset(strtime,0,sizeof(strtime));memset(yyyy,0,sizeof(yyyy));memset(mm,0,sizeof(mm));memset(dd,0,sizeof(dd));memset(hh,0,sizeof(hh));memset(mi,0,sizeof(mi));memset(ss,0,sizeof(ss));PickNumber(stime,strtime,false,false);if (strlen(strtime) != 14) return -1;strncpy(yyyy,strtime,4);strncpy(mm,strtime+4,2);strncpy(dd,strtime+6,2);strncpy(hh,strtime+8,2);strncpy(mi,strtime+10,2);strncpy(ss,strtime+12,2);struct tm time_str;time_str.tm_year = atoi(yyyy) - 1900;time_str.tm_mon = atoi(mm) - 1;time_str.tm_mday = atoi(dd);time_str.tm_hour = atoi(hh);time_str.tm_min = atoi(mi);time_str.tm_sec = atoi(ss);time_str.tm_isdst = 0;return mktime(&time_str);
}// 从一个字符串中提取数字,bWithSign==true表示包括负号,bWithDOT==true表示包括圆点
void PickNumber(const char *strSrc,char *strDst,const bool bWithSign,const bool bWithDOT)
{char strtemp[1024];memset(strtemp,0,sizeof(strtemp));strncpy(strtemp,strSrc,1000);DeleteLRChar(strtemp,' ');// 为了防止strSrc和strDst为同一变量的情况,所以strDst不能初始化// 判断字符串中的负号是否合法if ( (bWithSign==true) && (JudgeSignDOT(strtemp,"-") == false) ){strcpy(strDst,""); return;}// 判断字符串中的正号是否合法if ( (bWithSign==true) && (JudgeSignDOT(strtemp,"+") == false) ){strcpy(strDst,""); return;}// 判断字符串中的圆点是否合法if ( (bWithDOT==true) && (JudgeSignDOT(strtemp,".") == false) ){strcpy(strDst,""); return;}int iPosSrc,iPosDst,iLen;iPosSrc=iPosDst=iLen=0;iLen=strlen(strtemp);for (iPosSrc=0;iPosSrc<iLen;iPosSrc++){if ( (bWithSign==true) && (strtemp[iPosSrc] == '+') ){strDst[iPosDst++]=strtemp[iPosSrc]; continue;}if ( (bWithSign==true) && (strtemp[iPosSrc] == '-') ){strDst[iPosDst++]=strtemp[iPosSrc]; continue;}if ( (bWithDOT==true) && (strtemp[iPosSrc] == '.') ){strDst[iPosDst++]=strtemp[iPosSrc]; continue;}if (isdigit(strtemp[iPosSrc])) strDst[iPosDst++]=strtemp[iPosSrc];}strDst[iPosDst]=0;return;
}// 判断字符串中的负号和圆点是否合法
bool JudgeSignDOT(const char *strSrc,const char *strBZ)
{char *pos=0;pos=(char *)strstr(strSrc,strBZ);// 如果没有包括待搜索的字符串,就返回trueif (pos == 0) return true;// 如果strlen(pos)==1,表示结果中只有符号,没有其它字符,返回falseif (strlen(pos)==1) return false;// 如果待搜索的字符串是+号,就一定要是第一个字符if ( (strcmp(strBZ,"+") == 0) && (strncmp(strSrc,"+",1) != 0) ) return false;// 如果待搜索的字符串是-号,就一定要是第一个字符if ( (strcmp(strBZ,"-") == 0) && (strncmp(strSrc,"-",1) != 0) ) return false;// 如果包括多个待搜索的字符串,就返回falseif (strstr(pos+1,strBZ) > 0) return false;return true;
}/*把一个字符串表格的时间加上一个偏移量,得到偏移后的时间in_stime是传入的时间,任意格式,但是一定要包括yyyymmddhh24miss,是否有分隔符没有关系。把yyyy-mm-dd hh24:mi:ss偏移in_interval秒传出的格式由fmt决定,fmt目前的取值如下,如果需要,可以增加:yyyy-mm-dd hh24:mi:ss(此格式是缺省格式)yyyymmddhh24missyyyymmddhh24missyyyy-mm-ddyyyymmddhh24:mi:sshh24misshh24:mihh24mi返回值:0-成功,-1-失败。
*/
int AddTime(const char *in_stime,char *out_stime,const int in_interval,const char *in_fmt)
{time_t timer;struct tm nowtimer;timer=UTCTime(in_stime)+in_interval;nowtimer = *localtime ( &timer ); nowtimer.tm_mon++;// 为了防止in_stime和out_stime为同一变量的情况,所以out_stime在此处初始化,代码不可提前out_stime[0]=0;if (in_fmt==0){snprintf(out_stime,20,"%04u-%02u-%02u %02u:%02u:%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec); return 0;}if (strcmp(in_fmt,"yyyy-mm-dd hh24:mi:ss") == 0){snprintf(out_stime,20,"%04u-%02u-%02u %02u:%02u:%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec); return 0;}if (strcmp(in_fmt,"yyyymmddhh24miss") == 0){snprintf(out_stime,15,"%04u%02u%02u%02u%02u%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec); return 0;}if (strcmp(in_fmt,"yyyy-mm-dd") == 0){snprintf(out_stime,11,"%04u-%02u-%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday); return 0;}if (strcmp(in_fmt,"yyyymmdd") == 0){snprintf(out_stime,9,"%04u%02u%02u",nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday); return 0;}if (strcmp(in_fmt,"hh24:mi:ss") == 0){snprintf(out_stime,9,"%02u:%02u:%02u",nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec); return 0;}if (strcmp(in_fmt,"hh24:mi") == 0){snprintf(out_stime,9,"%02u:%02u",nowtimer.tm_hour,nowtimer.tm_min); return 0;}if (strcmp(in_fmt,"hh24mi") == 0){snprintf(out_stime,7,"%02u%02u",nowtimer.tm_hour,nowtimer.tm_min); return 0;}return -1;
}// 获取文件的时间,即modtime
void FileMTime(const char *in_FullFileName,char *out_ModTime)
{struct tm nowtimer;struct stat st_filestat;stat(in_FullFileName,&st_filestat);nowtimer = *localtime(&st_filestat.st_mtime);nowtimer.tm_mon++;snprintf(out_ModTime,15,"%04u%02u%02u%02u%02u%02u",\nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,\nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);
}// 操作XMLBuffer的函数
// in_XMLBuffer,XML格式的字符串
// in_FieldName,字段的标签名
// out_Value,获取内容存放的变量的指针
bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,char *out_Value,const int in_Len)
{strcpy(out_Value,"");char *start=NULL,*end=NULL;char m_SFieldName[51],m_EFieldName[51];int m_NameLen = strlen(in_FieldName);memset(m_SFieldName,0,sizeof(m_SFieldName));memset(m_EFieldName,0,sizeof(m_EFieldName));snprintf(m_SFieldName,50,"<%s>",in_FieldName);snprintf(m_EFieldName,50,"</%s>",in_FieldName);start=0; end=0;start = (char *)strstr(in_XMLBuffer,m_SFieldName);if (start != 0){end = (char *)strstr(start,m_EFieldName);}if ((start==0) || (end == 0)){return false;}int m_ValueLen = end - start - m_NameLen - 2 + 1 ;if ( ((m_ValueLen-1) <= in_Len) || (in_Len == 0) ){strncpy(out_Value,start+m_NameLen+2,m_ValueLen-1); out_Value[m_ValueLen-1]=0;}else{strncpy(out_Value,start+m_NameLen+2,in_Len); out_Value[in_Len]=0;}DeleteLRChar(out_Value,' ');return true;
}bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,bool *out_Value)
{(*out_Value) = false;char strTemp[51];memset(strTemp,0,sizeof(strTemp));if (GetXMLBuffer(in_XMLBuffer,in_FieldName,strTemp,10) == true){if ( (strcmp(strTemp,"true")==0) || (strcmp(strTemp,"true")==0) ) (*out_Value)=true; return true;}return false;
}bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,int *out_Value)
{(*out_Value) = 0;char strTemp[51];memset(strTemp,0,sizeof(strTemp));if (GetXMLBuffer(in_XMLBuffer,in_FieldName,strTemp,50) == true){(*out_Value) = atoi(strTemp); return true;}return false;
}bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,unsigned int *out_Value)
{(*out_Value) = 0;char strTemp[51];memset(strTemp,0,sizeof(strTemp));if (GetXMLBuffer(in_XMLBuffer,in_FieldName,strTemp,50) == true){(*out_Value) = (unsigned int)atoi(strTemp); return true;}return false;
}bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,long *out_Value)
{(*out_Value) = 0;char strTemp[51];memset(strTemp,0,sizeof(strTemp));if (GetXMLBuffer(in_XMLBuffer,in_FieldName,strTemp,50) == true){(*out_Value) = atol(strTemp); return true;}return false;
}bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,unsigned long *out_Value)
{(*out_Value) = 0;char strTemp[51];memset(strTemp,0,sizeof(strTemp));if (GetXMLBuffer(in_XMLBuffer,in_FieldName,strTemp,50) == true){(*out_Value) = (unsigned long)atol(strTemp); return true;}return false;
}bool GetXMLBuffer(const char *in_XMLBuffer,const char *in_FieldName,double *out_Value)
{(*out_Value) = 0;char strTemp[51];memset(strTemp,0,sizeof(strTemp));if (GetXMLBuffer(in_XMLBuffer,in_FieldName,strTemp,50) == true){(*out_Value) = atof(strTemp); return true;}return false;
}// 判断文件名是否和MatchFileName匹配,如果不匹配,返回失败
bool MatchFileName(const string in_FileName,const string in_MatchStr)
{// 如果用于比较的字符是空的,返回falseif (in_MatchStr.size() == 0) return false;// 如果被比较的字符串是“*”,返回trueif (in_MatchStr == "*") return true;// 处理文件名匹配规则中的时间匹配dd-nn.mmchar strTemp[2049];memset(strTemp,0,sizeof(strTemp));strncpy(strTemp,in_MatchStr.c_str(),2000);int ii,jj;int iPOS1,iPOS2;CCmdStr CmdStr,CmdSubStr;string strFileName,strMatchStr;strFileName=in_FileName;strMatchStr=strTemp;// 把字符串都转换成大写后再来比较ToUpper(strFileName);ToUpper(strMatchStr);CmdStr.SplitToCmd(strMatchStr,",");for (ii=0;ii<CmdStr.CmdCount();ii++){// 如果为空,就一定要跳过,否则就会被配上if (CmdStr.m_vCmdStr[ii].empty() == true) continue;iPOS1=iPOS2=0;CmdSubStr.SplitToCmd(CmdStr.m_vCmdStr[ii],"*");for (jj=0;jj<CmdSubStr.CmdCount();jj++){// 如果是文件名的首部if (jj == 0){if (strncmp(strFileName.c_str(),CmdSubStr.m_vCmdStr[jj].c_str(),CmdSubStr.m_vCmdStr[jj].size()) != 0) break;}// 如果是文件名的尾部if (jj == CmdSubStr.CmdCount()-1){if (strcmp(strFileName.c_str()+strFileName.size()-CmdSubStr.m_vCmdStr[jj].size(),CmdSubStr.m_vCmdStr[jj].c_str()) != 0) break;}iPOS2=strFileName.find(CmdSubStr.m_vCmdStr[jj],iPOS1);if (iPOS2 < 0) break;iPOS1=iPOS2+CmdSubStr.m_vCmdStr[jj].size();}if (jj==CmdSubStr.CmdCount()) return true;}return false;
}void ToUpper(char *str)
{if (str == 0) return;if (strlen(str) == 0) return;int istrlen=strlen(str);for (int ii=0;ii<istrlen;ii++){if ( (str[ii] >= 97) && (str[ii] <= 122) ) str[ii]=str[ii] - 32;}
}void ToUpper(string &str)
{if (str.empty()) return;char strtemp[str.size()+1];memset(strtemp,0,sizeof(strtemp));strcpy(strtemp,str.c_str());ToUpper(strtemp);str=strtemp;return;
}void ToLower(char *str)
{if (str == 0) return;if (strlen(str) == 0) return;int istrlen=strlen(str);for (int ii=0;ii<istrlen;ii++){if ( (str[ii] >= 65) && (str[ii] <= 90) ) str[ii]=str[ii] + 32;}
}void ToLower(string &str)
{if (str.empty()) return;char strtemp[str.size()+1];memset(strtemp,0,sizeof(strtemp));strcpy(strtemp,str.c_str());ToLower(strtemp);str=strtemp;return;
}CDir::CDir()
{m_uPOS=0;memset(m_DateFMT,0,sizeof(m_DateFMT));strcpy(m_DateFMT,"yyyy-mm-dd hh24:mi:ss");m_vFileName.clear();initdata();
}void CDir::initdata()
{memset(m_DirName,0,sizeof(m_DirName));memset(m_FileName,0,sizeof(m_FileName));memset(m_FullFileName,0,sizeof(m_FullFileName));m_FileSize=0;memset(m_CreateTime,0,sizeof(m_CreateTime));memset(m_ModifyTime,0,sizeof(m_ModifyTime));memset(m_AccessTime,0,sizeof(m_AccessTime));
}// 设置日期时间的格式,支持"yyyy-mm-dd hh24:mi:ss"和"yyyymmddhh24miss"两种格式,缺省是前者
void CDir::SetDateFMT(const char *in_DateFMT)
{memset(m_DateFMT,0,sizeof(m_DateFMT));strcpy(m_DateFMT,in_DateFMT);
}// 打开目录,获取文件名信息,存放于m_vFileName容器中
// in_dirname,待打开的目录名
// in_MatchStr,待获取文件名的匹配规则
// in_MaxCount,获取文件的最大数量
// bAndChild,是否打开各级子目录
// bSort,是否对结果时行排序
bool CDir::OpenDir(const char *in_DirName,const char *in_MatchStr,const unsigned int in_MaxCount,const bool bAndChild,bool bSort)
{m_uPOS=0;m_vFileName.clear();// 如果目录不存在,就创建该目录if (MKDIR(in_DirName,false) == false) return false;bool bRet=_OpenDir(in_DirName,in_MatchStr,in_MaxCount,bAndChild);if (bSort==true){sort(m_vFileName.begin(), m_vFileName.end());}return bRet;
}// 打开目录,这是个递归函数
bool CDir::_OpenDir(const char *in_DirName,const char *in_MatchStr,const unsigned int in_MaxCount,const bool bAndChild)
{DIR *dir;if ( (dir=opendir(in_DirName)) == NULL ) return false;char strTempFileName[1024];struct dirent *st_fileinfo;struct stat st_filestat;while ((st_fileinfo=readdir(dir)) != NULL){// 以"."打头的文件不处理if (st_fileinfo->d_name[0]=='.') continue;memset(strTempFileName,0,sizeof(strTempFileName));snprintf(strTempFileName,300,"%s//%s",in_DirName,st_fileinfo->d_name);UpdateStr(strTempFileName,"//","/");stat(strTempFileName,&st_filestat);// 判断是否是目录if (S_ISDIR(st_filestat.st_mode)){if (bAndChild == true){if (_OpenDir(strTempFileName,in_MatchStr,in_MaxCount,bAndChild) == false) {closedir(dir); return false;}}}else{if (MatchFileName(st_fileinfo->d_name,in_MatchStr) == false) continue;m_vFileName.push_back(strTempFileName);if ( m_vFileName.size()>in_MaxCount ) break;}}closedir(dir);return true;
}bool CDir::ReadDir()
{initdata();// 如果已读完,清空容器if (m_uPOS >= m_vFileName.size()) {m_uPOS=0; m_vFileName.clear(); return false;}int pos=0;pos=m_vFileName[m_uPOS].find_last_of("/");// 目录名memset(m_DirName,0,sizeof(m_DirName));strcpy(m_DirName,m_vFileName[m_uPOS].substr(0,pos).c_str());// 文件名memset(m_FileName,0,sizeof(m_FileName));strcpy(m_FileName,m_vFileName[m_uPOS].substr(pos+1,m_vFileName[m_uPOS].size()-pos-1).c_str());// 文件全名,包括路径snprintf(m_FullFileName,300,"%s",m_vFileName[m_uPOS].c_str());struct stat st_filestat;stat(m_FullFileName,&st_filestat);m_FileSize=st_filestat.st_size;struct tm nowtimer;if (strcmp(m_DateFMT,"yyyy-mm-dd hh24:mi:ss") == 0){nowtimer = *localtime(&st_filestat.st_mtime); nowtimer.tm_mon++;snprintf(m_ModifyTime,20,"%04u-%02u-%02u %02u:%02u:%02u",\nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,\nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);nowtimer = *localtime(&st_filestat.st_ctime); nowtimer.tm_mon++;snprintf(m_CreateTime,20,"%04u-%02u-%02u %02u:%02u:%02u",\nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,\nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);nowtimer = *localtime(&st_filestat.st_atime); nowtimer.tm_mon++;snprintf(m_AccessTime,20,"%04u-%02u-%02u %02u:%02u:%02u",\nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,\nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);}if (strcmp(m_DateFMT,"yyyymmddhh24miss") == 0){nowtimer = *localtime(&st_filestat.st_mtime); nowtimer.tm_mon++;snprintf(m_ModifyTime,20,"%04u%02u%02u%02u%02u%02u",\nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,\nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);nowtimer = *localtime(&st_filestat.st_ctime); nowtimer.tm_mon++;snprintf(m_CreateTime,20,"%04u%02u%02u%02u%02u%02u",\nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,\nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);nowtimer = *localtime(&st_filestat.st_atime); nowtimer.tm_mon++;snprintf(m_AccessTime,20,"%04u%02u%02u%02u%02u%02u",\nowtimer.tm_year+1900,nowtimer.tm_mon,nowtimer.tm_mday,\nowtimer.tm_hour,nowtimer.tm_min,nowtimer.tm_sec);}m_uPOS++;return true;
}CDir::~CDir()
{m_vFileName.clear();// m_vDirName.clear();
}// 把字符串中的某字符串用另一个字符串代替
void UpdateStr(char *in_string,const char *in_str1,const char *in_str2,bool bLoop)
{if (in_string == 0) return;if (strlen(in_string) == 0) return;char strTemp[2048];char *strStart=in_string;char *strPos=0;while (true){if (strlen(in_string) >2000) break;if (bLoop == true){strPos=strstr(in_string,in_str1);}else{strPos=strstr(strStart,in_str1);}if (strPos == 0) break;memset(strTemp,0,sizeof(strTemp));strncpy(strTemp,in_string,strPos-in_string);strcat(strTemp,in_str2);strcat(strTemp,strPos+strlen(in_str1));strcpy(in_string,strTemp);strStart=strPos+strlen(in_str2);}
}// 删除文件,如果删除失败,会尝试in_times次
bool REMOVE(const char *in_filename,const int in_times)
{// 如果文件不存在,直接返回失败if (access(in_filename,R_OK) != 0) return false;for (int ii=0;ii<in_times;ii++){if (remove(in_filename) == 0) return true;usleep(100000);}return false;
}// 把in_srcfilename改名为in_dstfilename,如果改名失败,会尝试in_times次
bool RENAME(const char *in_srcfilename,const char *in_dstfilename,const int in_times)
{// 如果文件不存在,直接返回失败if (access(in_srcfilename,R_OK) != 0) return false;if (MKDIR(in_dstfilename) == false) return false;for (int ii=0;ii<in_times;ii++){if (rename(in_srcfilename,in_dstfilename) == 0) return true;usleep(100000);}return false;
}CTcpClient::CTcpClient()
{m_sockfd=-1;memset(m_ip,0,sizeof(m_ip));m_port=0;m_btimeout=false;
}bool CTcpClient::ConnectToServer(const char *ip,const int port)
{if (m_sockfd != -1) { close(m_sockfd); m_sockfd = -1; }strcpy(m_ip,ip);m_port=port;struct hostent* h;struct sockaddr_in servaddr;if ( (m_sockfd = socket(AF_INET,SOCK_STREAM,0) ) < 0) return false;if ( !(h = gethostbyname(m_ip)) ){close(m_sockfd); m_sockfd = -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_sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0){close(m_sockfd); m_sockfd = -1; return false;}return true;
}bool CTcpClient::Read(char *buffer,const int itimeout)
{if (m_sockfd == -1) return false;if (itimeout>0){fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(m_sockfd,&tmpfd);struct timeval timeout;timeout.tv_sec = itimeout; timeout.tv_usec = 0;m_btimeout = false;int i;if ( (i = select(m_sockfd+1,&tmpfd,0,0,&timeout)) <= 0 ){if (i==0) m_btimeout = true;return false;}}m_buflen = 0;return (TcpRead(m_sockfd,buffer,&m_buflen));
}bool CTcpClient::Write(const char *buffer,const int ibuflen)
{if (m_sockfd == -1) return false;fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(m_sockfd,&tmpfd);struct timeval timeout;timeout.tv_sec = 5; timeout.tv_usec = 0;m_btimeout = false;int i;if ( (i=select(m_sockfd+1,0,&tmpfd,0,&timeout)) <= 0 ){if (i==0) m_btimeout = true;return false;}int ilen=ibuflen;if (ibuflen==0) ilen=strlen(buffer);return(TcpWrite(m_sockfd,buffer,ilen));
}void CTcpClient::Close()
{if (m_sockfd > 0) close(m_sockfd); m_sockfd=-1;memset(m_ip,0,sizeof(m_ip));m_port=0;m_btimeout=false;
}CTcpClient::~CTcpClient()
{Close();
}CTcpServer::CTcpServer()
{m_listenfd=-1;m_connfd=-1;m_socklen=0;m_btimeout=false;
}bool CTcpServer::InitServer(const unsigned int port)
{if (m_listenfd > 0) { close(m_listenfd); m_listenfd=-1; }m_listenfd = socket(AF_INET,SOCK_STREAM,0);// WINDOWS平台如下//char b_opt='1';//setsockopt(m_listenfd,SOL_SOCKET,SO_REUSEADDR,&b_opt,sizeof(b_opt));//setsockopt(m_listenfd,SOL_SOCKET,SO_KEEPALIVE,&b_opt,sizeof(b_opt));// Linux如下int opt = 1; unsigned int len = sizeof(opt);setsockopt(m_listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,len);setsockopt(m_listenfd,SOL_SOCKET,SO_KEEPALIVE,&opt,len);memset(&m_servaddr,0,sizeof(m_servaddr));m_servaddr.sin_family = AF_INET;m_servaddr.sin_addr.s_addr = htonl(INADDR_ANY);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,5) != 0 ){CloseListen(); return false;}m_socklen = sizeof(struct sockaddr_in);return true;
}bool CTcpServer::Accept()
{if (m_listenfd == -1) return false;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(char *buffer,const int itimeout)
{if (m_connfd == -1) return false;if (itimeout>0){fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(m_connfd,&tmpfd);struct timeval timeout;timeout.tv_sec = itimeout; timeout.tv_usec = 0;m_btimeout = false;int i;if ( (i = select(m_connfd+1,&tmpfd,0,0,&timeout)) <= 0 ){if (i==0) m_btimeout = true;return false;}}m_buflen = 0;return(TcpRead(m_connfd,buffer,&m_buflen));
}bool CTcpServer::Write(const char *buffer,const int ibuflen)
{if (m_connfd == -1) return false;fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(m_connfd,&tmpfd);struct timeval timeout;timeout.tv_sec = 5; timeout.tv_usec = 0;m_btimeout = false;int i;if ( (i=select(m_connfd+1,0,&tmpfd,0,&timeout)) <= 0 ){if (i==0) m_btimeout = true;return false;}int ilen = ibuflen;if (ilen==0) strlen(buffer);return(TcpWrite(m_connfd,buffer,ilen));
}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,char *buffer,int *ibuflen,const int itimeout)
{if (sockfd == -1) return false;if (itimeout > 0){fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(sockfd,&tmpfd);struct timeval timeout;timeout.tv_sec = itimeout; timeout.tv_usec = 0;int i;if ( (i = select(sockfd+1,&tmpfd,0,0,&timeout)) <= 0 ) return false;}(*ibuflen) = 0;if (Readn(sockfd,(char*)ibuflen,4) == false) return false;if (Readn(sockfd,buffer,(*ibuflen)) == false) return false;return true;
}bool TcpWrite(const int sockfd,const char *buffer,const int ibuflen)
{if (sockfd == -1) return false;fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(sockfd,&tmpfd);struct timeval timeout;timeout.tv_sec = 5; timeout.tv_usec = 0;if ( select(sockfd+1,0,&tmpfd,0,&timeout) <= 0 ) return false;int ilen=0;// 如果长度为0,就采用字符串的长度if (ibuflen==0) ilen=strlen(buffer);else ilen=ibuflen;char strTBuffer[ilen+4];memset(strTBuffer,0,sizeof(strTBuffer));memcpy(strTBuffer,&ilen,4);memcpy(strTBuffer+4,buffer,ilen);if (Writen(sockfd,strTBuffer,ilen+4) == false) return false;return true;
}bool Readn(const int sockfd,char *buffer,const size_t n)
{int nLeft,nread,idx;nLeft = n;idx = 0;while(nLeft > 0){if ( (nread = recv(sockfd,buffer + idx,nLeft,0)) <= 0) return false;idx += nread;nLeft -= nread;}return true;
}bool Writen(const int sockfd,const char *buffer,const size_t n)
{int nLeft,idx,nwritten;nLeft = n; idx = 0;while(nLeft > 0 ){ if ( (nwritten = send(sockfd, buffer + idx,nLeft,0)) <= 0) return false; nLeft -= nwritten;idx += nwritten;}return true;
}// 把文件通过sockfd发送给对端
bool SendFile(int sockfd,struct st_fileinfo *stfileinfo,CLogFile *logfile)
{char strSendBuffer[301],strRecvBuffer[301];memset(strSendBuffer,0,sizeof(strSendBuffer));snprintf(strSendBuffer,300,"<filename>%s</filename><filesize>%d</filesize><mtime>%s</mtime>",stfileinfo->filename,stfileinfo->filesize,stfileinfo->mtime);if (TcpWrite(sockfd,strSendBuffer) == false){if (logfile!=0) logfile->Write("SendFile TcpWrite() failed.\n"); return false;}int bytes=0;int total_bytes=0;int onread=0;char buffer[1000];FILE *fp=0;if ( (fp=FOPEN(stfileinfo->filename,"rb")) == 0 ){if (logfile!=0) logfile->Write("SendFile FOPEN(%s) failed.\n",stfileinfo->filename); return false;}while (true){memset(buffer,0,sizeof(buffer));if ((stfileinfo->filesize-total_bytes) > 1000) onread=1000;else onread=stfileinfo->filesize-total_bytes;bytes=fread(buffer,1,onread,fp);if (bytes > 0){if (Writen(sockfd,buffer,bytes) == false){if (logfile!=0) logfile->Write("SendFile Writen() failed.\n"); fclose(fp); fp=0; return false;}}total_bytes = total_bytes + bytes;if ((int)total_bytes == stfileinfo->filesize) break;}fclose(fp);// 接收对端返回的确认报文int buflen=0;memset(strRecvBuffer,0,sizeof(strRecvBuffer));if (TcpRead(sockfd,strRecvBuffer,&buflen)==false){if (logfile!=0) logfile->Write("SendFile TcpRead() failed.\n"); return false;}if (strcmp(strRecvBuffer,"ok")!=0) return false;return true;
}// 接收通过socdfd发送过来的文件
bool RecvFile(int sockfd,struct st_fileinfo *stfileinfo,CLogFile *logfile)
{char strSendBuffer[301],strRecvBuffer[301];char strfilenametmp[301]; memset(strfilenametmp,0,sizeof(strfilenametmp));sprintf(strfilenametmp,"%s.tmp",stfileinfo->filename);FILE *fp=0;if ( (fp=FOPEN(strfilenametmp,"wb")) ==0) // FOPEN可创建目录{if (logfile!=0) logfile->Write("RecvFile FOPEN %s failed.\n",strfilenametmp); return false;}int total_bytes=0;int onread=0;char buffer[1000];while (true){memset(buffer,0,sizeof(buffer));if ((stfileinfo->filesize-total_bytes) > 1000) onread=1000;else onread=stfileinfo->filesize-total_bytes;if (Readn(sockfd,buffer,onread) == false){if (logfile!=0) logfile->Write("RecvFile Readn() failed.\n"); fclose(fp); fp=0; return false;}fwrite(buffer,1,onread,fp);total_bytes = total_bytes + onread;if ((int)total_bytes == stfileinfo->filesize) break;}fclose(fp);// 重置文件的时间UTime(strfilenametmp,stfileinfo->mtime);memset(strSendBuffer,0,sizeof(strSendBuffer));if (RENAME(strfilenametmp,stfileinfo->filename)==true) strcpy(strSendBuffer,"ok");else strcpy(strSendBuffer,"failed");// 向对端返回响应内容if (TcpWrite(sockfd,strSendBuffer)==false){if (logfile!=0) logfile->Write("RecvFile TcpWrite() failed.\n"); return false;}if (strcmp(strSendBuffer,"ok") != 0) return false;return true;
}// 把某一个文件复制到另一个文件
bool COPY(const char *srcfilename,const char *dstfilename)
{if (MKDIR(dstfilename) == false) return false;char strdstfilenametmp[301];memset(strdstfilenametmp,0,sizeof(strdstfilenametmp));snprintf(strdstfilenametmp,300,"%s.tmp",dstfilename);int srcfd,dstfd;srcfd=dstfd=-1;int iFileSize=FileSize(srcfilename);int bytes=0;int total_bytes=0;int onread=0;char buffer[5000];if ( (srcfd=open(srcfilename,O_RDONLY)) < 0 ) return false;if ( (dstfd=open(strdstfilenametmp,O_WRONLY|O_CREAT|O_TRUNC,S_IWUSR|S_IRUSR|S_IXUSR)) < 0) { close(srcfd); return false; }while (true){memset(buffer,0,sizeof(buffer));if ((iFileSize-total_bytes) > 5000) onread=5000;else onread=iFileSize-total_bytes;bytes=read(srcfd,buffer,onread);if (bytes > 0) write(dstfd,buffer,bytes);total_bytes = total_bytes + bytes;if (total_bytes == iFileSize) break;}close(srcfd);close(dstfd);// 更改文件的修改时间属性char strmtime[21];memset(strmtime,0,sizeof(strmtime));FileMTime(srcfilename,strmtime);UTime(strdstfilenametmp,strmtime);if (RENAME(strdstfilenametmp,dstfilename) == false) { REMOVE(strdstfilenametmp); return false; }return true;
}CTimer::CTimer()
{memset(&m_start,0,sizeof(struct timeval));memset(&m_end,0,sizeof(struct timeval));// 开始计时Start();
}// 开始计时
void CTimer::Start()
{gettimeofday( &m_start, NULL );
}// 计算已逝去的时间,单位:秒,小数点后面是微秒
double CTimer::Elapsed()
{gettimeofday( &m_end, NULL );double dstart,dend;dstart=dend=0;char strtemp[51];memset(strtemp,0,sizeof(strtemp));snprintf(strtemp,30,"%ld.%ld",m_start.tv_sec,m_start.tv_usec);dstart=atof(strtemp);memset(strtemp,0,sizeof(strtemp));snprintf(strtemp,30,"%ld.%ld",m_end.tv_sec,m_end.tv_usec);dend=atof(strtemp);// 重新开始计时Start();return dend-dstart;
}
12._cmpublic.h
#ifndef _cmpublic_H
#define _cmpublic_H#include <stdio.h>
#include <utime.h>
//#include <curl/curl.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 <semaphore.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iconv.h>
//#include<openssl/md5.h>#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <list>
#include <vector>
#include <deque>
#include <algorithm>//#include <boost/thread/thread.hpp>
//#include <boost/shared_ptr.hpp>
//#include <boost/signal.hpp>// 定义C++标准模板库的命名空间
using namespace std;// 以下是定义布尔数据类型的几个宏,在有的UNIX中,不一定支持布尔类型,
// 所以在这里自定义
#ifndef BOOL#define BOOL unsigned char
#endif#ifndef bool#define bool unsigned char
#endif#ifndef TRUE#define TRUE 1
#endif#ifndef true#define true 1
#endif#ifndef FALSE#define FALSE 0
#endif#ifndef false#define false 0
#endif#ifndef INT#define INT long
#endif#ifndef UINT#define UINT unsigned long
#endif#ifndef UCHAR#define UCHAR unsigned long
#endif#endif
13._ooci.h
//connection和sqlstatement类,CDA_DEF结构体
#ifndef __OOCI_H
#define __OOCI_H// C/C++库常用头文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>// oracle数据库接口库头文件,必须在装有oracle数据库主机运行
#include <oci.h>// OCI登录环境
struct LOGINENV
{char user[32]; // 数据库的用户名char pass[32]; // 数据库的密码char tnsname[51]; // 数据库的tnsname,在ORACLE_HOME/network/admin/tnsnames.ora中配置OCIEnv *envhp; // 环境变量的句柄
};// OCI上下文
struct OCI_CXT
{OCISvcCtx *svchp;OCIError *errhp;OCIEnv *envhp; // 环境变量的句柄
};// 语句
struct OCI_HANDLE
{OCISvcCtx *svchp; // 服务器上下文的句柄引用context句柄OCIStmt *smthp;OCIBind *bindhp;OCIDefine *defhp;OCIError *errhp; // 错误句柄引用context句柄OCIEnv *envhp; // 环境变量的句柄 引用context句柄
};//11111111111111111111.OCI执行的结果
struct CDA_DEF
{int rc; // 执行结果unsigned long rpc; // 执行SQL语句后,影响记录的行数char message[2048]; // 执行SQL语句如果失败,存放错误信息
};int oci_init(LOGINENV *env);
int oci_close(LOGINENV *env);
int oci_context_create(LOGINENV *env,OCI_CXT *cxt);
int oci_context_close(OCI_CXT *cxt);int oci_stmt_create(OCI_CXT *cxt,OCI_HANDLE *handle);
int oci_stmt_close(OCI_HANDLE *handle);//1111111111111111112.数据库连接池类
class connection
{
private:// 服务器环境句柄LOGINENV m_env;public:// 服务器上下文OCI_CXT m_cxt;// 连接状态,0-未连接,1-已连接int m_state;// 自动提交标志,0-关闭自动提交;1-开启自动提交int m_autocommitopt; // 数据库种类,固定取值为oraclechar m_dbtype[21];// 用于存放connection操作数据库的错误或最后一次执行SQL语句的结果CDA_DEF m_cda;connection();~connection();// 设置字符集,如果客户端的字符集与数据库的不一致,就会出现乱码。// 本方法要在connecttodb()之前调用。void character(char *charset);// 连接数据库,connstr的格式为:username/password@tnsname,autocommitopt:0-关闭自动提交,1-启用自动提交int connecttodb(char *connstr,char *charset,int autocommitopt=0);// 从connstr中解析username,password,tnsnamevoid setdbopt(char *connstr);// 断开与数据库的连接int disconnect();// 提交事务int commit(); // 回滚事务int rollback();// 获取错误信息void err_report();
};//1111111111111111113.SQL语言数据操作类
class sqlstatement
{
public:// 与数据库连接池的状态,0-未绑定,1-已绑定int m_state; // 语句句柄OCI_HANDLE m_handle;connection *m_conn;// SQL语句char m_sql[10240];// 执行结果CDA_DEF m_cda;int m_sqltype; // 待执行的SQL语句的类型,0-查询语句;1-非查询语句// 自动提交标志,0-关闭自动提交;1-开启自动提交int m_autocommitopt; sqlstatement();sqlstatement(connection *conn);~sqlstatement();// 设定数据库连接池int connect(connection *conn); // 断开与数据库连接池的连接int disconnect();// 分析SQL语句,支持存储过程,采用立刻分析的方式,即时返回分析结果int prepare(const char *fmt,...);// 绑定输入变量的地址int bindin(unsigned int position,int *value);int bindin(unsigned int position,long *value);int bindin(unsigned int position,unsigned int *value);int bindin(unsigned int position,unsigned long *value);int bindin(unsigned int position,float *value);int bindin(unsigned int position,double *value);int bindin(unsigned int position,char *value,unsigned int len);// 绑定输出变量的地址int bindout(unsigned int position,int *value);int bindout(unsigned int position,long *value);int bindout(unsigned int position,unsigned int *value);int bindout(unsigned int position,unsigned long *value);int bindout(unsigned int position,float *value);int bindout(unsigned int position,double *value);int bindout(unsigned int position,char *value,unsigned int len);// 执行SQL语句int execute();// 如果SQL语句不需要输入和输出变量,可以直接执行。int execute(const char *fmt,...);// 取下一条记录,只有当SQL语句是查询语句时才有意义 int next();// 错误报告void err_report();// 指向LOB字段的指针,在执行execute后,用bindblob或bindclob绑定,再用next获取指针OCILobLocator *m_lob;// 初始化lob指针,在sqlstatement::connect()中调用int alloclob();// 释放lob指针,在sqlstatement::disconnect()中调用void freelob();// 绑定lob指针int bindblob();int bindclob();// 以下两个函数都是把文件中的内容写入LOB字段,第二个函数是被第一个函数调用的int filetolob(char *filename);int filetolob(FILE *fp);// 把LOB字段中的内容写入文件int lobtofile(char *filename);int lobtofile(FILE *fp);
};
#endif
14._ooci.cpp
// 如果是windows平台,要用以下行,否则会报以下错误
// _ooci.cpp(845) : fatal error C1010: unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source?
// #include "stdafx.h"#include "_ooci.h"/*
OCI_SUCCESS 0 // maps to SQL_SUCCESS of SAG CLI 函数执行成功
OCI_SUCCESS_WITH_INFO 1 // maps to SQL_SUCCESS_WITH_INFO 执行成功,但有诊断消息返回,// 可能是警告信息,但是,在测试的时候,我还从未见// 识到OCI_SUCCESS_WITH_INFO是怎么回事
OCI_RESERVED_FOR_INT_USE 200 // reserved
OCI_NO_DATA 100 // maps to SQL_NO_DATA 函数执行完成,但没有其他数据
OCI_ERROR -1 // maps to SQL_ERROR 函数执行错误
OCI_INVALID_HANDLE -2 // maps to SQL_INVALID_HANDLE 传递给函数的参数为无效句柄,// 或传回的句柄无效
OCI_NEED_DATA 99 // maps to SQL_NEED_DATA 需要应用程序提供运行时刻的数据
OCI_STILL_EXECUTING -3123 // OCI would block error 服务环境建立在非阻塞模式,// OCI函数调用正在执行中OCI_CONTINUE -24200 // Continue with the body of the OCI function // 说明回调函数需要OCI库恢复其正常的处理操作
OCI_ROWCBK_DONE -24201 // done with user row callback
*/int oci_init(LOGINENV *env)
{//初始化Oracle 环境变量int oci_ret;oci_ret = OCIEnvCreate(&env->envhp,OCI_DEFAULT,NULL,NULL,NULL,NULL,0,NULL);if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ) {oci_close(env); return -1;}return 0;
} int oci_close(LOGINENV *env)
{int oci_ret;// 释放Oracle 环境变量oci_ret=OCIHandleFree(env->envhp,OCI_HTYPE_ENV);env->envhp = 0;return oci_ret;
}int oci_context_create(LOGINENV *env,OCI_CXT *cxt )
{// 创建数据库连接的上下文对象,连接服务器,认证并建立会话if (env->envhp == 0) return -1;int oci_ret;oci_ret = OCIHandleAlloc(env->envhp,(dvoid**)&cxt->errhp,OCI_HTYPE_ERROR,(size_t) 0,NULL);if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ) {oci_context_close(cxt); return -1;}// 登录oci_ret = OCILogon(env->envhp,cxt->errhp,&cxt->svchp,(OraText*)env->user,strlen(env->user),(OraText*)env->pass,strlen(env->pass),(OraText*)env->tnsname,strlen(env->tnsname));if( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){oci_context_close(cxt); return -1;}cxt->envhp = env->envhp;return 0;
}int oci_context_close(OCI_CXT *cxt)
{// 关闭数据库连接的上下文int oci_ret;oci_ret = OCILogoff(cxt->svchp,cxt->errhp);oci_ret = OCIHandleFree(cxt->svchp,OCI_HTYPE_SVCCTX);oci_ret = OCIHandleFree(cxt->errhp, OCI_HTYPE_ERROR);cxt->svchp=0;cxt->errhp=0;return oci_ret;
}int oci_stmt_create(OCI_CXT *cxt,OCI_HANDLE *handle )
{//创建语句int oci_ret;oci_ret = OCIHandleAlloc( cxt->envhp, (dvoid**)&handle->smthp, OCI_HTYPE_STMT,(size_t)0, NULL);if( oci_ret == OCI_SUCCESS || oci_ret == OCI_SUCCESS_WITH_INFO ){handle->svchp = cxt->svchp;handle->errhp = cxt->errhp;handle->envhp = cxt->envhp;oci_ret = OCI_SUCCESS;}return oci_ret;
}int oci_stmt_close(OCI_HANDLE *handle)
{// 关闭语句int oci_ret=0;oci_ret = OCIHandleFree(handle->smthp,OCI_HTYPE_STMT);return oci_ret;
}connection::connection()
{ m_state = 0; memset(&m_cxt,0,sizeof(OCI_CXT));memset(&m_env,0,sizeof(LOGINENV));memset(&m_cda,0,sizeof(m_cda));m_cda.rc=-1;strncpy(m_cda.message,"database not open.",128);// 数据库种类memset(m_dbtype,0,sizeof(m_dbtype));strcpy(m_dbtype,"oracle");
}connection::~connection()
{disconnect();
}// 从connstr中解析username,password,tnsname
void connection::setdbopt(char *connstr)
{char strtemp[201];memset(strtemp,0,sizeof(strtemp));strncpy(strtemp,connstr,128);char *pos=0;// tnsnamepos = strstr(strtemp,"@");if (pos > 0) {strncpy(m_env.tnsname,pos+1,50); pos[0]=0;}// passwordpos = strstr(strtemp,"/");if (pos > 0) {strncpy(m_env.pass,pos+1,30); pos[0]=0;}// usernamestrncpy(m_env.user,strtemp,30);
}// 设置字符集,如果客户端的字符集与数据库的不一致,就会出现乱码。
void connection::character(char *charset)
{// UNIX平台是采用setenv函数setenv("NLS_LANG",charset,1);// windows是采用putenv,如下/*char str[100];memset(str,0,sizeof(str));_snprintf(str,50,"NLS_LANG=%s",charset);putenv(str);*/
}int connection::connecttodb(char *connstr,char *charset,int autocommitopt)
{// 如果已连接上数据库,就不再连接// 所以,如果想重连数据库,必须显示的调用disconnect()方法后才能重连if (m_state == 1) return 0;// 设置字符集character(charset);// 设置日期字段的输出格式// UNIX平台是采用setenv函数// setenv("NLS_DATE_FORMAT","yyyy-mm-dd hh24:mi:ss",1);// windows是采用putenv,如下// putenv("NLS_DATE_FORMAT=yyyy-mm-dd hh24:mi:ss");// 从connstr中解析username,password,tnsnamesetdbopt(connstr);memset(&m_cda,0,sizeof(m_cda));// 初始化环境int oci_ret = oci_init(&m_env);if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){oci_close(&m_env); m_cda.rc=-1; strncpy(m_cda.message,"initialize oracle call interface failed.\n",128); return -1;}// 创建句柄,登录数据库oci_ret = oci_context_create(&m_env,&m_cxt);if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){oci_close(&m_env); m_cda.rc=1017; strncpy(m_cda.message,"ORA-01017: invalid username/password,logon denied.\n",128); return -1;}m_state = 1;m_autocommitopt=autocommitopt;return 0;
}int connection::disconnect()
{memset(&m_cda,0,sizeof(m_cda));if (m_state == 0) { m_cda.rc=-1; strncpy(m_cda.message,"database not open.",128); return -1;}rollback();oci_context_close(&m_cxt);oci_close(&m_env);m_state = 0; return 0;
}int connection::rollback()
{ memset(&m_cda,0,sizeof(m_cda));if (m_state == 0) { m_cda.rc=-1; strncpy(m_cda.message,"database not open.",128); return -1;}int oci_ret = OCITransRollback( m_cxt.svchp, m_cxt.errhp, OCI_DEFAULT ); if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){err_report(); return m_cda.rc;}return 0;
}int connection::commit()
{ memset(&m_cda,0,sizeof(m_cda));if (m_state == 0) { m_cda.rc=-1; strncpy(m_cda.message,"database not open.",128); return -1;}int oci_ret = OCITransCommit( m_cxt.svchp, m_cxt.errhp, OCI_DEFAULT );if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){err_report(); return m_cda.rc;}return 0;
}void connection::err_report()
{if (m_state == 0) { m_cda.rc=-1; strncpy(m_cda.message,"database not open.",128); return;}memset(&m_cda,0,sizeof(m_cda));m_cda.rc=-1;strncpy(m_cda.message,"call err_report failed.",128);if (m_cxt.errhp != NULL){if (OCIErrorGet(m_cxt.errhp,1,NULL,&m_cda.rc,(OraText*)m_cda.message,sizeof(m_cda.message),OCI_HTYPE_ERROR) == OCI_NO_DATA){// 如果获取不到错误信息,就返回正确的memset(&m_cda,0,sizeof(m_cda)); return;}}
}sqlstatement::sqlstatement()
{m_state=0; memset(&m_handle,0,sizeof(m_handle));memset(&m_cda,0,sizeof(m_cda));memset(m_sql,0,sizeof(m_sql));m_cda.rc=-1;strncpy(m_cda.message,"sqlstatement not connect to connection.\n",128);
}sqlstatement::sqlstatement(connection *conn)
{sqlstatement();connect(conn);
}sqlstatement::~sqlstatement()
{disconnect();
}int sqlstatement::connect(connection *conn)
{// 注意,一个sqlstatement在程序中只能连一个connection,不允许连多个connection// 所以,只要这个光标已打开,就不允许再次打开,直接返回成功if ( m_state == 1 ) return 0;memset(&m_cda,0,sizeof(m_cda));m_conn=conn;// 如果数据库连接类的指针为空,直接返回失败if (m_conn == 0) {m_cda.rc=-1; strncpy(m_cda.message,"database not open.\n",128); return -1;}// 如果数据库没有连接好,直接返回失败if (m_conn->m_state == 0) {m_cda.rc=-1; strncpy(m_cda.message,"database not open.\n",128); return -1;}m_cda.rc = oci_stmt_create(&m_conn->m_cxt,&m_handle);if ( m_cda.rc != OCI_SUCCESS && m_cda.rc != OCI_SUCCESS_WITH_INFO ){err_report(); return m_cda.rc;}m_state = 1; // 光标成功打开m_autocommitopt=m_conn->m_autocommitopt;m_cda.rc = OCI_SUCCESS; alloclob();return 0;
}int sqlstatement::disconnect()
{if (m_state == 0) return 0;memset(&m_cda,0,sizeof(m_cda));freelob();m_cda.rc = oci_stmt_close(&m_handle);m_state=0;memset(&m_handle,0,sizeof(m_handle));memset(&m_cda,0,sizeof(m_cda));memset(m_sql,0,sizeof(m_sql));m_cda.rc=-1;strncpy(m_cda.message,"cursor not open.",128);return 0;
}// 在新的OCI方法中,当SQL语句有错误时,OCIStmtPrepare返回的是0,不是失败
// 所以,程序员在程序中一般不必处理prepare的结果
int sqlstatement::prepare(const char *fmt,...)
{ memset(&m_cda,0,sizeof(m_cda));if (m_state == 0) {m_cda.rc=-1; strncpy(m_cda.message,"cursor not open.\n",128); return -1;}memset(m_sql,0,sizeof(m_sql));va_list ap;va_start(ap,fmt);vsnprintf(m_sql,10000,fmt,ap);int oci_ret = OCIStmtPrepare(m_handle.smthp,m_handle.errhp,(OraText*)m_sql,strlen(m_sql),OCI_NTV_SYNTAX,OCI_DEFAULT);if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){err_report(); return m_cda.rc;}// 判断是否是查询语句,如果是,把m_sqltype设为0,其它语句设为1。m_sqltype=1;// 从待执行的SQL语句中截取15个字符,如果这15个字符中包括了“select”,就认为是查询语句char strtemp[16]; memset(strtemp,0,sizeof(strtemp)); strncpy(strtemp,m_sql,15);if ( (strstr(strtemp,"select") > 0) || (strstr(strtemp,"Select") > 0) || (strstr(strtemp,"SELECT") > 0) ) m_sqltype=0; m_cda.rc = OCI_SUCCESS; return 0;
}int sqlstatement::bindin(unsigned int position,int *value)
{return OCIBindByPos(m_handle.smthp, &m_handle.bindhp, m_handle.errhp, (ub4)position, value, sizeof(int),SQLT_INT, NULL, NULL,NULL,0, NULL, OCI_DEFAULT);
}int sqlstatement::bindin(unsigned int position,long *value)
{return OCIBindByPos(m_handle.smthp, &m_handle.bindhp, m_handle.errhp, (ub4)position, value, sizeof(long),SQLT_INT, NULL, NULL,NULL,0, NULL, OCI_DEFAULT);
}int sqlstatement::bindin(unsigned int position,unsigned int *value)
{return OCIBindByPos(m_handle.smthp, &m_handle.bindhp,m_handle.errhp,(ub4)position,value,sizeof(unsigned int),SQLT_INT, NULL, NULL,NULL,0, NULL, OCI_DEFAULT);
}int sqlstatement::bindin(unsigned int position,unsigned long *value)
{return OCIBindByPos(m_handle.smthp,&m_handle.bindhp,m_handle.errhp,(ub4)position,value,sizeof(unsigned long),SQLT_INT, NULL, NULL,NULL,0, NULL, OCI_DEFAULT);
}int sqlstatement::bindin(unsigned int position,char *value,unsigned int len)
{return OCIBindByPos(m_handle.smthp, &m_handle.bindhp, m_handle.errhp, (ub4)position, value, len+1,SQLT_STR, NULL, NULL,NULL,0, NULL, OCI_DEFAULT);
}int sqlstatement::bindin(unsigned int position,float *value)
{return OCIBindByPos(m_handle.smthp, &m_handle.bindhp, m_handle.errhp, (ub4)position, value, sizeof(float),SQLT_FLT, NULL, NULL,NULL,0, NULL, OCI_DEFAULT);
}int sqlstatement::bindin(unsigned int position,double *value)
{return OCIBindByPos(m_handle.smthp, &m_handle.bindhp, m_handle.errhp, (ub4)position, value, sizeof(double),SQLT_FLT, NULL, NULL,NULL,0, NULL, OCI_DEFAULT);
}int sqlstatement::bindout(unsigned int position,int *value)
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, position, value, sizeof(int), SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::bindout(unsigned int position,long *value)
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, position, value, sizeof(long), SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::bindout(unsigned int position,unsigned int *value)
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, position, value, sizeof(unsigned int), SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT );
}
int sqlstatement::bindout(unsigned int position,unsigned long *value)
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, position, value, sizeof(unsigned long), SQLT_INT, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::bindout(unsigned int position,float *value)
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, position, value, sizeof(float), SQLT_FLT, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::bindout(unsigned int position,double *value)
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, position, value, sizeof(double), SQLT_FLT, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::bindout(unsigned int position,char *value,unsigned int len)
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, position, value, len+1, SQLT_STR, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::bindblob()
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, 1,(dvoid *) &m_lob,-1, SQLT_BLOB, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::bindclob()
{return OCIDefineByPos(m_handle.smthp, &m_handle.defhp, m_handle.errhp, 1,(dvoid *) &m_lob,-1, SQLT_CLOB, NULL, NULL, NULL, OCI_DEFAULT );
}int sqlstatement::execute()
{memset(&m_cda,0,sizeof(m_cda));if (m_state == 0) {m_cda.rc=-1; strncpy(m_cda.message,"cursor not open.\n",128); return -1;}ub4 mode=OCI_DEFAULT;if (m_sqltype==1 && m_autocommitopt==1) mode=OCI_COMMIT_ON_SUCCESS;int oci_ret = OCIStmtExecute(m_handle.svchp,m_handle.smthp,m_handle.errhp,m_sqltype,0,NULL,NULL,mode);if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){// 发生了错误或查询没有结果err_report(); return m_cda.rc;}// 如果不是查询语句,就获取影响记录的行数if (m_sqltype == 1){OCIAttrGet((CONST dvoid *)m_handle.smthp,OCI_HTYPE_STMT,(dvoid *)&m_cda.rpc, (ub4 *)0,OCI_ATTR_ROW_COUNT, m_handle.errhp);}return 0;
}int sqlstatement::execute(const char *fmt,...)
{char strtmpsql[10240];memset(strtmpsql,0,sizeof(strtmpsql));va_list ap;va_start(ap,fmt);vsnprintf(strtmpsql,10000,fmt,ap);if (prepare(strtmpsql) != 0) return m_cda.rc;return execute();
}int sqlstatement::next()
{ // 注意,在该函数中,不可随意用memset(&m_cda,0,sizeof(m_cda)),否则会清空m_cda.rpc的内容if (m_state == 0) {m_cda.rc=-1; strncpy(m_cda.message,"cursor not open.\n",128); return -1;}// 如果语句未执行成功,直接返回失败。if (m_cda.rc != 0) return m_cda.rc;// 判断是否是查询语句,如果不是,直接返回错误if (m_sqltype != 0){m_cda.rc=-1; strncpy(m_cda.message,"no recordset found.\n",128); return -1;}int oci_ret = OCIStmtFetch(m_handle.smthp,m_handle.errhp,1,OCI_FETCH_NEXT,OCI_DEFAULT);if ( oci_ret != OCI_SUCCESS && oci_ret != OCI_SUCCESS_WITH_INFO ){err_report(); // 只有当m_cda.rc不是1405和1406的时候,才返回错误,1405和1406不算错// 并且,返回错误的时候,不要清空了m_cda.rpcif (m_cda.rc != 1405 && m_cda.rc != 1406) {// 如果返回代码不是0,1403,1405,1406,就表是next出现了其它异常// 必须关闭数据库连接,让程序错误退出。if ( (m_cda.rc!=0) && (m_cda.rc!=1403) && (m_cda.rc!=1405) && (m_cda.rc!=1406) ) m_conn->disconnect();return m_cda.rc;}}// 获取结果集成功// 如果返回的是1405或1406,就把它强置为0if (m_cda.rc == 1405 || m_cda.rc == 1406) m_cda.rc=0;// 获取影响记录的行数据OCIAttrGet((CONST dvoid *)m_handle.smthp,OCI_HTYPE_STMT,(dvoid *)&m_cda.rpc, (ub4 *)0,OCI_ATTR_ROW_COUNT, m_handle.errhp);return 0;
}void sqlstatement::err_report()
{// 注意,在该函数中,不可随意用memset(&m_cda,0,sizeof(m_cda)),否则会清空m_cda.rpc的内容if (m_state == 0) {m_cda.rc=-1; strncpy(m_cda.message,"cursor not open.\n",128); return;}m_cda.rc=-1;strncpy(m_cda.message,"call sqlstatement::err_report() failed.\n",128);if (m_handle.errhp != NULL){if (OCIErrorGet(m_handle.errhp,1,NULL,&m_cda.rc,(OraText*)m_cda.message,sizeof(m_cda.message),OCI_HTYPE_ERROR) == OCI_NO_DATA){// 如果没有获取到任何错误信息,就返回正确的// 这里可以用memset清空m_cda,因为如果没有任何错误m_cda.rpc在next中会重新赋值m_cda.rc=0; memset(m_cda.message,0,sizeof(m_cda.message)); return;}}m_conn->err_report();
}int sqlstatement::alloclob()
{return OCIDescriptorAlloc((dvoid *)m_handle.envhp,(dvoid **)&m_lob,(ub4)OCI_DTYPE_LOB,(size_t)0,(dvoid **)0);
}void sqlstatement::freelob()
{OCIDescriptorFree((dvoid *)m_lob, (ub4)OCI_DTYPE_LOB);
}ub4 file_length(FILE *fp)
{fseek(fp, 0, SEEK_END);return (ub4) (ftell(fp));
}int sqlstatement::filetolob(char *filename)
{FILE *fp=0;if ( (fp = fopen(filename,"rb")) == 0) {m_cda.rc=-1; strncpy(m_cda.message,"fopen failed",128); return -1;}int iret = filetolob(fp);fclose(fp);return iret;
}/* 操作CLOB和BLOB内容时,缓冲区的大小,一般不需要修改。 */
#define LOBMAXBUFLEN 10240int sqlstatement::filetolob(FILE *fp)
{ub4 offset = 1;ub4 loblen = 0;ub1 bufp[LOBMAXBUFLEN+1];ub4 amtp;ub1 piece;sword retval;ub4 nbytes;ub4 remainder;ub4 filelen = file_length(fp);if (filelen == 0) return 0;amtp = filelen;remainder = filelen;(void) OCILobGetLength(m_handle.svchp, m_handle.errhp, m_lob, &loblen);(void) fseek(fp, 0, 0);if (filelen > LOBMAXBUFLEN){nbytes = LOBMAXBUFLEN;}else{nbytes = filelen;}memset(bufp,0,sizeof(bufp));if (fread((void *)bufp, (size_t)nbytes, 1, fp) != 1){m_cda.rc=-1; strncpy(m_cda.message,"fread failed",128); return -1;}remainder -= nbytes;if (remainder == 0) {// exactly one piece in the file// Only one piece, no need for stream writeif ( (retval = OCILobWrite(m_handle.svchp, m_handle.errhp, m_lob, &amtp, offset, (dvoid *) bufp,(ub4) nbytes, OCI_ONE_PIECE, (dvoid *)0,(sb4 (*)(dvoid *, dvoid *, ub4 *, ub1 *)) 0,(ub2) 0, (ub1) SQLCS_IMPLICIT)) != OCI_SUCCESS){err_report(); return m_cda.rc;}}else{// more than one pieceif (OCILobWrite(m_handle.svchp, m_handle.errhp, m_lob, &amtp, offset, (dvoid *) bufp,(ub4) LOBMAXBUFLEN, OCI_FIRST_PIECE, (dvoid *)0,(sb4 (*)(dvoid *, dvoid *, ub4 *, ub1 *)) 0,(ub2) 0, (ub1) SQLCS_IMPLICIT) != OCI_NEED_DATA){err_report(); return m_cda.rc;}piece = OCI_NEXT_PIECE;do{if (remainder > LOBMAXBUFLEN){nbytes = LOBMAXBUFLEN;}else{nbytes = remainder; piece = OCI_LAST_PIECE;}memset(bufp,0,sizeof(bufp));if (fread((void *)bufp, (size_t)nbytes, 1, fp) != 1){m_cda.rc=-1; strncpy(m_cda.message,"fread failed",128); return -1;}retval = OCILobWrite(m_handle.svchp, m_handle.errhp, m_lob, &amtp, offset, (dvoid *) bufp,(ub4) nbytes, piece, (dvoid *)0,(sb4 (*)(dvoid *, dvoid *, ub4 *, ub1 *)) 0,(ub2) 0, (ub1) SQLCS_IMPLICIT);remainder -= nbytes;} while (retval == OCI_NEED_DATA && !feof(fp));}if (retval != OCI_SUCCESS){err_report(); return m_cda.rc;}(void) OCILobGetLength(m_handle.svchp, m_handle.errhp, m_lob, &loblen);return 0;
}// 把LOB字段中的内容写入文件
int sqlstatement::lobtofile(char *filename)
{FILE *fp=0;if ( (fp = fopen(filename,"wb")) == 0){if ( (fp = fopen(filename,"wb")) == 0){m_cda.rc=-1; strncpy(m_cda.message,"fopen failed",128); return -1;}}fseek(fp, 0, 0);int iret = lobtofile(fp);fclose(fp);// 如果文件在生成的过程中发生了错误,就删除该文件,因为它是一个不完整的文件if (iret != 0) remove(filename); return iret;
}int sqlstatement::lobtofile(FILE *fp)
{ub4 offset = 1;ub4 loblen = 0;ub1 bufp[LOBMAXBUFLEN+1];ub4 amtp = 0;sword retval;OCILobGetLength(m_handle.svchp, m_handle.errhp, m_lob, &loblen);if (loblen == 0) return 0;amtp = loblen;memset(bufp,0,sizeof(bufp));retval = OCILobRead(m_handle.svchp, m_handle.errhp, m_lob, &amtp, offset, (dvoid *) bufp,(loblen < LOBMAXBUFLEN ? loblen : LOBMAXBUFLEN), (dvoid *)0,(sb4 (*)(dvoid *, const dvoid *, ub4, ub1)) 0,(ub2) 0, (ub1) SQLCS_IMPLICIT);switch (retval){case OCI_SUCCESS: /* only one piece */fwrite((void *)bufp, (size_t)amtp, 1, fp);break;case OCI_NEED_DATA: /* there are 2 or more pieces */fwrite((void *)bufp, amtp, 1, fp); while (1){amtp = 0;memset(bufp,0,sizeof(bufp));retval = OCILobRead(m_handle.svchp, m_handle.errhp, m_lob, &amtp, offset, (dvoid *) bufp,(ub4) LOBMAXBUFLEN, (dvoid *)0,(sb4 (*)(dvoid *, const dvoid *, ub4, ub1)) 0,(ub2) 0, (ub1) SQLCS_IMPLICIT);fwrite((void *)bufp, (size_t)amtp, 1, fp);if (retval != OCI_NEED_DATA) break;} break;case OCI_ERROR:err_report(); return m_cda.rc;}return 0;
}
相关文章:
【C++1】函数重载,类和对象,引用,string类,vector容器,类继承和多态,/socket,进程信号,public,ooci
文章目录1.函数重载:writetofile(),Ctrue和false,C0和非02.类和对象:vprintf2.1 构造函数:对成员变量初始化2.2 析构函数:一个类只有一个,不允许被重载3.引用:C中&取地址&#x…...
asio网络编程 tcp、udp、rpc
轻量级的tcp/udp/rpc库,简化socket程序编写。 同时,RPC部分也提供了方便易用的功能。 仓库地址 https://github.com/shuai132/asio_net asio_net a Tiny Async TCP/UDP/RPC library based on ASIO and RpcCore Features 简化TCP、UDP相关程序的编写…...
双目测距------双目相机V1.0,将双目相机采集到任意一点的深度数据进行串口传输(带源码)
Depth2Uart 双目测距------双目相机V1.0,将双目相机采集到任意一点的深度数据进行串口传输 一、项目说明/Overview 所实现的功能:基于Intel Realsense官方提供的SDK,双目深度相机能获取到相机任何一个像素点距离前方障碍物的距离࿰…...
jetson nano(ubuntu)安装Cmake
文章目录安装环境一.命令行安装二.Cmake源码编译安装安装环境 jetson nano 系统:4.6.1 一.命令行安装 sudo apt install cmake这种直接安装cmake的方式,其实安装的版本都太老了,这种方式不推荐 二.Cmake源码编译安装 更新一下系统软件 su…...
图的基本介绍和表示方式
图的基本介绍 为什么要有图这个基本数据结构? 我们还学习过线性表(数组、队列、链表和栈)和树,但是我们可以发现,线性表局限于一个直接前驱(就是只能有唯一一个前面的结点)和一个直接后继的(…...
本周大新闻|传微软解散工业元宇宙团队,MIT研发垂直堆叠全彩Micro LED
本周大新闻,AR方面,消息称微软解散工业元宇宙团队;德国AR公司Gixel GmbH亮相;Brilliant推出单片式附加形态AR眼镜;MIT研发垂直堆叠全彩Micro LED;谷歌XR串流正式上线。VR方面,索尼发布了PS VR2的…...
SpringMVC:拦截器(12)
拦截器1. 拦截器概念2. 拦截器入门案例2.1 环境准备2.2 拦截器开发步骤1: 创建拦截器类步骤2: 配置拦截器类步骤3: SpringMVC添加SpringMvcSupport包扫描和interceptor包扫描步骤4: 简化SpringMvcSupport的编写5 测试3. 拦截器参数解析(了解)3.1 前置处理…...
计算机网络3:HTTP1.0和HTTP1.1的区别
目录1. HTTP是什么2.HTTP1.0和HTTP1.1的区别3.名词解释(1)If-Modified-Since(IMS)、Expires(2)If-None-Match,Etag(3)If-Unmodified-Since1. HTTP是什么 超文本传输协议…...
Urho3D 编辑器说明
Urho3D编辑器是一个脚本应用程序,可以与Urho3D播放器应用程序一起运行。要开始,请执行以下任意命令:(在bin目录中)Editor.bat、Editor.sh或Urho3DPlayer Scripts/Editor.as Urho3D播放器应用程序支持的所有命令行选项…...
C++类基础(十一)
运算符重载(二) ● 对称运算符通常定义为非成员函数以支持首个操作数的类型转换 struct Str {int val 0;Str(int input): val(input){}auto operator(Str x){std::cout << "auto operator(Str x)\n";return Str(val x.val);} }; int …...
Windows安装系列:SVN Server服务
一、下载与安装 1、下载VisualSVN-Server-5.1.1-x64.msi 地址:Download | VisualSVN Server 2、找到最新版本SVN 5.1.1,直接双击它,弹出如下安装界面 3、点击Next 4、勾选我接受, 点击"Next" 5、默认选项,…...
快速傅里叶算法(FFT)快在哪里?
目录 前言 1、DFT算法 2、FFT算法 2.1 分类 2.2 以基2 DIT(时间抽取) FFT 算法为例 2.2.1 一次分解 2.2.2 多次分解 参考 前言 对信号分析的过程中,为了能换一个角度观察问题,很多时候需要把时域信号波形变换到频域进行分…...
利用Markdown写学术论文资料汇总贴
1是最详细的,重点看! Markdown 写作,Pandoc 转换:我的纯文本学术写作流程 2补充一些细节,也可以看看。 用Markdown写作学术论文 3写得和上面差不多,如果上面两篇有什么问题还没解决,可以看看…...
MySQL 高级查询
目录1.左关联2.右关联3.子查询4.联合查询5.分组查询1.左关联 MySQL中的左关联(Left Join)是一种基于共同列的连接操作, 它将左侧表中的所有行与右侧表中匹配的行结合在一起, 如果右侧表中没有匹配的行,则结果集中右侧…...
JavaSE学习day4_01 循环for,while,do...while
1. 循环高级 1.1 无限循环 for、while、do...while都有无限循环的写法。 最为常用的是while格式的。 因为无限循环是不知道循环次数的,所以用while格式的 代码示例: while(true){} 1.2 跳转控制语句(掌握) 跳转控制语句&…...
C/C++中的static关键字
概述在C/C中都有static关键字的使用,可以分别修饰变量和函数,分为静态变量【静态成员】、静态成员函数。2. static用法概况静态变量的作用范围在一个文件内,程序开始时分配空间,结束时释放空间,默认初始化为0ÿ…...
67 自注意力【动手学深度学习v2】
67 自注意力【动手学深度学习v2】 深度学习学习笔记 学习视频:https://www.bilibili.com/video/BV19o4y1m7mo/?spm_id_fromautoNext&vd_source75dce036dc8244310435eaf03de4e330 给定长为n 的序列,每个xi为长为d的向量,自注意力将xi 既当…...
电子学会2022年12月青少年软件编程(图形化)等级考试试卷(二级)答案解析
青少年软件编程(图形化)等级考试试卷(二级) 一、单选题(共25题,共50分) 1. 一个骰子,从3个不同角度看过去的点数如图所示,请问5的对面是什么点数?( ) …...
关于链表中插入结点的操作……
服了,好久没敲链表了,这都忘了 newnode->next cur->next; cur->next newnode; newnode->next cur->next; cur->next newnode; newnode->next cur->next; cur->next newnode; newnode->next cur->next; cur-…...
【项目精选】百货中心供应链管理系统
点击下载源码 近年来,随着计算机技术的发展,以及信息化时代下企业对效率的需求,计算机技术与通信技术已经被越来越多地应用到各行各业中去。百货中心作为物流产业链中重要的一环,为了应对新兴消费方式的冲击,从供货到销…...
Qt优秀开源项目之十六:SQLite数据库管理系统—SQLiteStudio
首先,感谢CSDN官方认可 SQLiteStudio是一款开源、跨平台(Windows、Linux和MacOS)的SQLite数据库管理系统。 github地址:https://github.com/pawelsalawa/sqlitestudio 官网:https://sqlitestudio.pl/ 特性很多…...
Python __doc__属性:查看文档
在使用 dir() 函数和 __all__ 变量的基础上,虽然我们能知晓指定模块(或包)中所有可用的成员(变量、函数和类),比如:import string print(string.__all__)程序执行结果为:[ascii_lett…...
电子科技大学操作系统期末复习笔记(一):操作系统概述
目录 前言 操作系统概述 操作系统的目标与功能 操作系统的定义 目标 功能 操作系统的历史 单用户系统 简单批处理系统 多道批处理系统 分时系统 个人电脑 → 分布式系统 → 互联网时代 → 移动计算时代 → ...... 实时系统 操作系统的基本特征 并发 共享 虚拟…...
[实践篇]13.20 Qnx进程管理slm学习笔记(三)
【QNX Hypervisor 2.2用户手册】目录(完结) 4.2 模块 我们可以将组件组合成一个模块。模块中的进程可以组成一个子系统,也可以用于建立一组系统状态,例如基本操作和各种更高级别操作。注意,必须命名模块,以便可以在内部引用它们。而且每个模块必须描述成一个元素,形势如…...
冰冰学习笔记:多线程
欢迎各位大佬光临本文章!!! 还请各位大佬提出宝贵的意见,如发现文章错误请联系冰冰,冰冰一定会虚心接受,及时改正。 本系列文章为冰冰学习编程的学习笔记,如果对您也有帮助,还请各位…...
补充一些前端面试题
javascript有哪些库指路>js中的库uniapp和vue有什么区别什么是uniappuni-app(uni,读you ni,是统一的意思)是一个使用Vue.js开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、Web&#…...
七大设计原则之单一职责原则应用
目录1 单一职责原则介绍2 单一职责原则应用1 单一职责原则介绍 单一职责(Simple Responsibility Pinciple,SRP)是指不要存在多于一个导致类变更的原因。假设我们有一个 Class 负责两个职责,一旦发生需求变更,修改其中…...
[USACO23JAN] Leaders B
题面翻译 题面描述 FJ 有 NNN 头奶牛,每一头奶牛的品种是根西岛 G 或荷斯坦 H 中的一种。 每一头奶牛都有一个名单,第 iii 头奶牛的名单上记录了从第 iii 头奶牛到第 EiE_iEi 头奶牛的所有奶牛。 每一种奶牛都有且仅有一位“领导者”,对…...
C++模板初阶
C模板初阶泛型编程函数模板概念函数模板格式函数模板原理函数模板的实例化模板参数的匹配原则类模板类模板的定义格式类模板的实例化泛型编程 我们前面学习了C的函数重载功能,那么我们如何实现一个通用的交换函数呢,比如:我传入int就是交换intÿ…...
文献阅读:Scaling Instruction-Finetuned Language Models
文献阅读:Scaling Instruction-Finetuned Language Models 1. 文章简介2. 实验 1. 数据集 & 模型 1. 数据集考察2. 使用模型 2. scale up对模型效果的影响3. CoT对模型效果的影响4. 不同模型下Flan的影响5. 开放接口人工标注指标 3. 结论 文献链接:…...
教育培训的网站建设/软文是什么
作为一个从事.NET Web技术的开发人员,似乎没有什么理由可以不懂微软自己的AJAX框架,虽然它可能不太好用,或者用起来没有像jQuery这样的框架那么爽。我没有怎么用过UpdatePanel来做过复杂的东西,所以对于这个的优缺点就不予置评了。…...
内链wordpress/百度手机助手最新版下载
http://news.cnblogs.com/n/37759/转载于:https://www.cnblogs.com/kexb/p/3792412.html...
做网站开发数据库怎么写/百度搜索量最大的关键词
最近学了图的遍历,关于其中的算法详情请见此处 关于深搜和广搜是非常重要的,对一些矩阵和图的遍历起到不错的作用。下面以一些题目来训练,其中每道题都尽量给出深搜和广搜的2种做法。 总结:深搜类似于递归(毕竟需要回溯…...
帮别人做诈骗网站技术/百度一下你就知道了主页
环境 VMware 15 Windows server 2016 原因 虚拟机与宿主机之间,虚拟机可以ping通宿主机,但是宿主机却ping不通虚拟机; 且 虚拟机和宿主机不在同一网段内。 新的解决方法 ①、虚拟机中配置好 IP地址 ②、Nat 这个是VMnet8 ③、启动 …...
北京网站首页排名公司/企业培训课程
心态炸裂 昨晚忙活到半夜照着攻略,一顿操作,中间虽然有几个小错误还都纠正过来了,成功搭建好了博客。结果今天下午想着去给博客换个好点的theme,结果照着一顿操作,各种报错,关键是我也不懂也找不到如何解决…...
论述站点的几种推广方式/百度网址入口
前言前段时间学习了HTTP的原理,并用Python语言实现了一个简单的HTTP服务器(带有多线程功能的哦);后面突发一想,我能把挂在本地服务器上,每次运行都需要打开虚拟机并运行程序(这样真的很麻烦)。我想起之前阿里云服务器打折的时候&a…...