马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
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中的现实路径]:
但是这里我们要增长一个特殊处理惩罚:
- 当访问根目次时,必要手动让它访问我们设定的首页文件
- 并且当访问不存在的页面时,必要手动让它访问我们设定好的错误页面
代码
- #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_;
- };
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |