基于http协议的服务器代码编写(可以访问指定路径的资源版+添加跳转网页功能 ...

打印 上一主题 下一主题

主题 1035|帖子 1035|积分 3105

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

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

x
目次
可以访问指定路径资源的服务端
编写
引入
介绍
代码
Serialization.hpp
404_err.html
示例
增长跳转网页功能
href
介绍 
代码 
root.html
page1.html
page2.html
示例
添加重定向功能
引入
介绍
代码
示例
添加显示图片功能
引入
介绍
代码
http_server.hpp
Serialization.hpp
root_page.html
示例 
修改为多线程版(短毗连) 
介绍
代码


可以访问指定路径资源的服务端

编写

   引入

  如果我们要访问客户端指定路径的资源,就得把请求拆分出来
  

  • url里记录了要访问资源的路径
  • 拆分的过程着实就是反序列化
    介绍

  那么题目就酿成了 -- 怎样序列化/反序列化 
  

  • 因为我们只用编写服务端(因为客户端有浏览器主动帮我们进行处理惩罚),所以只涉及请求的反序列化+响应的序列化
  • 着实响应的序列化我们早就在上一个版本里做过了,我们把它再做个处理惩罚并且拆分出来
  请求怎样序列化(格式化数据 -> 字符串)呢?
  

  • 把响应报头放在vector<string>里
  • 响应正文单独存放在string里
  • 在拼接的时候,利用某个分隔符分开他们(比如http协议里规定的\r\n,固然这个分隔符只适用于报头部分)
  响应怎样反序列化(字符串-> 格式化数据)呢?
  

  • 就是把上述过程反过来
  • 找到分隔符,将得到的数据赋值给结构体对应的字段
  • 详细来说就是:
  • 以\r\n为分隔符,找出报头的每一行,将每一行push到容器里,同时删除掉源串里的内容+"/r/n"
  • 如果读到空行(内容为空)时,就已经将报头读完了
  • 然后探求报头里的content-length字段,找到后拿到正文长度,就可以继承把正文也拆出来了
  怎样得到我们的目标url呢?
  

  • 也就是要继承细化
  • url在请求行里,也就是报头的第一行=vector里的第一个元素
  • 把它以空格为分隔符,拆出三大部分,即可得到纯净的url
  • 我们可以手动分割
  • 也可以利用ss流,它默认以空格为分隔符主动分割,只必要用变量给他接着就行(就像水倒出来,用杯子接着它哈哈哈)
  得到url后,我们必要将它和我们自定义的web根目次的位置拼接起来
  

  • 也就是在上一个版本那边介绍的那样,拼接起来得到[要访问的资源在linux中的现实路径]:

  但是这里我们要增长一个特殊处理惩罚:
  

  • 当访问根目次时,必要手动让它访问我们设定的首页文件
  • 并且当访问不存在的页面时,必要手动让它访问我们设定好的错误页面
  
   代码

  1. #pragma once
  2. #include <signal.h>
  3. #include <unistd.h>
  4. #include <cstring>
  5. #include <functional>
  6. #include "socket.hpp"
  7. #include "Serialization.hpp"
  8. static MY_SOCKET my_socket;
  9. #define buff_size 1024 * 30
  10. #define root_path "./root_page"
  11. #define def_file "root.html"
  12. class http_server
  13. {
  14. public:
  15.     http_server(const uint16_t port, const std::string &ip = "0.0.0.0")
  16.         : port_(port), ip_(ip) {}
  17.     ~http_server() {}
  18.     void run()
  19.     {
  20.         init();
  21.         while (true)
  22.         {
  23.             uint16_t client_port;
  24.             std::string client_ip;
  25.             lg(DEBUG, "accepting ...");
  26.             int sockfd = my_socket.Accept(client_ip, client_port);
  27.             if (sockfd == -1)
  28.             {
  29.                 continue;
  30.             }
  31.             lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);
  32.             int ret = fork();
  33.             if (ret == 0)
  34.             {
  35.                 my_socket.Close();
  36.                 char buffer[buff_size];
  37.                 std::string in_buffer;
  38.                 while (true)
  39.                 {
  40.                     memset(buffer, 0, sizeof(buffer));
  41.                     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n
  42.                     if (n > 0)
  43.                     {
  44.                         buffer[n] = 0;
  45.                         lg(INFO, "get request");
  46.                         in_buffer += buffer; // 连续读取
  47.                         lg(INFO, "%s", in_buffer.c_str());
  48.                         request req;
  49.                         req.deserialize(in_buffer);
  50.                         // 构建访问资源的路径
  51.                         std::string def_page = root_path;
  52.                         if (req.url_ == "/")
  53.                         {
  54.                             def_page += "/";
  55.                             def_page += def_file;
  56.                         }
  57.                         else
  58.                         {
  59.                             def_page += req.url_;
  60.                         }
  61.                         // 构建响应
  62.                         response res;
  63.                         res.version_ = "HTTP/1.1";
  64.                         std::string text = get_page(def_page);
  65.                         if (text.empty())
  66.                         {
  67.                              res.code_ = 404;
  68.                              res.desc_ = "Not Found";
  69.                              def_page = root_path;
  70.                              def_page += "/";
  71.                              def_page += "404_err.html";
  72.                              res.text_ = get_page(def_page);
  73.                               
  74.                         }
  75.                         else
  76.                         {
  77.                             res.code_ = 200;
  78.                             res.desc_ = "OK";
  79.                             res.text_ = text;
  80.                         }
  81.                         std::string cl = "Content-Length: ";
  82.                         cl += std::to_string((res.text_).size());
  83.                         cl += protocol_sep;
  84.                         (res.title_).push_back(cl);
  85.                         std::string content;
  86.                         res.serialize(content);
  87.                         write(sockfd, content.c_str(), content.size());
  88.                     }
  89.                     else if (n == 0)
  90.                     {
  91.                         lg(INFO, "%s quit", client_ip.c_str());
  92.                         break;
  93.                     }
  94.                     else // 读出错误
  95.                     {
  96.                         break;
  97.                     }
  98.                 }
  99.                 // lg(INFO, "fork quit");
  100.                 exit(0);
  101.                 close(sockfd);
  102.             }
  103.         }
  104.     }
  105. private:
  106.     void init()
  107.     {
  108.         signal(SIGPIPE, SIG_IGN);
  109.         signal(SIGCHLD, SIG_IGN);
  110.         my_socket.Socket();
  111.         my_socket.Bind(port_);
  112.         my_socket.Listen();
  113.         lg(INFO, "server init done");
  114.     }
  115.     std::string get_page(std::string path)
  116.     {
  117.         std::ifstream in(path.c_str());
  118.         if (!in.is_open())
  119.         {
  120.             return "";
  121.         }
  122.         std::string content, tmp;
  123.         while (std::getline(in, tmp))
  124.         {
  125.             content += tmp;
  126.         }
  127.         return content;
  128.     }
  129. private:
  130.     uint16_t port_;
  131.     std::string ip_;
  132. };
