C/C++15天气APP:服务端(client.cpp,shtqappserver.cpp)

Posted 码农编程录

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++15天气APP:服务端(client.cpp,shtqappserver.cpp)相关的知识,希望对你有一定的参考价值。


1.APP需求和架构:图片用linux下工具imagemagick用system函数调用,多进程,nextval

多进程用信号灯加锁麻烦,一般用多线程并加锁。数据库连接也是socket服务端,不可能让多个进程同时写如事务。下面是多个线程可共用一个socket连接(协调单个发),但不能说多个线程共享一个socket连接(一起发),一个socket连接同一时间只能有一个线程使用,用完后提交事务,连接才释放出来。

如下开始APP服务端设计,客户端就是手机app软件。第一次客户端将手机编号传给服务端,服务端将站点信息传给客户端。

短连接:客户端即用户点击按钮一次建立一次socket连接请求,处理完一个就断开。响应慢:建立一次socket连接费时间,服务端fork一个进程也要时间,之后和数据库连接也要时间。

长连接:客户端与服务端socket一直连接着进行数据通信,没有数据通信时用心跳(之前文件传输都用的是长连接),用户关了app,连接才断开。费服务端资源:长连接连上后,数据库连接和进程都已准备好,一直通信完才断开。响应快:用户看到数据越快越好控制在1秒内。如下项目组织(shtqapp是一个独立的项目)。

如下第一行是上面创建用户sql,pdm文件是数据结构设计。

如下不需收集用户密码,T_USERINFO表T_OBTCODE表放入shtqapp数据库用户里,T_USERLOG表放入shappdata数据库用户里。


如下preview的sql语句是建表。

以下是多进程,正式上线时用http80端口(用户在某个网络内部80端口肯定开放,不开放外网上不了,qq微信虽不是用http协议但也采用80端口)。

// client.cpp,模拟tcp手机客户端,客户端用短链接还是长连接由客户端自己安排
#include "_freecplus.h"
CTcpClient TcpClient;
char strSendBuffer[301],strRecvBuffer[301];
bool biz10000();  // 心跳
bool biz10001();  // 新用户登录:只传个设备编号id,服务端把城市站点信息传给客户端,手机利用定位匹配

int main(int argc,char *argv[])

  //if (TcpClient.ConnectToServer("127.0.0.1",5015)==false)  printf("conn failed.\\n"); return -1; 
  if (TcpClient.ConnectToServer("172.16.0.15",5015)==false)  printf("conn failed.\\n"); return -1; 
  //if (TcpClient.ConnectToServer("118.89.50.198",5015)==false)  printf("conn failed.\\n"); return -1; 
  if (biz10000()==false) return 0;   // 心跳
  CTimer Timer;
  if (biz10001()==false) return 0;   // 新用户登录 
  printf("biz10001=%lf\\n",Timer.Elapsed());
  sleep(1);  
  return 0;


bool biz10000()

  memset(strSendBuffer,0,sizeof(strSendBuffer));
  memset(strRecvBuffer,0,sizeof(strRecvBuffer));
  strcpy(strSendBuffer,"<bizid>10000</bizid>");
  //printf("send=%s=\\n",strSendBuffer);
  if (TcpClient.Write(strSendBuffer)==false)  printf("send failed.\\n"); return false; 
  if (TcpClient.Read(strRecvBuffer,20)==false)   printf("recv failed.\\n"); return false; 
  //printf("recv=%s=\\n",strRecvBuffer);
  return true;


bool biz10001()

  memset(strSendBuffer,0,sizeof(strSendBuffer)); 
  memset(strRecvBuffer,0,sizeof(strRecvBuffer));
  // 如下请求报文
  strcpy(strSendBuffer,"<bizid>10001</bizid><userid>52:54:00:83:0f:c1</userid><ttytype>1</ttytype><lat>20.234518</lat><lon>115.90832</lon><height>150.5</height>");
  //printf("send=%s=\\n",strSendBuffer);
  if (TcpClient.Write(strSendBuffer)==false)  printf("send failed.\\n"); return false; 
  //如下用一个循环接收全部的站点信息
  while (1)
  
    memset(strRecvBuffer,0,sizeof(strRecvBuffer));
    if (TcpClient.Read(strRecvBuffer,20)==false)   printf("recv failed.\\n"); return false; 
    // printf("recv=%s=\\n",strRecvBuffer); //手机端没数据库,手机软件真正处理方法把数据保存到xml文件里
    if (strcmp(strRecvBuffer,"ok")==0) break; //接收到ok的话表示数据处理完了
  
  return true;

