中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

網(wǎng)站建設(shè)入門到精通aso網(wǎng)站

網(wǎng)站建設(shè)入門到精通,aso網(wǎng)站,做網(wǎng)站怎么賺錢,ui設(shè)計(jì)網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn)文章目錄1.函數(shù)重載:writetofile(),Ctrue和false,C0和非02.類和對(duì)象:vprintf構(gòu)造函數(shù):對(duì)成員變量初始化析構(gòu)函數(shù):一個(gè)類只有一個(gè),不允許被重載3.引用:C中&取地址,C中…

文章目錄

  • 1.函數(shù)重載:writetofile(),C++true和false,C0和非0
  • 2.類和對(duì)象:vprintf
    • 構(gòu)造函數(shù):對(duì)成員變量初始化
    • 析構(gòu)函數(shù):一個(gè)類只有一個(gè),不允許被重載
  • 3.引用:C中&取地址,C++中&引用。引用就像起別名,typedef,宏define。對(duì)引用的操作與對(duì)變量直接操作一樣
  • 4.string類:string str,str=,str.c_str()
  • 5.vector容器:vector與string類一樣屬于STL
  • 6.類繼承:class派生類名:public基類名
  • 7.類多態(tài):子類必重寫父類純虛函數(shù)
  • 8.socket:send/recv
    • 8.1 簡(jiǎn)單文件傳輸:CTcpClient,CTcpServer
    • 8.2 文件下載模塊:不建議將tcpgetfile.cpp,tcpfileserver.cpp反過(guò)來(lái),雖然全雙工但會(huì)出現(xiàn)連不上服務(wù)器被上網(wǎng)行為審計(jì)系統(tǒng)攔截
    • 8.3 高性能網(wǎng)絡(luò)服務(wù):多線程+數(shù)據(jù)庫(kù)連接池(多線程每啟一個(gè)線程都要連數(shù)據(jù)庫(kù)耗資源)
  • 9.進(jìn)程:fork(),ps -ef (同-aux) | more
  • 10.信號(hào):signal(, EXIT),jps
    • 10.1 捕捉信號(hào):ctrl+c:2
    • 10.2 捕捉信號(hào):kill -9:9
    • 10.3 捕捉信號(hào):kill:15
    • 10.4 程序后臺(tái)運(yùn)行兩種方法:&:ctrl+c無(wú)法中止,用killall book1或kill 進(jìn)程號(hào)。if (fork()>0)return 0 父進(jìn)程退出
  • 1._public.h
  • 2._public.cpp
  • 3._cmpublic.h
  • 4._ooci.h
  • 5._ooci.cpp


1.函數(shù)重載:writetofile(),C++true和false,C0和非0

C++動(dòng)態(tài)內(nèi)存分配:在C語(yǔ)言中,動(dòng)態(tài)分配內(nèi)存用malloc()函數(shù),釋放內(nèi)存用free()函數(shù)。C++中new和delete。C++函數(shù)重載:C中不允許函數(shù)同名如下:
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
以上為C寫法,下面為C++函數(shù)重載寫法。函數(shù)重載規(guī)則:1.函數(shù)名必須同+2.參數(shù)列表必須不同。C++是如何做到函數(shù)重載的:C++代碼在編譯時(shí)會(huì)根據(jù)參數(shù)列表對(duì)函數(shù)進(jìn)行重命名。
在這里插入圖片描述

2.類和對(duì)象:vprintf

在這里插入圖片描述
上面完整,下面兩行中下行是上行改進(jìn),效果一樣,但沒(méi)有涉及類和對(duì)象。
在這里插入圖片描述
上面完整,下面結(jié)構(gòu)體升級(jí)為類。
在這里插入圖片描述
下面為三種show函數(shù)重載實(shí)現(xiàn),如下字符串理論上可定義為char name[10],但在函數(shù)里字符串也只能傳地址,所以只能定義為char * name。char name不行,char類型是單個(gè)字符,調(diào)用時(shí)直接給字符串值。
在這里插入圖片描述
下面為三種Show調(diào)用。
在這里插入圖片描述

構(gòu)造函數(shù):對(duì)成員變量初始化

在這里插入圖片描述
CFile是類,CFile()是函數(shù)。
在這里插入圖片描述
如下兩個(gè)構(gòu)造函數(shù)(該類對(duì)象被創(chuàng)建時(shí),編譯系統(tǒng)對(duì)象分配內(nèi)存空間,并自動(dòng)調(diào)用該構(gòu)造函數(shù),由構(gòu)造函數(shù)完成成員的初始化工作),屬于成員函數(shù)。
在這里插入圖片描述

析構(gòu)函數(shù):一個(gè)類只有一個(gè),不允許被重載

在這里插入圖片描述
在這里插入圖片描述

3.引用:C中&取地址,C++中&引用。引用就像起別名,typedef,宏define。對(duì)引用的操作與對(duì)變量直接操作一樣

引用的聲明方法:類型標(biāo)識(shí)符 &引用名=目標(biāo)變量名;如int a; int &ra=a; 定義了引用ra,它是變量a的引用即別名。引用可以用const修飾,表示只讀,用這種方式聲明的引用,不能通過(guò)引用對(duì)目標(biāo)變量的值進(jìn)行修改。
在這里插入圖片描述
在這里插入圖片描述

4.string類:string str,str=,str.c_str()

C中以0結(jié)尾的字符數(shù)組表示字符串(定義后大小不可變),C++中string隨著存放字符長(zhǎng)度自動(dòng)伸縮,不用擔(dān)心內(nèi)存溢出。string類是一個(gè)模板類,位于std命名空間,如果不加using namespace std;就要用std::string str。
在這里插入圖片描述
string特性描述函數(shù):int size()返回當(dāng)前字符串大小int length()返回當(dāng)前字符串的長(zhǎng)度void clear()清空字符串。string本質(zhì)是一個(gè)類,通過(guò)動(dòng)態(tài)分配內(nèi)存實(shí)現(xiàn)對(duì)字符串的存儲(chǔ),string對(duì)象用于存放字符的內(nèi)存地址是變化的。也就是地址存放的下就不再重新分配,存放不下就重新分配地址。

5.vector容器:vector與string類一樣屬于STL

在這里插入圖片描述
容器的使用:1.存放整數(shù)
在這里插入圖片描述
訪問(wèn)容器中元素可以像數(shù)組形式一樣。
在這里插入圖片描述
在這里插入圖片描述
2.存放字符串
在這里插入圖片描述
3.存放結(jié)構(gòu)體4.存放類:存放字符串中,string就是類。
在這里插入圖片描述
vector其他成員函數(shù):1.定位的函數(shù)
在這里插入圖片描述
2.增加元素的函數(shù)
在這里插入圖片描述
3.刪除元素的函數(shù)
在這里插入圖片描述
4.判斷容器的大小
bool empty():判斷容器是否為空
int size():返回容器中元素的個(gè)數(shù)
5.作業(yè)題:封裝隨機(jī)數(shù)
在這里插入圖片描述

//此程序用于生成一組隨機(jī)數(shù), 指定數(shù)組范圍和是否重復(fù)
#include"_public.h"class CRand
{
public:CRand();~CRand();vector <int> m_val;  //m_val容器	bool checkexit(const int aryyval, const int aryysize); // 用于檢查是否為重復(fù)數(shù)據(jù),aryyval為重復(fù)的值,這函數(shù)不單用,用于Rand成員函數(shù)里void Rand(const int minvalue,const int maxvalue,bool brep=true, const int nog=5); //brep為是否允許重復(fù); 默認(rèn)為允許重復(fù),nog指定生成多少個(gè)隨機(jī)數(shù)
};//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生成第幾個(gè),jtmp實(shí)際生成共多少個(gè),itmp生成的值m_val.clear();if(brep==true)    // 允許重復(fù){jtmp = nog;for(ii=0;ii<jtmp;ii++){itmp = rand()%(len+1)+minvalue; // (0~len)+minvalue,itmp就是min~max之間的值,不是len長(zhǎng)度m_val.push_back(itmp);    }return; //return是函數(shù)直接返回, 也就是結(jié)束該函數(shù)。//要跳出循環(huán)用break, if代碼段是不能用break跳出的, 在一個(gè)函數(shù)內(nèi)任意位置調(diào)用return, 直接退出Rand函數(shù),下面代碼不執(zhí)行。}jtmp = nog;    // 以下為不允許重復(fù) ,因?yàn)闆](méi)進(jìn)入if(brep==true)if (nog>len) jtmp = len + 1;  // 比如5-1=4,但1到5可以生成5個(gè),所以如果nog大于len的話就取len+1個(gè),前提不允許重復(fù)。while(1)   {if (jtmp == m_val.size()) break;  //生成滿了跳出循環(huán)itmp = rand()%(len+1)+minvalue;if (ii==0)  // 生成第一個(gè)不用管checkexit重不重復(fù){m_val.push_back(itmp);ii++;continue;} if (checkexit(itmp,ii) == false) continue;  // checkexit為false則不允許重復(fù)m_val.push_back(itmp); ii++;}return;
}//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
bool CRand::checkexit(const int aryyval, const int aryysize) // aryyval重復(fù)的值,aryysize允許多少個(gè)重復(fù)
{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這個(gè)類
{CRand CrtRand;CrtRand.Rand(0, 10, false);   // 若false為true允許重復(fù),不管范圍多少取nog個(gè)for(int ii=0;ii<CrtRand.m_val.size();ii++){printf("%d\n",CrtRand.m_val[ii]);}	return 0;
}

6.類繼承:class派生類名:public基類名

在這里插入圖片描述
如下子類可直接用父類屬性和方法。
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

7.類多態(tài):子類必重寫父類純虛函數(shù)

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

8.socket:send/recv

在這里插入圖片描述
服務(wù)端:
在這里插入圖片描述
在這里插入圖片描述
客戶端:
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
1.send函數(shù)。
在這里插入圖片描述
在這里插入圖片描述
2.recv函數(shù)。
在這里插入圖片描述
在這里插入圖片描述
1.socket函數(shù)
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

8.1 簡(jiǎn)單文件傳輸:CTcpClient,CTcpServer

// 本程序演示采用CTcpClient類,實(shí)現(xiàn)socket通訊的客戶端和文件傳輸,demo13.cpp
#include "_public.h"
bool SendFile(int sockfd,char *filename,int filesize); //把文件的內(nèi)容發(fā)送給服務(wù)端
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類,實(shí)現(xiàn)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)); //獲取文件的時(shí)間和大小FileMTime(argv[3],strMTime);  uFileSize=FileSize(argv[3]); //獲取文件的大小// 把文件的信息封裝成一個(gè)xml報(bào)文,發(fā)送給服務(wù)端memset(strSendBuffer,0,sizeof(strSendBuffer));snprintf(strSendBuffer,100,"<filename>%s</filename><mtime>%s</mtime><size>%lu</size>",argv[3],strMTime,uFileSize);CTcpClient TcpClient;//1111111111111111111111111111111111111111111111111111.向服務(wù)器發(fā)起連接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發(fā)送給服務(wù)端,并沒(méi)有接收服務(wù)端回應(yīng),沒(méi)必要,減少tcp交互次數(shù)if (TcpClient.Write(strSendBuffer)==false){printf("TcpClient.Write() failed.\n"); return -1;}printf("send xml:%s\n",strSendBuffer);printf("send file ...");//111111111111111111111111111111111111111111111111111113.把文件的內(nèi)容發(fā)送給服務(wù)端if (SendFile(TcpClient.m_sockfd,argv[3],uFileSize)==false){printf("SendFile(%s) failed.\n",argv[3]); return -1;}  memset(strRecvBuffer,0,sizeof(strRecvBuffer));//1111111111111111111111111111111111111111111111111114.接收服務(wù)端返回的回應(yīng)報(bào)文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.把文件的內(nèi)容發(fā)送給服務(wù)端
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個(gè)字節(jié)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類,實(shí)現(xiàn)socket通訊的服務(wù)端和文件傳輸,demo14.cpp
#include "_public.h"
bool RecvFile(char *strRecvBuffer,int sockfd,char *strfilename); //接收文件的內(nèi)容
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類,實(shí)現(xiàn)socket通訊的服務(wù)端和文件傳輸。\n\n");return -1;}CTcpServer TcpServer;
//1111111111111111111111111111111111111111111111111111.服務(wù)端初始化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.讀取客戶端的報(bào)文,等時(shí)間是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.接收文件的內(nèi)容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后,向客戶端返回響應(yīng)內(nèi)容if (TcpServer.Write(strSendBuffer)==false) {printf("TcpServer.Write() failed.\n"); return -1;}printf("send:%s\n",strSendBuffer);return 0;
}//1111111111111111111111111111111111111111111111111114.接收文件的內(nèi)容
bool RecvFile(char *strRecvBuffer,int sockfd,char *strfilename)
{int  ufilesize=0;char strmtime[20]; memset(strmtime,0,sizeof(strmtime));// 獲取待接收的文件的時(shí)間和大小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; //根據(jù)文件大小知道文件接下來(lái)讀取多少內(nèi)容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個(gè)字節(jié)讀onread次total_bytes = total_bytes + onread;if ((int)total_bytes == ufilesize) break;}fclose(fp);// 讀完后重置文件原始的時(shí)間,不是本地接收生成的時(shí)間UTime(strfilename,strmtime);return true;
}

