⚡通信管理模块⚡

Posted 努力学习的少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了⚡通信管理模块⚡相关的知识,希望对你有一定的参考价值。

目录

🌳.通信管理模块的介绍

🌳.通信模块的设计

🌳.ListShow函数

🌳.UpLoad函数

🌳.DownLoad函数

下载的断点续传


🌳.通信管理模块的介绍

通信管理模块管理着服务器与浏览器之间的通信:

  • 当服务器收到浏览器的 get /listshow/ http/1.1请求时,服务器就会給浏览器响应一个文件展示页面,用户可以再这个页面上传文件或下载文件。
  • 当服务器收到浏览器的 个 get /download/文件名 http1.1请求时,服务器就会到backdir目录下或
  • packdir目录下查找该文件,如果找到了,就将该文件发送給浏览器。
  • 当服务器收到浏览器的 个post /upload/ http1.1请求时,则浏览器将文件上传到服务器上,文件名和文件数据存储在http响应中,服务器则将该文件放入backdir目录下。

🌳.通信模块的设计

将通信模块设计为单例模式,保证系统中只有一个通信模块对象.

服务器在运行的时候,先注册各个请求与对应函数的关心

   class Service
      private:
        std::string ip;
        int port;
        httplib::Server server;
        static std::mutex lock;
        static Service* instant;
      private:
        Service()
          ip=sjp::Config::GetInstant()->GetIp();
          port=sjp::Config::GetInstant()->GetServerPort();
        
  
        Service(Service&)=delete;
        static std::string mtimeToString(time_t mtime)
          std::string res= ctime(&mtime);
          return res;
        
      public:
        void Run()
          server.Post("/upload",UpLoad); 
          server.Get("/download/(.*)",DownLoad);
          server.Get("/listshow",ListShow);                                                                                                                                                 
          server.Get("/",ListShow);
          server.listen("0.0.0.0",port);//启动服务器
        

      static Service* GetInstant()
          if(instant==nullptr)
            lock.lock();
            if(instant==nullptr)
              instant=new Service();
            
            lock.unlock();
          
          return instant;
        
    ;
  
    std::mutex Service::lock;
    Service* Service::instant=nullptr;
                                                                

🌳.ListShow函数

  listshow页面的前端源代码如下:

  •  我们需要使用stringstream对象将前端页面的源代码组织起来,然后将在将stringstream组织在http响应中,httplib服务器在底层会将http响应发送給浏览器。
  • 为了保证浏览器能够将前端代码翻译成一个页面,需要在httplib响应中的Content-type字段设置为text/html.
static void ListShow(const httplib::Request& req,httplib::Response& rep)    
          cout<<"listshow"<<endl;    
          std::vector<sjp::FileInfo> arry;    
          sjp::FileInfoManger::GetInstant()->GetAllInfo(arry);    
          std::stringstream ss;    
      
          //    
          ss<<"<!DOCTYPE html>"<<"<html><meta charset=\\"UTF-8\\"><head><title>Download</title></head><body><h1>Download</h1><table>";      
                        ss<<"<tr><td><h3>文件名</h3></a></td>";        
          ss<<"<td align=\\"left\\">"<<"<h3>最近修改时间</h3>"<<"</td>";                                                 
          ss<<"<td align=\\"right\\">"<<"<h3>文件大小</h3>"<<"</td></tr>";     
W>        for(int i=0;i<arry.size();i++)    
            //>    
            /*<td><a href="/download/test.txt">test.txt</a></td>    
              <td align="right">2021-12-29 10:10:10</td>    
              <td align="right">28k</td>    
             * */    
            std::string path=arry[i].back_path;    
            sjp::FileUtil fu(path);    
            std::string atimestr=mtimeToString(arry[i].modify_time);    
            ss<<"<tr><td><a href=\\"/download/"<<fu.GetFilename()<<"\\">"<<fu.GetFilename()<<"</a></td>";    
            ss<<"<td align=\\"right\\">"<<atimestr<<"    </td>";    
            ss<<"<td align=\\"right\\">"<<arry[i].file_size/1024<<"k"<<"</td></tr>";                                                                                                          
              
          ss<<"</table></body></html>";
          ss<<"<form action=\\"http://119.23.41.13:8081/upload\\" method=\\"post\\" enctype=\\"multipart/form-data\\"><div><input type=\\"file\\" name=\\"file\\"></div><div><input type=\\"submit\\" va  lue=\\"上传\\"></div></form>";
          rep.body=ss.str();
          rep.status=200;
          rep.set_header("Content-Type","text/html;");
        
  

🌳.UpLoad函数

post /upload/ http1.1请求如下

上传文件的请求中请求正文包含文件的字段名和文件名,文件正文(文件包含的数据)。

实现:

  • 服务器收到上传请求后,需要先判断是否有文件字段名,如果有则提取该文件的信息。
  • 然后使用FileUtil文件工具打开一个文件存放在backdir下,并将文件正文写入到该文件中,并新插入一个文件信息到文件管理系统中_table表中,则完成服务器上传文件的请求。
  • 重新返回一个文件页面。
        static void UpLoad(const httplib::Request& req,httplib::Response& rep)
        
          /* 1.判断是否有文件上传
           * 2.如果有,则获取该文件的名称和内容
           * 3.在backdir中创建一个文件
           * 4.将文件信息存储到FileInfoManger中
           * */
          //判断是否有文件上传
          std::cout<<"upload begin"<<endl;
            auto res=req.has_file("file");
            if(res==false)
              rep.status=400;                                                                                                                                                               
              return;
             
          const auto& file=req.get_file_value("file");
            std::string filepath=sjp::Config::GetInstant()->GetBackDir()+file.filename;
            std::string body=file.content;
  
            sjp::FileUtil fu(filepath);
            fu.SetContent(body);
  
            sjp::FileInfo fileinfo(filepath);
            sjp::FileInfoManger::GetInstant()->Insert(fileinfo);  
            sjp::FileInfoManger::GetInstant()->Storage();
            ListShow(req,rep);
        
  