// 上海天气APP软件服务端主程序shtqappserver.cpp多进程。
#include "_public.h"
#include "_ooci.h"
struct st_biz  // 业务请求

  int  bizid;               // 业务代码
  char userid[51];          // 设备ID
  int  ttytype;             // 用户的设备类型,0-未知;1-ios;2-Andriod,2-鸿蒙。
  int  usertype;            // 用户分类,0-未知;1-普通用户;2-气象志愿者;3-内部用户。
  double lon;
  double lat;
  double height;
  char   obtid[11];
  char   xmlbuffer[1001];
 stbiz;
void xmltobiz(char *strxmlbuffer);  // 把xml解析到参数stbiz结构中
CTcpServer TcpServer;
CLogFile   logfile;
connection conn;
//如上定义为全局变量共享但不会在main函数主进程里连数据库,在子进程里连。如果在主进程连,子进程用是不行的
//conn若定义为局部变量,ChldEXIT()中conn不会调用析构函数,全局会调用释放资源。
//CTcpServer析构函数会自动调用CloseClient()方法关闭socket连接
char strRecvBuffer[TCPBUFLEN+10]; // 接收报文的缓冲区
char strSendBuffer[TCPBUFLEN+10]; // 发送报文的缓冲区
void FathEXIT(int sig); // 程序退出时调用的函数
void ChldEXIT(int sig);
void ChldMain(); // 业务处理进程主函数
bool biz10000(); // 心跳业务
bool biz10001(); // 新用户登录业务,用户基本信息表和用户使用日志都插入一条记录,select全国气象站点数据返回给客户端
bool biz10002(); // 获取天气实况
bool InsertUSERLOG(); // 插入用户请求日志表

int main(int argc,char *argv[])

  if (argc != 3)
  
    printf("\\n");
    printf("Using:/htidc/shtqapp/bin/shtqappserver logfilename port\\n");

    printf("Example:/htidc/shtqapp/bin/shtqappserver /log/shtqapp/shtqappserver.log 5015\\n\\n");
    printf("本程序是上海天气APP软件的服务端。\\n");
    printf("logfilename 日志文件名。\\n");
    printf("port 用于传输文件的TCP端口。\\n");
    return -1;
  
  CloseIOAndSignal(); signal(SIGINT,FathEXIT); signal(SIGTERM,FathEXIT);  
  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);
  
  
  while (true)
      
    if (TcpServer.Accept() == false) // 等待客户端的连接
    
      logfile.Write("TcpServer.Accept() failed.\\n"); continue;
    
    
    if (fork() > 0)  // 新的客户端连接来fork一个进程
         
      TcpServer.CloseClient(); continue;  //父进程关闭刚建立起来的sock连接,并回到Accept继续监听
        
    
    signal(SIGINT,ChldEXIT); signal(SIGTERM,ChldEXIT); //进入子进程的流程    
    TcpServer.CloseListen(); //子进程需要关掉监听的sock    
    ChldMain(); //子进程业务处理进程主函数
    ChldEXIT(0);
  
  return 0;


// 父进程退出时调用的函数
void FathEXIT(int sig)

  if (sig > 0) //如果收到信号就屏蔽信号
  
    signal(sig,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);
    logfile.Write("catching the signal(%d).\\n",sig);
  
  kill(0,15); //通知全部子进程退出
  logfile.Write("shtqappserver EXIT.\\n");
  exit(0);


// 子进程退出时调用的函数
void ChldEXIT(int sig)

  if (sig > 0)  //如果不屏蔽的话会收到两个退出信号
  
    signal(sig,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);
  
  exit(0);


