WebServer构建相应 && 发送相应

打印 上一主题 下一主题

主题 1845|帖子 1845|积分 5535

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
1.构建相应



  • 构建相应流程如下

    • 确认方法
    • 根据不同方法,以不同方法提参
    • 确认访问资源

  • 如果用户的URL没有指明要访问的某种资源(路径),虽然浏览器默认会添加/,但是依旧没有告知服务器,要访问什么资源

    • 此时,默认返回对应服务的首页
    • 这里的/不是Linux服务器的根目次,通常是http服务器设置的自己的WEB根目次

  • 关于stat

    • stat()返回一个文件的信息,信息都存在一个struct stat结构体变量中

      • 细节可以:man 2 stat

    • struct stat可以判断

      • 是否是一个可实验程序
      • 该文件的巨细



  • 判断文件是否是可实验程序

    • 若不是,则走ProcessNonCgi逻辑,返回静态网页
    • 如果,则走ProcessCgi逻辑,将参数传递给子程序处置惩罚

  • 请求资源是什么范例,取决于资源的后缀
  • 为什么要利用goto?

    • 分析协议的每一步,都有大概堕落
    • 所以在做堕落处置惩罚的时候,如果没有goto,会导致利用大量的if判断

  1. void BuildResponse()
  2. {
  3.     size_t pos = 0;
  4.     if (_request.method != "GET" && _request.method != "POST")
  5.     {
  6.         // 非法请求
  7.         _response.status_code = BAD_REQUEST; // TODO
  8.         LOG(WARNING, "Method is not right");
  9.         goto END;
  10.     }
  11.     if (_request.method == "GET")
  12.     {
  13.         // GET可能带参数,也可能不带参数,要区分出来
  14.         if (_request.uri.find('?') != std::string::npos)
  15.         {
  16.             Util::CutString(_request.uri, _request.path, _request.arg, "?");
  17.             _request.cgi = true;
  18.         }
  19.         else
  20.         {
  21.             _request.path = _request.uri;
  22.         }
  23.     }
  24.     else if(_request.method == "POST")
  25.     {
  26.         _request.cgi = true;
  27.         _request.path = _request.uri;
  28.     }
  29.     else
  30.     {
  31.         // Do Nothing
  32.     }
  33.     _request.path.insert(0, WEB_ROOT); // 从根目录开始
  34.     // 如果访问的是根目录,则默认访问主页
  35.     if(_request.path[_request.path.size() - 1] == '/')
  36.     {
  37.         _request.path += HOME_PAGE;
  38.     }
  39.     // 确认访问资源是否存在
  40.     struct stat st;
  41.     if(stat(_request.path.c_str(), &st) == 0) // TODO  待整理stat()
  42.     {
  43.         // 访问的是否是一个具体资源?
  44.         if(S_ISDIR(st.st_mode))
  45.         {
  46.             // 请求的是一个目录,需要特殊处理 -- 改为访问该目录下主页
  47.             // 虽然是目录,但是绝对不会以/结尾
  48.             _request.path += "/";
  49.             _request.path += HOME_PAGE;
  50.             // 上面更改了path指向,所以重新获取文件状态
  51.             stat(_request.path.c_str(), &st);
  52.         }
  53.         // 是否是一个可程序程序?
  54.         if (st.st_mode & S_IXUSR || st.st_mode & S_IXGRP || st.st_mode & S_IXOTH)
  55.         {
  56.             _request.cgi = true; // TODO CGI标志位感觉有多余
  57.         }
  58.         _response.fSize = st.st_size;
  59.     }
  60.     else
  61.     {
  62.         // 资源不存在
  63.         LOG(WARNING, _request.path + " Not Found");
  64.         _response.status_code = NOT_FOUND;
  65.         goto END;
  66.     }
  67.     // 读取文件后缀
  68.     pos = _request.path.rfind('.');
  69.     if (pos== std::string::npos)
  70.     {
  71.         _request.suffix = ".html"; // 没找到就默认设置为html
  72.     }
  73.     else
  74.     {
  75.         _request.suffix = _request.path.substr(pos);
  76.     }
  77.     if(_request.cgi)
  78.     {
  79.         _response.status_code = ProcessCgi(); // 执行目标程序,拿到结果到_response.response_body
  80.     }
  81.     else
  82.     {
  83.         // 1.至此,目标网页一定是存在的
  84.         // 2.返回并不是单单返回网页,而是要构建HTTP响应
  85.         _response.status_code = ProcessNonCgi(); // 简单的网页返回,返回静态网页,只需要打开即可
  86.     }
  87.     END:
  88.     // 最后统一构建响应
  89.     BuildResponseHelper();
  90.     return;
  91. }
  92. void BuildResponseHelper()
  93. {
  94.     // 此处status_line是干净的,没有内容的
  95.     // 构建状态行
  96.     _response.status_line += HTTP_VERSION;
  97.     _response.status_line += " ";
  98.     _response.status_line += std::to_string(_response.status_code);
  99.     _response.status_line += " ";
  100.     _response.status_line += Util::Code2Desc(_response.status_code);
  101.     _response.status_line += LINE_END;
  102.     // 构建响应正文,可能包括响应报头
  103.     std::string path = WEB_ROOT;
  104.     path += '/';
  105.     switch (_response.status_code)
  106.     {
  107.         case OK:
  108.             BuildOKResponse();
  109.             break;
  110.         case NOT_FOUND:
  111.             path += PAGE_404;
  112.             HandlerError(path);
  113.             break;
  114.         case BAD_REQUEST:  // 暂时先404处理,后面有需要再改
  115.             path += PAGE_404;
  116.             HandlerError(path);
  117.             break;
  118.         case SERVER_ERROR:
  119.             path += PAGE_404;
  120.             HandlerError(path);
  121.             break;
  122.         default:
  123.             break;
  124.     }
  125. }
  126. void BuildOKResponse()
  127. {
  128.     // Header
  129.     std::string header_line = "Content-Type: ";
  130.     header_line += Util::Suffix2Desc(_request.suffix);
  131.     header_line += LINE_END;
  132.     _response.response_header.push_back(header_line);
  133.     header_line = "Content-Length: ";
  134.     if(_request.cgi)
  135.     {
  136.         header_line += std::to_string(_response.response_body.size());
  137.     }
  138.     else
  139.     {
  140.         header_line += std::to_string(_response.fSize);
  141.     }
  142.     header_line += LINE_END;
  143.     _response.response_header.push_back(header_line);
  144. }