复制代码
Serialization.hpp

  1. #pragma once
  2. #include <string>
  3. #include <vector>
  4. #include <sstream>
  5. #include <fstream>
  6. #include <iostream>
  7. #define protocol_sep "\r\n"
  8. #define blank_sep ' '
  9. class request
  10. {
  11. public:
  12.     request()
  13.         : type_(""), url_(""), version_(""), text_("") {}
  14.     bool deserialize(std::string &content)
  15.     {
  16.         std::string tmp;
  17.         size_t left = 0;
  18.         while (true)
  19.         {
  20.             size_t pos = content.find(protocol_sep, left);
  21.             if (pos == std::string::npos)
  22.             {
  23.                 return false;
  24.             }
  25.             tmp = content.substr(left, pos - left); // 左闭右开
  26.             left = pos + 2;
  27.             if (tmp.empty()) // 读到空行
  28.             {
  29.                 break;
  30.             }
  31.             else
  32.             {
  33.                 title_.push_back(tmp);
  34.             }
  35.         }
  36.         // 细分请求行
  37.         std::string request_line = title_[0];
  38.         std::stringstream ss(request_line);
  39.         ss >> type_ >> url_ >> version_;
  40.         // 如果有正文的话
  41.         std::string comp = "Content-Length: ";
  42.         bool is_find = false;
  43.         int size = 0;
  44.         for (auto &it : title_)
  45.         {
  46.             size_t pos = it.find(comp);
  47.             if (pos != std::string::npos)
  48.             {
  49.                 ssize_t right = it.find(protocol_sep);
  50.                 std::string s_size = it.substr(pos + comp.size(), right - pos - comp.size());
  51.                 size = stoi(s_size);
  52.                 is_find = true;
  53.                 break;
  54.             }
  55.         }
  56.         if (!is_find)
  57.         { // 没有Content-Length字段
  58.             content.erase(0, left + 2);
  59.         }
  60.         else
  61.         {
  62.             text_ = content.substr(left + 2, size);
  63.             content.erase(0, left + 2 + size);
  64.         }
  65.         return true;
  66.     }
  67. public:
  68.     std::string type_;
  69.     std::string url_;
  70.     std::string version_;
  71.     std::vector<std::string> title_;
  72.     std::string text_;
  73. };
  74. class response
  75. {
  76. public:
  77.     response()
  78.         : version_(""), code_(0), desc_(""), text_("") {}
  79.     void serialize(std::string &content)
  80.     {
  81.         // 响应行
  82.         std::string header = version_;
  83.         header += blank_sep;
  84.         header += std::to_string(code_);
  85.         header += blank_sep;
  86.         header += desc_;
  87.         header += protocol_sep;
  88.         // 响应报头
  89.         std::string attribute;
  90.         for (auto &it : title_)
  91.         {
  92.             attribute += it;
  93.             attribute += protocol_sep;
  94.         }
  95.         content = header + attribute + protocol_sep + text_;
  96.     }
  97. public:
  98.     std::string version_;
  99.     int code_;
  100.     std::string desc_;
  101.     std::vector<std::string> title_;
  102.     std::string text_;
  103. };
