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

Posted 码农编程录

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C/C++14天气APP:文件传输系统(tcpput/getfile.cpp客户端,tcpfileserver.cpp)相关的知识,希望对你有一定的参考价值。


1.TCP粘包/超时:全双工,拆粘包

ftp协议进行文件传输性能不够【FTP协议是TCP/IP协议的一部分,严格说是应用层协议,TCP通信两大瓶颈:带宽交互次数过多(获取对方服务器时间,文件列表,改名等)】。windows平台ftp安装服务端麻烦,不同ftp服务器在使用时略有区别,兼容性不好【比如ftp.list里*号圆点都可以,有的不行】。系统内部不用ftp,外部用ftp(因为不能把自己程序部署到别人服务器上,别人服务器上也只能装个ftp服务端)。

如下比如B站作者将视频文件发到视频上传服务器进行审核,审核完后通过文件传输系统发给视频播放服务器【一个播放服务器是不可能响应那么多播放请求的,肯定是服务器集群】。

为什么要传文件而不是tcp报文(有些数据就是文件方式组织的,比如视频),因为传文件更快。如下拆包和粘包:

如下解决粘包拆包:头/定长/分隔符。


如下是第一种方法,粘包还是会出现,但可以区分开。

如下是第三种方法,自定义分隔符。


如下在_public.h客户端。


如下在_public.h服务端,InitServer函数中没指定ip,说明哪任何ip都能连。


如下是_public.h定义的几个方法,TcpRead方法最后调用recv函数,recv会阻塞一直等待,实际开发不会等,加上一个超时机制:比如一分钟还没给我发信息的话,我会断开你释放资源进程退出。ReadnWriten是对recv和send扩展。

如下在_public.h定义解决粘包问题,自定义TCP报文格式。



select不是sql语句是系统调用。select(非阻塞)原本用于I/O复用,现用于超时机制。客户端连上来后启动一个进程消耗资源,所以服务端在客户端某原因断开时主动将客户端进程退出释放资源。

如下CTcpClient::Read()没用超时机制(超时不管在客户端还是服务端都用得着)。

// 本程序演示采用CTcpClient类,实现socket通讯的客户端,demo11.cpp
#include "_public.h"
int main(int argc,char *argv[])

  if (argc != 3)
  
    printf("\\n");
    printf("Using:./demo11 ip port\\n\\n");
    printf("Example:./demo11 118.89.50.198 5010\\n\\n");
    printf("本程序演示采用CTcpClient类,实现socket通讯的客户端。\\n\\n");
    return -1;
  
  CTcpClient TcpClient;

//111111111111111111111111111111111111111111111111111111111.向服务器发起连接
  if (TcpClient.ConnectToServer(argv[1],atoi(argv[2])) == false)
  
    printf("TcpClient.ConnectToServer(%s,%d) failed.\\n",argv[1],atoi(argv[2])); return -1;
  

  char strRecvBuffer[1024],strSendBuffer[1024];
  memset(strSendBuffer,0,sizeof(strSendBuffer));
  strcpy(strSendBuffer,\\
        "英超最后一轮,卡里克踢完了在曼联的最后一场正式比赛,这意味着红魔上次称霸欧冠的黄金一代全部退场。");

//11111111111111111111111111111111111111111111111111112.把strSendBuffer内容发送给服务端
  if (TcpClient.Write(strSendBuffer)==false)
  
    printf("TcpClient.Write() failed.\\n"); return -1;
  
  printf("send ok:%s\\n",strSendBuffer);
  memset(strRecvBuffer,0,sizeof(strRecvBuffer));

//1111111111111111111111111111111111111111111111111113.接收服务端返回的报文(下面Read没有超时机制)
  if (TcpClient.Read(strRecvBuffer)==false)
  
    if (TcpClient.m_btimeout==true) printf("timeout\\n");
    printf("TcpClient.Read() failed.\\n"); return -1;
  
  printf("recv ok:%s\\n",strRecvBuffer);
  return 0;

// 本程序演示采用CTcpServer类,实现socket通讯的服务端,demo12.cpp
#include "_public.h"
int main(int argc,char *argv[])

  if (argc != 2)
  
    printf("\\n");
    printf("Using:./demo12 port\\n\\n");
    printf("Example:./demo12 5010\\n\\n");
    printf("本程序演示采用CTcpServer类,实现socket通讯的服务端。\\n\\n");
    return -1;
  
  CTcpServer TcpServer;
  
//1111111111111111111111111111111111111111111111111111.服务端初始化
  if (TcpServer.InitServer(atoi(argv[1])) == FALSE)
  
    printf("TcpServer.InitServer(%s) failed.\\n",argv[1]); return -1;
  

