【C++1】函数重载,类和对象,引用,/string类,vector容器,类继承和多态,/socket,进程信号
文章目录
- 1.函数重载:writetofile(),C++true和false,C0和非0
- 2.类和对象:vprintf
- 构造函数:对成员变量初始化
- 析构函数:一个类只有一个,不允许被重载
- 3.引用:C中&取地址,C++中&引用。引用就像起别名,typedef,宏define。对引用的操作与对变量直接操作一样
- 4.string类:string str,str=,str.c_str()
- 5.vector容器:vector与string类一样属于STL
- 6.类继承:class派生类名:public基类名
- 7.类多态:子类必重写父类纯虚函数
- 8.socket:send/recv
- 8.1 简单文件传输:CTcpClient,CTcpServer
- 8.2 文件下载模块:不建议将tcpgetfile.cpp,tcpfileserver.cpp反过来,虽然全双工但会出现连不上服务器被上网行为审计系统拦截
- 8.3 高性能网络服务:多线程+数据库连接池(多线程每启一个线程都要连数据库耗资源)
- 9.进程:fork(),ps -ef (同-aux) | more
- 10.信号:signal(, EXIT),jps
- 10.1 捕捉信号:ctrl+c:2
- 10.2 捕捉信号:kill -9:9
- 10.3 捕捉信号:kill:15
- 10.4 程序后台运行两种方法:&:ctrl+c无法中止,用killall book1或kill 进程号。if (fork()>0)return 0 父进程退出
- 1._public.h
- 2._public.cpp
- 3._cmpublic.h
- 4._ooci.h
- 5._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调用。
构造函数:对成员变量初始化
CFile是类,CFile()是函数。
如下两个构造函数(该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作),属于成员函数。
析构函数:一个类只有一个,不允许被重载
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.类多态:子类必重写父类纯虚函数
8.socket:send/recv
服务端:
客户端:
1.send函数。
2.recv函数。
1.socket函数
8.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;
}
如下传二进制文件。
8.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; }}
}
8.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; //理论上不会来到这里,除非连接池搞乱了
}
心跳报文业务不需要连接池,先开启服务端。
9.进程:fork(),ps -ef (同-aux) | more
一个进程(正在内存中运行的程序)在内存里有三部分数据:代码段(相同),堆栈段+数据段(不同)
。getpid库函数功能是获取进程编号,该函数没有参数,返回值是进程的编号(相同程序在不同时间执行,进程编号不同)。
进程应用:并发:把socket服务端改为多进程,每次accept到一个客户端连接后,生成一个子进程,让子进程负责与这个客户端通讯。父进程继续accept客户端连接
。以下在服务端中,在CTcpServer类中增加两个成员函数。
僵尸进程
:一个进程执行了exit系统调用退出时会向父进程发送SIGCHLD信号,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态,如进程ID、终止状态等等)的进程,ps显示有< default >。总结:仔细处理子进程死后的信息,如果不想处理就需要交给系统处理。
10.信号: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
的信号。
10.1 捕捉信号:ctrl+c:2
如下在死循环之前注册下信号的处理,如下让ctrl+c无效。
如下点击Build后就会将类编译出来。
如上ctrl+c无法停止,只能用kill,jps查看进程pid。
10.2 捕捉信号:kill -9:9
10.3 捕捉信号:kill:15
将如上KILL换成TERM即SIGTERM,重新build project。win下信号比linux下信号少很多,将当前程序打包成jar包传到linux。用如下命令行打包。
如下用压缩软件打开1.jar。
如下:后有一个空格,最后一行是空行。
10.4 程序后台运行两种方法:&:ctrl+c无法中止,用killall book1或kill 进程号。if (fork()>0)return 0 父进程退出
信号作用:服务程序在后台运行,如果想终止它,杀了它不是个好办法,因为没有释放资源。如果能向程序发一个信号,程序收到这个信号后调用一个函数,在函数中编写释放资源代码,程序就可以安全体面退出。
下面 EXIT函数就是自定义函数,TcpServer设为全局变量因为EXIT函数要访问它并关闭socket。
1._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
2._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;
}
3._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
4._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
5._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,进程信号
文章目录1.函数重载:writetofile(),Ctrue和false,C0和非02.类和对象:vprintf构造函数:对成员变量初始化析构函数:一个类只有一个,不允许被重载3.引用:C中&取地址,C中…...
JetpackCompose从入门到实战学习笔记8—ConstraintLayout的简单使用
JetpackCompose从入门到实战学习笔记8—ConstraintLayout的简单使用 1.简介: Compose 中的 ConstraintLayout ConstraintLayout 是一种布局,让您可以相对于屏幕上的其他可组合项来放置可组合项。它是一种实用的替代方案,可代替使用多个已嵌…...
Spring Boot 快速入门(绝对经典)
目录 1、理论概述 1.1、什么是Spring Boot? 1.2、Spring Boot的特点 1.3、开发环境 2、实战——创建和配置项目 2.1、Spring Boot项目创建的两种方式 2.1.1、方法一:通过网站构建项目 2.1.2、使用Spring Initializr创建(推荐) 2.2、…...
golang context上下文
文章目录一、为什么需要context二、context 接口三、Background 方法四、 with 系列函数1、WithCancel 方法2、WithDeadline 方法3、WithTimeout 方法4、WithValue 方法五、使用注意事项一、为什么需要context 在 Go http包的Server中,每一个请求在都有一个对应的 …...
Linux---Linux是什么
Linux 便成立的核心网站: http://www.kernel.org Linux是什么 Linux 就是一套操作系统 Linux 就是核心与系统呼叫接口那两层 软件移植:如果能够参考硬件的功能函数并据以修改你的操作系统程序代码, 那经过改版后的操作系统就能够在另一个硬…...
C语言(Tgmath.h库(C99),exit和atexit)
一.Tgmath.h库(C99) C99标准提供得tgmath.h头文件定义了泛型类型宏。比如在math.h中为一个函数定义了3中类型(float,double和long double)的版本,那么tgmath.h文件就创建一个泛型类型宏,与原来的float,double和long double版本的…...
LeetCode 刷题系列 -- 739. 每日温度
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。示例 1:输入:temperatures …...
如何生成毕业论文的目录和创建模板
有粉丝同学最近在写毕业论文,其中比较让人恼火的是毕业论文的目录,折腾了几遍没弄好,想让我写个简单地教程,那就来吧。主要分为三步:第一步是从模板里面提取标题的样式,第二步是对自己的论文使用设置好的标…...
新来的23岁软件测试员上来秀了波操作,把几个老员工看傻了
春招了,公司来了个小伙子,一看简历,嘿?22岁,这不刚毕业的小毛孩子嘛,结果没想到人家上来就把现有项目的性能优化了一遍,给公司节省了一半的成本,这种“王炸”打法,直接给…...
Window10开放某个端口
需求:由于防火墙原因,开放某个端口:如9999 在开始那里搜索防火墙-进入防火墙 第一步:核实是否启动了防火墙,之后进行 第二步:点击“高级设置”,→“入站规则”→“新建规则”→“端口”→ “下一步” …...
进阶7 分页查询
进阶7 分页查询!!! 目录概述练习题概述 应用场景:当要显示的数据一页显示不全,需要分页提交SQL请求 语法: select 查询列表 from 表名 【join type join 表2 on 连接条件 where 筛选条件 group by 分组字段…...
利用升序定时器链表处理非活动连接
参考自游双《Linux高性能服务器编程》 背景 服务器同常需要定期处理非活动连接:给客户发一个重连请求,或关闭该连接,或者其他。我们可以通过使用升序定时器链表处理非活动连接,下面的代码利用alarm函数周期性的触发SIGALRM信号&a…...
MySQL 开发规范
一、数据库命名规范所有数据对象名称必须小写 :db_user禁止使用MySQL 保留关键字,若是则引用 临时表以tmp_ 开头,备份表以bak_ 开头并以时间戳结尾所有存储相同数据的列名和列类型必须一致二、数据库基本设计规范1、MySQL…...
【C语言进阶】预处理与程序环境
目录一.详解编译与链接1.前言2.翻译环境3.剖析编译过程4.运行环境二.预处理详解1.预定义符号2.剖析#define(1).定义标识符(2).定义宏(3).替换规则(4).#和##(5).宏与函数的对比(6).#undef3.条件编译4.文件包含(1).头文件包含的方式(2).嵌套文件包含一.详解编译与链接 1.前言 在…...
【Docker知识】将环境变量传递到容器
一、说明 程序通常通过与软件捆绑在一起的配置来控制操作,环境变量允许用户在运行时设置它们。但是,在 Docker 容器中运行进程会使事情变得复杂,那么如何将环境变量传递给容器呢?下面介绍若干个传递办法。 二、环境变量有何用途 环…...
Allegro如何更改铜皮显示密度操作指导
Allegro如何更改铜皮显示密度操作指导 用Allegro做PCB设计的时候,铜皮正常显示模式如下图 铜皮的密度是基本填充满的,Allegro支持更改铜皮的显示密度 如下图 如何更改密度,具体操作如下 点击setup...
ThinkPHP5酒店预订管理系统
有需要请私信或看评论链接哦 可远程调试 ThinkPHP5酒店预订管理系统一 介绍 此酒店预订管理系统基于ThinkPHP5框架开发,数据库mysql,采用了ueditor富文本编辑器。系统角色分为用户,员工和管理员。用户可注册登录并预订酒店和评论等ÿ…...
【MySQL】MyCat分库分表分片规则配置详解与实战(MySQL专栏启动)
📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建工设优化。文章内容兼具广度深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公…...
OpenWrt路由器设置域名动态解析手把手教程
文章目录0、前言1、准备工作2、详细步骤2.1、OpenWrt路由器软件包安装2.2、防火墙放行入站数据(修改为“接受”并保存应用)2.3、域名解析服务商对域名的解析设置2.4、路由器中动态域名插件的设置0、前言 因为一直用着内网穿透(zerotier或者是…...
java流浪动物救助系统(毕业设计)
项目类型:Java web项目/Java EE项目(非开源) 项目名称:基于JSPServlet的流浪动物救助网站[dwjz_web] 进行源码获取 用户类型:双角色(爱心人士、管理员) 项目架构:B/S架构 设计思…...
阿里代码规范插件中,Apache Beanutils为什么被禁止使用?
在实际的项目开发中,对象间赋值普遍存在,随着双十一、秒杀等电商过程愈加复杂,数据量也在不断攀升,效率问题,浮出水面。 问:如果是你来写对象间赋值的代码,你会怎么做? 答…...
NFC enable NFC使能流程
同学,别退出呀,我可是全网最牛逼的 WIFI/BT/GPS/NFC分析博主,我写了上百篇文章,请点击下面了解本专栏,进入本博主主页看看再走呗,一定不会让你后悔的,记得一定要去看主页置顶文章哦。 NFC enable NFC使能流程 认识nfc系统如何工作,最好的方法就是了解nfc的各个流程,…...
Redis实例绑定CPU物理核优化Redis性能
进入本次Redis性能调优之前,首先要知道CPU结构也会影响Redis的性能。接下来,具体了解一下!为什么CPU结构也会影响Redis的性能?主流的 CPU 架构一个 CPU 处理器中一般有多个物理核,每个物理核都可以运行应用程序。每个物…...
STC15中断系统介绍
STC15中断系统介绍✨本篇参考来源于STC官方stc15系列手册:538页- 589页。(文末提供该摘取部分的文档资料) 🎉在官方提供的手册资料中,一个系列一份手册,手册内容涵盖了数据手册和参考手册以及例程案例。对于学习着来说…...
力扣HOT100 11-15
11.盛水最多的容器 思路:最大水量 底边 * 高度。较短的一边控制最大水量,因此,采用双指针的方式,左、右指针指向开始和末尾,逐个向中间移动,判断左右指针所指向的高度哪个更低,它就向中间移动一…...
深入浅出单调栈与单调队列
目录一、单调栈情形一:寻找一个数左边第一个小于它的数情形二:寻找一个数左边第一个小于它的数的下标情形三:寻找一个数右边第一个大于它的数情形四:寻找一个数右边第一个大于它的数的下标二、单调栈的应用2.1 单调栈模板题I2.2 单…...
深入C语言——实现可变参数函数
文章目录初步示例函数解析最大值函数初步示例 stdarg.h提供了C语言对可变参数的支持,先举一个简短的例子 //testStdArg.c #include <stdarg.h> #include <stdio.h>void printIntList(int N, ...){va_list args; //存放...所代表的参数va_start(…...
41-Dockerfile-Dockerfile简介
Dockerfile简介前言Dockerfile 简介基础知识使用Dockerfile 构建镜像步骤Dockerfile 构建过程Dockerfile基本结构Dockerfile示例总结前言 本篇开始来学习下Dockerfile相关的用法 Dockerfile 简介 Dockerfile : 是用来构建 Docker 镜像的文本文件,是有一条条构建镜…...
【408】操作系统 - 刻骨铭心自测题1(上)
文章目录OS练习题第一部分:1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17&am…...
【老卫拆书】009期:Vue+Node肩挑全栈!《Node.js+Express+MongoDB+Vue.js全栈开发实战》开箱
今天刚拿到一本新书,叫做《Node.jsExpressMongoDBVue.js全栈开发实战》,做个开箱。 外观 先从外观上讲,这本是全新的未开封的,膜还在。 这本书介绍从技术原理到整合开发实战,以丰富的项目展现全栈开发的一个技巧。 …...
网站建设的流程电子商务/西安seo服务
目录 前言 什么是shell shell的作用 脚本的类型 shell脚本的构成 创建shell 程序的步骤 shell脚本执行 总结 前言 运维不能不会使用脚本,就像西方不能失去耶路撒冷。脚本可以极大的减少我们的工作量,让我们可以光明正大的摸鱼。可以很快…...
平度网站建设公司/举例一个成功的网络营销案例
转载自 https://www.cnblogs.com/linxiyue/p/7834817.html https://www.jianshu.com/p/f0513d18742a...
企业网站建设 新天地网络/seo顾问服务 乐云践新专家
一、只想让TextView显示一行,但是文字超过TextView的长度怎么办? 在开头显示省略号android:singleLine"true" android:ellipsize"start"在结尾显示省略号android:singleLine"true" android:ellipsize"end"在中…...
哪个网站做阿里首页模板/云计算培训
1、给出了一个序列,你需要处理如下两种询问。 "C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。 "Q a b" 询问[a, b]区间中所有值的和。 2、线段树单点更新太费时,所以使用区间更新 3、 #include <cstdio>…...
东营可以做网站的公司在哪/新产品推广
gradle构建工具学习系列 参考官方文档:Building Java Applications Sample 创建一个demo名称的目录。进入该目录中打开命令行窗口,输入 gradle init并依次选择6 1 1,项目名称和资源包名称都默认。 创建项目: 将该项目导入到ID…...
广告设计与制作学什么/网站优化服务
文章摘要:幼儿园的教育方式在逐步发生改变,以游戏贯穿幼儿教学已经成为当下幼儿教育的主流.在对幼儿实施游戏化教育中,我们对此进行了如下探索与研究.一、幼儿为什么要游戏化教学幼儿教学游戏化,是指把幼儿教育的目标、内容、要求融于各种游戏之中,教学游……幼儿教…...