复制代码
404_err.html

  这是我上网任意搜的一个(毕竟咱也不是做前端的,没必要本身写)
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <title>404-对不起!您访问的页面不存在</title>
  5.     <meta charset="UTF-8" http-equiv="Content-Type" content="text/html; charset=utf-8" />
  6.     <style>
  7.         body {
  8.             margin: 0;
  9.             padding: 0;
  10.             width: 100%;
  11.             height: 100%;
  12.             color: #0e356c;
  13.             display: table;
  14.             font-weight: 100;
  15.             font-family: 'Lato';
  16.         }
  17.         .container {
  18.             text-align: center;
  19.             display: table-cell;
  20.             vertical-align: middle
  21.         }
  22.         .content {
  23.             text-align: center;
  24.             display: inline-block;
  25.         }
  26.         .title {
  27.             font-size: 42px;
  28.             margin-bottom: 40px;
  29.         }
  30.     </style>
  31. </head>
  32. <body>
  33.     <div class="container">
  34.         <div class="content">
  35.             <div class="title">404-对不起!您访问的页面不存在</div>
  36.         </div>
  37.     </div>
  38. </body>
  39. </html>
复制代码

  
   示例

  这是我们创建的多个html文件结构:
  


  访问指定资源:
  



  如果访问的资源不存在:
  


  
增长跳转网页功能

   href

  是 HTML 元素中常用的属性,用于指定链接的目标地址
  


  跳转网页时不一定都发送了请求,因为浏览器会缓存一些网页
    介绍 

  路径可以是绝对路径,也可以是相对路径
  

  • 所以可以实现跳转外部/内部网页
    代码 

  root.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>my title</title>
  6. </head>
  7. <body>
  8.     <h1>hello.</h1>
  9.     <h2>hello..</h2>
  10.     <h3>hello...</h3>
  11.     <p>hello world!</p>
  12.     <h2>跳转至第一个页面</h2>
  13.     <p><a href="a/page1.html">page1</a></p>
  14.     <h2>跳转至第二个页面</h2>
  15.     <p><a href="a/b/page2.html">page2</a></p>
  16. </body>
  17. </html>
复制代码
page1.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>my title</title>
  6. </head>
  7. <body>
  8.     <h1>第一个页面</h1>
  9.     <p>hello first</p>
  10.     <h2>跳转至第二个页面</h2>
  11.     <p><a href="b/page2.html">page2</a></p>
  12.     <h2>跳转至首页</h2>
  13.     <p><a href="../root.html">root</a></p>
  14. </body>
  15. </html>
复制代码
page2.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>my title</title>
  6. </head>
  7. <body>
  8.     <h1>第二个页面</h1>
  9.     <p>hello second</p>
  10.     <h2>跳转至第一个页面</h2>
  11.     <p><a href="../page1.html">page1</a></p>
  12.     <h2>跳转至首页</h2>
  13.     <p><a href="../../root.html">root</a></p>
  14. </body>
  15. </html>
复制代码
   示例

  当我们起首访问page2:
  


  点击跳转首页后:
  


  点击跳转page1后:
  


  