//1111111111111111111111111111111111111111111111111112.等待客户端的连接
  if (TcpServer.Accept() == FALSE)
  
    printf("TcpServer.Accept() failed.\\n"); return -1;
  
  char strRecvBuffer[1024],strSendBuffer[1024];
  memset(strRecvBuffer,0,sizeof(strRecvBuffer));

//111111111111111111111111111111111111111111111111113.读取客户端的报文,等时间是20秒,有超时机制
  if (TcpServer.Read(strRecvBuffer,20)==FALSE) 
  
    printf("TcpServer.Read() failed.\\n"); return -1;
  
  printf("recv ok:%s\\n",strRecvBuffer);
  memset(strSendBuffer,0,sizeof(strSendBuffer));
  strcpy(strSendBuffer,"ok");

//111111111111111111111111111111111111111111111111114.向客户端返回响应内容
  if (TcpServer.Write(strSendBuffer)==FALSE) 
  
    printf("TcpServer.Write() failed.\\n"); return -1;
  
  printf("send ok:%s\\n",strSendBuffer);
  return 0;




下面添加睡眠即改为超时机制。

下面利用m_btimeout成员(超时为true)打印出是否超时。


服务端分不清客户端是网络原因还是程序自身出问题断开,只知道TCP通道异常。

下面是GetIP()。

2.简单文件传输:CTcpClient,CTcpServer

// 本程序演示采用CTcpClient类,实现socket通讯的客户端和文件传输,demo13.cpp
#include "_public.h"
bool SendFile(int sockfd,char *filename,int filesize); //把文件的内容发送给服务端
int main(int argc,char *argv[])

  if (argc != 4)
  
    printf("\\n");
    printf("Using:./demo13 ip port filename\\n\\n");
    printf("Example:./demo13 118.89.50.198 5010 test1.jpg\\n\\n");
    printf("本程序演示采用CTcpClient类,实现socket通讯的客户端和文件传输。\\n\\n");
    return -1;
   
  
  if (access(argv[3],R_OK) != 0)  //判断文件是否存
  
    printf("file %s not exist.\\n",argv[3]); return -1;
  
  int uFileSize=0;
  char strMTime[20],strRecvBuffer[1024],strSendBuffer[1024];
  
  memset(strMTime,0,sizeof(strMTime)); //获取文件的时间和大小
  FileMTime(argv[3],strMTime);  
  uFileSize=FileSize(argv[3]); //获取文件的大小

  // 把文件的信息封装成一个xml报文,发送给服务端
  memset(strSendBuffer,0,sizeof(strSendBuffer));
  snprintf(strSendBuffer,100,"<filename>%s</filename><mtime>%s</mtime><size>%lu</size>",argv[3],strMTime,uFileSize);
  CTcpClient TcpClient;
  
//1111111111111111111111111111111111111111111111111111.向服务器发起连接
  if (TcpClient.ConnectToServer(argv[1],atoi(argv[2])) == false)
  
    printf("TcpClient.ConnectToServer(%s,%d) failed.\\n",argv[1],atoi(argv[2])); return -1;
  

//1111111111111111111111111111111111111111112.把文件信息的xml发送给服务端,并没有接收服务端回应,没必要,减少tcp交互次数
  if (TcpClient.Write(strSendBuffer)==false)
  
    printf("TcpClient.Write() failed.\\n"); return -1;
  
  printf("send xml:%s\\n",strSendBuffer);
  printf("send file ...");

//111111111111111111111111111111111111111111111111111113.把文件的内容发送给服务端
  if (SendFile(TcpClient.m_sockfd,argv[3],uFileSize)==false)
  
    printf("SendFile(%s) failed.\\n",argv[3]); return -1;
    
  memset(strRecvBuffer,0,sizeof(strRecvBuffer));
  
//1111111111111111111111111111111111111111111111111114.接收服务端返回的回应报文
  if (TcpClient.Read(strRecvBuffer)==false)
  
    printf("TcpClient.Read() failed.\\n"); return -1;
  
  if (strcmp(strRecvBuffer,"ok")==0)
    printf("ok.\\n");
  else
    printf("failed.\\n");
  return 0;


