仿muduo库实现高并发服务器----HttpServer

一、接口

添加请求--处理函数映射消息(GET/POST/PUT/DELETE)

设置静态资源根目录

设置是否启动超时连接关闭

设置线程池中线程的数量

启动服务器

OnConnected -- 用于给TcpServer设置协议上下文

OnMessage -- 用于进行缓冲区数据解析处理

请求的路由查找

静态资源请求查找和处理

功能性请求

组织响应进行回复

二、成员变量

cpp 复制代码
 using PtrConnection = std::shared_ptr<Connection>;
    using Hander = std::function<void(const HttpResquest&,HttpResponse*)>;
    using Handers = std::vector<std::pair<std::regex,Hander>>;
    Handers _get_route;
    Handers _post_route;
    Handers _put_route;
    Handers _delete_route;
    std::string _basedir;//静态资源根目录
    TcpServer _server;

三、成员函数

构造

开启TcpServer的非活跃销毁,设置事件,设置Tcp的连接完成事件回调,信息事件回调

连接完成事件回调

设置上下文

cpp 复制代码
  void OnConnected(const PtrConnection& conn)
    {
        conn->SetContext(HttpContext());
        DBG_LOG("NEW CONNECTION %p", conn.get());
    }

信息事件回调

1、获取上下文

2、通过上下文对缓冲区数据进行解析,得到HttpRequest对象

如果缓冲区的数据解析出错,就直接回复出错响应(进行错误响应,关闭连接,填充一个错误显示页面数据发送到rsp中,组织响应发送给客户端)

如果解析正常,且请求已经获取完毕,才开始去进行处理

如果当前请求还没有接收完整,则退出,等新的数据到来重新继续处理

3、请求路由+业务处理

4、对HttpResponse进行组织发送

5、重置上下文

6、根据长短连接是否关闭连接或继续处理(短链接则直接关闭)

cpp 复制代码
 void OnMessage(const PtrConnection& conn, Buffer*buffer)
    {
        while(buffer->ReadAblesize()>0)
        {
            HttpContext *context = conn->GetContext()->get<HttpContext>();
            context->RecvHttpRequest(buffer);
            HttpResquest &req = context->Requst();
            HttpResponse rsp(context->RespStatu());
            if(context->RespStatu()>=404)
            {
               
                ErrorHandler(req,&rsp);
                WriteResponse(conn,req,rsp);
                conn->Shutdown();
                context->ReSet();
                buffer->MoveReadoffset(buffer->ReadAblesize());
                return;
            }
            if(context->Recvstatu()!=RECV_HTTP_OVER)
            {
                return;
            }
            
            Route(req,&rsp);
            WriteResponse(conn,req,rsp);
            context->ReSet();
            if(rsp.Close()==true)
            {
                conn->Shutdown();
            }
          
        }  
        return;
    }

错误页面书写

cpp 复制代码
void ErrorHandler(HttpResquest&req,HttpResponse*rsp)
   {
       std::string body;
       body += "<html>";
       body += "<head>";
       body += "<meta http-equiv='Content-Type'content = 'text/html;charset=utf-8' > ";
       body += "</head>";
       body += "<body>";
       body += "<h1>";
       body += std::to_string(rsp->_statu);
       body += " ";
       body += Util::StatuDesc(rsp->_statu);
       body += "</h1>";
       body += "</body>";
       body += "</html>";
       rsp->SetContent(body,"text/html");
   }

响应

1、先完善头部字段

2、将rsp中的要素,按照http协议格式进行组织

3、发送数据

cpp 复制代码
 void WriteResponse(const PtrConnection& conn,HttpResquest&req,HttpResponse&rsp)
    {
        if(req.Close() == true)
        {
            rsp.SetHeader("Connection","close");
        }
        else
        {
            rsp.SetHeader("Connection","keep-alive");

        }
        if(req._body.empty()==false&&req.HasHeader("Content-Length")==false)
        {
            rsp.SetHeader("Content-Length",std::to_string(rsp._body.size()));
        }
        if(req._body.empty()==false&&req.HasHeader("Content-Type")==false)
        {
            rsp.SetHeader("Content-Type", "application/octet-stream");
        }
        if (rsp._redirect_flag == true) 
        {
            rsp.SetHeader("Location", rsp._redirect_url);
        }
        std::stringstream rsp_str;
        rsp_str << req._version << " " << std::to_string(rsp._statu) << " "<< Util::StatuDesc(rsp._statu) << "\r\n";
        for (auto &head : rsp._headers)
        {
            rsp_str << head.first << ": " << head.second << "\r\n";
        }
        rsp_str << "\r\n";
        rsp_str << rsp._body;
   
        conn->Send(rsp_str.str().c_str(), rsp_str.str().size());
    }

请求分辨

1、对请求进行分辨,是一个静态资源请求,还是一个功能性请求

静态资源请求,则进行静态资源的处理