复制代码

2.发送相应




  • 注意:

    • 分多次发和把所有内容合成一个字符串一次性发送是没区别的
    • send只是把内容拷贝到发送缓冲区中
    • 具体什么时候发,一次性发多少,是由TCP决定的

  • 此处_response.response_body发送逻辑确保了_response.response_body能全部发完
  • 此次请求为ProcessNonCgi时,必要从磁盘读取文件内容,再把内容拷贝到发送缓冲区中,这样是效率低下的

    • sendfile()可以在两个文件形貌符之间拷贝数据

      • 即:sendfile()可以把文件内容,直接拷贝到Tcp的发送缓冲区中,进步了效率

    • 这个拷贝着实内核态完成的,所以比read() / write()高效的多

  1. void SendResponse()
  2. {
  3.     send(_sock, _response.status_line.c_str(), _response.status_line.size(), 0);
  4.     for(auto& str : _response.response_header)
  5.     {
  6.         send(_sock, str.c_str(), str.size(), 0);
  7.     }
  8.     send(_sock, _response.blank.c_str(), _response.blank.size(), 0);
  9.     if(_request.cgi)
  10.     {
  11.         const char *start = _response.response_body.c_str();
  12.         size_t size = 0, total = 0;
  13.         while (total < _response.response_body.size() &&
  14.                (size = send(_sock, start + total, _response.response_body.size() - total, 0) > 0))
  15.         {
  16.             total += size;
  17.         }
  18.     }
  19.     else
  20.     {
  21.         sendfile(_sock, _response.fd, nullptr, _response.fSize);
  22.         close(_response.fd);
  23.     }
  24. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

欢乐狗

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表