//111111111111111111111111111111111111111111111111113.把文件的内容发送给服务端
bool SendFile(int sockfd,char *filename,int filesize)

  int  bytes=0;
  int  total_bytes=0;
  int  onread=0;
  char buffer[1000];
  FILE *fp=NULL;
  if ( (fp=fopen(filename,"rb")) == NULL ) 
  
    printf("fopen(%s) failed.\\n",filename); return false;
  
  while (true)
  
    memset(buffer,0,sizeof(buffer));

    if ((filesize-total_bytes) > 1000) onread=1000; //一次读1000个字节
    else onread=filesize-total_bytes;
    bytes=fread(buffer,1,onread,fp); 
    if (bytes > 0)
    
      if (Writen(sockfd,buffer,bytes) == false)
      
        printf("Writen() failed.\\n"); fclose(fp); fp=NULL; return false;
      
    
    total_bytes = total_bytes + bytes;
    if ((int)total_bytes == filesize) break;
  
  fclose(fp);
  return true;

// 本程序演示采用CTcpServer类,实现socket通讯的服务端和文件传输,demo14.cpp
#include "_public.h"
bool RecvFile(char *strRecvBuffer,int sockfd,char *strfilename); //接收文件的内容
int main(int argc,char *argv[])

  if (argc != 3)
  
    printf("\\n");
    printf("Using:./demo14 port filename\\n\\n");
    printf("Example:./demo14 5010 test2.jpg\\n\\n"); //test2.jpg重新命名
    printf("本程序演示采用CTcpServer类,实现socket通讯的服务端和文件传输。\\n\\n");
    return -1;
  

  CTcpServer TcpServer;
//1111111111111111111111111111111111111111111111111111.服务端初始化
  if (TcpServer.InitServer(atoi(argv[1])) == false)
  
    printf("TcpServer.InitServer(%s) failed.\\n",argv[1]); return -1;
  

//1111111111111111111111111111111111111111111111111112.等待客户端的连接
  if (TcpServer.Accept() == false)
  
    printf("TcpServer.Accept() failed.\\n"); return -1;
  
  
//11111111111111111111111111111111111111111113.读取客户端的报文,等时间是20秒
  char strRecvBuffer[1024],strSendBuffer[1024];
  memset(strRecvBuffer,0,sizeof(strRecvBuffer));
  if (TcpServer.Read(strRecvBuffer,20)==false) 
  
    printf("TcpServer.Read() failed.\\n"); return -1;
  
  printf("recv:%s\\n",strRecvBuffer);
  printf("recv file ...");

//111111111111111111111111111111111111111111114.接收文件的内容
  memset(strSendBuffer,0,sizeof(strSendBuffer));
  if (RecvFile(strRecvBuffer,TcpServer.m_connfd,argv[2])==true)
  
    strcpy(strSendBuffer,"ok");
    printf("ok.\\n");
  
  else
  
    strcpy(strSendBuffer,"failed");
    printf("failed.\\n");
  

//1111111111111111111111111111111111111111111111111115.接收ok后,向客户端返回响应内容
  if (TcpServer.Write(strSendBuffer)==false) 
  
    printf("TcpServer.Write() failed.\\n"); return -1;
  
  printf("send:%s\\n",strSendBuffer);
  return 0;


//1111111111111111111111111111111111111111111111111114.接收文件的内容
bool RecvFile(char *strRecvBuffer,int sockfd,char *strfilename)

  int  ufilesize=0;
  char strmtime[20]; 
  memset(strmtime,0,sizeof(strmtime));
  // 获取待接收的文件的时间和大小
  GetXMLBuffer(strRecvBuffer,"mtime",strmtime);
  GetXMLBuffer(strRecvBuffer,"size",&ufilesize);
  
  FILE *fp=NULL;
  if ( (fp=fopen(strfilename,"wb")) ==NULL)
  
    printf("create %s failed.\\n",strfilename); return false;
  

  int  total_bytes=0;
  int  onread=0;
  char buffer[1000];
  while (true)
  
    memset(buffer,0,sizeof(buffer));
    if ((ufilesize-total_bytes) > 1000) onread=1000; //根据文件大小知道文件接下来读取多少内容
    else onread=ufilesize-total_bytes;

    if (Readn(sockfd,buffer,onread) == false)
    
      printf("Readn() failed.\\n"); fclose(fp); fp=NULL; return false;
    
    
    fwrite(bu

以上是关于C/C++14天气APP:文件传输系统(tcpput/getfile.cpp客户端,tcpfileserver.cpp)的主要内容,如果未能解决你的问题,请参考以下文章

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

C/C++8天气APP:Oracle数据库安装,表操作,C语言操作Oracle数据库

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

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

C/C++13天气APP:数据挖掘/HTTP协议/非结构化数据存储(filetoblob.cpp),数据管理/监控告警(hsmtable.cpp,tbspaceinfo.cpp)

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