如下傳二進(jìn)制文件。
在這里插入圖片描述

8.2 文件下載模塊:不建議將tcpgetfile.cpp,tcpfileserver.cpp反過(guò)來(lái),雖然全雙工但會(huì)出現(xiàn)連不上服務(wù)器被上網(wǎng)行為審計(jì)系統(tǒng)攔截

在這里插入圖片描述
在這里插入圖片描述

// 這是一個(gè)通用的功能模塊,采用TCP協(xié)議獲取文件的 客戶端tcpgetfile.cpp
#include "_public.h"
struct st_arg
{char ip[31];              // 服務(wù)器端的IP地址。int  port;                // 服務(wù)器端的端口。int  ptype;         // 文件獲取成功后文件的處理方式:1-保留文件;2-刪除文件;3-移動(dòng)到備份目錄。char clientpath[301];     // 本地文件存放的根目錄。char srvpath[301];        // 服務(wù)端文件存放的根目錄。char srvpathbak[301];     // 文件成功獲取后,服務(wù)端文件備份的根目錄,當(dāng)ptype==3時(shí)有效。bool andchild;            // 是否獲取srvpath目錄下各級(jí)子目錄的文件,true-是;false-否。char matchname[301];      // 待獲取文件名的匹配方式,如"*.TXT,*.XML",注意用大寫。char okfilename[301];     // 已獲取成功文件名清單。listfilename不需要了,服務(wù)端返回的報(bào)文直接放容器里了int  timetvl;             // 掃描本地目錄文件的時(shí)間間隔,單位:秒。
} starg;
char strRecvBuffer[TCPBUFLEN+10]; // 接收?qǐng)?bào)文的緩沖區(qū)
char strSendBuffer[TCPBUFLEN+10]; // 發(fā)送報(bào)文的緩沖區(qū)
vector<struct st_fileinfo> vlistfile,vlistfile1;
vector<struct st_fileinfo> vokfilename,vokfilename1;
bool LoadListFile(); // 把服務(wù)端srvpath目錄下的文件加載到vlistfile容器中
bool LoadOKFileName(); // 把okfilename文件內(nèi)容加載到vokfilename容器中
// 把vlistfile容器中的文件與vokfilename容器中文件對(duì)比,得到兩個(gè)容器
// 一、在vlistfile中存在,并已經(jīng)采集成功的文件vokfilename1
// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1
bool CompVector();
bool WriteToOKFileName(); // 把vokfilename1容器中的內(nèi)容先寫入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解析到參數(shù)starg結(jié)構(gòu)中
bool ClientLogin(const char *argv); // 登錄服務(wù)器
bool ActiveTest(); // 向服務(wù)端發(fā)送心跳報(bào)文
bool _tcpgetfiles(); // 實(shí)現(xiàn)文件獲取的功能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解析到參數(shù)starg結(jié)構(gòu)中while (true){     ClientLogin(argv[2]); // 向服務(wù)器發(fā)起連接并登錄// 實(shí)現(xiàn)文件獲取的功能,_tcpgetfiles()出現(xiàn)通訊故障沒(méi)有關(guān)socket,_tcpgetfiles函數(shù)返回后vlistfile容器是不空的//循環(huán)到了ClientLogin這里判斷登錄,ClientLogin里不判斷socket有沒(méi)有問(wèn)題不會(huì)去重新登錄,又到_tcpgetfiles死循環(huán)_tcpgetfiles();if (vlistfile.size()==0){     ActiveTest();     // 向服務(wù)端發(fā)送心跳報(bào)文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("這是一個(gè)通用的功能模塊,采用TCP協(xié)議獲取文件的客戶端。\n");printf("logfilename   本程序運(yùn)行的日志文件。\n");printf("xmlbuffer     本程序運(yùn)行的參數(shù),如下:\n");printf("ip            服務(wù)器端的IP地址。\n");printf("port          服務(wù)器端的端口。\n");printf("clientpath    客戶端文件存放的根目錄。\n");printf("srvpath       服務(wù)端文件存放的根目錄。\n");printf("ptype         文件獲取成功后服務(wù)端文件的處理方式:1-保留文件;2-刪除文件;3-移動(dòng)到備份目錄。\n");printf("srvpathbak    文件成功獲取后,服務(wù)端文件備份的根目錄,當(dāng)ptype==3時(shí)有效,缺省為空。\n");printf("andchild      是否獲取srvpath目錄下各級(jí)子目錄的文件,true-是;false-否,缺省為false。\n");printf("matchname     待獲取文件名的匹配方式,如\"*.TXT,*.XML\",注意用大寫。\n");printf("okfilename    已獲取成功文件名清單,缺省為空。\n");printf("timetvl       掃描本地目錄文件的時(shí)間間隔,單位:秒,取值在1-50之間。\n\n\n");
}//1111111111111111111111111111111111111111111111把xml解析到參數(shù)starg結(jié)構(gòu)中
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登錄服務(wù)器
bool ClientLogin(const char *argv)
{if (TcpClient.m_sockfd>0) return true;int ii=0;while (true){if (ii++>0) sleep(20);    // 第一次進(jìn)入循環(huán)不休眠// 向服務(wù)器發(fā)起連接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向服務(wù)端發(fā)送心跳報(bào)文
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實(shí)現(xiàn)文件獲取的功能
bool _tcpgetfiles()
{// 把服務(wù)端srvpath目錄下的文件加載到vlistfile容器中if (LoadListFile()==false){logfile.Write("LoadListFile() failed.\n"); TcpClient.Close(); return false;}if (starg.ptype==1){// 加載okfilename文件中的內(nèi)容到容器vokfilename中LoadOKFileName();// 把vlistfile容器中的文件與vokfilename容器中文件對(duì)比,得到兩個(gè)容器// 一、在vlistfile中存在,并已經(jīng)采集成功的文件vokfilename1// 二、在vlistfile中存在,新文件或需要重新采集的文件vlistfile1CompVector();    WriteToOKFileName(); // 把vokfilename1容器中的內(nèi)容先寫入okfilename文件中,覆蓋之前的舊okfilename文件    vlistfile.clear(); vlistfile.swap(vlistfile1); // 把vlistfile1容器中的內(nèi)容復(fù)制到vlistfile容器中}for (int ii=0;ii<vlistfile.size();ii++)  // 從服務(wù)端逐個(gè)獲取新文件或已改動(dòng)過(guò)的文件{    memset(strSendBuffer,0,sizeof(strSendBuffer)); // 向服務(wù)端發(fā)送將獲取(下載)的文件信息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;}// 文件信息已知道,此報(bào)文有些多余,但是為了兼容SendFile和RecvFile函數(shù),對(duì)性能不會(huì)有影響。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,要小心第三個(gè)參數(shù)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是增量傳輸,對(duì)服務(wù)端來(lái)說(shuō)什么都不干,保留oklistfile是客戶端的事   if (RecvFile(&logfile,TcpClient.m_sockfd,&stfileinfo)== false)  // 接收文件的內(nèi)容{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把服務(wù)端srvpath目錄下的文件加載到vlistfile容器中
bool LoadListFile()
{vlistfile.clear();memset(strSendBuffer,0,sizeof(strSendBuffer));strcpy(strSendBuffer,"<list>"); //向服務(wù)端發(fā)<list>,就像向ftp服務(wù)端發(fā)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到的報(bào)文就是文件總數(shù)int totalfile=0; GetXMLBuffer(strRecvBuffer,"totalfile",&totalfile);struct st_fileinfo stfileinfo;for (int ii=0;ii<totalfile;ii++) //利用循環(huán)接收文件清單報(bào)文,解析出來(lái)放入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文件內(nèi)容加載到vokfilename容器中
bool LoadOKFileName()
{vokfilename.clear();CFile File;// 注意:如果程序是第一次采集,okfilename是不存在的,并不是錯(cuò)誤,所以也返回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容器中文件對(duì)比,得到兩個(gè)容器
// 一、在vlistfile中存在,并已經(jīng)采集成功的文件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容器中的內(nèi)容先寫入okfilename文件中,覆蓋之前的舊okfilename文件
bool WriteToOKFileName()
{CFile File;if (File.Open(starg.okfilename,"w",false) == false) // 注意,打開文件不要采用緩沖機(jī)制{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;
}
//這是一個(gè)通用的功能模塊,采用TCP協(xié)議實(shí)現(xiàn)文件傳輸?shù)姆?wù)端,tcpfileserver.cpp多線程。
#include "_public.h"
struct st_arg
{int clienttype;char ip[31];              // 服務(wù)器端的IP地址。int  port;                // 服務(wù)器端的端口。int  ptype;      // 文件發(fā)送成功后文件的處理方式:1-保留文件;2-移動(dòng)到備份目錄;3-刪除文件。char clientpath[301];     // 本地文件存放的根目錄。char clientpathbak[301];  // 文件成功發(fā)送后,本地文件備份的根目錄,當(dāng)ptype==2時(shí)有效。char srvpath[301];        // 服務(wù)端文件存放的根目錄。char srvpathbak[301];     // 文件成功接收后,服務(wù)端文件備份的根目錄,當(dāng)ptype==2時(shí)有效。bool andchild;            // 是否發(fā)送clientpath目錄下各級(jí)子目錄的文件,true-是;false-否。char matchname[301];      // 待發(fā)送文件名的匹配方式,如"*.TXT,*.XML",注意用大寫。char okfilename[301];     // 已發(fā)送成功文件名清單。int  timetvl;             // 掃描本地目錄文件的時(shí)間間隔,單位:秒。
};
bool _xmltoarg(char *strxmlbuffer,struct st_arg *starg); //把xml解析到參數(shù)starg結(jié)構(gòu)中
CLogFile logfile;
bool ClientLogin(int clientfd,struct st_arg *starg); // 等待登錄
bool ListFile(int clientfd,struct st_arg *starg); // 列出srvpath目錄下文件的清單,返回給客戶端。
void EXIT(int sig); // 程序退出時(shí)調(diào)用的函數(shù)
void *pth_main(void *arg); // 與客戶端通信線程的主函數(shù)
bool RecvFilesMain(int clientfd,struct st_arg *starg); // 接收文件主函數(shù)
bool SendFilesMain(int clientfd,struct st_arg *starg); // 發(fā)送文件主函數(shù)
vector<int> vclientfd;  // 存放客戶端已連接的socket的容器
void AddClient(int clientfd);      // 把客戶端新的socket加入vclientfd容器中
void RemoveClient(int clientfd);   // 關(guān)閉客戶端的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("本程序是一個(gè)公共功能模塊,采用TCP/IP傳輸文件的服務(wù)端。\n");printf("本程序采用的是多線程的服務(wù)端,多進(jìn)程的服務(wù)端程序是tcpfileserver.cpp。\n");printf("logfilename 日志文件名。\n");printf("port 用于傳輸文件的TCP端口。\n");return -1;}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);// 打開程序運(yùn)行日志,這是一個(gè)多進(jìn)程程序,日志不能自動(dòng)切換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);   //保存服務(wù)端的listenfd到vclientfdwhile (true){    if (TcpServer.Accept() == false)  //等待客戶端的連接{logfile.Write("TcpServer.Accept() failed.\n"); continue;}pthread_t pthid;  //客戶端連上后創(chuàng)建一線程,下面將socket參數(shù)傳進(jìn)去,與新連接上來(lái)的客戶端通信// int4字節(jié),long8字節(jié),*指針8字節(jié),TcpServer.m_connfd定義的是整數(shù)intif (pthread_create(&pthid,NULL,pth_main,(void*)(long)TcpServer.m_connfd)!=0){ //主線程等子線程結(jié)束才行logfile.Write("創(chuàng)建線程失敗,程序退出。n"); close(TcpServer.m_connfd); EXIT(-1);}logfile.Write("%s is connected.\n",TcpServer.GetIP());    AddClient(TcpServer.m_connfd); //保存每個(gè)客戶端的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);// 關(guān)閉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]; // 接收?qǐng)?bào)文的緩沖區(qū)char strSendBuffer[TCPBUFLEN+10]; // 發(fā)送報(bào)文的緩沖區(qū)memset(strRecvBuffer,0,sizeof(strRecvBuffer));memset(strSendBuffer,0,sizeof(strSendBuffer));//以前用TcpServer.Read,現(xiàn)在改為TcpRead,對(duì)于線程里沒(méi)有TcpServer這個(gè)對(duì)象了//TcpServer.Read里也是調(diào)用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;// 把參數(shù)解析出來(lái)_xmltoarg(strRecvBuffer,starg);return true;
}//11111111111111111111111111111111111111111111111111111接收文件主函數(shù)
bool RecvFilesMain(int clientfd,struct st_arg *starg)
{int  ibuflen=0;char strRecvBuffer[TCPBUFLEN+10]; // 接收?qǐng)?bào)文的緩沖區(qū)char strSendBuffer[TCPBUFLEN+10]; // 發(fā)送報(bào)文的緩沖區(qū)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// 處理心跳報(bào)文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));// 獲取待接收的文件的時(shí)間和大小GetXMLBuffer(strRecvBuffer,"filename",stfileinfo.filename);GetXMLBuffer(strRecvBuffer,"filesize",&stfileinfo.filesize);GetXMLBuffer(strRecvBuffer,"mtime",stfileinfo.mtime);// 把文件名中的clientpath替換成srvpath,要小心第三個(gè)參數(shù)UpdateStr(stfileinfo.filename,starg->clientpath,starg->srvpath,false);// 接收文件的內(nèi)容if (RecvFile(&logfile,clientfd,&stfileinfo)== false){logfile.Write("RecvFile() failed.\n"); return false;}logfile.Write("recv %s ok.\n",stfileinfo.filename);}return true;
}//11111111111111111111111111111111111111111111111111111111發(fā)送文件主函數(shù)
bool SendFilesMain(int clientfd,struct st_arg *starg)
{int  ibuflen=0;char strRecvBuffer[TCPBUFLEN+10]; // 接收?qǐng)?bào)文的緩沖區(qū)char strSendBuffer[TCPBUFLEN+10]; // 發(fā)送報(bào)文的緩沖區(qū)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// 處理心跳報(bào)文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;}// 處理獲取文件列表報(bào)文if (strcmp(strRecvBuffer,"<list>")==0){if (ListFile(clientfd,starg)==false){logfile.Write("ListFile() failed.\n"); return false;}continue;}// 取文件報(bào)文if (strncmp(strRecvBuffer,"<filename>",10)==0){// 獲取待接收的文件的時(shí)間和大小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);// 把文件發(fā)送給客戶端if (SendFile(&logfile,clientfd,&stfileinfo)==false) return false;logfile.Write("put %s ...ok.\n",stfileinfo.filename);// 刪除服務(wù)端的文件if (starg->ptype==2) REMOVE(stfileinfo.filename);// 備份服務(wù)端的文件if (starg->ptype==3) {char strfilenamebak[301];memset(strfilenamebak,0,sizeof(strfilenamebak));strcpy(strfilenamebak,stfileinfo.filename);UpdateStr(strfilenamebak,starg->srvpath,starg->srvpathbak,false);  // 要小心第三個(gè)參數(shù)if (RENAME(stfileinfo.filename,strfilenamebak)==false){logfile.Write("RENAME %s to %s failed.\n",stfileinfo.filename,strfilenamebak); return false;}}}}return true;
}//11111111111111111111111111111111111111111111111111111把xml解析到參數(shù)starg結(jié)構(gòu)中
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]; // 接收?qǐng)?bào)文的緩沖區(qū)char strSendBuffer[TCPBUFLEN+10]; // 發(fā)送報(bào)文的緩沖區(qū)CDir Dir;// 注意,如果目錄下的總文件數(shù)超過(guò)50000,增量發(fā)送文件功能將有問(wèn)題if (Dir.OpenDir(starg->srvpath,starg->matchname,50000,starg->andchild,false)==false){logfile.Write("Dir.OpenDir(%s) 失敗。\n",starg->srvpath); return false;}// 先把文件總數(shù)返回給客戶端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與客戶端通信線程的主函數(shù)
void *pth_main(void *arg)
{int clientfd=(long) arg; // arg參數(shù)為新客戶端的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); }// 接收文件主函數(shù)if (starg.clienttype==1) {if (RecvFilesMain(clientfd,&starg) == false) { RemoveClient(clientfd); pthread_exit(0); }}// 發(fā)送文件主函數(shù)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關(guān)閉客戶端的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 高性能網(wǎng)絡(luò)服務(wù):多線程+數(shù)據(jù)庫(kù)連接池(多線程每啟一個(gè)線程都要連數(shù)據(jù)庫(kù)耗資源)

如下開始APP服務(wù)端設(shè)計(jì),客戶端就是手機(jī)app軟件。第一次客戶端將手機(jī)編號(hào)傳給服務(wù)端,服務(wù)端將站點(diǎn)信息傳給客戶端。
在這里插入圖片描述
短連接:客戶端即用戶點(diǎn)擊按鈕一次建立一次socket連接請(qǐng)求,處理完一個(gè)就斷開。響應(yīng)慢:建立一次socket連接費(fèi)時(shí)間,服務(wù)端fork一個(gè)進(jìn)程也要時(shí)間,之后和數(shù)據(jù)庫(kù)連接也要時(shí)間。

長(zhǎng)連接:客戶端與服務(wù)端socket一直連接著進(jìn)行數(shù)據(jù)通信,沒(méi)有數(shù)據(jù)通信時(shí)用心跳(之前文件傳輸都用的是長(zhǎng)連接),用戶關(guān)了app,連接才斷開。費(fèi)服務(wù)端資源:長(zhǎng)連接連上后,數(shù)據(jù)庫(kù)連接和進(jìn)程都已準(zhǔn)備好,一直通信完才斷開。響應(yīng)快:用戶看到數(shù)據(jù)越快越好控制在1秒內(nèi)。如下項(xiàng)目組織(shtqapp是一個(gè)獨(dú)立的項(xiàng)目)。
在這里插入圖片描述
如下第一行是上面創(chuàng)建用戶sql,pdm文件是數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)。
在這里插入圖片描述

// client.cpp,模擬tcp手機(jī)客戶端,客戶端用短鏈接還是長(zhǎng)連接由客戶端自己安排
#include "_freecplus.h"
CTcpClient TcpClient;
char strSendBuffer[301],strRecvBuffer[301];
bool biz10000();  // 心跳
bool biz10001();  // 新用戶登錄:只傳個(gè)設(shè)備編號(hào)id,服務(wù)端把城市站點(diǎn)信息傳給客戶端,手機(jī)利用定位匹配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));// 如下請(qǐng)求報(bào)文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; }//如下用一個(gè)循環(huán)接收全部的站點(diǎn)信息while (1){memset(strRecvBuffer,0,sizeof(strRecvBuffer));if (TcpClient.Read(strRecvBuffer,20)==false)  { printf("recv failed.\n"); return false; }// printf("recv=%s=\n",strRecvBuffer); //手機(jī)端沒(méi)數(shù)據(jù)庫(kù),手機(jī)軟件真正處理方法把數(shù)據(jù)保存到xml文件里if (strcmp(strRecvBuffer,"ok")==0) break; //接收到ok的話表示數(shù)據(jù)處理完了}return true;
}

數(shù)據(jù)庫(kù)連接池的設(shè)計(jì)可用一個(gè)參數(shù)去控制連接池的總大小,比如這連接池里有10個(gè)connection連接就需要10把鎖。在sqlstatement每次想使用數(shù)據(jù)庫(kù)連接時(shí)就會(huì)從10個(gè)已創(chuàng)建好的connection看看哪個(gè)沒(méi)鎖就拿1個(gè)過(guò)來(lái)用。

//上海天氣APP軟件服務(wù)端主程序。shtqappserver.cpp多線程方式,采用連接池。
#include "_freecplus.h"
#include "_ooci.h"
#define MAXCONNS 10  // 數(shù)據(jù)庫(kù)連接池的大小。
pthread_mutex_t mutex[MAXCONNS];  // 鎖數(shù)組。
connection conns[MAXCONNS];  // 數(shù)據(jù)庫(kù)連接數(shù)組。
bool initconns();   // 初始數(shù)據(jù)庫(kù)連接池。
connection *getconns();  // 從連接池中獲取一個(gè)數(shù)據(jù)庫(kù)連接。
bool freeconns(connection *in_conn);  // 釋放數(shù)據(jù)庫(kù)連接。struct st_biz  // 業(yè)務(wù)請(qǐng)求
{int  bizid;               // 業(yè)務(wù)代碼char userid[51];          // 設(shè)備IDint  ttytype;             // 用戶的設(shè)備類型,0-未知;1-IOS;2-Andriod,2-鴻蒙。int  usertype;            // 用戶分類,0-未知;1-普通用戶;2-氣象志愿者;3-內(nèi)部用戶。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); // 程序退出時(shí)調(diào)用的函數(shù)
void *pth_main(void *arg); // 與客戶端通信線程的主函數(shù)
bool biz10000(int clientfd); // 心跳業(yè)務(wù)
bool biz10001(struct st_biz *stbiz,int clientfd); // 新用戶登錄業(yè)務(wù)
bool biz10002(struct st_biz *stbiz,int clientfd); // 獲取天氣實(shí)況
bool InsertUSERLOG(struct st_biz *stbiz,connection *conn); // 插入用戶請(qǐng)求日志表
vector<int> vclientfd; // 存放客戶端已連接的socket的容器
void AddClient(int clientfd);  // 把客戶端新的socket加入vclientfd容器中
void RemoveClient(int clientfd);  // 關(guān)閉客戶端的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軟件的服務(wù)端。\n");printf("logfilename 日志文件名。\n");printf("port 用于傳輸文件的TCP端口。\n");return -1;}CloseIOAndSignal(); signal(SIGINT,EXIT); signal(SIGTERM,EXIT);// 打開程序運(yùn)行日志,這是一個(gè)多進(jìn)程程序,日志不能自動(dòng)切換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);}// 保存服務(wù)端的listenfd到vclientfdAddClient(TcpServer.m_listenfd);if (initconns()==false)  // 初始化數(shù)據(jù)庫(kù)連接池。{logfile.Write("initconns() failed.\n"); EXIT(-1);}while (true){if (TcpServer.Accept() == false) // 等待客戶端的連接{logfile.Write("TcpServer.Accept() failed.\n"); continue;}pthread_t pthid;   // 創(chuàng)建一線程,與新連接上來(lái)的客戶端通信if (pthread_create(&pthid,NULL,pth_main,(void*)(long)TcpServer.m_connfd)!=0){logfile.Write("創(chuàng)建線程失敗,程序退出。n"); close(TcpServer.m_connfd); EXIT(-1);}logfile.Write("%s is connected.\n",TcpServer.GetIP());    AddClient(TcpServer.m_connfd); // 保存每個(gè)客戶端的socket到vclientfd}return 0;
}void EXIT(int sig)  // 退出時(shí)調(diào)用的函數(shù)
{signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);if (sig>0) signal(sig,SIG_IGN);logfile.Write("tcpfileserver1 exit,sig=%d...\n",sig);// 關(guān)閉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與客戶端通信線程的主函數(shù)
void *pth_main(void *arg)
{int clientfd=(long) arg; // arg參數(shù)為新客戶端的socket。pthread_detach(pthread_self());struct st_biz stbiz;int  ibuflen=0;char strRecvBuffer[1024]; // 接收?qǐng)?bào)文的緩沖區(qū)while (true){memset(strRecvBuffer,0,sizeof(strRecvBuffer));// 接收客戶端的業(yè)務(wù)請(qǐng)求報(bào)文,如果返回false,認(rèn)為是客戶端退出或網(wǎng)絡(luò)原因,不寫錯(cuò)誤日志if (TcpRead(clientfd,strRecvBuffer,&ibuflen,50) == false){// logfile.Write("TcpRead() failed.\n"); break;}logfile.Write("strRecvBuffer=%s\n",strRecvBuffer);  // xxxxxx// 把參數(shù)解析出來(lái)xmltobiz(strRecvBuffer,&stbiz);if (stbiz.bizid==10000)    // 心跳報(bào)文{if (biz10000(clientfd)==true) continue;else break;}// 新用戶登錄 if (stbiz.bizid==10001)    {if (biz10001(&stbiz,clientfd)==true) continue;else break;}// 獲取天氣實(shí)況if (stbiz.bizid==10002)    {if (biz10002(&stbiz,clientfd)==true) continue;else break;}// 體力活logfile.Write("非法報(bào)文%s\n",strRecvBuffer); break;}RemoveClient(clientfd);pthread_exit(0);
}//111111111111111111111111111111111111111111把xml解析到參數(shù)starg結(jié)構(gòu)中
void xmltobiz(char *strxmlbuffer,struct st_biz *stbiz)
{memset(stbiz,0,sizeof(struct st_biz));// 業(yè)務(wù)代碼GetXMLBuffer(strxmlbuffer,"bizid",&stbiz->bizid);// logfile.Write("bizid=%d\n",stbiz->bizid);// 用戶設(shè)備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心跳業(yè)務(wù)
bool biz10000(int clientfd)
{char strSendBuffer[1024]; // 發(fā)送報(bào)文的緩沖區(qū)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]; // 發(fā)送報(bào)文的緩沖區(qū)  connection *conn=getconns();  // 獲取一個(gè)數(shù)據(jù)庫(kù)連接。// 插入用戶基本信息表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());// 插入用戶請(qǐng)求日志表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"); //最后發(fā)送一個(gè)okif (TcpWrite(clientfd,strSendBuffer) == false){logfile.Write("biz10001 TcpWrite() failed.\n"); freeconns(conn); return false;}freeconns(conn);return true;
}//11111111111111111111111111111111111111111111111插入用戶請(qǐng)求日志表
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獲取天氣實(shí)況
bool biz10002(struct st_biz *stbiz,int clientfd)
{return true;
}//1111111111111111111111111111111111111把客戶端新的socket加入vclientfd容器中
void AddClient(int clientfd)
{vclientfd.push_back(clientfd);
}//111111111111111111111111111111111111關(guān)閉客戶端的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初始數(shù)據(jù)庫(kù)連接池:連接好數(shù)據(jù)庫(kù),初始化鎖
bool initconns()  
{for (int ii=0;ii<MAXCONNS;ii++){logfile.Write("%d,connecttodb and pthread_mutex_init.\n",ii);// 連接數(shù)據(jù)庫(kù)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); // 創(chuàng)建鎖}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; //理論上不會(huì)來(lái)到這里,除非連接池搞亂了
}

在這里插入圖片描述
心跳報(bào)文業(yè)務(wù)不需要連接池,先開啟服務(wù)端。
在這里插入圖片描述

9.進(jìn)程:fork(),ps -ef (同-aux) | more

一個(gè)進(jìn)程(正在內(nèi)存中運(yùn)行的程序)在內(nèi)存里有三部分?jǐn)?shù)據(jù):代碼段(相同),堆棧段+數(shù)據(jù)段(不同)。getpid庫(kù)函數(shù)功能是獲取進(jìn)程編號(hào),該函數(shù)沒(méi)有參數(shù),返回值是進(jìn)程的編號(hào)(相同程序在不同時(shí)間執(zhí)行,進(jìn)程編號(hào)不同)。
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
進(jìn)程應(yīng)用:并發(fā):把socket服務(wù)端改為多進(jìn)程,每次accept到一個(gè)客戶端連接后,生成一個(gè)子進(jìn)程,讓子進(jìn)程負(fù)責(zé)與這個(gè)客戶端通訊。父進(jìn)程繼續(xù)accept客戶端連接。以下在服務(wù)端中,在CTcpServer類中增加兩個(gè)成員函數(shù)。
在這里插入圖片描述
在這里插入圖片描述
僵尸進(jìn)程:一個(gè)進(jìn)程執(zhí)行了exit系統(tǒng)調(diào)用退出時(shí)會(huì)向父進(jìn)程發(fā)送SIGCHLD信號(hào),而其父進(jìn)程并沒(méi)有為它收尸(調(diào)用wait或waitpid來(lái)獲得它的結(jié)束狀態(tài),如進(jìn)程ID、終止?fàn)顟B(tài)等等)的進(jìn)程,ps顯示有< default >。總結(jié):仔細(xì)處理子進(jìn)程死后的信息,如果不想處理就需要交給系統(tǒng)處理。
在這里插入圖片描述

10.信號(hào):signal(, EXIT),jps

進(jìn)程間通信方式:1.管道:ls | grep 1。
2.消息隊(duì)列:內(nèi)核創(chuàng)建的一個(gè)消息隊(duì)列,os中多個(gè)進(jìn)程都能操作這個(gè)消息隊(duì)列,可以往里面發(fā)送消息,可以接收消息。類似socket。
3.共享內(nèi)存:每個(gè)進(jìn)程訪問(wèn)內(nèi)存時(shí),有一個(gè)虛擬內(nèi)存地址和物理內(nèi)存地址的一個(gè)映射,一般兩個(gè)進(jìn)程的虛擬內(nèi)存地址可以一樣,但映射的物理內(nèi)存地址一般不一樣。共享內(nèi)存就是將它們映射的物理內(nèi)存地址也變一樣,這時(shí)兩個(gè)進(jìn)程能同時(shí)訪問(wèn)一塊相同的物理內(nèi)存,于是借助這塊物理內(nèi)存實(shí)現(xiàn)通信。

4.套接字:常見(jiàn),訪問(wèn)數(shù)據(jù)庫(kù)進(jìn)程和數(shù)據(jù)庫(kù)進(jìn)程本身,這兩個(gè)進(jìn)程間通信就是通過(guò)3306號(hào)端口建立起的tcp套接字。本機(jī)訪問(wèn)mysql不走tcp的套接字,而是走linux底層套接字。
5.信號(hào)量/燈:類似一個(gè)計(jì)數(shù)器,控制多個(gè)進(jìn)程對(duì)一個(gè)共享資源的訪問(wèn),起到控制數(shù)量的鎖機(jī)制。
6.信號(hào):一個(gè)進(jìn)程可向另一個(gè)進(jìn)程發(fā)送一個(gè)信號(hào),進(jìn)程可處理這個(gè)信號(hào)。如下列出所有信號(hào),linux中信號(hào)大多數(shù)是把另一個(gè)進(jìn)程殺死,干脆把這個(gè)指令叫kill了,如下64種死法。 如下是鍵盤中斷ctrl+c,是當(dāng)前shell向tail -f這個(gè)進(jìn)程發(fā)送一個(gè)信號(hào)值為2的2)SIGINT的信號(hào)。
在這里插入圖片描述
在這里插入圖片描述