添加重定向功能

   引入

  我们在上一篇博客中介绍过重定向的概念 -- 域名介绍,url的介绍+原理+特殊字符的处理惩罚,网络行为,http协议请求/响应的格式+结构,状态码介绍,临时/永世重定向,http报头常见字段,fiddler-CSDN博客
    介绍

  我们这里假定当客户端访问/302时,就触发重定向功能
  

  • 也就是让响应的状态码为302
  • 浏览器会帮我们根据http协议 -- 当状态码为302时,主动跳转到location字段定义的资源(这里我们设置为百度主页,都行的啦)
  • 因为只是一个测试嘛,起到一个看看重定向结果的作用
    代码

  只必要修改动态构建响应部分即可:
  1.     void run()
  2.     {
  3.         init();
  4.         while (true)
  5.         {
  6.             uint16_t client_port;
  7.             std::string client_ip;
  8.             lg(DEBUG, "accepting ...");
  9.             int sockfd = my_socket.Accept(client_ip, client_port);
  10.             if (sockfd == -1)
  11.             {
  12.                 continue;
  13.             }
  14.             lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);
  15.             int ret = fork();
  16.             if (ret == 0)
  17.             {
  18.                 my_socket.Close();
  19.                 char buffer[buff_size];
  20.                 std::string in_buffer;
  21.                 while (true)
  22.                 {
  23.                     memset(buffer, 0, sizeof(buffer));
  24.                     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n
  25.                     if (n > 0)
  26.                     {
  27.                         bool code_302 = false;
  28.                         buffer[n] = 0;
  29.                         lg(INFO, "get request");
  30.                         in_buffer += buffer; // 连续读取
  31.                         lg(INFO, "%s", in_buffer.c_str());
  32.                         request req;
  33.                         req.deserialize(in_buffer);
  34.                         // 构建访问资源的路径
  35.                         std::string def_page = root_path;
  36.                         if (req.url_ == "/")
  37.                         {
  38.                             def_page += "/";
  39.                             def_page += def_file;
  40.                         }
  41.                         else if (req.url_ == "/302")
  42.                         {
  43.                             code_302 = true;
  44.                         }
  45.                         else
  46.                         {
  47.                             def_page += req.url_;
  48.                         }
  49.                         // 构建响应
  50.                         response res;
  51.                         res.version_ = "HTTP/1.1";
  52.                         if (code_302)
  53.                         {
  54.                             res.code_ = 302;
  55.                             res.desc_ = "Found";
  56.                             std::string cl = "Location: ";
  57.                             cl += "https://www.baidu.com";
  58.                             cl += protocol_sep;
  59.                             (res.title_).push_back(cl);
  60.                         }
  61.                         else
  62.                         {
  63.                             std::string text = get_page(def_page);
  64.                             if (text.empty())
  65.                             {
  66.                                 res.code_ = 404;
  67.                                 res.desc_ = "Not Found";
  68.                                 def_page = root_path;
  69.                                 def_page += "/";
  70.                                 def_page += "404_err.html";
  71.                                 res.text_ = get_page(def_page);
  72.                             }
  73.                             else
  74.                             {
  75.                                 res.code_ = 200;
  76.                                 res.desc_ = "OK";
  77.                                 res.text_ = text;
  78.                             }
  79.                             std::string cl = "Content-Length: ";
  80.                             cl += std::to_string((res.text_).size());
  81.                             cl += protocol_sep;
  82.                             (res.title_).push_back(cl);
  83.                         }
  84.                         std::string content;
  85.                         res.serialize(content);
  86.                         write(sockfd, content.c_str(), content.size());
  87.                     }
  88.                     else if (n == 0)
  89.                     {
  90.                         lg(INFO, "%s quit", client_ip.c_str());
  91.                         break;
  92.                     }
  93.                     else // 读出错误
  94.                     {
  95.                         break;
  96.                     }
  97.                 }
  98.                 // lg(INFO, "fork quit");
  99.                 exit(0);
  100.                 close(sockfd);
  101.             }
  102.         }
  103.     }
复制代码
   示例

  服务端收到的请求:
  


   成功跳转到我们设定好的百度主页
  

  • 偶然候大概会一直转圈圈,多切换下网络
  
