基于http协议的服务器代码编写(可以访问指定路径的资源版+添加跳转网页功能+临时重定向+加载图片的原理/方法+多线程版),http请求/响应的序列化/反序列化,href介绍

avatar
作者
猴君
阅读量:0

目录

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

编写

引入

介绍

代码

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中的实际路径]:

但是这里我们要增加一个特殊处理:

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

代码

#pragma once  #include <signal.h> #include <unistd.h> #include <cstring> #include <functional>  #include "socket.hpp" #include "Serialization.hpp"  static MY_SOCKET my_socket;  #define buff_size 1024 * 30 #define root_path "./root_page" #define def_file "root.html"  class http_server { public:     http_server(const uint16_t port, const std::string &ip = "0.0.0.0")         : port_(port), ip_(ip) {}     ~http_server() {}     void run()     {         init();         while (true)         {             uint16_t client_port;             std::string client_ip;             lg(DEBUG, "accepting ...");             int sockfd = my_socket.Accept(client_ip, client_port);             if (sockfd == -1)             {                 continue;             }             lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);              int ret = fork();             if (ret == 0)             {                 my_socket.Close();                 char buffer[buff_size];                 std::string in_buffer;                  while (true)                 {                     memset(buffer, 0, sizeof(buffer));                     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n                     if (n > 0)                     {                         buffer[n] = 0;                         lg(INFO, "get request");                         in_buffer += buffer; // 连续读取                         lg(INFO, "%s", in_buffer.c_str());                         request req;                         req.deserialize(in_buffer);                          // 构建访问资源的路径                         std::string def_page = root_path;                         if (req.url_ == "/")                         {                             def_page += "/";                             def_page += def_file;                         }                         else                         {                             def_page += req.url_;                         }                          // 构建响应                         response res;                         res.version_ = "HTTP/1.1";                         std::string text = get_page(def_page);                         if (text.empty())                         {                              res.code_ = 404;                              res.desc_ = "Not Found";                              def_page = root_path;                              def_page += "/";                              def_page += "404_err.html";                               res.text_ = get_page(def_page);                                                         }                         else                         {                             res.code_ = 200;                             res.desc_ = "OK";                             res.text_ = text;                         }                          std::string cl = "Content-Length: ";                         cl += std::to_string((res.text_).size());                         cl += protocol_sep;                         (res.title_).push_back(cl);                          std::string content;                         res.serialize(content);                          write(sockfd, content.c_str(), content.size());                     }                     else if (n == 0)                     {                         lg(INFO, "%s quit", client_ip.c_str());                         break;                     }                     else // 读出错误                     {                         break;                     }                 }                 // lg(INFO, "fork quit");                 exit(0);                 close(sockfd);             }         }     }  private:     void init()     {         signal(SIGPIPE, SIG_IGN);         signal(SIGCHLD, SIG_IGN);          my_socket.Socket();         my_socket.Bind(port_);         my_socket.Listen();         lg(INFO, "server init done");     }     std::string get_page(std::string path)     {         std::ifstream in(path.c_str());         if (!in.is_open())         {             return "";         }         std::string content, tmp;         while (std::getline(in, tmp))         {             content += tmp;         }         return content;     }  private:     uint16_t port_;     std::string ip_; }; 
Serialization.hpp
#pragma once  #include <string> #include <vector> #include <sstream> #include <fstream> #include <iostream>  #define protocol_sep "\r\n" #define blank_sep ' '  class request { public:     request()         : type_(""), url_(""), version_(""), text_("") {}     bool deserialize(std::string &content)     {         std::string tmp;         size_t left = 0;         while (true)         {             size_t pos = content.find(protocol_sep, left);             if (pos == std::string::npos)             {                 return false;             }              tmp = content.substr(left, pos - left); // 左闭右开             left = pos + 2;             if (tmp.empty()) // 读到空行             {                 break;             }             else             {                 title_.push_back(tmp);             }         }          // 细分请求行         std::string request_line = title_[0];         std::stringstream ss(request_line);         ss >> type_ >> url_ >> version_;          // 如果有正文的话         std::string comp = "Content-Length: ";         bool is_find = false;         int size = 0;         for (auto &it : title_)         {             size_t pos = it.find(comp);             if (pos != std::string::npos)             {                 ssize_t right = it.find(protocol_sep);                 std::string s_size = it.substr(pos + comp.size(), right - pos - comp.size());                 size = stoi(s_size);                 is_find = true;                 break;             }         }         if (!is_find)         { // 没有Content-Length字段             content.erase(0, left + 2);         }         else         {             text_ = content.substr(left + 2, size);             content.erase(0, left + 2 + size);         }         return true;     }  public:     std::string type_;     std::string url_;     std::string version_;     std::vector<std::string> title_;     std::string text_; };  class response { public:     response()         : version_(""), code_(0), desc_(""), text_("") {}     void serialize(std::string &content)     {         // 响应行         std::string header = version_;         header += blank_sep;         header += std::to_string(code_);         header += blank_sep;         header += desc_;         header += protocol_sep;          // 响应报头         std::string attribute;         for (auto &it : title_)         {             attribute += it;             attribute += protocol_sep;         }          content = header + attribute + protocol_sep + text_;     }  public:     std::string version_;     int code_;     std::string desc_;     std::vector<std::string> title_;     std::string text_; };
404_err.html