//11111111111111111111111111111111111111111111111111业务处理进程主函数
void ChldMain()

  while (true)
  
    memset(strRecvBuffer,0,sizeof(strRecvBuffer));
    memset(strSendBuffer,0,sizeof(strSendBuffer));
    // 接收客户端的业务请求报文,如果返回false,认为是客户端退出或网络原因,直接return不写错误日志
    if (TcpServer.Read(strRecvBuffer,50) == false)
    
	  // 比如用户手机app回到桌面,程序挂起不需要app体面退出,Read失败很多不用写日志
      // logfile.Write("TcpServer.Read() failed.\\n"); 
      return;
    
    logfile.Write("strRecvBuffer=%s\\n",strRecvBuffer);  // xxxxxx     
    // 把参数解析出来,客户端与服务端通信全用xml,每种业务请求弄一个业务编号
    xmltobiz(strRecvBuffer);
    if (stbiz.bizid==10000)    // 心跳报文
    
      if (biz10000()==true) continue;
      else return;
    
    CTimer Timer;
    // 连接数据库,第三个参数为true自动提交
    if (conn.connecttodb("shtqapp/pwdidc@snorcl11g_198","Simplified Chinese_China.ZHS16GBK",true)!=0)
    
      logfile.Write("conn.connettodb() failed.\\n"); return;
    
    logfile.Write("conn=%lf\\n",Timer.Elapsed());
    // 新用户登录 
    if (stbiz.bizid==10001)    
    
      if (biz10001()==true) continue;
      else return;
    
    // 获取天气实况
    if (stbiz.bizid==10002)    
    
      if (biz10002()==true) continue;
      else return;
    
    // 协议:你发什么给我,我发什么给你。报文格式不符合要求的话直接return
    logfile.Write("非法报文%s\\n",strRecvBuffer); return;
  


//11111111111111111111把xml解析到参数starg结构中,客户端全部请求放入starg数据结构里
void xmltobiz(char *strxmlbuffer)

  memset(&stbiz,0,sizeof(struct st_biz));
  // 客户端发给服务端全部报文都会有个业务代码
  GetXMLBuffer(strxmlbuffer,"bizid",&stbiz.bizid);
  // logfile.Write("bizid=%d\\n",stbiz.bizid);
  // 用户设备ID
  GetXMLBuffer(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;


//111111111111111111111111111111111111111111心跳业务
bool biz10000()

  memset(strSendBuffer,0,sizeof(strSendBuffer));
  strcpy(strSendBuffer,"ok");
  if (TcpServer.Write(strSendBuffer) == false)
  
    logfile.Write("biz10000 TcpServer.Write() failed.\\n"); return false;
  
  return true;


//1111111111111111111111111111111111111111111111新用户登录
bool biz10001()

CTimer Timer;
//1111111111111111 插入用户基本信息表T_USERINFO:设备ID和设备类型
  sqlstatement 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); return false;
    
  
  logfile.Write("insert T_USERINFO =%lf\\n",Timer.Elapsed());
  
//11111111111111111111111111111111插入用户请求日志表
  if (InsertUSERLOG()==false) return false; // 是模块通用功能,所以写成函数
  logfile.Write("insert T_USERLOG =%lf\\n",Timer.Elapsed());
  
//1111111111111111111111111111111拿出全国站点参数信息返回给客户端
  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); 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;
    //获得一条记录后生成一个buffer写过去
    sprintf(strSendBuffer,"<obtid>%s</obtid><obtname>%s</obtname><lon>%s</lon><lat>%s</lat><endl/>",strobtid,strobtname,strlon,strlat);

    if (TcpServer.Write(strSendBuffer) == false)
    
      logfile.Write("biz10001 TcpServer.Write() failed.\\n"); return false;
    
  
  logfile.Write("select =%lf\\n",Timer.Elapsed());
  // 直到全部记录被处理完,最后发送一个ok
  strcpy(strSendBuffer,"ok"

以上是关于C/C++15天气APP:服务端(client.cpp,shtqappserver.cpp)的主要内容,如果未能解决你的问题,请参考以下文章

C/C++7天气APP:生成观测数据txt/xml文件(crtsurfdata.cpp),ftp协议及ftp采集模块(_ftp.h,_ftp.cpp,ftpgetfiles.cpp)

C/C++12天气APP:不同数据建表入表,数据交换(exptables.cpp,ftpputfiles.cpp)

C/C++10天气APP:MySQL,PostgreSQL,环境变量,动静态库,Linux/Oracle字符集

C/C++9天气APP:Oracle的虚表/日期/序列,索引/视图/链路/同义词,数据库高可用性

C/C++14天气APP:文件传输系统(tcpput/getfile.cpp客户端,tcpfileserver.cpp)

C/C++11天气APP:txt/xml文件处理入库(psurfdata.cpp,_shqx.h),数据结构设计(PowerDesigner)