添加显示图片功能

   引入

  网页一样平常都会有图片
  

  • 图片也属于网络资源,也必要浏览器向服务器请求这个资源
  • 所以图片资源也会放在web根目次下
  这也就意味着:
  

  • 当我们请求一个页面后,浏览器不仅必要请求网页,还必要根据页面标签定义的图片资源挨个请求
  这也就是为什么新版本的http要支持长毗连:
  

  • 如果还采用短毗连,一个毗连=一个资源,那万一网页有几百张图片资源,那不得毗连死
    介绍

  插入图片资源的html语法: -- HTML 图像
  


  图片资源着实就是二进制文件,它必要被上层以某种格式解释,才气呈现出我们想要看到的样子
  

  • 所以就必要添加conten-type字段,来指定该资源的类型
  • 我们之前都没有利用过这个字段,所以他默认以html格式解释
  • 如果是图片的话,就得是image/jpeg / image/png
  但是,那么多资源,怎样识别哪个资源是那个类型呢?
  

  • 我们就必要继承细分url了
  • url不是记录了客户端要访问的资源路径吗,就肯定会带上资源的文件名
  • 如果文件名带有.jpg / .png后缀,则为图片资源
  • 如果带有.html后缀,则是我们平常的网页资源
  • 如果是其他的,就默认以html格式解释(这里就简朴一点处理惩罚啦)
  那么,有了后缀,我们还必要将后缀与对应的conten-type的添补值相关联
  

  • 所以我们必要定义一个键值对容器
  既然图片是二进制,那如果还利用[读取文本文件的一行一行读]的方式就不可了
  

  • 必要增长一个读取二进制的函数,用来区分两者
    代码

  除了增长了读取图片资源的功能,我还把代码结构修改了很多
  http_server.hpp

  1. #pragma once
  2. #include <signal.h>
  3. #include <unistd.h>
  4. #include <cstring>
  5. #include <functional>
  6. #include <unordered_map>
  7. #include "socket.hpp"
  8. #include "Serialization.hpp"
  9. static MY_SOCKET my_socket;
  10. #define buff_size 1024 * 30
  11. class http_server
  12. {
  13. public:
  14.     http_server(const uint16_t port, const std::string &ip = "0.0.0.0")
  15.         : port_(port), ip_(ip)
  16.     {
  17.         content_type_[".html"] = "text/html";
  18.         content_type_[".png"] = "image/png";
  19.         content_type_[".jpg"] = "image/jpeg";
  20.         content_type_[".jpeg"] = "image/jpeg";
  21.     }
  22.     ~http_server() {}
  23.     void run()
  24.     {
  25.         init();
  26.         while (true)
  27.         {
  28.             uint16_t client_port;
  29.             std::string client_ip;
  30.             lg(DEBUG, "accepting ...");
  31.             int sockfd = my_socket.Accept(client_ip, client_port);
  32.             if (sockfd == -1)
  33.             {
  34.                 continue;
  35.             }
  36.             lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);
  37.             int ret = fork();
  38.             if (ret == 0)
  39.             {
  40.                 my_socket.Close();
  41.                 char buffer[buff_size];
  42.                 std::string in_buffer;
  43.                 while (true)
  44.                 {
  45.                     memset(buffer, 0, sizeof(buffer));
  46.                     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n
  47.                     if (n > 0)
  48.                     {
  49.                         buffer[n] = 0;
  50.                         in_buffer += buffer; // 连续读取
  51.                         lg(INFO, "get request: \n%s", in_buffer.c_str());
  52.                         // 构建请求
  53.                         request req;
  54.                         req.deserialize(in_buffer);
  55.                         //lg(DEBUG, "path: %s ,url: %s ", (req.path_).c_str(), (req.url_).c_str());
  56.                         // 构建响应
  57.                         response res;
  58.                         handle_response(res, req);
  59.                         // 响应序列化
  60.                         std::string content;
  61.                         res.serialize(content);
  62.                         write(sockfd, content.c_str(), content.size());
  63.                     }
  64.                     else if (n == 0)
  65.                     {
  66.                         lg(INFO, "%s quit", client_ip.c_str());
  67.                         break;
  68.                     }
  69.                     else // 读出错误
  70.                     {
  71.                         break;
  72.                     }
  73.                 }
  74.                 exit(0);
  75.                 close(sockfd);
  76.             }
  77.         }
  78.     }
  79. private:
  80.     void init()
  81.     {
  82.         signal(SIGPIPE, SIG_IGN);
  83.         signal(SIGCHLD, SIG_IGN);
  84.         my_socket.Socket();
  85.         my_socket.Bind(port_);
  86.         my_socket.Listen();
  87.         lg(INFO, "server init done");
  88.     }
  89.     void handle_response(response &res, request &req)
  90.     {
  91.         int code = req.code_;
  92.         std::string path = req.path_;
  93.         std::string content_type_data = content_type_[req.suffix_];
  94.         //lg(DEBUG, "content_type_data: %s", content_type_data.c_str());
  95.         res.version_ = "HTTP/1.1";
  96.         if (code == 302)
  97.         {
  98.             res.code_ = 302;
  99.             res.desc_ = "Found";
  100.             std::string cl = "Location: ";
  101.             cl += "https://www.qq.com";
  102.             (res.title_).push_back(cl);
  103.             return ;
  104.         }
  105.         if (code == 404)
  106.         {
  107.             res.code_ = 404;
  108.             res.desc_ = "Not Found";
  109.         }
  110.         else
  111.         {
  112.             res.code_ = 200;
  113.             res.desc_ = "OK";
  114.         }
  115.         // 将读取网页和图片资源的方式分开
  116.         if (req.suffix_ == ".html")
  117.         {
  118.             res.text_ = get_page(path);
  119.             //lg(DEBUG, "text: %s", (res.text_).c_str());
  120.         }
  121.         else
  122.         {
  123.             res.text_ = b_get_page(path);
  124.         }
  125.         //  构建响应报头
  126.         std::string cl = "Content-Length: ";
  127.         cl += std::to_string((res.text_).size());
  128.         //lg(DEBUG, "text_size: %d", (res.text_).size());
  129.         (res.title_).push_back(cl);
  130.         cl = "Content-Type: ";
  131.         cl += content_type_data;
  132.         (res.title_).push_back(cl);
  133.     }
  134. private:
  135.     uint16_t port_;
  136.     std::string ip_;
  137.     std::unordered_map<std::string, std::string> content_type_;
  138. };
