WebServer构建响应 发送响应

ops/2024/10/21 5:06:19/

1.构建响应

  • 构建响应流程如下
    • 确认方法
    • 根据不同方法,以不同方法提参
    • 确认访问资源
  • 如果用户的URL没有指明要访问的某种资源(路径),虽然浏览器默认会添加/,但是依旧没有告知服务器,要访问什么资源
    • 此时,默认返回对应服务的首页
    • 这里的/不是Linux服务器的根目录,通常是http服务器设置的自己的WEB根目录
  • 关于stat
    • stat()返回一个文件的信息,信息都存在一个struct stat结构体变量中
      • 细节可以:man 2 stat
    • struct stat可以判断
      • 是否是一个可执行程序
      • 该文件的大小
  • 判断文件是否是可执行程序
    • 若不是,则走ProcessNonCgi逻辑,返回静态网页
    • 若是,则走ProcessCgi逻辑,将参数传递给子程序处理
  • 请求资源是什么类型,取决于资源的后缀
  • 为什么要使用goto?
    • 分析协议的每一步,都有可能出错
    • 所以在做出错处理的时候,如果没有goto,会导致使用大量的if判断
void BuildResponse()
{size_t pos = 0;if (_request.method != "GET" && _request.method != "POST"){// 非法请求_response.status_code = BAD_REQUEST; // TODOLOG(WARNING, "Method is not right");goto END;}if (_request.method == "GET"){// GET可能带参数,也可能不带参数,要区分出来if (_request.uri.find('?') != std::string::npos){Util::CutString(_request.uri, _request.path, _request.arg, "?");_request.cgi = true;}else{_request.path = _request.uri;}}else if(_request.method == "POST"){_request.cgi = true;_request.path = _request.uri;}else{// Do Nothing}_request.path.insert(0, WEB_ROOT); // 从根目录开始// 如果访问的是根目录,则默认访问主页if(_request.path[_request.path.size() - 1] == '/'){_request.path += HOME_PAGE;}// 确认访问资源是否存在struct stat st;if(stat(_request.path.c_str(), &st) == 0) // TODO  待整理stat(){// 访问的是否是一个具体资源?if(S_ISDIR(st.st_mode)){// 请求的是一个目录,需要特殊处理 -- 改为访问该目录下主页// 虽然是目录,但是绝对不会以/结尾_request.path += "/";_request.path += HOME_PAGE;// 上面更改了path指向,所以重新获取文件状态stat(_request.path.c_str(), &st);}// 是否是一个可程序程序?if (st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH){_request.cgi = true; // TODO CGI标志位感觉有多余}_response.fSize = st.st_size;}else{// 资源不存在LOG(WARNING, _request.path + " Not Found");_response.status_code = NOT_FOUND;goto END;}// 读取文件后缀pos = _request.path.rfind('.');if (pos== std::string::npos){_request.suffix = ".html"; // 没找到就默认设置为html}else{_request.suffix = _request.path.substr(pos);}if(_request.cgi){_response.status_code = ProcessCgi(); // 执行目标程序,拿到结果到_response.response_body}else{// 1.至此,目标网页一定是存在的// 2.返回并不是单单返回网页,而是要构建HTTP响应_response.status_code = ProcessNonCgi(); // 简单的网页返回,返回静态网页,只需要打开即可}END:// 最后统一构建响应BuildResponseHelper();return;
}void BuildResponseHelper()
{// 此处status_line是干净的,没有内容的// 构建状态行_response.status_line += HTTP_VERSION;_response.status_line += " ";_response.status_line += std::to_string(_response.status_code);_response.status_line += " ";_response.status_line += Util::Code2Desc(_response.status_code);_response.status_line += LINE_END;// 构建响应正文,可能包括响应报头std::string path = WEB_ROOT;path += '/';switch (_response.status_code){case OK:BuildOKResponse();break;case NOT_FOUND:path += PAGE_404;HandlerError(path);break;case BAD_REQUEST:  // 暂时先404处理,后面有需要再改path += PAGE_404;HandlerError(path);break;case SERVER_ERROR:path += PAGE_404;HandlerError(path);break;default:break;}
}void BuildOKResponse()
{// Headerstd::string header_line = "Content-Type: ";header_line += Util::Suffix2Desc(_request.suffix);header_line += LINE_END;_response.response_header.push_back(header_line);header_line = "Content-Length: ";if(_request.cgi){header_line += std::to_string(_response.response_body.size());}else{header_line += std::to_string(_response.fSize);}header_line += LINE_END;_response.response_header.push_back(header_line);
}


2.发送响应

  • 注意:
    • 分多次发和把所有内容合成一个字符串一次性发送是没区别的
    • send只是把内容拷贝到发送缓冲区中
    • 具体什么时候发,一次性发多少,是由TCP决定的
  • 此处_response.response_body发送逻辑确保了_response.response_body能全部发完
  • 此次请求为ProcessNonCgi时,需要从磁盘读取文件内容,再把内容拷贝到发送缓冲区中,这样是效率低下的
    • sendfile()可以在两个文件描述符之间拷贝数据
      • 即:sendfile()可以把文件内容,直接拷贝到Tcp的发送缓冲区中,提高了效率
    • 这个拷贝实在内核态完成的,所以比read() / write()高效的多
void SendResponse()
{send(_sock, _response.status_line.c_str(), _response.status_line.size(), 0);for(auto& str : _response.response_header){send(_sock, str.c_str(), str.size(), 0);}send(_sock, _response.blank.c_str(), _response.blank.size(), 0);if(_request.cgi){const char *start = _response.response_body.c_str();size_t size = 0, total = 0;while (total < _response.response_body.size() &&(size = send(_sock, start + total, _response.response_body.size() - total, 0) > 0)){total += size;}}else{sendfile(_sock, _response.fd, nullptr, _response.fSize);close(_response.fd);}
}


http://www.ppmy.cn/ops/127184.html

相关文章

科研绘图系列:R语言突出强调部分的饼图(pie plot)

文章目录 介绍加载R包数据数据预处理画饼图画图例合并图形系统信息介绍 饼图(Pie Chart),也称为圆图(Circle Graph),是一种圆形的统计图表,通过将圆分成若干扇形来展示数据的比例关系。每个扇形的角度大小代表了相应数据在总量中的占比。饼图的特点包括: 比例展示:直…

基于微信小程序的智能校园社区服务推荐系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

电脑输入账号密码后,屏幕黑屏只有鼠标解决办法

最近办公电脑出现了两次输入密码后,屏幕黑屏之后鼠标能动的问题,只能有手机查一些资料尝试自己解决,具体什么原因导致的暂时还不清楚。解决这个问题大概有两个方案吧&#xff0c;第一次黑屏用的第一个方案&#xff0c;第二次发现第一个方案不好用了就用的第二个方案。 方案一 …

原型基于颜色的图像检索与MATLAB

摘要 基于内容的检索数据库(图像)已经变得越来越受欢迎。为了达到这一目的&#xff0c;需要发展算法检测/模拟工具&#xff0c;但市场上没有合适的商业工具。 本文介绍了一个模拟环境&#xff0c;能够从数据库中检索图像直方图的相似之处。该环境允许使用不同的色彩空间及柱&a…

2024软考网络工程师笔记 - 第3章.广域通信网

文章目录 广域网物理层特性1️⃣公共交换电话网 PSTN2️⃣本地回路3️⃣机械特性4️⃣电气特性 &#x1f551;流量与差错控制1️⃣流量与差错控制2️⃣流量控制——亭等协议3️⃣流控机制——滑动窗口协议4️⃣差错控制5️⃣差错控制——停等协议6️⃣差错控制——选择重发ARQ协…

Qt-系统网络HTTP客户端(66)

目录 描述 相关函数 使用 准备工作 处理响应 测试 代码 补充 描述 进⾏ Qt 开发时, 和服务器之间的通信很多时候也会⽤到 HTTP 协议 Qt 中提供了客户端&#xff0c;但是并没有提供相应的服务器的库&#xff0c;所以这里我们只讨论 客户端 • 通过 HTTP 从服务器获取…

《京东金融APP的鸿蒙之旅系列专题》鸿蒙工程化:Hvigor构建技术

作者&#xff1a;京东科技 杨拓 一、构建工具概述 Hvigor构建工具是一款基于TypeScript实现的构建任务编排工具&#xff0c;专为提升构建和测试应用的效率而设计。它主要提供以下关键功能&#xff1a; 1.任务管理机制&#xff1a;包括任务注册和编排&#xff0c;帮助开发者高效…

CentOS 7.9安装MySQL

下载Linux版MySQL安装包 下载地址https://downloads.mysql.com/archives/community/ 下载解压后 安装&#xff0c;按照从上至下顺序&#xff0c;一条一条执行即可安装完毕。 进入到rpm所在目录rpm -ivh mysql-community-common-8.0.26-1.el7.x86_64.rpm rpm -ivh mysql-comm…