1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计

这篇具有很好参考价值的文章主要介绍了1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、HttpContext模块

1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计,CodeCrafters,c++,服务器,开发语言

要实现渐变的搭建HTTP服务器,所需要提供的要素和功能!
要素:
1. GET请求的路由映射表
2. POST请求的路由映射表
3. PUT请求的路由映射表
4. DELETE请求的路由映射表 —— 路由映射表记录对应请求方法的请求的处理函数映射关系
5. 高性能TCP服务器—— 进行连接的IO操作
6. 静态资源相对根目录 —— 实现静态资源的处理
接口:
服务器处理流程:
1. 从socket接受数据放到接受缓冲区
2. 调用onmessage回调函数进行业务处理
3. 对请求进行介意,得到了一个HTTPREQUEST结构,包含了所有的请求要素!
4. 进行请求的路由映射 —— 找到对应请求的处理方法
    1. 静态资源请求 —— 一些实体文件资源的请求 html,image
        将静态资源文件的数据读取出来,填充到HTTPresponse结构中
    2. 功能性请求 —— 在请求路由映射表中查找处理函数,找到了则执行函数
        具体的业务请求,并进行HTTPREsponse结构的数据填充
5. 对静态资源请求——功能性请求处理完毕后,得到一个填充了相应信息的httpResponse 的对象,组织http响应格式进行发送!
接口:
  • 添加请求—— 处理函数映射信息(GET/POST/PUT/DELETE)
  • 设置静态资源根目录
  • 设置是否启动超时连接关闭
  • 设置线程池中线程数量
  • 启动服务器
  • OnConnected - 用于给TcpServer设置协议上下文
  • OnMessage - 用于进行缓冲区数据解析处理
  • 获取上下文,进行缓冲区数据对象
  • 请求的路由查1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计,CodeCrafters,c++,服务器,开发语言
  • 静态资源请求查找和处理
  • 功能性请求查找和处理组织响应进程回复

二、HttpServer模块

1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计,CodeCrafters,c++,服务器,开发语言

三、HttpContext模块实现思想

(一)功能

记录HTTP请求的接受和处理进度。

(二)意义

有可能出现接收的数据并不是一条完整的HTTP请求数据,也就是请求的处理需要在多次受到数据之后才能处理完成,因此在每次处理的时候,就需要将进度处理记录下来,以便于下次从当前进度继续向下处理。

(三)接口

  • 接收并且处理数据
  • 接受请求行
  • 解析请求行
  • 接收头部
  • 解析头部
  • 接受正文
  • 返回解析完成的请求信息

四、HttpServer模块实现思想

(一)功能

对于HTTP协议支持所有模块的整合

(二)意义

让HTTP服务器的搭建变得更加简单

(三)分析

HttpServer模块:用于实现HTTP服务器的搭建
1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计,CodeCrafters,c++,服务器,开发语言

表中记录了针对哪个请求,应该使用哪个函数来进行业务处理的映射关系
当服务器收到了一一个请求,就在请求路由表中,查找有没有对应请求的处理函数,如果有,则执行对应的处理函数即可说白了,什么请求,怎么处理,由用户来设定,服务器收到了请求只需要执行函数即可,这样做的好处:用户只需要实现业务处理函数,然后将请求与处理函数的映射关系,添加到服务器中而服务器只需要接收数据,解析数据,查找路由表映射关系,执行业务处理函数。文章来源地址https://www.toymoban.com/news/detail-722990.html

五、代码

  • HttpResponse

class HttpResponse {
    public:
        int _statu;
        bool _redirect_flag;
        std::string _body;
        std::string _redirect_url;
        std::unordered_map<std::string, std::string> _headers;
    public:
        HttpResponse():_redirect_flag(false), _statu(200) {}
        HttpResponse(int statu):_redirect_flag(false), _statu(statu) {} 
        void ReSet() {
            _statu = 200;
            _redirect_flag = false;
            _body.clear();
            _redirect_url.clear();
            _headers.clear();
        }
        //插入头部字段
        void SetHeader(const std::string &key, const std::string &val) {
            _headers.insert(std::make_pair(key, val));
        }
        //判断是否存在指定头部字段
        bool HasHeader(const std::string &key) {
            auto it = _headers.find(key);
            if (it == _headers.end()) {
                return false;
            }
            return true;
        }
        //获取指定头部字段的值
        std::string GetHeader(const std::string &key) {
            auto it = _headers.find(key);
            if (it == _headers.end()) {
                return "";
            }
            return it->second;
        }
        void SetContent(const std::string &body,  const std::string &type = "text/html") {
            _body = body;
            SetHeader("Content-Type", type);
        }
        void SetRedirect(const std::string &url, int statu = 302) {
            _statu = statu;
            _redirect_flag = true;
            _redirect_url = url;
        }
        //判断是否是短链接
        bool Close() {
            // 没有Connection字段,或者有Connection但是值是close,则都是短链接,否则就是长连接
            if (HasHeader("Connection") == true && GetHeader("Connection") == "keep-alive") {
                return false;
            }
            return true;
        }
};


  • HttpServer模块