复制代码
Serialization.hpp

  1. #pragma once
  2. #include <string>
  3. #include <vector>
  4. #include <sstream>
  5. #include <fstream>
  6. #include <iostream>
  7. #define protocol_sep "\r\n"
  8. #define blank_sep ' '
  9. #define root_path "./root_page"
  10. #define def_file "root.html"
  11. std::string get_page(std::string path)
  12. {
  13.     std::string content, tmp;
  14.     std::ifstream in(path.c_str());
  15.     if (!in.is_open())
  16.     {
  17.         return "";
  18.     }
  19.     while (std::getline(in, tmp))
  20.     {
  21.         content += tmp;
  22.     }
  23.     in.close();
  24.    // printf("content : %s", content.c_str());
  25.     return content;
  26. }
  27. std::string b_get_page(std::string path)
  28. {
  29.     std::ifstream in(path.c_str(), std::ios_base::binary);
  30.     if (!in.is_open())
  31.     {
  32.         return "";
  33.     }
  34.     in.seekg(0, std::ios_base::end);
  35.     auto len = in.tellg();
  36.     in.seekg(0, std::ios_base::beg);
  37.     std::string content;
  38.     content.resize(len);
  39.     in.read((char *)content.c_str(), content.size());
  40.     in.close();
  41.     return content;
  42. }
  43. class request
  44. {
  45. public:
  46.     request()
  47.         : type_(""), url_(""), version_(""), text_(""), path_(""), code_(0), suffix_("") {}
  48.     bool deserialize(std::string &content)
  49.     {
  50.         std::string tmp;
  51.         size_t left = 0;
  52.         while (true)
  53.         {
  54.             size_t pos = content.find(protocol_sep, left);
  55.             if (pos == std::string::npos)
  56.             {
  57.                 return false;
  58.             }
  59.             tmp = content.substr(left, pos - left); // 左闭右开
  60.             left = pos + 2;
  61.             if (tmp.empty()) // 读到空行
  62.             {
  63.                 break;
  64.             }
  65.             else
  66.             {
  67.                 title_.push_back(tmp);
  68.             }
  69.         }
  70.         // 判断是否有正文
  71.         std::string comp = "Content-Length: ";
  72.         bool is_find = false;
  73.         int size = 0;
  74.         for (auto &it : title_)
  75.         {
  76.             size_t pos = it.find(comp);
  77.             if (pos != std::string::npos)
  78.             {
  79.                 ssize_t right = it.find(protocol_sep);
  80.                 std::string s_size = it.substr(pos + comp.size(), right - pos - comp.size());
  81.                 size = stoi(s_size);
  82.                 is_find = true;
  83.                 break;
  84.             }
  85.         }
  86.         if (!is_find)
  87.         { // 没有Content-Length字段
  88.             content.erase(0, left + 2);
  89.         }
  90.         else
  91.         {
  92.             text_ = content.substr(left + 2, size);
  93.             content.erase(0, left + 2 + size);
  94.         }
  95.         handle_path();
  96.         return true;
  97.     }
  98. private:
  99.     void handle_path() // 构建访问资源的路径
  100.     {
  101.         // 细分请求行
  102.         std::string request_line = title_[0];
  103.         std::stringstream ss(request_line);
  104.         ss >> type_ >> url_ >> version_;
  105.         // 构建路径
  106.         path_ = root_path;
  107.         if (url_ == "/")
  108.         {
  109.             path_ += "/";
  110.             path_ += def_file;
  111.         }
  112.         else if (url_ == "/302")
  113.         {
  114.             code_ = 302; // 这里设置为重定向到百度页面,所以不能走本地读取
  115.             return;
  116.         }
  117.         else
  118.         {
  119.             path_ += url_;
  120.         }
  121.         // 判断该资源是否存在
  122.         if (get_page(path_).empty())
  123.         {
  124.             code_ = 404; // 需要告诉响应,这里发生了404错误
  125.             path_ = root_path;
  126.             path_ += "/";
  127.             path_ += "404_err.html";
  128.         }
  129.         // 拿到资源后缀
  130.         size_t pos = path_.rfind(".");
  131.         if (pos == std::string::npos)
  132.         {
  133.             suffix_ = ".html";
  134.         }
  135.         else
  136.         {
  137.             suffix_ = path_.substr(pos);
  138.         }
  139.     }
  140. public:
  141.     std::string type_;
  142.     std::string url_;
  143.     std::string path_;
  144.     int code_;
  145.     std::string suffix_;
  146.     std::string version_;
  147.     std::vector<std::string> title_;
  148.     std::string text_;
  149. };
  150. class response
  151. {
  152. public:
  153.     response()
  154.         : version_(""), code_(0), desc_(""), text_("") {}
  155.     void serialize(std::string &content)
  156.     {
  157.         // 响应行
  158.         std::string header = version_;
  159.         header += blank_sep;
  160.         header += std::to_string(code_);
  161.         header += blank_sep;
  162.         header += desc_;
  163.         header += protocol_sep;
  164.         // 响应报头
  165.         std::string attribute;
  166.         for (auto &it : title_)
  167.         {
  168.             attribute += it;
  169.             attribute += protocol_sep;
  170.         }
  171.         content = header + attribute + protocol_sep + text_;
  172.     }
  173. public:
  174.     std::string version_;
  175.     int code_;
  176.     std::string desc_;
  177.     std::vector<std::string> title_;
  178.     std::string text_;
  179. };