这是我上网随便搜的一个(毕竟咱也不是做前端的,没必要自己写)

<!DOCTYPE html> <html>  <head>     <title>404-对不起!您访问的页面不存在</title>     <meta charset="UTF-8" http-equiv="Content-Type" content="text/html; charset=utf-8" />     <style>         body {             margin: 0;             padding: 0;             width: 100%;             height: 100%;             color: #0e356c;             display: table;             font-weight: 100;             font-family: 'Lato';         }          .container {             text-align: center;             display: table-cell;             vertical-align: middle         }          .content {             text-align: center;             display: inline-block;         }          .title {             font-size: 42px;             margin-bottom: 40px;         }     </style>  </head>  <body>     <div class="container">         <div class="content">             <div class="title">404-对不起!您访问的页面不存在</div>         </div>     </div> </body>  </html>

示例

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

访问指定资源:

如果访问的资源不存在:

增加跳转网页功能

href

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

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

介绍 

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

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

代码 

root.html
<!DOCTYPE html> <html>  <head>     <meta charset="UTF-8">     <title>my title</title> </head>  <body>     <h1>hello.</h1>     <h2>hello..</h2>     <h3>hello...</h3>     <p>hello world!</p>     <h2>跳转至第一个页面</h2>     <p><a href="a/page1.html">page1</a></p>     <h2>跳转至第二个页面</h2>     <p><a href="a/b/page2.html">page2</a></p>  </body>  </html>
page1.html
<!DOCTYPE html> <html>  <head>     <meta charset="UTF-8">     <title>my title</title> </head>  <body>     <h1>第一个页面</h1>     <p>hello first</p>     <h2>跳转至第二个页面</h2>     <p><a href="b/page2.html">page2</a></p>     <h2>跳转至首页</h2>     <p><a href="../root.html">root</a></p>  </body>  </html>
page2.html
<!DOCTYPE html> <html>  <head>     <meta charset="UTF-8">     <title>my title</title> </head>  <body>     <h1>第二个页面</h1>     <p>hello second</p>     <h2>跳转至第一个页面</h2>     <p><a href="../page1.html">page1</a></p>     <h2>跳转至首页</h2>     <p><a href="../../root.html">root</a></p>  </body>  </html>

示例

当我们首先访问page2:

点击跳转首页后:

点击跳转page1后:

添加重定向功能

引入

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

介绍

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

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

代码

只需要修改动态构建响应部分即可:

    void run()     {         init();         while (true)         {             uint16_t client_port;             std::string client_ip;             lg(DEBUG, "accepting ...");             int sockfd = my_socket.Accept(client_ip, client_port);             if (sockfd == -1)             {                 continue;             }             lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);              int ret = fork();             if (ret == 0)             {                 my_socket.Close();                 char buffer[buff_size];                 std::string in_buffer;                  while (true)                 {                     memset(buffer, 0, sizeof(buffer));                     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n                     if (n > 0)                     {                         bool code_302 = false;                         buffer[n] = 0;                         lg(INFO, "get request");                         in_buffer += buffer; // 连续读取                         lg(INFO, "%s", in_buffer.c_str());                         request req;                         req.deserialize(in_buffer);                          // 构建访问资源的路径                         std::string def_page = root_path;                         if (req.url_ == "/")                         {                             def_page += "/";                             def_page += def_file;                         }                         else if (req.url_ == "/302")                         {                             code_302 = true;                         }                         else                         {                             def_page += req.url_;                         }                          // 构建响应                         response res;                         res.version_ = "HTTP/1.1";                         if (code_302)                         {                             res.code_ = 302;                             res.desc_ = "Found";                              std::string cl = "Location: ";                             cl += "https://www.baidu.com";                             cl += protocol_sep;                             (res.title_).push_back(cl);                         }                         else                         {                             std::string text = get_page(def_page);                             if (text.empty())                             {                                 res.code_ = 404;                                 res.desc_ = "Not Found";                                 def_page = root_path;                                 def_page += "/";                                 def_page += "404_err.html";                                  res.text_ = get_page(def_page);                             }                             else                             {                                 res.code_ = 200;                                 res.desc_ = "OK";                                 res.text_ = text;                             }                              std::string cl = "Content-Length: ";                             cl += std::to_string((res.text_).size());                             cl += protocol_sep;                             (res.title_).push_back(cl);                         }                          std::string content;                         res.serialize(content);                          write(sockfd, content.c_str(), content.size());                     }                     else if (n == 0)                     {                         lg(INFO, "%s quit", client_ip.c_str());                         break;                     }                     else // 读出错误                     {                         break;                     }                 }                 // lg(INFO, "fork quit");                 exit(0);                 close(sockfd);             }         }     }

示例

服务端收到的请求:

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

  • 有时候可能会一直转圈圈,多切换下网络

添加显示图片功能

引入

网页一般都会有图片

  • 图片也属于网络资源,也需要浏览器向服务器请求这个资源
  • 所以图片资源也会放在web根目录下

这也就意味着:

  • 当我们请求一个页面后,浏览器不仅需要请求网页,还需要根据页面标签定义的图片资源挨个请求

这也就是为什么新版本的http要支持长连接:

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

介绍

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

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

  • 所以就需要添加conten-type字段,来指定该资源的类型
  • 我们之前都没有使用过这个字段,所以他默认以html格式解释
  • 如果是图片的话,就得是image/jpeg / image/png

但是,那么多资源,如何识别哪个资源是那个类型呢?

  • 我们就需要继续细分url了
  • url不是记录了客户端要访问的资源路径吗,就肯定会带上资源的文件名
  • 如果文件名带有.jpg / .png后缀,则为图片资源
  • 如果带有.html后缀,则是我们普通的网页资源
  • 如果是其他的,就默认以html格式解释(这里就简单一点处理啦)

那么,有了后缀,我们还需要将后缀与对应的conten-type的填充值相关联

  • 所以我们需要定义一个键值对容器

既然图片是二进制,那如果还使用[读取文本文件的一行一行读]的方式就不行了

  • 需要增加一个读取二进制的函数,用来区分两者

代码

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

http_server.hpp
#pragma once  #include <signal.h> #include <unistd.h> #include <cstring> #include <functional> #include <unordered_map>  #include "socket.hpp" #include "Serialization.hpp"  static MY_SOCKET my_socket;  #define buff_size 1024 * 30  class http_server { public:     http_server(const uint16_t port, const std::string &ip = "0.0.0.0")         : port_(port), ip_(ip)     {         content_type_[".html"] = "text/html";         content_type_[".png"] = "image/png";         content_type_[".jpg"] = "image/jpeg";         content_type_[".jpeg"] = "image/jpeg";     }     ~http_server() {}     void run()     {         init();         while (true)         {             uint16_t client_port;             std::string client_ip;             lg(DEBUG, "accepting ...");             int sockfd = my_socket.Accept(client_ip, client_port);             if (sockfd == -1)             {                 continue;             }             lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);              int ret = fork();             if (ret == 0)             {                 my_socket.Close();                 char buffer[buff_size];                 std::string in_buffer;                  while (true)                 {                     memset(buffer, 0, sizeof(buffer));                     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n                     if (n > 0)                     {                         buffer[n] = 0;                         in_buffer += buffer; // 连续读取                         lg(INFO, "get request: \n%s", in_buffer.c_str());                          // 构建请求                         request req;                         req.deserialize(in_buffer);                          //lg(DEBUG, "path: %s ,url: %s ", (req.path_).c_str(), (req.url_).c_str());                          // 构建响应                         response res;                         handle_response(res, req);                          // 响应序列化                         std::string content;                         res.serialize(content);                          write(sockfd, content.c_str(), content.size());                     }                     else if (n == 0)                     {                         lg(INFO, "%s quit", client_ip.c_str());                         break;                     }                     else // 读出错误                     {                         break;                     }                 }                 exit(0);                 close(sockfd);             }         }     }  private:     void init()     {         signal(SIGPIPE, SIG_IGN);         signal(SIGCHLD, SIG_IGN);          my_socket.Socket();         my_socket.Bind(port_);         my_socket.Listen();         lg(INFO, "server init done");     }     void handle_response(response &res, request &req)     {         int code = req.code_;         std::string path = req.path_;         std::string content_type_data = content_type_[req.suffix_];         //lg(DEBUG, "content_type_data: %s", content_type_data.c_str());          res.version_ = "HTTP/1.1";         if (code == 302)         {             res.code_ = 302;             res.desc_ = "Found";              std::string cl = "Location: ";             cl += "https://www.qq.com";             (res.title_).push_back(cl);             return ;         }          if (code == 404)         {             res.code_ = 404;             res.desc_ = "Not Found";         }         else         {             res.code_ = 200;             res.desc_ = "OK";         }          // 将读取网页和图片资源的方式分开         if (req.suffix_ == ".html")         {             res.text_ = get_page(path);             //lg(DEBUG, "text: %s", (res.text_).c_str());         }         else         {             res.text_ = b_get_page(path);         }          //  构建响应报头         std::string cl = "Content-Length: ";         cl += std::to_string((res.text_).size());         //lg(DEBUG, "text_size: %d", (res.text_).size());         (res.title_).push_back(cl);          cl = "Content-Type: ";         cl += content_type_data;         (res.title_).push_back(cl);     }  private:     uint16_t port_;     std::string ip_;      std::unordered_map<std::string, std::string> content_type_; }; 
Serialization.hpp
#pragma once  #include <string> #include <vector> #include <sstream> #include <fstream> #include <iostream>  #define protocol_sep "\r\n" #define blank_sep ' ' #define root_path "./root_page" #define def_file "root.html"  std::string get_page(std::string path) {     std::string content, tmp;     std::ifstream in(path.c_str());     if (!in.is_open())     {         return "";     }     while (std::getline(in, tmp))     {         content += tmp;     }     in.close();     // printf("content : %s", content.c_str());     return content; } std::string b_get_page(std::string path) {     std::ifstream in(path.c_str(), std::ios_base::binary);     if (!in.is_open())     {         return "";     }     in.seekg(0, std::ios_base::end);     auto len = in.tellg();     in.seekg(0, std::ios_base::beg);      std::string content;     content.resize(len);      in.read((char *)content.c_str(), content.size());     in.close();      return content; }  class request { public:     request()         : type_(""), url_(""), version_(""), text_(""), path_(""), code_(0), suffix_("") {}     bool deserialize(std::string &content)     {         std::string tmp;         size_t left = 0;         while (true)         {             size_t pos = content.find(protocol_sep, left);             if (pos == std::string::npos)             {                 return false;             }              tmp = content.substr(left, pos - left); // 左闭右开             left = pos + 2;             if (tmp.empty()) // 读到空行             {                 break;             }             else             {                 title_.push_back(tmp);             }         }          // 判断是否有正文         std::string comp = "Content-Length: ";         bool is_find = false;         int size = 0;         for (auto &it : title_)         {             size_t pos = it.find(comp);             if (pos != std::string::npos)             {                 ssize_t right = it.find(protocol_sep);                 std::string s_size = it.substr(pos + comp.size(), right - pos - comp.size());                 size = stoi(s_size);                 is_find = true;                 break;             }         }         if (!is_find)         { // 没有Content-Length字段             content.erase(0, left + 2);         }         else         {             text_ = content.substr(left + 2, size);             content.erase(0, left + 2 + size);         }          handle_path();          return true;     }  private:     void handle_path() // 构建访问资源的路径     {         // 细分请求行         std::string request_line = title_[0];         std::stringstream ss(request_line);         ss >> type_ >> url_ >> version_;          // 构建路径         path_ = root_path;         if (url_ == "/")         {             path_ += "/";             path_ += def_file;         }         else if (url_ == "/302")         {             code_ = 302; // 这里设置为重定向到百度页面,所以不能走本地读取             return;         }         else         {             path_ += url_;         }          // 判断该资源是否存在         if (get_page(path_).empty())         {             code_ = 404; // 需要告诉响应,这里发生了404错误             path_ = root_path;             path_ += "/";             path_ += "404_err.html";         }          // 拿到资源后缀         size_t pos = path_.rfind(".");         if (pos == std::string::npos)         {             suffix_ = ".html";         }         else         {             suffix_ = path_.substr(pos);         }     }  public:     std::string type_;     std::string url_;     std::string path_;     int code_;     std::string suffix_;     std::string version_;     std::vector<std::string> title_;     std::string text_; };  class response { public:     response()         : version_(""), code_(0), desc_(""), text_("") {}     void serialize(std::string &content)     {         // 响应行         std::string header = version_;         header += blank_sep;         header += std::to_string(code_);         header += blank_sep;         header += desc_;         header += protocol_sep;          // 响应报头         std::string attribute;         for (auto &it : title_)         {             attribute += it;             attribute += protocol_sep;         }          content = header + attribute + protocol_sep + text_;     }  public:     std::string version_;     int code_;     std::string desc_;     std::vector<std::string> title_;     std::string text_; };
root_page.html
<!DOCTYPE html> <html>  <head>     <meta charset="UTF-8">     <title>my title</title> </head>  <body>     <h1>hello.</h1>     <h2>hello..</h2>     <h3>hello...</h3>     <p>hello world!</p>      <form action="a/page1.html" method="get">         name:<input type="text" name="name"><br>         password:<input type="password" name="password"><br>         <input type="submit" name="submit">     </form>      <img src="1.jpeg" alt="猫猫" width="500" height="400">      <h2>跳转至第一个页面</h2>     <p><a href="a/page1.html">page1</a></p>     <h2>跳转至第二个页面</h2>     <p><a href="a/b/page2.html">page2</a></p>  </body>  </html>

示例 

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

  • 访问首页后,浏览器就会根据页面上的html标签继续申请其他资源
  • 这是网页的样式(可以看到图片成功被加载出来了):

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

介绍

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

  • 并且添加了重连功能

注意:

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

代码

#pragma once  #include <signal.h> #include <unistd.h> #include <cstring> #include <functional> #include <pthread.h> #include <unordered_map>  #include "socket.hpp" #include "Serialization.hpp"  static MY_SOCKET my_socket;  #define buff_size 1024 * 30  class http_server; struct thread_data {     int sockfd_;     std::string ip_;     std::string &in_buffer_;     http_server *this_; };  class http_server { public:     http_server(const uint16_t port, const std::string &ip = "0.0.0.0")         : port_(port), ip_(ip)     {         content_type_[".html"] = "text/html";         content_type_[".png"] = "image/png";         content_type_[".jpg"] = "image/jpeg";         content_type_[".jpeg"] = "image/jpeg";     }     ~http_server() {}     void run()     {         init();         while (true)         {             uint16_t client_port;             std::string client_ip;              // 一个线程处理一次请求(短连接)             pthread_t pid;             std::string in_buffer;              int sockfd = 0;             int count = 5;             do             {                 lg(DEBUG, "accepting ...");                 sockfd = my_socket.Accept(client_ip, client_port);                 if (sockfd != -1 || --count == 0)                 {                     break;                 }             } while (true);             if (sockfd == -1)             {                 lg(ERROR, "accepting error");             }              lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);              thread_data *td = new thread_data{sockfd, client_ip, in_buffer, this};             lg(DEBUG, "create pthread");             pthread_create(&pid, nullptr, entry, reinterpret_cast<void *>(td));              // 一个进程服务一个客户端              // lg(DEBUG, "accepting ...");             // int sockfd = my_socket.Accept(client_ip, client_port);             // if (sockfd == -1)             // {             //     continue;             // }             // lg(INFO, "get a new link..., sockfd: %d, client ip: %s, client port: %d", sockfd, client_ip.c_str(), client_port);             //  int ret = fork();             //  if (ret == 0)             //  {             //      my_socket.Close();             //  char buffer[buff_size];             //  std::string in_buffer;              // while (true)             // {             //     memset(buffer, 0, sizeof(buffer));             //     int n = read(sockfd, buffer, sizeof(buffer)); //"size"\n"a op b"\n             //     if (n > 0)             //     {             //         buffer[n] = 0;             //         in_buffer += buffer; // 连续读取             //         lg(INFO, "get request: \n%s", in_buffer.c_str());              //         // 构建请求             //         request req;             //         req.deserialize(in_buffer);              //         // lg(DEBUG, "path: %s ,url: %s ", (req.path_).c_str(), (req.url_).c_str());              //         // 构建响应             //         response res;             //         handle_response(res, req);              //         // 响应序列化             //         std::string content;             //         res.serialize(content);              //         write(sockfd, content.c_str(), content.size());             //     }             //     else if (n == 0)             //     {             //         lg(INFO, "%s quit", client_ip.c_str());             //         break;             //     }             //     else // 读出错误             //     {             //         break;             //     }             // }             //     exit(0);             //     close(sockfd);             // }         }     }  private:     void init()     {         signal(SIGPIPE, SIG_IGN);         signal(SIGCHLD, SIG_IGN);          my_socket.Socket();         my_socket.Bind(port_);         my_socket.Listen();         lg(INFO, "server init done");     }     void handle_response(response &res, request &req)     {         int code = req.code_;         std::string path = req.path_;         std::string content_type_data = content_type_[req.suffix_];         // lg(DEBUG, "content_type_data: %s", content_type_data.c_str());          res.version_ = "HTTP/1.1";         if (code == 302)         {             res.code_ = 302;             res.desc_ = "Found";              std::string cl = "Location: ";             cl += "https://www.qq.com";             (res.title_).push_back(cl);             return;         }          if (code == 404)         {             res.code_ = 404;             res.desc_ = "Not Found";         }         else         {             res.code_ = 200;             res.desc_ = "OK";         }          // 将读取网页和图片资源的方式分开         if (req.suffix_ == ".html")         {             res.text_ = get_page(path);             // lg(DEBUG, "text: %s", (res.text_).c_str());         }         else         {             res.text_ = b_get_page(path);         }          //  构建响应报头         std::string cl = "Content-Length: ";         cl += std::to_string((res.text_).size());         // lg(DEBUG, "text_size: %d", (res.text_).size());         (res.title_).push_back(cl);          cl = "Content-Type: ";         cl += content_type_data;         (res.title_).push_back(cl);     }     static void *entry(void *args)     {         pthread_detach(pthread_self());          thread_data *td = reinterpret_cast<thread_data *>(args);         int sockfd = td->sockfd_;         std::string ip = td->ip_;         std::string in_buffer = td->in_buffer_;         http_server *it = td->this_;          // 读取请求         char buffer[buff_size];         bool flag = true;         request req;         while (true) // 虽说是短连接,但也得确保读出来的内容是一个完整的请求         {             memset(buffer, 0, sizeof(buffer));             int n = read(sockfd, buffer, sizeof(buffer));             if (n > 0)             {                 buffer[n] = 0;                 in_buffer += buffer; // 连续读取                 lg(INFO, "get request: \n%s", in_buffer.c_str());                  // 构建请求                 flag = req.deserialize(in_buffer);                 if (flag == false)                 {                     continue;                 }                 else                 {                     break;                 }             }             else if (n == 0)             {                 lg(INFO, "%s quit", ip.c_str());                 return nullptr;             }             else             {                 lg(ERROR, "%s read error", ip.c_str());                 return nullptr;             }         }          // lg(DEBUG, "path: %s ,url: %s ", (req.path_).c_str(), (req.url_).c_str());          // 构建响应         response res;         it->handle_response(res, req);          // 响应序列化         std::string content;         res.serialize(content);          write(sockfd, content.c_str(), content.size());          //  销毁资源         delete td;         close(sockfd);          return nullptr;     }  private:     uint16_t port_;     std::string ip_;      std::unordered_map<std::string, std::string> content_type_; }; 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!