class HttpServer {
    private:
        using Handler = std::function<void(const HttpRequest &, HttpResponse *)>;
        using Handlers = std::vector<std::pair<std::regex, Handler>>;
        Handlers _get_route;
        Handlers _post_route;
        Handlers _put_route;
        Handlers _delete_route;
        std::string _basedir; //静态资源根目录
        TcpServer _server;
    private:
        void ErrorHandler(const HttpRequest &req, HttpResponse *rsp) {
            //1. 组织一个错误展示页面
            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>";
            //2. 将页面数据,当作响应正文,放入rsp中
            rsp->SetContent(body, "text/html");
        }
        //将HttpResponse中的要素按照http协议格式进行组织,发送
        void WriteReponse(const PtrConnection &conn, const HttpRequest &req, HttpResponse &rsp) {
            //1. 先完善头部字段
            if (req.Close() == true) {
                rsp.SetHeader("Connection", "close");
            }else {
                rsp.SetHeader("Connection", "keep-alive");
            }
            if (rsp._body.empty() == false && rsp.HasHeader("Content-Length") == false) {
                rsp.SetHeader("Content-Length", std::to_string(rsp._body.size()));
            }
            if (rsp._body.empty() == false && rsp.HasHeader("Content-Type") == false) {
                rsp.SetHeader("Content-Type", "application/octet-stream");
            }
            if (rsp._redirect_flag == true) {
                rsp.SetHeader("Location", rsp._redirect_url);
            }
            //2. 将rsp中的要素,按照http协议格式进行组织
            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;
            //3. 发送数据
            conn->Send(rsp_str.str().c_str(), rsp_str.str().size());
        }
        bool IsFileHandler(const HttpRequest &req) {
            // 1. 必须设置了静态资源根目录
            if (_basedir.empty()) {
                return false;
            }
            // 2. 请求方法,必须是GET / HEAD请求方法
            if (req._method != "GET" && req._method != "HEAD") {
                return false;
            }
            // 3. 请求的资源路径必须是一个合法路径
            if (Util::ValidPath(req._path) == false) {
                return false;
            }
            // 4. 请求的资源必须存在,且是一个普通文件
            //    有一种请求比较特殊 -- 目录:/, /image/, 这种情况给后边默认追加一个 index.html
            // index.html    /image/a.png
            // 不要忘了前缀的相对根目录,也就是将请求路径转换为实际存在的路径  /image/a.png  ->   ./wwwroot/image/a.png
            std::string req_path = _basedir + req._path;//为了避免直接修改请求的资源路径,因此定义一个临时对象
            if (req._path.back() == '/')  {
                req_path += "index.html";
            }
            if (Util::IsRegular(req_path) == false) {
                return false;
            }
            return true;
        }
        //静态资源的请求处理 --- 将静态资源文件的数据读取出来,放到rsp的_body中, 并设置mime
        void FileHandler(const HttpRequest &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;
        }
        //功能性请求的分类处理
        void Dispatcher(HttpRequest &req, HttpResponse *rsp, Handlers &handlers) {
            //在对应请求方法的路由表中,查找是否含有对应资源请求的处理函数,有则调用,没有则发挥404
            //思想:路由表存储的时键值对 -- 正则表达式 & 处理函数
            //使用正则表达式,对请求的资源路径进行正则匹配,匹配成功就使用对应函数进行处理
            //  /numbers/(\d+)       /numbers/12345
            for (auto &handler : handlers) {
                const std::regex &re = handler.first;
                const Handler &functor = handler.second;
                bool ret = std::regex_match(req._path, req._matches, re);
                if (ret == false) {
                    continue;
                }
                return functor(req, rsp);//传入请求信息,和空的rsp,执行处理函数
            }
            rsp->_statu = 404;
        }
        void Route(HttpRequest &req, HttpResponse *rsp) {
            //1. 对请求进行分辨,是一个静态资源请求,还是一个功能性请求
            //   静态资源请求,则进行静态资源的处理
            //   功能性请求,则需要通过几个请求路由表来确定是否有处理函数
            //   既不是静态资源请求,也没有设置对应的功能性请求处理函数,就返回405
            if (IsFileHandler(req) == true) {
                //是一个静态资源请求, 则进行静态资源请求的处理
                return FileHandler(req, rsp);
            }
            if (req._method == "GET" || req._method == "HEAD") {
                return Dispatcher(req, rsp, _get_route);
            }else if (req._method == "POST") {
                return Dispatcher(req, rsp, _post_route);
            }else if (req._method == "PUT") {
                return Dispatcher(req, rsp, _put_route);
            }else if (req._method == "DELETE") {
                return Dispatcher(req, rsp, _delete_route);
            }
            rsp->_statu = 405;// Method Not Allowed
            return ;
        }
        //设置上下文
        void OnConnected(const PtrConnection &conn) {
            conn->SetContext(HttpContext());
            DBG_LOG("NEW CONNECTION %p", conn.get());
        }
        //缓冲区数据解析+处理
        void OnMessage(const PtrConnection &conn, Buffer *buffer) {
            while(buffer->ReadAbleSize() > 0){
                //1. 获取上下文
                HttpContext *context = conn->GetContext()->get<HttpContext>();
                //2. 通过上下文对缓冲区数据进行解析,得到HttpRequest对象
                //  1. 如果缓冲区的数据解析出错,就直接回复出错响应
                //  2. 如果解析正常,且请求已经获取完毕,才开始去进行处理
                context->RecvHttpRequest(buffer);
                HttpRequest &req = context->Request();
                HttpResponse rsp(context->RespStatu());
                if (context->RespStatu() >= 400) {
                    //进行错误响应,关闭连接
                    ErrorHandler(req, &rsp);//填充一个错误显示页面数据到rsp中
                    WriteReponse(conn, req, rsp);//组织响应发送给客户端
                    context->ReSet();
                    buffer->MoveReadOffset(buffer->ReadAbleSize());//出错了就把缓冲区数据清空
                    conn->Shutdown();//关闭连接
                    return;
                }
                if (context->RecvStatu() != RECV_HTTP_OVER) {
                    //当前请求还没有接收完整,则退出,等新数据到来再重新继续处理
                    return;
                }
                //3. 请求路由 + 业务处理
                Route(req, &rsp);
                //4. 对HttpResponse进行组织发送
                WriteReponse(conn, req, rsp);
                //5. 重置上下文
                context->ReSet();
                //6. 根据长短连接判断是否关闭连接或者继续处理
                if (rsp.Close() == true) conn->Shutdown();//短链接则直接关闭
            }
            return;
        }
    public:
        HttpServer(int port, int timeout = DEFALT_TIMEOUT):_server(port) {
            _server.EnableInactiveRelease(timeout);
            _server.SetConnectedCallback(std::bind(&HttpServer::OnConnected, this, std::placeholders::_1));
            _server.SetMessageCallback(std::bind(&HttpServer::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
        }
        void SetBaseDir(const std::string &path) {
            assert(Util::IsDirectory(path) == true);
            _basedir = path;
        }
        /*设置/添加,请求(请求的正则表达)与处理函数的映射关系*/
        void Get(const std::string &pattern, const Handler &handler) {
            _get_route.push_back(std::make_pair(std::regex(pattern), handler));
        }
        void Post(const std::string &pattern, const Handler &handler) {
            _post_route.push_back(std::make_pair(std::regex(pattern), handler));
        }
        void Put(const std::string &pattern, const Handler &handler) {
            _put_route.push_back(std::make_pair(std::regex(pattern), handler));
        }
        void Delete(const std::string &pattern, const Handler &handler) {
            _delete_route.push_back(std::make_pair(std::regex(pattern), handler));
        }
        void SetThreadCount(int count) {
            _server.SetThreadCount(count);
        }
        void Listen() {
            _server.Start();
        }
};

到了这里,关于1.16.C++项目:仿muduo库实现并发服务器之HttpContext以及HttpServer模块的设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 1.9.C++项目:仿muduo库实现并发服务器之Connection模块的设计

    项目完整在: Connection模块,一个连接有任何的事件怎么处理都是有这个模块来进行处理的,因为组件的设计也不知道使用者要如何处理事件,因此只能是提供一些事件回调函数由使用者设置。 发送数据 —— 给用户提供的发生数据的接口,并不是真的发送接口,而只是把数

    2024年02月07日
    浏览(44)
  • 1.5.C++项目:仿muduo库实现并发服务器之socket模块的设计

    项目完整版在: Socket模块是对套接字操作封装的一个模块,主要实现的socket的各项操作。 对socket套接字的操作进行封装。 对socket套接字的操作进行封装。 创建套接字 绑定地址信息 开始监听 向服务器发起连接 获取新连接 接受数据 发送数据 关闭套接字 创建一个监听链接

    2024年02月07日
    浏览(46)
  • 1.4.C++项目:仿muduo库实现并发服务器之buffer模块的设计

    项目完整版在: Buffer模块是一个缓冲区模块,用于实现通信中用户态的接收缓冲区和发送缓冲区功能。 存储数据,取出数据 1.实现换出去得有一块内存空间,采用vector ,vector底层是一个线性的内存空间! 1.默认空间大小 2.当前的读取数据位置! 3.当前的写入数据位置! 写入

    2024年02月07日
    浏览(43)
  • 仿muduo库实现one thread one loop式并发服务器

      文章目录 一、项目简介 二、项目整体认识 2、1 HTTP服务器 2、2 Reactor模型 三、预备知识 3、1 C++11 中的 bind 3、2 简单的秒级定时任务实现 3、3 正则库的简单使用 3、4 通用类型any类型的实现 四、服务器功能模块划分与实现 4、1 Buffer模块 4、2 Socket模块 4、3 Channel模块 4、

    2024年04月09日
    浏览(53)
  • 8核16G的CentOS服务器,Spring boot undertow如何优化参数提高并发,电商项目

    对于运行在8核16GB内存的CentOS服务器上的Spring Boot电商项目,使用Undertow作为嵌入式服务器时,可以通过以下参数优化来提高并发性能: 1. 线程池配置 io-threads :IO线程数,负责处理非阻塞的网络IO。通常设置为与CPU核心数相等的值,即8。 worker-threads :工作线程数,处理阻塞操

    2024年04月25日
    浏览(43)
  • 基于muduo网络库实现的集群聊天服务器

    !!!项目是照着腾讯课堂施磊老师的视频学习,仅供个人学习记录使用!!! !!!项目是照着腾讯课堂施磊老师的视频学习,仅供个人学习记录使用!!! !!!项目是照着腾讯课堂施磊老师的视频学习,仅供个人学习记录使用!!! 使用muduo网络库搭建网络核心模块

    2024年04月26日
    浏览(34)
  • 【实战项目】c++实现基于reactor的高并发服务器

    基于Reactor的高并发服务器,分为反应堆模型,多线程,I/O模型,服务器,Http请求和响应五部分 ​全局 Channel 描述了文件描述符以及读写事件,以及对应的读写销毁回调函数,对应存储arg读写回调对应的参数 ​Channel 异或 |:相同为0,异为1 按位与:只有11为1,其它组合全部

    2024年02月12日
    浏览(51)
  • C++项目——集群聊天服务器项目(一)项目介绍、环境搭建、Boost库安装、Muduo库安装、Linux与vscode配置

    今天开始想更新一个C++项目,实现一个 支持跨服务器通信、支持负载均衡的集群聊天服务器项目 。项目会应用muduo网络库、CMake编译、MySQL数据库、JSon序列化与反序列化、Redis消息订阅模式以及Nginx负载均衡功能。 有兴趣的宝可以跟我一起实操起来,巩固自己的C++学习吧~ 本项

    2024年04月14日
    浏览(56)
  • C++项目:仿mudou库one thread one loop式并发服务器实现

    目录 1.实现目标 2.HTTP服务器 3.Reactor模型 3.1分类 4.功能模块划分: 4.1SERVER模块: 4.2HTTP协议模块: 5.简单的秒级定时任务实现 5.1Linux提供给我们的定时器 5.2时间轮思想: 6.正则库的简单使用 7.通用类型any类型的实现 8.日志宏的实现 9.缓冲区buffer类的实现 10.套接字Socket类实现 11.

    2024年02月08日
    浏览(41)
  • linux并发服务器 —— 项目实战(九)

    数据就绪 - 根据系统IO操作的就绪状态 阻塞 - 调用IO方法的线程进入阻塞状态(挂起) 非阻塞 - 不会改变线程的状态,通过返回值判断 数据读写 - 根据应用程序和内核的交互方式 同步 - 数据的读写需要应用层去读写 异步 - 操作系统提供相应服务 阻塞/非阻塞都是同步IO,只用

    2024年02月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包