10.1 捕捉信號(hào):ctrl+c:2

如下在死循環(huán)之前注冊(cè)下信號(hào)的處理,如下讓ctrl+c無(wú)效。
在這里插入圖片描述
如下點(diǎn)擊Build后就會(huì)將類編譯出來(lái)。
在這里插入圖片描述
如上ctrl+c無(wú)法停止,只能用kill,jps查看進(jìn)程pid。
在這里插入圖片描述

10.2 捕捉信號(hào):kill -9:9

在這里插入圖片描述

10.3 捕捉信號(hào):kill:15

將如上KILL換成TERM即SIGTERM,重新build project。win下信號(hào)比linux下信號(hào)少很多,將當(dāng)前程序打包成jar包傳到linux。用如下命令行打包。
在這里插入圖片描述
在這里插入圖片描述
如下用壓縮軟件打開1.jar。
在這里插入圖片描述
如下:后有一個(gè)空格,最后一行是空行。
在這里插入圖片描述

10.4 程序后臺(tái)運(yùn)行兩種方法:&:ctrl+c無(wú)法中止,用killall book1或kill 進(jìn)程號(hào)。if (fork()>0)return 0 父進(jìn)程退出

在這里插入圖片描述
信號(hào)作用:服務(wù)程序在后臺(tái)運(yùn)行,如果想終止它,殺了它不是個(gè)好辦法,因?yàn)闆](méi)有釋放資源。如果能向程序發(fā)一個(gè)信號(hào),程序收到這個(gè)信號(hào)后調(diào)用一個(gè)函數(shù),在函數(shù)中編寫釋放資源代碼,程序就可以安全體面退出。
在這里插入圖片描述
下面 EXIT函數(shù)就是自定義函數(shù),TcpServer設(shè)為全局變量因?yàn)镋XIT函數(shù)要訪問(wèn)它并關(guān)閉socket。
在這里插入圖片描述