🌳.DownLoad函数

  • 当服务器收到一个下载请求时,服务器需要执行的步骤:
  • 文件信息管理模块通过请求url获取该文件信息,如果没有该文件信息,则说明该文件不存在,返回404.
  • 如果存在该文件,则通过文件信息中的packsign判断是否被压缩
  • 如果packsign=false,说明该文件没有没有被压缩,则服务器直接从backdir目录下将获取文件内容发送給浏览器。
  • 如果packsign=true,说明该文件已经被压缩,则服务器从packdir目录下将该文件进行解压缩到backdir,并删除packdir目录下的压缩包,将文件信息中的packsign修改为false,最后将文件内容发送給浏览器。
  • 如果浏览器想要以下载的形式接受文件,则服务器需要将http响应中的Content-Type字段设置为application/octet-stream

下载的断点续传

用户在下载文件的过程中,可能因为网络或者其它原因导致服务器下载失败了,如果没有断点续传,则用户重新下载文件则需要从文件的最开始的位置下载,如果实现了断点续传,则用户重新下载 文件则会从上次失败的位置开始下载

http下载的断点续传的思想

当浏览器第一次下载的时候,服务器給浏览器发送的http响应添加Accept-Ranges和Etag这两个字段:

  • Accept-Ranges一般被设置为bytes ,表示这个字段表示服务器支持断点续传,以字节为单位传输数据.
  • ETag表示的是服务器上某一版本资源的唯一标识,如果资源被改动过,则ETag会改变,客户端收到后则会保证这个信息。

如果下载中断了,浏览器重新下载文件,则第二次的http请求中需要包含If-Range字段和Range字段.

  • If-Range字段:"保存服务端的响应的ETag字段的信息",用于判断与服务端中与上一次请求资源是否一致,如果一致则断点续传,如果不一致,则重新开始下载。
  • Range字段:bytes start-end,表示的是请求服务器资源从start个字节开始到end个字节结束的数据。

则服务端发送http响应給客户端需要包含Content-Range字段和ETag字段:

  • Content-Range:start-end/文件大小,表示http响应包含文件数据从start开始到end结束的文件数据,文件大小表示文件总大小。
  • ETag:表示的是服务器上资源的唯一标识。

ETag字段的设定

ETag某一版本资源的唯一标识,如果资源被改动过,则ETag则会改变,所以我们可以通过"文件名-文件大小-文件修改时间"来设定ETag(当然这不是唯一的方式)。

UpDown函数的实现

如果浏览器发生断点续传请求,则在httplib内部实现了对Range字段的解析,开发人员只需要读取文件中的内容,设定好字段,底层的httplib库则会自动定位好具体的数据范围,发送給浏览器。


        
      //文件名-文件大小-最后一次修改时间
        static  std::string GetEtag(std::string filename)
           sjp::FileUtil fu(filename);
           std::string etag=fu.GetFilename();
           etag+="-";
           etag+=std::to_string(fu.GetFileSize());
           etag+="-";
           etag+=std::to_string(fu.GetFileModfityTime());
           return etag;
        
  

     static void DownLoad(const httplib::Request& req,httplib::Response& rep)
        
 
          cout<<"download begin"<<endl;
          std::string url=req.path;
          sjp::FileInfo fileinfo;
          sjp::FileInfoManger::GetInstant()->GetoneByURL(url,fileinfo);                                

         if(!sjp::FileInfoManger::GetInstant()->Exist(fileinfo.back_path))
            /* sjp::FileUtil fu(back_dir); 
             std::vector<std::string> arry;
             fu.GetPathInDir(arry);
             */
            rep.status=404;
            return ;
          
  
          if(fileinfo.pack_sign==true)
          
            //解压缩
            sjp::FileUtil fu(fileinfo.pack_path);
            fu.UnpackFile(fileinfo.back_path);
            fileinfo.pack_sign=false;
            sjp::FileInfoManger::GetInstant()->Insert(fileinfo);
            fu.Remove();
          
  
  
          bool flag=false;
          //判断是否为断点续传
          if(req.has_header("If-Range"))
            //断点续传
            std::string oldetag=req.get_header_value("If-Range"); 
            if(oldetag==GetEtag(fileinfo.back_path))
               //需要断点续传
               cout<<"oldetag: "<<oldetag<<endl;
               cout<<"etag: "<<GetEtag(fileinfo.back_path)<<endl;                                                                                                                           
               flag=true;
            
          
         sjp::FileUtil fu(fileinfo.back_path);
          //获取文件内容
          std::string body;
          fu.GetContent(body);
          rep.body=body;
          rep.content_length_=body.size();
          if(flag==false)
          //不需要断点续传
           rep.status=200;
           rep.set_header("Accept-Ranges","bytes");
           rep.set_header("Content-Type","application/octet-stream");
           rep.set_header("Etag",GetEtag(fileinfo.back_path));
          
          else
            //断点续传
            rep.status=206;
            rep.set_header("Accept-Ranges","bytes");
            rep.set_header("Content-Type","application/octet-stream");
            rep.set_header("ETag",GetEtag(fileinfo.back_path));
          
          return ;
        

以上是关于⚡通信管理模块⚡的主要内容,如果未能解决你的问题,请参考以下文章

⚡通信管理模块⚡

多线程下的队列问题

AVS 通信模块之AVSConnectionManager

一种适应10KV配网建设的智能通讯管理单元(带加密模块)分布式DTU综合测控通信单元

Spark存储管理

Linux系统启动流程内核及模块管理