功能性请求,则需要通过路由表来确定是否有处理函数

既不是静态资源请求,也没有设置对应的功能性请求处理函数,返回405

cpp 复制代码
 void Route(HttpResquest&req,HttpResponse*rsp)
    {
         std::cout << req._path<<std::endl;
        if(IsFileHandler(req))//是否是静态资源访问
        {
            
             return FileHander(req,rsp);
            
        }
        if(req._method=="GET"||req._method=="HEAD")
        {
            std::cout << "GET" << std::endl;
            return Dispatcher(req,rsp,_get_route);

        }
        if(req._method=="POST")
        {
            return Dispatcher(req,rsp,_post_route);
        }
        if(req._method=="PUT")
        {
            return Dispatcher(req,rsp,_put_route);
        }
        if(req._method=="DELETE")
        {
            return Dispatcher(req,rsp,_delete_route);
        }
        rsp->_statu = 405;
        return;
    }

判断是否是一个静态资源请求

1、必须设置了静态资源根目录

2、请求方法必须是 GET 或者HEAD请求方法

3、请求的资源必须是一个合法路径

4、请求的资源必须存在,且是一个普通文件

如果请求是/需要将他进行转化

cpp 复制代码
void FileHander(HttpResquest&req,HttpResponse*rsp)
    {
        std::string req_path = _basedir + req._path;
        
        if(req_path.back()=='/')
        {
            req_path+="index.html";
        }
        
        bool ret = Util::ReadFile(req_path,&rsp->_body);
        if(ret == false)
        {
            return;
        }
        std::string mime = Util::ExtMime(req._path);
        rsp->SetHeader("Content-Type",mime);
        return;
    }
    

静态资源的请求处理

将静态资源文件的数据读取出来,放到rsp的_body中,并设置mime

cpp 复制代码
 void FileHander(HttpResquest&req,HttpResponse*rsp)
    {
        std::string req_path = _basedir + req._path;
        
        if(req_path.back()=='/')
        {
            req_path+="index.html";
        }
        
        bool ret = Util::ReadFile(req_path,&rsp->_body);
        if(ret == false)
        {
            return;
        }
        std::string mime = Util::ExtMime(req._path);
        rsp->SetHeader("Content-Type",mime);
        return;
    }
    

功能性请求的分类处理

在对应的请求方法的路由表中,查找是否含有对应资源请求的处理函数,有则调用,没有则404

思想: 路由表储存的键值对 --- 正则表达式 & 处理函数

使用正则表达式,对请求的资源路径进行正则匹配,匹配成功就使用对应函数进行处理

cpp 复制代码
  void Dispatcher(HttpResquest&req,HttpResponse*rsp,Handers& handlers)
    {
        for(auto&handler:handlers)
        {
            const std::regex& re = handler.first;
            auto it = handler.first;
            
            const Hander& functor = handler.second;
            bool ret = std::regex_match(req._path,req._matches,re);
            if(ret == false)
            {
               
                continue;
            }
            return functor(req,rsp);
        }
        rsp->_statu=404;
    }

设置函数

cpp 复制代码
 void Get(const std::string pattern,const Hander&handler)
    {
        std::cout << std::endl;
        _get_route.push_back(std::make_pair(std::regex(pattern),handler));
    }
    
    void Post(const std::string pattern,const Hander&handler)
    {
        
        _post_route.push_back(std::make_pair(std::regex(pattern),handler));

    }
    void Delete(const std::string pattern,const Hander&handler)
    {
     
        _delete_route.push_back(std::make_pair(std::regex(pattern),handler));
    }
    void Put(const std::string pattern,const Hander&handler)
    {
        _put_route.push_back(std::make_pair(std::regex(pattern),handler));
    }

设置静态路径

cpp 复制代码
  void SetBaseDir(const std::string path)
    {
        assert(Util::IsDirectory(path) == true);
        _basedir = path;
    }

设置线程个数

cpp 复制代码
 void SetThreadCount(int count)
    {
        _server.SetThreadCount(count);
    }
    

设置监听状态

cpp 复制代码
 void Listen()
    {
        _server.Start();
    }
相关推荐
暮冬-  Gentle°2 小时前
移动设备上的C++优化
开发语言·c++·算法
IMPYLH2 小时前
Linux 的 dd 命令
linux·运维·服务器
2401_874732532 小时前
C++中的装饰器模式高级应用
开发语言·c++·算法
匆匆整棹还2 小时前
window下安装minio
运维·服务器
季明洵2 小时前
回溯介绍及实战
java·数据结构·算法·leetcode·回溯
m0_662577972 小时前
模板编译期哈希计算
开发语言·c++·算法
m0_662577972 小时前
C++代码静态检测
开发语言·c++·算法
阿贵---2 小时前
编译器命令选项优化
开发语言·c++·算法
minji...2 小时前
Linux 进程间通信(一)进程间通信与匿名管道
linux·运维·服务器·数据结构·数据库·c++