1._public.h

#ifndef _PUBLIC_H
#define _PUBLIC_H 1
#include "_cmpublic.h"
//全路徑文件名,大小,時(shí)間的結(jié)構(gòu)體
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];       // 文件最后一次被修改的時(shí)間char m_CreateTime[21];       // 文件生成的時(shí)間char m_AccessTime[21];       // 文件最后一次被訪問(wèn)的時(shí)間int m_uPOS;                 // 已讀取m_vFileName容器的位置vector<string> m_vFileName;  //  存放OpenDir方法獲取到的文件列表CDir();// 變量初始化void initdata();// 設(shè)置日期時(shí)間的格式,支持"yyyy-mm-dd hh24:mi:ss"和"yyyymmddhh24miss"兩種格式,缺省是前者char m_DateFMT[21];void SetDateFMT(const char *in_DateFMT);// 打開目錄,獲取文件名信息,存放于m_vFileName容器中// in_DirName,待打開的目錄名// in_MatchStr,待獲取文件名的匹配規(guī)則// in_MaxCount,獲取文件的最大數(shù)量// bAndChild,是否打開各級(jí)子目錄// bSort,是否對(duì)結(jié)果集進(jìn)行排序bool OpenDir(const char *in_DirName,const char *in_MatchStr,const unsigned int in_MaxCount=10000,const bool bAndChild=false,bool bSort=false);// 這是一個(gè)遞歸函數(shù),用于OpenDir()的調(diào)用。bool _OpenDir(const char *in_DirName,const char *in_MatchStr,const unsigned int in_MaxCount,const bool bAndChild);// 逐個(gè)讀取目錄中的文件信息bool ReadDir();~CDir();
};//111111111111111111112.STRCPY、STRNCPY、STRCAT、STRNCAT四個(gè)函數(shù),彌補(bǔ)庫(kù)函數(shù)的缺陷
// 以后可以用這四個(gè)函數(shù)取代strcpy、strncpy、strcat、strncat
// 函數(shù)的第二個(gè)參數(shù)是第一個(gè)參數(shù)dest有效長(zhǎng)度,即占用內(nèi)存的字節(jié)數(shù)-1。
// 該系列函數(shù)解決三個(gè)問(wèn)題:1)變量初始化;2)內(nèi)存溢出;3)修復(fù)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, ...);// 把整數(shù)的時(shí)間轉(zhuǎn)換為字符串格式的時(shí)間,格式如:"2019-02-08 12:05:08",如果轉(zhuǎn)換成功函數(shù)返回0,失敗返回-1,函數(shù)的聲明如下:
int timetostr(const time_t ti,char *strtime);// 把字符串格式的時(shí)間轉(zhuǎn)換為整數(shù)的時(shí)間,函數(shù)的聲明如下:
int strtotime(const char *strtime,time_t *ti);// 從文件文件中讀取一行
// strEndStr是一行數(shù)據(jù)的結(jié)束標(biāo)志,如果為空,則以換行符"\n"為結(jié)束標(biāo)志。
// 本函數(shù)不會(huì)刪除行的結(jié)束標(biāo)志
bool FGETS(const FILE *fp,char *strBuffer,const int ReadSize,const char *strEndStr=0);//11111111111111111111113.文件操作類聲明
class CFile
{
private:FILE *m_fp;        // 文件指針bool  m_bEnBuffer; // 是否啟用緩沖區(qū),true-啟用;false-不啟用char  m_filename[301]; // 文件名char  m_filenametmp[301]; // 臨時(shí)文件名public:CFile();   // 類的構(gòu)造函數(shù)~CFile();   // 類的析構(gòu)函數(shù)bool IsOpened();  // 判斷文件是否已打開// 打開文件,參數(shù)與fopen相同,打開成功true,失敗返回false          bool Open(const char *filename,const char *openmode,bool bEnBuffer=true);// 關(guān)閉文件指針,并刪除文件bool CloseAndRemove();// 專為改名而創(chuàng)建文件,參數(shù)與fopen相同,打開成功true,失敗返回false          // 函數(shù)將會(huì)創(chuàng)建filename后加.tmp的臨時(shí)文件,調(diào)用CloseAndRename()后才把臨時(shí)文件改名為正式文件bool OpenForRename(const char *filename,const char *openmode,bool bEnBuffer=true);// 關(guān)閉文件并改名bool CloseAndRename();// 調(diào)用fprintf向文件寫入數(shù)據(jù)void Fprintf(const char *fmt, ... );// 調(diào)用fgets從文件中讀取一行,bDelCRT=true刪除換行符,false不刪除,缺省為falsebool Fgets(char *strBuffer,const int ReadSize,bool bDelCRT=false);// 從文件文件中讀取一行// strEndStr是一行數(shù)據(jù)的結(jié)束標(biāo)志,如果為空,則以換行符"\n"為結(jié)束標(biāo)志。// 與Fgets不同,本函數(shù)不刪除結(jié)束標(biāo)志bool FFGETS(char *strBuffer,const int ReadSize,const char *strEndStr=0);// 調(diào)用fread從文件中讀取數(shù)據(jù)size_t Fread(void *ptr,size_t size);// 調(diào)用fwrite向文件中寫數(shù)據(jù)size_t Fwrite(const void *ptr,size_t size);// 關(guān)閉文件指針,如果存在臨時(shí)文件,就刪除它。void Close();
};//1111111111111111111111111111114.拆分字符串的類
// 字符串的格式為:內(nèi)容1+分隔字符串+內(nèi)容2+分隔字符串+內(nèi)容3
// 如:num~!~name~!~address,分隔符為"~!~"
class CCmdStr
{
public:vector<string> m_vCmdStr;  // 存放拆分后的字段內(nèi)容。CCmdStr();// 拆分字符串到容器中void SplitToCmd(const string in_string,const char *in_sep,const bool bdeletespace=true);int CmdCount();// 獲取字段的值,取每個(gè)字段的值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.取操作系統(tǒng)的時(shí)間
/*out_stime是輸出結(jié)果in_interval是偏移常量,單位是秒返回的格式由fmt決定,fmt目前的取值如下,如果需要,可以增加:yyyy-mm-dd hh:mi:ss,此格式是缺省格式,長(zhǎng)度為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,是否自動(dòng)備份bool    m_bEnBuffer;         // 寫入日志時(shí),是否啟用操作系統(tǒng)的緩沖機(jī)制CLogFile();// filename日志文件名// openmode打開文件的方式,操作日志文件的權(quán)限,同打開文件函數(shù)(fopen)使用方法一致,一般采用"a+"// bBackup,true-備份,false-不備份,在多進(jìn)程的服務(wù)程序中,如果多個(gè)進(jìn)行共用一個(gè)日志文件,bBackup必須為false// bEnBuffer:true-啟用緩沖區(qū),false-不啟用緩沖區(qū),如果啟用緩沖區(qū),那么寫進(jìn)日志文件中的內(nèi)容不會(huì)立即寫入文件// 注意,bEnBuffer參數(shù)的值如果設(shè)置為true,可能會(huì)導(dǎo)致日志文件內(nèi)容不完整。bool Open(const char *in_filename,const char *in_openmode,bool bBackup=true,bool bEnBuffer=false);// 如果日志文件大于100M,就備份它// 備份后的文件會(huì)在源文件名后加上日期時(shí)間// 注意,在多進(jìn)程程序中,日志文件不可切換,多線程程序中,日志文件可切換。bool BackupLogFile();// 寫日志文件,它是個(gè)可變參數(shù)的方法,同printf函數(shù)。// Write()方法會(huì)寫入時(shí)間,WriteEx()方法不寫時(shí)間。bool Write(const char *fmt,...);bool WriteEx(const char *fmt,...);// 關(guān)閉日志文件void Close();~CLogFile();
};// 關(guān)閉全部的信號(hào)和輸入輸出
void CloseIOAndSignal();// 用某文件或目錄的全路徑中的目錄創(chuàng)建目錄,以及該目錄下的各級(jí)子目錄
// pathorfilename 目錄名或文件名
// bisfilename true-pathorfilename是文件名,否則是目錄名
bool MKDIR(const char *pathorfilename,bool bisfilename=true);// 刪除文件,如果刪除失敗,會(huì)嘗試in_times次
bool REMOVE(const char *in_filename,const int in_times=3);// 把in_srcfilename改名為in_dstfilename,如果改名失敗,會(huì)嘗試in_times次
bool RENAME(const char *in_srcfilename,const char *in_dstfilename,const int in_times=3);// 把某一個(gè)文件復(fù)制到另一個(gè)文件
bool COPY(const char *srcfilename,const char *dstfilename);// 調(diào)用fopen函數(shù)打開文件,如果文件名中包含的目錄不存在,就創(chuàng)建目錄
FILE *FOPEN(const char *filename,const char *mode);// 獲取文件的大小,返回字節(jié)數(shù)
int FileSize(const char *in_FullFileName);// 獲取文件的時(shí)間,即modtime
void FileMTime(const char *in_FullFileName,char *out_ModTime);// 更改文件的修改時(shí)間屬性,mtime指定了時(shí)間,格式不限,只要包括了yyyy,mm,dd,hh24,mi,ss即可。
int UTime(const char *filename,const char *mtime);// 把字符串格式的時(shí)間轉(zhuǎn)換為time_t
// stime為輸入的時(shí)間,格式不限,但一定要包括yyyymmddhh24miss
time_t UTCTime(const char *stime);// 從一個(gè)字符串中提取數(shù)字、符號(hào)和小數(shù)點(diǎn),并判斷是否是一個(gè)合法的數(shù)
// 如果不合法,將返回空字符串
// bWithSign==true表示包括符號(hào),bWithDOT==true表示包括圓點(diǎn)
void PickNumber(const char *strSrc,char *strDst,const bool bWithSign,const bool bWithDOT);// 判斷字符串中的負(fù)號(hào)和圓點(diǎn)是否合法
bool JudgeSignDOT(const char *strSrc,const char *strBZ);/*把一個(gè)字符串表格的時(shí)間加上一個(gè)偏移量,得到偏移后的時(shí)間in_stime是傳入的時(shí)間,任意格式,但是一定要包括yyyymmddhh24miss,是否有分隔符沒(méi)有關(guān)系。把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格式字符串的相關(guān)操作函數(shù)和類
// 操作XMLBuffer的函數(shù)
// in_XMLBuffer,XML格式的字符串,如下:
// <filename>/tmp/readme.txt</filename><mtime>2018-01-01 12:20:35</mtime><size>10241</size>
// in_FieldName,字段的標(biāo)簽名
// out_Value,獲取內(nèi)容存放的變量的指針
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指定的規(guī)則
// in_FileName文件名
// in_MatchStr規(guī)則表達(dá)式,如"*.txt,*.xml",中間用逗號(hào)分隔
bool MatchFileName(const string in_FileName,const string in_MatchStr);// 把小寫轉(zhuǎn)換成大寫,忽略不是字母的字符
void ToUpper(char *str);
void ToUpper(string &str);// 把大寫轉(zhuǎn)換成小寫,忽略不是字母的字符
void ToLower(char *str);
void ToLower(string &str);// 字符串替換函數(shù)
// 把in_string中的in_str1替換為in_str2
// bLoop是否循環(huán)執(zhí)行替換
// 注意
// 1、如果in_str2比in_str1要長(zhǎng),替換后in_string會(huì)變長(zhǎng),所以必須保證in_string有足夠的長(zhǎng)度, 否則內(nèi)存會(huì)溢出
// 2、如果in_str2中包函了in_str1的內(nèi)容,且bLoop為true,就會(huì)進(jìn)入死循環(huán),最終導(dǎo)致內(nèi)存溢出
void UpdateStr(char *in_string,const char *in_str1,const char *in_str2,bool bLoop=true);//111111111111111111111118.以下是TCP/IP通訊的函數(shù)和類
// socket通信的客戶端類
class CTcpClient
{
public:int  m_sockfd;    // 客戶端的socket.char m_ip[21];    // 服務(wù)端的ip地址。int  m_port;      // 與服務(wù)端通信的端口。bool m_state;     // 與服務(wù)端的socket連接狀態(tài)。bool m_btimeout;  // 調(diào)用Read和Write方法時(shí),失敗的原因是否是超時(shí):true-未超時(shí),false-已超時(shí)。int  m_buflen;    // 調(diào)用Read方法后,接收到的報(bào)文的大小,單位:字節(jié)。CTcpClient();  // 構(gòu)造函數(shù)。// 向服務(wù)端發(fā)起連接請(qǐng)求。// ip:服務(wù)端的ip地址。// port:服務(wù)端監(jiān)聽(tīng)的端口。// 返回值:true-成功;false-失敗。bool ConnectToServer(const char *ip,const int port);// 接收服務(wù)端發(fā)送過(guò)來(lái)的數(shù)據(jù)。// buffer:接收數(shù)據(jù)緩沖區(qū)的地址,數(shù)據(jù)的長(zhǎng)度存放在m_buflen成員變量中。// itimeout:等待數(shù)據(jù)的超時(shí)時(shí)間,單位:秒,缺省值是0-無(wú)限等待。// 返回值:true-成功;false-失敗,失敗有兩種情況:1)等待超時(shí),成員變量m_btimeout的值被設(shè)置為true;2)socket連接已不可用。bool Read(char *buffer,const int itimeout=0);// 向服務(wù)端發(fā)送數(shù)據(jù)。// buffer:待發(fā)送數(shù)據(jù)緩沖區(qū)的地址。// ibuflen:待發(fā)送數(shù)據(jù)的大小,單位:字節(jié),缺省值為0,如果發(fā)送的是ascii字符串,ibuflen取0,如果是二進(jìn)制流數(shù)據(jù),ibuflen為二進(jìn)制數(shù)據(jù)塊的大小。// 返回值:true-成功;false-失敗,如果失敗,表示socket連接已不可用。bool Write(const char *buffer,const int ibuflen=0);// 斷開與服務(wù)端的連接void Close();~CTcpClient();  // 析構(gòu)函數(shù)自動(dòng)關(guān)閉socket,釋放資源。
};// socket通信的服務(wù)端類
class CTcpServer
{
private:int m_socklen;                    // 結(jié)構(gòu)體struct sockaddr_in的大小。struct sockaddr_in m_clientaddr;  // 客戶端的地址信息。struct sockaddr_in m_servaddr;    // 服務(wù)端的地址信息。
public:int  m_listenfd;   // 服務(wù)端用于監(jiān)聽(tīng)的socket。int  m_connfd;     // 客戶端連接上來(lái)的socket。bool m_btimeout;   // 調(diào)用Read和Write方法時(shí),失敗的原因是否是超時(shí):true-未超時(shí),false-已超時(shí)。int  m_buflen;     // 調(diào)用Read方法后,接收到的報(bào)文的大小,單位:字節(jié)。CTcpServer();  // 構(gòu)造函數(shù)。// 服務(wù)端初始化。// port:指定服務(wù)端用于監(jiān)聽(tīng)的端口。// 返回值:true-成功;false-失敗,一般情況下,只要port設(shè)置正確,沒(méi)有被占用,初始化都會(huì)成功。bool InitServer(const unsigned int port); // 阻塞等待客戶端的連接請(qǐng)求。// 返回值:true-有新的客戶端已連接上來(lái),false-失敗,Accept被中斷,如果Accept失敗,可以重新Accept。bool Accept();// 獲取客戶端的ip地址。// 返回值:客戶端的ip地址,如"192.168.1.100"。char *GetIP();// 接收客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù)。// buffer:接收數(shù)據(jù)緩沖區(qū)的地址,數(shù)據(jù)的長(zhǎng)度存放在m_buflen成員變量中。// itimeout:等待數(shù)據(jù)的超時(shí)時(shí)間,單位:秒,缺省值是0-無(wú)限等待。// 返回值:true-成功;false-失敗,失敗有兩種情況:1)等待超時(shí),成員變量m_btimeout的值被設(shè)置為true;2)socket連接已不可用。bool Read(char *buffer,const int itimeout);// 向客戶端發(fā)送數(shù)據(jù)。// buffer:待發(fā)送數(shù)據(jù)緩沖區(qū)的地址。// ibuflen:待發(fā)送數(shù)據(jù)的大小,單位:字節(jié),缺省值為0,如果發(fā)送的是ascii字符串,ibuflen取0,如果是二進(jìn)制流數(shù)據(jù),ibuflen為二進(jìn)制數(shù)據(jù)塊的大小。// 返回值:true-成功;false-失敗,如果失敗,表示socket連接已不可用。bool Write(const char *buffer,const int ibuflen=0);// 關(guān)閉監(jiān)聽(tīng)的socket,即m_listenfd,常用于多進(jìn)程服務(wù)程序的子進(jìn)程代碼中。void CloseListen();// 關(guān)閉客戶端的socket,即m_connfd,常用于多進(jìn)程服務(wù)程序的父進(jìn)程代碼中。void CloseClient();~CTcpServer();  // 析構(gòu)函數(shù)自動(dòng)關(guān)閉socket,釋放資源。
};// 接收socket的對(duì)端發(fā)送過(guò)來(lái)的數(shù)據(jù)。
// sockfd:可用的socket連接。
// buffer:接收數(shù)據(jù)緩沖區(qū)的地址。
// ibuflen:本次成功接收數(shù)據(jù)的字節(jié)數(shù)。
// itimeout:接收等待超時(shí)的時(shí)間,單位:秒,缺省值是0-無(wú)限等待。
// 返回值:true-成功;false-失敗,失敗有兩種情況:1)等待超時(shí);2)socket連接已不可用。
bool TcpRead(const int sockfd,char *buffer,int *ibuflen,const int itimeout=0);// 向socket的對(duì)端發(fā)送數(shù)據(jù)。
// sockfd:可用的socket連接。
// buffer:待發(fā)送數(shù)據(jù)緩沖區(qū)的地址。
// ibuflen:待發(fā)送數(shù)據(jù)的字節(jié)數(shù),如果發(fā)送的是ascii字符串,ibuflen取0,如果是二進(jìn)制流數(shù)據(jù),ibuflen為二進(jìn)制數(shù)據(jù)塊的大小。
// 返回值:true-成功;false-失敗,如果失敗,表示socket連接已不可用。
bool TcpWrite(const int sockfd,const char *buffer,const int ibuflen=0);// 從已經(jīng)準(zhǔn)備好的socket中讀取數(shù)據(jù)。
// sockfd:已經(jīng)準(zhǔn)備好的socket連接。
// buffer:接收數(shù)據(jù)緩沖區(qū)的地址。
// n:本次接收數(shù)據(jù)的字節(jié)數(shù)。
// 返回值:成功接收到n字節(jié)的數(shù)據(jù)后返回true,socket連接不可用返回false。
bool Readn(const int sockfd,char *buffer,const size_t n);// 向已經(jīng)準(zhǔn)備好的socket中寫入數(shù)據(jù)。
// sockfd:已經(jīng)準(zhǔn)備好的socket連接。
// buffer:待發(fā)送數(shù)據(jù)緩沖區(qū)的地址。
// n:待發(fā)送數(shù)據(jù)的字節(jié)數(shù)。
// 返回值:成功發(fā)送完n字節(jié)的數(shù)據(jù)后返回true,socket連接不可用返回false。
bool Writen(const int sockfd,const char *buffer,const size_t n);//111111111111111111119.這是一個(gè)精確到微秒的計(jì)時(shí)器
class CTimer
{
public:struct timeval m_start,m_end;CTimer();// 開始計(jì)時(shí)void Start();// 計(jì)算已逝去的時(shí)間,單位:秒,小數(shù)點(diǎn)后面是微秒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;
}// 把整數(shù)的時(shí)間轉(zhuǎn)換為字符串格式的時(shí)間,格式如:"2019-02-08 12:05:08",如果轉(zhuǎn)換成功函數(shù)返回0,失敗返回-1,函數(shù)的聲明如下:
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;
}// 把字符串格式的時(shí)間轉(zhuǎn)換為整數(shù)的時(shí)間,函數(shù)的聲明如下:
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()   // 類的構(gòu)造函數(shù)
{m_fp=0;m_bEnBuffer=true;memset(m_filename,0,sizeof(m_filename));memset(m_filenametmp,0,sizeof(m_filenametmp));
}// 關(guān)閉文件指針
void CFile::Close() 
{if (m_fp==0) return;fclose(m_fp);  // 關(guān)閉文件指針m_fp=0;memset(m_filename,0,sizeof(m_filename));// 如果存在臨時(shí)文件,就刪除它。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;
}// 關(guān)閉文件指針,并刪除文件
bool CFile::CloseAndRemove()
{if (m_fp==0) return true;fclose(m_fp);  // 關(guān)閉文件指針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()   // 類的析構(gòu)函數(shù)
{Close();
}// 打開文件,參數(shù)與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;
}// 專為改名而打開文件,參數(shù)與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;
}// 關(guān)閉文件并改名
bool CFile::CloseAndRename()
{if (m_fp==0) return false;fclose(m_fp);  // 關(guān)閉文件指針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;
}// 調(diào)用fprintf向文件寫入數(shù)據(jù)
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);
}// 調(diào)用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是一行數(shù)據(jù)的結(jié)束標(biāo)志,如果為空,則以換行符"\n"為結(jié)束標(biāo)志。
// 與Fgets不同,本函數(shù)不刪除結(jié)束標(biāo)志
bool CFile::FFGETS(char *strBuffer,const int ReadSize,const char *strEndStr)
{return FGETS(m_fp,strBuffer,ReadSize,strEndStr);
}// 調(diào)用fread從文件中讀取數(shù)據(jù)。
size_t CFile::Fread(void *ptr, size_t size)
{if ( m_fp == 0 ) return -1;return fread(ptr,1,size,m_fp);
}// 調(diào)用fwrite向文件中寫數(shù)據(jù)
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是一行數(shù)據(jù)的結(jié)束標(biāo)志,如果為空,則以換行符"\n"為結(jié)束標(biāo)志。
// 本函數(shù)不會(huì)刪除行的結(jié)束標(biāo)志
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)
{// 清除所有的舊數(shù)據(jù)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);
}/*取操作系統(tǒng)的時(shí)間out_stime是輸出結(jié)果in_interval是偏移常量,單位是秒返回的格式由fmt決定,fmt目前的取值如下,如果需要,可以增加:yyyy-mm-dd hh24:mi:ss,此格式是缺省格式y(tǒng)yyymmddhh24missyyyy-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;  //日志文件一般不啟用緩沖區(qū),可以立即看到
}CLogFile::~CLogFile()
{Close();
}void CLogFile::Close()
{if (m_tracefp != 0){fclose(m_tracefp); m_tracefp=0;}
}// filename日志文件名
// openmode打開文件的方式,操作日志文件的權(quán)限,同打開文件函數(shù)(FOPEN)使用方法一致
// bBackup,true-備份,false-不備份,在多進(jìn)程的服務(wù)程序中,如果多個(gè)進(jìn)行共用一個(gè)日志文件,bBackup必須為false
// bEnBuffer:true-啟用緩沖區(qū),false-不啟用緩沖區(qū),如果啟用緩沖區(qū),那么寫進(jìn)日志文件中的內(nèi)容不會(huì)立即寫入文件是
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+這種方式,一打開任何時(shí)候文件指針都是在文件最后,這行代碼可省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語(yǔ)言缺省的標(biāo)準(zhǔn)輸出,strtime:輸出時(shí)間vfprintf(stdout,fmt,ap);  // ap:輸出內(nèi)容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;
}// 關(guān)閉全部的信號(hào)和輸入輸出
void CloseIOAndSignal()
{int ii=0;for (ii=0;ii<50;ii++){signal(ii,SIG_IGN); close(ii);}
}// 用某文件或目錄的全路徑中的目錄創(chuàng)建目錄,以級(jí)該目錄下的各級(jí)子目錄
bool MKDIR(const char *filename,bool bisfilename)
{// 檢查目錄是否存在,如果不存在,逐級(jí)創(chuàng)建子目錄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;
}// 調(diào)用fopen函數(shù)打開文件,如果文件名中包含的目錄不存在,就創(chuàng)建目錄
FILE *FOPEN(const char *filename,const char *mode)
{if (MKDIR(filename) == false) return NULL;return fopen(filename,mode);
}// 獲取文件的大小,返回字節(jié)數(shù)
int FileSize(const char *in_FullFileName)
{struct stat st_filestat;if (stat(in_FullFileName,&st_filestat) < 0) return -1;return st_filestat.st_size;
}// 更改文件的修改時(shí)間屬性
int UTime(const char *filename,const char *mtime)
{struct utimbuf stutimbuf;stutimbuf.actime=stutimbuf.modtime=UTCTime(mtime);return utime(filename,&stutimbuf);
}// 把字符串格式的時(shí)間轉(zhuǎn)換為time_t
// stime為輸入的時(shí)間,格式不限,但一定要包括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);
}// 從一個(gè)字符串中提取數(shù)字,bWithSign==true表示包括負(fù)號(hào),bWithDOT==true表示包括圓點(diǎn)
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不能初始化// 判斷字符串中的負(fù)號(hào)是否合法if ( (bWithSign==true) && (JudgeSignDOT(strtemp,"-") == false) ){strcpy(strDst,""); return;}// 判斷字符串中的正號(hào)是否合法if ( (bWithSign==true) && (JudgeSignDOT(strtemp,"+") == false) ){strcpy(strDst,""); return;}// 判斷字符串中的圓點(diǎn)是否合法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;
}// 判斷字符串中的負(fù)號(hào)和圓點(diǎn)是否合法
bool JudgeSignDOT(const char *strSrc,const char *strBZ)
{char *pos=0;pos=(char *)strstr(strSrc,strBZ);// 如果沒(méi)有包括待搜索的字符串,就返回trueif (pos == 0) return true;// 如果strlen(pos)==1,表示結(jié)果中只有符號(hào),沒(méi)有其它字符,返回falseif (strlen(pos)==1) return false;// 如果待搜索的字符串是+號(hào),就一定要是第一個(gè)字符if ( (strcmp(strBZ,"+") == 0) && (strncmp(strSrc,"+",1) != 0) ) return false;// 如果待搜索的字符串是-號(hào),就一定要是第一個(gè)字符if ( (strcmp(strBZ,"-") == 0) && (strncmp(strSrc,"-",1) != 0) ) return false;// 如果包括多個(gè)待搜索的字符串,就返回falseif (strstr(pos+1,strBZ) > 0) return false;return true;
}/*把一個(gè)字符串表格的時(shí)間加上一個(gè)偏移量,得到偏移后的時(shí)間in_stime是傳入的時(shí)間,任意格式,但是一定要包括yyyymmddhh24miss,是否有分隔符沒(méi)有關(guān)系。把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;
}// 獲取文件的時(shí)間,即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的函數(shù)
// in_XMLBuffer,XML格式的字符串
// in_FieldName,字段的標(biāo)簽名
// out_Value,獲取內(nèi)容存放的變量的指針
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;// 處理文件名匹配規(guī)則中的時(shí)間匹配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;// 把字符串都轉(zhuǎn)換成大寫后再來(lái)比較ToUpper(strFileName);ToUpper(strMatchStr);CmdStr.SplitToCmd(strMatchStr,",");for (ii=0;ii<CmdStr.CmdCount();ii++){// 如果為空,就一定要跳過(guò),否則就會(huì)被配上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));
}// 設(shè)置日期時(shí)間的格式,支持"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,待獲取文件名的匹配規(guī)則
// in_MaxCount,獲取文件的最大數(shù)量
// bAndChild,是否打開各級(jí)子目錄
// bSort,是否對(duì)結(jié)果時(shí)行排序
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();// 如果目錄不存在,就創(chuàng)建該目錄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;
}// 打開目錄,這是個(gè)遞歸函數(shù)
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();
}// 把字符串中的某字符串用另一個(gè)字符串代替
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);}
}// 刪除文件,如果刪除失敗,會(huì)嘗試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,如果改名失敗,會(huì)嘗試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);  // 指定服務(wù)端的通訊端口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平臺(tái)如下//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;// 如果長(zhǎng)度為0,就采用字符串的長(zhǎng)度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;
}// 把文件通過(guò)sockfd發(fā)送給對(duì)端
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);// 接收對(duì)端返回的確認(rèn)報(bào)文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;
}// 接收通過(guò)socdfd發(fā)送過(guò)來(lái)的文件
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可創(chuàng)建目錄{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);// 重置文件的時(shí)間UTime(strfilenametmp,stfileinfo->mtime);memset(strSendBuffer,0,sizeof(strSendBuffer));if (RENAME(strfilenametmp,stfileinfo->filename)==true) strcpy(strSendBuffer,"ok");else strcpy(strSendBuffer,"failed");// 向?qū)Χ朔祷仨憫?yīng)內(nèi)容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;
}// 把某一個(gè)文件復(fù)制到另一個(gè)文件
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);// 更改文件的修改時(shí)間屬性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));// 開始計(jì)時(shí)Start();
}// 開始計(jì)時(shí)
void CTimer::Start()
{gettimeofday( &m_start, NULL );
}// 計(jì)算已逝去的時(shí)間,單位:秒,小數(shù)點(diǎn)后面是微秒
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);// 重新開始計(jì)時(shí)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++標(biāo)準(zhǔn)模板庫(kù)的命名空間
using namespace std;// 以下是定義布爾數(shù)據(jù)類型的幾個(gè)宏,在有的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結(jié)構(gòu)體
#ifndef __OOCI_H
#define __OOCI_H// C/C++庫(kù)常用頭文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>// oracle數(shù)據(jù)庫(kù)接口庫(kù)頭文件,必須在裝有oracle數(shù)據(jù)庫(kù)主機(jī)運(yùn)行
#include <oci.h>// OCI登錄環(huán)境
struct LOGINENV
{char user[32];     // 數(shù)據(jù)庫(kù)的用戶名char pass[32];     // 數(shù)據(jù)庫(kù)的密碼char tnsname[51];  // 數(shù)據(jù)庫(kù)的tnsname,在ORACLE_HOME/network/admin/tnsnames.ora中配置OCIEnv *envhp; // 環(huán)境變量的句柄
};// OCI上下文
struct OCI_CXT
{OCISvcCtx  *svchp;OCIError   *errhp;OCIEnv     *envhp;   // 環(huán)境變量的句柄
};// 語(yǔ)句
struct OCI_HANDLE
{OCISvcCtx  *svchp;  // 服務(wù)器上下文的句柄引用context句柄OCIStmt    *smthp;OCIBind    *bindhp;OCIDefine  *defhp;OCIError   *errhp;  // 錯(cuò)誤句柄引用context句柄OCIEnv     *envhp; // 環(huán)境變量的句柄 引用context句柄
};//11111111111111111111.OCI執(zhí)行的結(jié)果
struct CDA_DEF
{int      rc;             // 執(zhí)行結(jié)果unsigned long rpc;       // 執(zhí)行SQL語(yǔ)句后,影響記錄的行數(shù)char     message[2048];  // 執(zhí)行SQL語(yǔ)句如果失敗,存放錯(cuò)誤信息
};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.數(shù)據(jù)庫(kù)連接池類
class connection
{
private:// 服務(wù)器環(huán)境句柄LOGINENV m_env;public:// 服務(wù)器上下文OCI_CXT m_cxt;// 連接狀態(tài),0-未連接,1-已連接int m_state;// 自動(dòng)提交標(biāo)志,0-關(guān)閉自動(dòng)提交;1-開啟自動(dòng)提交int m_autocommitopt; // 數(shù)據(jù)庫(kù)種類,固定取值為oraclechar m_dbtype[21];// 用于存放connection操作數(shù)據(jù)庫(kù)的錯(cuò)誤或最后一次執(zhí)行SQL語(yǔ)句的結(jié)果CDA_DEF m_cda;connection();~connection();// 設(shè)置字符集,如果客戶端的字符集與數(shù)據(jù)庫(kù)的不一致,就會(huì)出現(xiàn)亂碼。// 本方法要在connecttodb()之前調(diào)用。void character(char *charset);// 連接數(shù)據(jù)庫(kù),connstr的格式為:username/password@tnsname,autocommitopt:0-關(guān)閉自動(dòng)提交,1-啟用自動(dòng)提交int connecttodb(char *connstr,char *charset,int autocommitopt=0);// 從connstr中解析username,password,tnsnamevoid setdbopt(char *connstr);// 斷開與數(shù)據(jù)庫(kù)的連接int  disconnect();// 提交事務(wù)int  commit(); // 回滾事務(wù)int  rollback();// 獲取錯(cuò)誤信息void err_report();
};//1111111111111111113.SQL語(yǔ)言數(shù)據(jù)操作類
class sqlstatement
{
public:// 與數(shù)據(jù)庫(kù)連接池的狀態(tài),0-未綁定,1-已綁定int m_state;    // 語(yǔ)句句柄OCI_HANDLE m_handle;connection *m_conn;// SQL語(yǔ)句char m_sql[10240];// 執(zhí)行結(jié)果CDA_DEF m_cda;int m_sqltype;  // 待執(zhí)行的SQL語(yǔ)句的類型,0-查詢語(yǔ)句;1-非查詢語(yǔ)句// 自動(dòng)提交標(biāo)志,0-關(guān)閉自動(dòng)提交;1-開啟自動(dòng)提交int m_autocommitopt; sqlstatement();sqlstatement(connection *conn);~sqlstatement();// 設(shè)定數(shù)據(jù)庫(kù)連接池int  connect(connection *conn); // 斷開與數(shù)據(jù)庫(kù)連接池的連接int  disconnect();// 分析SQL語(yǔ)句,支持存儲(chǔ)過(guò)程,采用立刻分析的方式,即時(shí)返回分析結(jié)果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);// 執(zhí)行SQL語(yǔ)句int  execute();// 如果SQL語(yǔ)句不需要輸入和輸出變量,可以直接執(zhí)行。int  execute(const char *fmt,...);// 取下一條記錄,只有當(dāng)SQL語(yǔ)句是查詢語(yǔ)句時(shí)才有意義 int  next();// 錯(cuò)誤報(bào)告void err_report();// 指向LOB字段的指針,在執(zhí)行execute后,用bindblob或bindclob綁定,再用next獲取指針OCILobLocator *m_lob;// 初始化lob指針,在sqlstatement::connect()中調(diào)用int  alloclob();// 釋放lob指針,在sqlstatement::disconnect()中調(diào)用void freelob();// 綁定lob指針int  bindblob();int  bindclob();// 以下兩個(gè)函數(shù)都是把文件中的內(nèi)容寫入LOB字段,第二個(gè)函數(shù)是被第一個(gè)函數(shù)調(diào)用的int  filetolob(char *filename);int  filetolob(FILE *fp);// 把LOB字段中的內(nèi)容寫入文件int  lobtofile(char *filename);int  lobtofile(FILE *fp);
};
#endif 

5._ooci.cpp

// 如果是windows平臺(tái),要用以下行,否則會(huì)報(bào)以下錯(cuò)誤
// _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  函數(shù)執(zhí)行成功
OCI_SUCCESS_WITH_INFO      1  // maps to SQL_SUCCESS_WITH_INFO   執(zhí)行成功,但有診斷消息返回,// 可能是警告信息,但是,在測(cè)試的時(shí)候,我還從未見(jiàn)// 識(shí)到OCI_SUCCESS_WITH_INFO是怎么回事
OCI_RESERVED_FOR_INT_USE 200  // reserved 
OCI_NO_DATA              100  // maps to SQL_NO_DATA 函數(shù)執(zhí)行完成,但沒(méi)有其他數(shù)據(jù) 
OCI_ERROR                 -1  // maps to SQL_ERROR 函數(shù)執(zhí)行錯(cuò)誤 
OCI_INVALID_HANDLE        -2  // maps to SQL_INVALID_HANDLE 傳遞給函數(shù)的參數(shù)為無(wú)效句柄,// 或傳回的句柄無(wú)效 
OCI_NEED_DATA             99  // maps to SQL_NEED_DATA 需要應(yīng)用程序提供運(yùn)行時(shí)刻的數(shù)據(jù)
OCI_STILL_EXECUTING    -3123  // OCI would block error 服務(wù)環(huán)境建立在非阻塞模式,// OCI函數(shù)調(diào)用正在執(zhí)行中OCI_CONTINUE          -24200  // Continue with the body of the OCI function // 說(shuō)明回調(diào)函數(shù)需要OCI庫(kù)恢復(fù)其正常的處理操作 
OCI_ROWCBK_DONE       -24201  // done with user row callback 
*/int oci_init(LOGINENV *env)
{//初始化Oracle 環(huán)境變量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 環(huán)境變量oci_ret=OCIHandleFree(env->envhp,OCI_HTYPE_ENV);env->envhp = 0;return oci_ret;
}int oci_context_create(LOGINENV *env,OCI_CXT *cxt )
{// 創(chuàng)建數(shù)據(jù)庫(kù)連接的上下文對(duì)象,連接服務(wù)器,認(rèn)證并建立會(huì)話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)
{// 關(guān)閉數(shù)據(jù)庫(kù)連接的上下文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 )
{//創(chuàng)建語(yǔ)句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)
{// 關(guān)閉語(yǔ)句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);// 數(shù)據(jù)庫(kù)種類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); 
}// 設(shè)置字符集,如果客戶端的字符集與數(shù)據(jù)庫(kù)的不一致,就會(huì)出現(xiàn)亂碼。
void connection::character(char *charset)
{// UNIX平臺(tái)是采用setenv函數(shù)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)
{// 如果已連接上數(shù)據(jù)庫(kù),就不再連接// 所以,如果想重連數(shù)據(jù)庫(kù),必須顯示的調(diào)用disconnect()方法后才能重連if (m_state == 1) return 0;// 設(shè)置字符集character(charset);// 設(shè)置日期字段的輸出格式// UNIX平臺(tái)是采用setenv函數(shù)// 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));// 初始化環(huán)境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;}// 創(chuàng)建句柄,登錄數(shù)據(jù)庫(kù)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){// 如果獲取不到錯(cuò)誤信息,就返回正確的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)
{// 注意,一個(gè)sqlstatement在程序中只能連一個(gè)connection,不允許連多個(gè)connection// 所以,只要這個(gè)光標(biāo)已打開,就不允許再次打開,直接返回成功if ( m_state == 1 ) return 0;memset(&m_cda,0,sizeof(m_cda));m_conn=conn;// 如果數(shù)據(jù)庫(kù)連接類的指針為空,直接返回失敗if (m_conn == 0) {m_cda.rc=-1; strncpy(m_cda.message,"database not open.\n",128); return -1;}// 如果數(shù)據(jù)庫(kù)沒(méi)有連接好,直接返回失敗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;  // 光標(biāo)成功打開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方法中,當(dāng)SQL語(yǔ)句有錯(cuò)誤時(shí),OCIStmtPrepare返回的是0,不是失敗
// 所以,程序員在程序中一般不必處理prepare的結(jié)果
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;}// 判斷是否是查詢語(yǔ)句,如果是,把m_sqltype設(shè)為0,其它語(yǔ)句設(shè)為1。m_sqltype=1;// 從待執(zhí)行的SQL語(yǔ)句中截取15個(gè)字符,如果這15個(gè)字符中包括了“select”,就認(rèn)為是查詢語(yǔ)句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 ){// 發(fā)生了錯(cuò)誤或查詢沒(méi)有結(jié)果err_report(); return m_cda.rc;}// 如果不是查詢語(yǔ)句,就獲取影響記錄的行數(shù)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() 
{ // 注意,在該函數(shù)中,不可隨意用memset(&m_cda,0,sizeof(m_cda)),否則會(huì)清空m_cda.rpc的內(nèi)容if (m_state == 0) {m_cda.rc=-1; strncpy(m_cda.message,"cursor not open.\n",128); return -1;}// 如果語(yǔ)句未執(zhí)行成功,直接返回失敗。if (m_cda.rc != 0) return m_cda.rc;// 判斷是否是查詢語(yǔ)句,如果不是,直接返回錯(cuò)誤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(); // 只有當(dāng)m_cda.rc不是1405和1406的時(shí)候,才返回錯(cuò)誤,1405和1406不算錯(cuò)// 并且,返回錯(cuò)誤的時(shí)候,不要清空了m_cda.rpcif (m_cda.rc != 1405 && m_cda.rc != 1406) {// 如果返回代碼不是0,1403,1405,1406,就表是next出現(xiàn)了其它異常// 必須關(guān)閉數(shù)據(jù)庫(kù)連接,讓程序錯(cuò)誤退出。if ( (m_cda.rc!=0) && (m_cda.rc!=1403) && (m_cda.rc!=1405) && (m_cda.rc!=1406) ) m_conn->disconnect();return m_cda.rc;}}// 獲取結(jié)果集成功// 如果返回的是1405或1406,就把它強(qiáng)置為0if (m_cda.rc == 1405 || m_cda.rc == 1406) m_cda.rc=0;// 獲取影響記錄的行數(shù)據(jù)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()
{// 注意,在該函數(shù)中,不可隨意用memset(&m_cda,0,sizeof(m_cda)),否則會(huì)清空m_cda.rpc的內(nèi)容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){// 如果沒(méi)有獲取到任何錯(cuò)誤信息,就返回正確的// 這里可以用memset清空m_cda,因?yàn)槿绻麤](méi)有任何錯(cuò)誤m_cda.rpc在next中會(huì)重新賦值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內(nèi)容時(shí),緩沖區(qū)的大小,一般不需要修改。 */
#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字段中的內(nèi)容寫入文件
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);// 如果文件在生成的過(guò)程中發(fā)生了錯(cuò)誤,就刪除該文件,因?yàn)樗且粋€(gè)不完整的文件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;
}
http://m.risenshineclean.com/news/59613.html

相關(guān)文章:

  • iis 建網(wǎng)站手機(jī)訪問(wèn)百度網(wǎng)站介紹
  • 做美容美發(fā)學(xué)校網(wǎng)站公司優(yōu)化水平
  • 天津視頻網(wǎng)站開發(fā)團(tuán)隊(duì)今日新聞國(guó)內(nèi)大事件
  • 個(gè)人網(wǎng)站可以做商業(yè)用途嗎愛(ài)站網(wǎng)怎么用
  • 石碣鎮(zhèn)網(wǎng)站仿做企業(yè)查詢免費(fèi)
  • 做音樂(lè)網(wǎng)站的目的和意義怎么推廣公眾號(hào)讓人關(guān)注
  • 17網(wǎng)站一起做網(wǎng)店可靠嗎百度指數(shù)的各項(xiàng)功能
  • 動(dòng)態(tài)網(wǎng)站開發(fā)的集成軟件有哪些培訓(xùn)學(xué)校資質(zhì)辦理?xiàng)l件
  • wordpress 獲取js路徑巢湖seo推廣
  • 學(xué)院網(wǎng)站整改及建設(shè)情況報(bào)告web網(wǎng)頁(yè)制作成品
  • 移動(dòng)端手機(jī)網(wǎng)站制作外國(guó)網(wǎng)站怎么進(jìn)入
  • 后臺(tái)管理網(wǎng)站建設(shè)谷歌推廣app
  • 專業(yè)網(wǎng)站建設(shè)系統(tǒng)淘大象關(guān)鍵詞排名查詢
  • 網(wǎng)站制作三站全網(wǎng)營(yíng)銷推廣方案
  • 手機(jī)網(wǎng)站后臺(tái)語(yǔ)言南寧seo優(yōu)化公司
  • 做網(wǎng)站平臺(tái)公司一諾網(wǎng)絡(luò)推廣公司
  • iis 網(wǎng)站 優(yōu)化免費(fèi)域名注冊(cè)官網(wǎng)
  • 網(wǎng)站未做安全隱患檢測(cè)怎么拿shellseo的培訓(xùn)課程
  • 江蘇官網(wǎng)建設(shè)公司代碼優(yōu)化
  • 有沒(méi)有做鏈接的網(wǎng)站企業(yè)網(wǎng)站多少錢一年
  • 網(wǎng)絡(luò)培訓(xùn)研修總結(jié)北京seo優(yōu)化排名推廣
  • 南寧建站價(jià)格近期網(wǎng)絡(luò)輿情事件熱點(diǎn)分析
  • 企業(yè)網(wǎng)站開發(fā)報(bào)價(jià)形式英文seo外鏈發(fā)布工具
  • 網(wǎng)站建設(shè)百度推廣防疫管控優(yōu)化措施
  • 南昌做網(wǎng)站哪家好唐山百度提升優(yōu)化
  • 建站什么程序好seo排名的公司
  • 數(shù)據(jù)庫(kù)做網(wǎng)站看今天的新聞
  • 網(wǎng)站建設(shè)需要數(shù)據(jù)庫(kù)嗎國(guó)內(nèi)企業(yè)網(wǎng)站模板
  • 網(wǎng)站關(guān)鍵詞推廣優(yōu)化如何找客戶資源
  • 滬上裝修排名前十有哪些品牌優(yōu)化問(wèn)題