复制代码
root_page.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.     <meta charset="UTF-8">
  5.     <title>my title</title>
  6. </head>
  7. <body>
  8.     <h1>hello.</h1>
  9.     <h2>hello..</h2>
  10.     <h3>hello...</h3>
  11.     <p>hello world!</p>
  12.     <form action="a/page1.html" method="get">
  13.         name:<input type="text" name="name"><br>
  14.         password:<input type="password" name="password"><br>
  15.         <input type="submit" name="submit">
  16.     </form>
  17.     <img src="1.jpeg" alt="猫猫" width="500" height="400">
  18.     <h2>跳转至第一个页面</h2>
  19.     <p><a href="a/page1.html">page1</a></p>
  20.     <h2>跳转至第二个页面</h2>
  21.     <p><a href="a/b/page2.html">page2</a></p>
  22. </body>
  23. </html>
复制代码
   示例 

  当我们在首页放上我们的图片资源时
  

  • 访问首页后,浏览器就会根据页面上的html标签继承申请其他资源

  • 这是网页的样式(可以看到图片成功被加载出来了):

  
修改为多线程版(短毗连) 

   介绍

  服务器收到一个毗连后,就创建一个线程去处理惩罚请求
  

  • 并且添加了重连功能
  留意:
  

  • 我们这里是短毗连版本,一次毗连=处理惩罚一次请求
  • 但纵然是短毗连,也得保证读取到的数据是一份完备的请求
  • 所以我在线程外部定义了一个缓冲区,用于保存处理惩罚过程中剩下的数据(因为它有大概是其他毗连的一部分)
  • 而线程内部在反序列化时,将完备请求从缓冲区中剥离
    代码

  1. #pragma once
  2. #include <signal.h>
  3. #include <unistd.h>
  4. #include <cstring>
  5. #include <functional>
  6. #include <pthread.h>
  7. #include <unordered_map>
  8. #include "socket.hpp"
  9. #include "Serialization.hpp"
  10. static MY_SOCKET my_socket;
  11. #define buff_size 1024 * 30
  12. class http_server;
  13. struct thread_data
  14. {
  15.     int sockfd_;
  16.     std::string ip_;
  17.     std::string &in_buffer_;
  18.     http_server *this_;
  19. };
  20. class http_server
  21. {
  22. public:
  23.     http_server(const uint16_t port, const std::string &ip = "0.0.0.0")
  24.         : port_(port), ip_(ip)
  25.     {
  26.         content_type_[".html"] = "text/html";
  27.         content_type_[".png"] = "image/png";
  28.         content_type_[".jpg"] = "image/jpeg";
  29.         content_type_[".jpeg"] = "image/jpeg";
  30.     }
  31.     ~http_server() {}
  32.     void run()
  33.     {
  34.         init();
  35.         while (true)
  36.         {
  37.             uint16_t client_port;
  38.             std::string client_ip;
  39.             // 一个线程处理一次请求(短连接)
  40.             pthread_t pid;
  41.             std::string in_buffer;
  42.             int sockfd = 0;
  43.             int count = 5;
  44.             do
  45.             {
  46.                 lg(DEBUG, "accepting ...");
  47.                 sockfd = my_socket.Accept(client_ip, client_port);
  48.                 if (sockfd != -1 || --count == 0)
  49.                 {
  50.                     break;
  51.                 }
  52.             } while (true);
  53.             if (sockfd == -1)
  54.             {
  55.                 lg(ERROR, "accepting error");
  56.             }
  57.             lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);
  58.             thread_data *td = new thread_data{sockfd, client_ip, in_buffer, this};
  59.             lg(DEBUG, "create pthread");
  60.             pthread_create(&pid, nullptr, entry, reinterpret_cast<void *>(td));
  61.             // 一个进程服务一个客户端
  62.             // lg(DEBUG, "accepting ...");
  63.             // int sockfd = my_socket.Accept(client_ip, client_port);
  64.             // if (sockfd == -1)
  65.             // {
  66.             //     continue;
  67.             // }
  68.             // lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);
  69.             //  int ret = fork();
  70.             //  if (ret == 0)
  71.             //  {
  72.             //      my_socket.Close();
  73.             //  char buffer[buff_size];
  74.             //  std::string in_buffer;
  75.             // while (true)
  76.             // {
  77.             //     memset(buffer, 0, sizeof(buffer));
  78.             //     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n
  79.             //     if (n > 0)
  80.             //     {
  81.             //         buffer[n] = 0;
  82.             //         in_buffer += buffer; // 连续读取
  83.             //         lg(INFO, "get request: \n%s", in_buffer.c_str());
  84.             //         // 构建请求
  85.             //         request req;
  86.             //         req.deserialize(in_buffer);
  87.             //         // lg(DEBUG, "path: %s ,url: %s ", (req.path_).c_str(), (req.url_).c_str());
  88.             //         // 构建响应
  89.             //         response res;
  90.             //         handle_response(res, req);
  91.             //         // 响应序列化
  92.             //         std::string content;
  93.             //         res.serialize(content);
  94.             //         write(sockfd, content.c_str(), content.size());
  95.             //     }
  96.             //     else if (n == 0)
  97.             //     {
  98.             //         lg(INFO, "%s quit", client_ip.c_str());
  99.             //         break;
  100.             //     }
  101.             //     else // 读出错误
  102.             //     {
  103.             //         break;
  104.             //     }
  105.             // }
  106.             //     exit(0);
  107.             //     close(sockfd);
  108.             // }
  109.         }
  110.     }
  111. private:
  112.     void init()
  113.     {
  114.         signal(SIGPIPE, SIG_IGN);
  115.         signal(SIGCHLD, SIG_IGN);
  116.         my_socket.Socket();
  117.         my_socket.Bind(port_);
  118.         my_socket.Listen();
  119.         lg(INFO, "server init done");
  120.     }
  121.     void handle_response(response &res, request &req)
  122.     {
  123.         int code = req.code_;
  124.         std::string path = req.path_;
  125.         std::string content_type_data = content_type_[req.suffix_];
  126.         // lg(DEBUG, "content_type_data: %s", content_type_data.c_str());
  127.         res.version_ = "HTTP/1.1";
  128.         if (code == 302)
  129.         {
  130.             res.code_ = 302;
  131.             res.desc_ = "Found";
  132.             std::string cl = "Location: ";
  133.             cl += "https://www.qq.com";
  134.             (res.title_).push_back(cl);
  135.             return;
  136.         }
  137.         if (code == 404)
  138.         {
  139.             res.code_ = 404;
  140.             res.desc_ = "Not Found";
  141.         }
  142.         else
  143.         {
  144.             res.code_ = 200;
  145.             res.desc_ = "OK";
  146.         }
  147.         // 将读取网页和图片资源的方式分开
  148.         if (req.suffix_ == ".html")
  149.         {
  150.             res.text_ = get_page(path);
  151.             // lg(DEBUG, "text: %s", (res.text_).c_str());
  152.         }
  153.         else
  154.         {
  155.             res.text_ = b_get_page(path);
  156.         }
  157.         //  构建响应报头
  158.         std::string cl = "Content-Length: ";
  159.         cl += std::to_string((res.text_).size());
  160.         // lg(DEBUG, "text_size: %d", (res.text_).size());
  161.         (res.title_).push_back(cl);
  162.         cl = "Content-Type: ";
  163.         cl += content_type_data;
  164.         (res.title_).push_back(cl);
  165.     }
  166.     static void *entry(void *args)
  167.     {
  168.         pthread_detach(pthread_self());
  169.         thread_data *td = reinterpret_cast<thread_data *>(args);
  170.         int sockfd = td->sockfd_;
  171.         std::string ip = td->ip_;
  172.         std::string in_buffer = td->in_buffer_;
  173.         http_server *it = td->this_;
  174.         // 读取请求
  175.         char buffer[buff_size];
  176.         bool flag = true;
  177.         request req;
  178.         while (true) // 虽说是短连接,但也得确保读出来的内容是一个完整的请求
  179.         {
  180.             memset(buffer, 0, sizeof(buffer));
  181.             int n = read(sockfd, buffer, sizeof(buffer));
  182.             if (n > 0)
  183.             {
  184.                 buffer[n] = 0;
  185.                 in_buffer += buffer; // 连续读取
  186.                 lg(INFO, "get request: \n%s", in_buffer.c_str());
  187.                 // 构建请求
  188.                 flag = req.deserialize(in_buffer);
  189.                 if (flag == false)
  190.                 {
  191.                     continue;
  192.                 }
  193.                 else
  194.                 {
  195.                     break;
  196.                 }
  197.             }
  198.             else if (n == 0)
  199.             {
  200.                 lg(INFO, "%s quit", ip.c_str());
  201.                 return nullptr;
  202.             }
  203.             else
  204.             {
  205.                 lg(ERROR, "%s read error", ip.c_str());
  206.                 return nullptr;
  207.             }
  208.         }
  209.         // lg(DEBUG, "path: %s ,url: %s ", (req.path_).c_str(), (req.url_).c_str());
  210.         // 构建响应
  211.         response res;
  212.         it->handle_response(res, req);
  213.         // 响应序列化
  214.         std::string content;
  215.         res.serialize(content);
  216.         write(sockfd, content.c_str(), content.size());
  217.         //  销毁资源
  218.         delete td;
  219.         close(sockfd);
  220.         return nullptr;
  221.     }
  222. private:
  223.     uint16_t port_;
  224.     std::string ip_;
  225.     std::unordered_map<std::string, std::string> content_type_;
  226. };
复制代码


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

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

三尺非寒

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