上学时,我总是梦想着能够用C++亲手打造一个属于自己的Web服务。随着时间的流逝,对计算机的了解越来越深,也逐渐认识到这个目标的复杂性。然而,多年后的今天,我已经对计算机有了更深的理解,也知道了这项任务的艰巨性。因此,我决定动手实现一个最简单的Web服务器。
在当今的网络编程中,构建高效的Web服务是一项复杂但令人激动的任务。C++作为一门高性能编程语言,其丰富的库和强大的性能使其成为构建网络应用程序的理想选择。其中,ASIO(Asynchronous Input/Output)库是一个跨平台的C++库,专门用于网络和底层I/O编程,提供了高效且灵活的异步I/O操作。
本文将带您一步步实现一个简易的Web服务器,该服务器能够处理基本的HTTP请求并返回响应。我们将使用C++和ASIO网络库,从基础开始,逐步构建一个可以运行的Web服务器,并深入了解其中的核心概念和实现细节。这个过程不仅是对网络编程技术的探索,更是对自己技术能力的挑战和提升。
什么是 ASIO?
ASIO 是一个用于网络编程的独立库,提供了异步 I/O 操作的支持。它的设计目标是提供高效、可扩展的 I/O 操作,适用于各种类型的网络应用。ASIO 主要特点包括:
- 跨平台支持:ASIO 支持多种操作系统,包括 Windows、Linux 和 macOS。
- 异步操作:通过异步 I/O 操作,ASIO 可以提高程序的响应性和性能。
- 易于集成:ASIO 可以很容易地与其他库和框架集成,如 Boost 库。
在开始编写代码之前,我们先捋捋逻辑
首先,我们需要一个 ASIO 服务类(Server
)来启动我们的 Web 服务。然后,我们需要一个线程池(ThreadPool
)将 Server
接收到的套接字请求提交到线程池中,以便在其中创建会话类(Session
)来处理套接字请求。接着,我们需要一个业务逻辑类(BusinessLogic
)来处理 HTTP 请求。最后,Session
类将处理结果写回客户端。此外,我们还需要一个 HttpStruct
类来保存请求头、响应头、HTTP 状态码、HTTP 头类型以及返回结果。
接下来我们将开始编写代码。首先,我们创建了一个服务类 Server
,然后在 main
函数中实例化该类并调用其启动方法,以便启动服务器。
C++
//
// Created by fallrain on 2023/4/20.
//
#ifndef ASIO_DEMO_SERVER_H
#include "Session.h"
#include "ThreadPool.h"
#define ASIO_DEMO_SERVER_H
class Server {
public:
Server(boost::asio::io_context &io_context, std::uint16_t port);
void start();
void stop();
private:
void do_accept();
boost::asio::ip::tcp::acceptor acceptor_;
ThreadPool pool;
};
#endif //ASIO_DEMO_SERVER_H
rust
//
// Created by fallrain on 2023/4/20.
//
#include "Server.h"
Server::Server(boost::asio::io_context &io_context, std::uint16_t port) :
acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
pool(std::thread::hardware_concurrency()) {
}
void Server::start() {
do_accept();
}
void Server::stop() {
acceptor_.close();
pool.stop();
}
void Server::do_accept() {
acceptor_.async_accept(
[this](std::error_code ec, boost::asio::ip::tcp::socket socket) {
if (!ec) {
pool.post([session = std::make_shared<Session>(socket)] {
session->start();
});
}
do_accept();
});
}
C++
#include "Server.h"
#include "user.h"
#include "result.h"
int main() {
boost::asio::io_context io_context;
Server server(io_context, 8090);
server.start();
io_context.run();
server.stop();
}
ThreadPool线程池 他主要是负责管理多个线程执行任务,避免频繁创建和销毁线程,提高系统性能和资源利用率。
C++
//
// Created by fallrain on 2023/4/20.
//
#ifndef ASIO_DEMO_THREADPOOL_H
#include "boost/asio.hpp"
#define ASIO_DEMO_THREADPOOL_H
class ThreadPool {
public:
ThreadPool(std::size_t size);
template<typename T>
void post(T &&task) {
io_context_.post(std::forward<T>(task));
}
void stop();
private:
boost::asio::io_context io_context_;
boost::asio::io_context::work work_;
std::vector<std::thread> threads_;
};
#endif //ASIO_DEMO_THREADPOOL_H
C++
//
// Created by fallrain on 2023/4/20.
//
#include "ThreadPool.h"
ThreadPool::ThreadPool(std::size_t size) : work_(io_context_) {
for (std::size_t i = 0; i < size; ++i) {
threads_.emplace_back([this] {
io_context_.run();
});
}
}
void ThreadPool::stop() {
io_context_.stop();
for (auto &thread: threads_) {
thread.join();
}
}
Session类在处理HTTP请求时会调用业务逻辑处理类(BusinessLogic
)来执行具体的业务逻辑。这包括根据请求内容进行数据处理、逻辑计算或其他操作,并根据处理结果生成HTTP响应。通过调用业务逻辑处理类,Session类能够将请求的具体逻辑与通信部分分离,提高了代码的可维护性和可扩展性。
C++
//
// Created by fallrain on 2023/4/20.
//
#ifndef ASIO_DEMO_SESSION_H
#include "boost/asio.hpp"
#include "boost/regex.hpp"
#include "iostream"
#include <boost/asio/ip/tcp.hpp>
#include "boost/algorithm/string.hpp"
#include "business_logic.h"
#include "http_struct.h"
#include "numeric"
#include "boost/beast.hpp"
#include "cctype"
#include "boost/network/protocol/http/client.hpp"
#define ASIO_DEMO_SESSION_H
class Session : public std::enable_shared_from_this<Session> {
public:
Session(boost::asio::ip::tcp::socket &socket);
void start();
private:
//读取请求
void do_read();
//写入请求
void do_write();
//处理参数
void process_params();
//处理内容
void process_content_type();
//请求头
http_request_struct request;
//应大头
http_response_struct response;
//客户端socket
boost::asio::ip::tcp::socket client_socket_;
//客服端的buffer
boost::asio::streambuf client_buffer_;
//
// std::map<std::string, std::string> headers;
// std::string request_body, response_body, method, uri, http_version;
};
#endif //ASIO_DEMO_SESSION_H
C++
//
// Created by fallrain on 2023/4/20.
//
#include "Session.h"
typedef std::string string;
Session::Session(boost::asio::ip::tcp::socket &socket) : client_socket_(std::move(socket)) {}
void Session::start() {
do_read();
}
// 处理请求的参数部分,从 URI 中解析参数
void Session::process_params() {
auto self = shared_from_this(); // 获取当前 Session 的 shared_ptr
size_t pos = self->request.uri.find("?"); // 查找 URI 中的参数部分起始位置
if (pos == std::string::npos) { // 如果没有找到参数部分,直接返回
return;
}
string query_string = self->request.uri.substr(pos + 1); // 提取参数部分
std::vector<string> query_line; // 存储每个参数的键值对
boost::split(query_line, query_string, boost::is_any_of("&")); // 使用 '&' 分割参数
self->request.params = std::accumulate(query_line.begin(), // 将参数键值对添加到 params 映射中
query_line.end(),
std::map<string, string>(),
[](std::map<std::string, std::string> &result, const string line) {
std::vector<string> params; // 存储每个参数的键值对
boost::split(params, line, boost::is_any_of("=")); // 使用 '=' 分割键值对
if (params.size() == 2) { // 如果键值对的大小为 2
result[params[0]] = params[1]; // 将键值对添加到 params 映射中
}
return result;
});
}
// 处理请求的Content-Type,根据不同的类型解析请求体
void Session::process_content_type() {
auto self = shared_from_this(); // 获取当前 Session 的 shared_ptr
string body = self->request.body; // 获取请求体
if (body.empty()) { // 如果请求体为空,则直接返回
return;
}
std::map<std::string, std::string> headers = self->request.headers; // 获取请求头
auto content_type = headers.find("Content-Type"); // 查找 Content-Type 头部
if (content_type == headers.end()) { // 如果未找到 Content-Type 头部,则直接返回
return;
}
ContentType contentType; // 定义 ContentType 枚举
// 遍历 ContentType 映射,找到对应的 ContentType
for (const auto &item: contentTypeToString) {
if (item.second == content_type->second) {
contentType = item.first;
break;
}
}
std::stringstream stringstream(body); // 创建 stringstream 用于解析请求体
switch (contentType) {
case ContentType::APPLICATION_XML: // 如果请求体是 XML 格式
boost::property_tree::read_xml(stringstream, self->request.ptree); // 解析 XML 数据
return;
case ContentType::APPLICATION_JSON: // 如果请求体是 JSON 格式
boost::property_tree::read_json(stringstream, self->request.ptree); // 解析 JSON 数据
return;
}
boost::regex contentTypeRegex(R"(multipart/form-data;\s*boundary=(.*))"); // 正则表达式匹配 multipart/form-data 类型的 Content-Type
boost::smatch match;
if (boost::regex_search(content_type->second, match, contentTypeRegex)) { // 如果 Content-Type 匹配成功
if (match.size() > 1) { // 如果匹配结果的大小大于 1
string boundary = match[1].str(); // 获取边界字符串
std::vector<string> params; // 存储字段参数
std::size_t pos = 0;
std::size_t lastPos = 0;
while ((pos = body.find(boundary, lastPos)) != std::string::npos) { // 循环查找边界
string field_name; // 字段名
string header = body.substr(lastPos, pos - lastPos); // 获取字段头部信息
std::string::size_type name_pos = header.find("name="); // 查找字段名的起始位置
if (name_pos != std::string::npos) { // 如果找到字段名
std::string::size_type name_end_pos = header.find('"', name_pos + 6); // 查找字段名的结束位置
field_name = header.substr(name_pos + 6, name_end_pos - name_pos - 6); // 提取字段名
std::cout << "field_name:" << field_name << std::endl; // 输出字段名
// 查找字段值的开始位置
std::string::size_type value_start_pos = body.find("\r\n\r\n", lastPos);
if (value_start_pos == string::npos) { // 如果找不到字段值的起始位置
std::cerr << "Invalid part format: missing value start" << std::endl; // 输出错误信息
continue;
} else {
value_start_pos += 4;
}
// 查找字段值的结束位置
std::string::size_type value_end_pos = body.find(boundary, value_start_pos);
if (value_start_pos == string::npos) { // 如果找不到字段值的结束位置
std::cerr << "Invalid part format: missing value end" << std::endl; // 输出错误信息
continue;
}
// 提取字段的值数据
std::string value = body.substr(value_start_pos, value_end_pos - value_start_pos - 4); // 提取字段的值
self->request.form_data[field_name] = value; // 将字段名和值添加到 form_data 映射中
}
lastPos = pos + boundary.length(); // 更新上一个边界的位置
}
}
return;
}
}
// 执行读取操作,从客户端读取请求
void Session::do_read() {
// 异步读取直到遇到"\r\n\r\n",表示请求头结束
boost::asio::async_read_until(client_socket_, client_buffer_, "\r\n\r\n",
// 读取完成后的回调函数
[self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
if (!ec) { // 如果没有发生错误
// 将客户端缓冲区中的数据转换为字符串
std::string request_header(
boost::asio::buffers_begin(self->client_buffer_.data()),
boost::asio::buffers_begin(self->client_buffer_.data()) +
bytes_transferred);
// 分割请求头字符串为单独的行
std::vector<string> header_vec;
boost::split(header_vec, request_header, boost::is_any_of("\r\n"),
boost::token_compress_on);
// 解构请求结构体
auto &[headers, params, form_data, cookie_map, json_map, session_id, method, body, uri, http_version] = self->request;
// 分割请求行,提取方法、URI和HTTP版本
std::vector<string> line;
boost::split(line, header_vec[0], boost::is_any_of(" "));
method = stringToHttpMethod[line[0]], uri = line[1], http_version = line[2];
// 解析请求头部并填充请求结构体的headers成员
std::for_each(header_vec.begin() + 1, header_vec.end() - 1,
[&](std::string v) {
std::vector<string> header;
boost::split(header, v, boost::is_any_of(":"),
boost::token_compress_on);
// 移除头部值中的空白字符
header[1].erase(std::remove_if(header[1].begin(),
header[1].end(),
[](unsigned char c) {
return std::isspace(c);
}), header[1].end());
// 填充请求头部到headers映射
self->request.headers[header[0]] = header[1];
});
// 解析Cookie头部
auto header_cookie = headers.find("Cookie");
if (header_cookie != headers.end()) {
std::vector<string> cookie_vec;
boost::split(cookie_vec, header_cookie->second, boost::is_any_of(";"),
boost::token_compress_on);
std::for_each(cookie_vec.begin(), cookie_vec.end(),
[&](const auto &item) {
std::vector<string> cookie;
boost::split(cookie, item, boost::is_any_of("="),
boost::token_compress_on);
// 填充Cookie到cookie_map映射
cookie_map[cookie[0]] = cookie[1];
});
}
// 检查是否存在会话ID,如果不存在则创建一个新的会话ID
if (cookie_map.find("session") == cookie_map.end()) {
session_id = business_logic::create_session_map();
} else {
session_id = cookie_map["session"];
}
// 检查是否有请求体
auto content_length = headers.find("Content-Length");
if (content_length != headers.end()) {
// 计算请求体中剩余的数据长度
std::size_t excess_data_length =
self->client_buffer_.size() - bytes_transferred;
std::vector<char> excess_data(excess_data_length);
// 将多余的数据复制到excess_data中
boost::asio::buffer_copy(boost::asio::buffer(excess_data),
self->client_buffer_.data() + bytes_transferred);
// 将多余的数据添加到请求体中
body = string(excess_data.data(), excess_data_length);
// 计算需要读取的数据长度
size_t content_length = std::stoi(
headers.find("Content-Length")->second);
if (content_length - body.size() > 0) {
std::vector<char> buffer(content_length - body.size());
boost::system::error_code error;
// 从套接字中读取剩余的请求体数据
size_t bytes_read = boost::asio::read(self->client_socket_,
boost::asio::buffer(buffer),
boost::asio::transfer_exactly(
content_length -
body.size()),
error);
if (!error) {
// 将读取的数据附加到请求体中
body.append(buffer.data(), bytes_read);
} else {
// 输出错误信息
std::cout << "Error reading request body: " << error.message()
<< std::endl;
}
}
}
// 处理请求的参数和内容类型
self->process_params();
self->process_content_type();
try {
// 处理请求并获取响应
self->response = business_logic::process_request(self->request);
// 如果请求中不包含会话ID,则将会话ID添加到响应的Cookie中
if (self->request.cookie.find("session") == cookie_map.end()) {
self->response.cookie["session"] = session_id;
}
} catch (const std::exception &e) {
// 如果发生异常,将异常信息添加到响应体中
self->response.body = e.what();
}
// 执行写入操作,将响应发送给客户端
self->do_write();
}
}
);
}
// 执行写入操作,将响应发送给客户端
void Session::do_write() {
// 解构响应结构体
auto &[headers, cookie, body, content_type, http_status] = shared_from_this()->response;
// 创建输出流
std::ostringstream response_stream;
// 获取状态码和状态字符串
int status_code = static_cast<int>(http_status);
string status_string = httpStatusToString[http_status];
string contentType = contentTypeToString[content_type];
// 如果Cookie不为空,构建Set-Cookie头部
if (!cookie.empty()) {
std::ostringstream ostringstream;
for (const auto &item: cookie) {
ostringstream << item.first << "=" << item.second << ";";
}
ostringstream.str().pop_back();
headers["Set-Cookie"] = ostringstream.str();
}
// 构建响应头部
response_stream << "HTTP/1.1" << " " << status_code << " " << status_string << "\r\n";
response_stream << "Content-Type: " << contentType << "\r\n";
response_stream << "Server: c++ server" << "\r\n";
for (auto &item: headers) {
response_stream << item.first << ":" << item.second << "\r\n";
}
// 如果内容类型为TEXT_HTML,则添加Content-Length头部
if (content_type == ContentType::TEXT_HTML) {
response_stream << "Content-Length:" << body.size() << "\r\n";
}
response_stream << "\r\n"; // 空行,分隔头部和主体
response_stream << body; // 添加主体内容
string response_str = response_stream.str(); // 获取完整的响应字符串
// 异步写入响应到客户端
boost::asio::async_write(client_socket_, boost::asio::buffer(response_str),
[self = shared_from_this()](std::error_code ec, std::size_t length) {
if (!ec) {
} else {
// 如果发生错误,输出错误信息
std::cerr << "write to remote server error: " << ec.message() << std::endl;
}
});
}
定义了一些用于处理 HTTP 请求和响应的结构体和映射。在这些结构体和映射中,我们将 HTTP 状态码映射为对应的状态消息,将内容类型枚举映射为 MIME 类型字符串,并将 HTTP 请求方法字符串映射为枚举值。除此之外,我们还定义了 http_response_struct
结构体,用于表示 HTTP 响应,并提供了多个构造函数,以便于创建不同类型的响应对象。
C++
//
// Created by fallrain on 2023/5/18.
//
#ifndef ASIO_DEMO_HTTP_STRUCT_H
#define ASIO_DEMO_HTTP_STRUCT_H
#include <boost/property_tree/ptree.hpp>
#include "map"
enum class HttpStatusCode {
OK = 200,
FOUND = 302,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
FORBIDDEN = 403,
NOT_FOUND = 404,
INTERNAL_SERVER_ERROR = 500
};
enum class ContentType {
TEXT_PLAIN,
TEXT_HTML,
APPLICATION_JSON,
APPLICATION_XML
};
enum class HttpMethod {
GET, POST, HEAD, OPTION, PUT, DELECT, TRACE, CONNECT
};
extern std::map<HttpStatusCode, std::string> httpStatusToString;
extern std::map<ContentType, std::string> contentTypeToString;
//extern std::map<HttpMethod, std::string> httpMethodToString;
extern std::map<std::string, HttpMethod> stringToHttpMethod;
struct http_request_struct {
std::map<std::string, std::string> headers;
std::map<std::string, std::string> params;
std::map<std::string, std::string> form_data;
std::map<std::string, std::string> cookie;
boost::property_tree::ptree ptree;
std::string session_id;
HttpMethod method;
std::string body;
std::string uri, http_version;
};
struct http_response_struct {
http_response_struct(std::string body);
http_response_struct(std::string body, ContentType content_type);
http_response_struct(HttpStatusCode http_status);
http_response_struct();
std::map<std::string, std::string> headers;
std::map<std::string, std::string> cookie;
std::string body;
ContentType content_type = ContentType::TEXT_HTML;
HttpStatusCode http_status = HttpStatusCode::OK;
};
#endif //ASIO_DEMO_HTTP_STRUCT_H
C++
//
// Created by fallrain on 2023/5/18.
//
#include "http_struct.h"
std::map<HttpStatusCode, std::string> httpStatusToString = {
{HttpStatusCode::OK, "OK"},
{HttpStatusCode::BAD_REQUEST, "Bad Request"},
{HttpStatusCode::UNAUTHORIZED, "Unauthorized"},
{HttpStatusCode::FORBIDDEN, "Forbidden"},
{HttpStatusCode::NOT_FOUND, "Not Found"},
{HttpStatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error"},
{HttpStatusCode::FOUND, "Found"}
};
std::map<ContentType, std::string> contentTypeToString = {
{ContentType::TEXT_PLAIN, "text/plain"},
{ContentType::TEXT_HTML, "text/html;charset=UTF-8"},
{ContentType::APPLICATION_JSON, "application/json"},
{ContentType::APPLICATION_XML, "application/xml"}
};
std::map<std::string, HttpMethod> stringToHttpMethod = {
{"GET", HttpMethod::GET},
{"POST", HttpMethod::POST},
{"HEAD", HttpMethod::HEAD},
{"OPTION", HttpMethod::OPTION},
{"PUT", HttpMethod::PUT},
{"DELECT", HttpMethod::DELECT},
{"TRACE", HttpMethod::TRACE},
{"CONNECT", HttpMethod::CONNECT}
};
http_response_struct::http_response_struct(std::string body) : body(body) {}
http_response_struct::http_response_struct(std::string body, ContentType content_type) : body(body),
content_type(content_type) {}
http_response_struct::http_response_struct(HttpStatusCode http_status) : http_status(http_status) {}
http_response_struct::http_response_struct() {}
business_logic
业务类提供了 register_handle
和 set_root
方法,允许在主函数中直接添加业务逻辑而不必修改整体代码。register_handle
方法用于注册处理函数,根据 HTTP 请求的方法和 URI 进行匹配;set_root
方法用于设置服务器根目录。这种设计使得业务逻辑的添加和修改变得简单和灵活。
C++
//
// Created by fallrain on 2023/5/18.
//
#ifndef ASIO_DEMO_BUSINESS_LOGIC_H
#define ASIO_DEMO_BUSINESS_LOGIC_H
#include <functional>
#include "algorithm"
#include "map"
#include "http_struct.h"
#include "boost/property_tree/json_parser.hpp"
#include "boost/property_tree/xml_parser.hpp"
#include "boost/uuid/uuid.hpp"
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <filesystem>
typedef std::function<http_response_struct(http_request_struct)> FunctionPtr;
class business_logic {
public:
static void register_handle(HttpMethod httpMethod, const std::string &handle_name, FunctionPtr function);
static http_response_struct process_request(const http_request_struct &request);
static std::string create_session_map();
static std::map<std::string, std::string> &get_session_map(const std::string &cookie_id);
static void set_root(const std::string &value);
private:
static std::string root;
static std::map<std::string, std::map<std::string, std::string >> session_map;
static std::map<HttpMethod, std::map<std::string, FunctionPtr>> function_map;
};
#endif //ASIO_DEMO_BUSINESS_LOGIC_H
C++
//
// Created by fallrain on 2023/5/18.
//
#include <iostream>
#include <utility>
#include "business_logic.h"
std::map<HttpMethod, std::map<std::string, FunctionPtr>> business_logic::function_map = {
{HttpMethod::GET, {}},
{HttpMethod::POST, {}},
{HttpMethod::HEAD, {}},
{HttpMethod::OPTION, {}},
{HttpMethod::PUT, {}},
{HttpMethod::DELECT, {}},
{HttpMethod::TRACE, {}},
{HttpMethod::CONNECT, {}}
};
std::map<std::string, std::map<std::string, std::string >> business_logic::session_map;
std::string business_logic::root;
void business_logic::set_root(const std::string &value) {
root = value; // 设置根路径
}
void business_logic::register_handle(HttpMethod httpMethod, const std::string &handle_name, FunctionPtr function) {
function_map[httpMethod][handle_name] = std::move(function);
}
http_response_struct business_logic::process_request(const http_request_struct &request) {
std::string uri = request.uri;
size_t pos = uri.find("?");
if (pos != std::string::npos) {
auto func = request.uri.substr(0, pos);
uri = uri.substr(0, pos);
}
auto func = function_map.find(request.method)->second.find(uri);
if (func != function_map.find(request.method)->second.end()) {
return func->second(request);
}
if (request.method == HttpMethod::GET) {
for (const auto &item: session_map[request.session_id]) {
std::cout << item.first << "=" << item.second << std::endl;
}
std::string filePath = root + uri;
if (std::filesystem::is_directory(std::filesystem::path(filePath))) {
filePath += "index.html";
}
if (std::filesystem::exists(std::filesystem::path(filePath))) {
std::ifstream file_stream(filePath);
if (file_stream.is_open()) {
std::ostringstream ss;
ss << file_stream.rdbuf();
return {ss.str()};
} else {
std::cout << "Failed to open file: " << filePath << std::endl;
}
}
}
return {HttpStatusCode::NOT_FOUND};
}
std::string business_logic::create_session_map() {
boost::uuids::uuid uuid = boost::uuids::random_generator()();
std::string uuidStr = boost::uuids::to_string(uuid);
session_map[uuidStr] = {};
return uuidStr;
}
std::map<std::string, std::string> &business_logic::get_session_map(const std::string &cookie_id) {
return session_map[cookie_id];
}
result
类用于统一封装业务处理的返回结果。它支持包含消息、响应体和状态码的返回。通过 to_json()
方法,可以将返回结果转换为 JSON 格式的属性树,而 to_json_string()
方法则将属性树转换为 JSON 字符串返回。这种设计使得业务处理的返回结果能够被统一处理和序列化。
C++
//
// Created by fallrain on 2023/6/15.
//
#ifndef ASIO_DEMO_RESULT_H
#include <variant>
#include "string"
#include "boost/property_tree/json_parser.hpp"
#define ASIO_DEMO_RESULT_H
class result {
public:
result(std::string message, std::variant<boost::property_tree::ptree, std::string, std::nullptr_t> body, int code);
result(std::string message, int code);
result();
std::string message;
std::variant<boost::property_tree::ptree, std::string, std::nullptr_t> body;
// boost::property_tree::ptree body;
int code;
boost::property_tree::ptree to_json();
std::string to_json_string();
};
#endif //ASIO_DEMO_RESULT_H
C++
//
// Created by fallrain on 2023/6/15.
//
#include "result.h"
result::result(std::string message, int code) : message(message), code(code) {}
result::result(std::string message, std::variant<boost::property_tree::ptree, std::string, std::nullptr_t> body,
int code) : message(message), body(body), code(code) {}
result::result() {}
boost::property_tree::ptree result::to_json() {
boost::property_tree::ptree ptree;
ptree.put("message", message);
if (std::holds_alternative<boost::property_tree::ptree>(body)) {
ptree.add_child("body", std::get<boost::property_tree::ptree>(body));
} else if (std::holds_alternative<std::string>(body)) {
ptree.put("body", std::get<std::string>(body));
}
ptree.put("code", code);
return ptree;
}
std::string result::to_json_string() {
std::stringstream stringstream;
boost::property_tree::write_json(stringstream, to_json());
return stringstream.str();
}
最后是业务的user
类用于表示用户信息。它包含用户的 ID、用户名、密码和姓名等属性。通过 to_json()
方法,可以将用户信息转换为 JSON 格式的属性树,以便于在业务处理中进行序列化和传输。
C++
//
// Created by fallrain on 2023/6/8.
//
#ifndef ASIO_DEMO_USER_H
#include "string"
#include "boost/property_tree/json_parser.hpp"
#define ASIO_DEMO_USER_H
class user {
public:
user(std::string id, std::string username, std::string password, std::string name);
boost::property_tree::ptree to_json();
std::string id;
std::string username;
std::string password;
std::string name;
};
#endif //ASIO_DEMO_USER_H
C++
//
// Created by fallrain on 2023/6/8.
//
#include "user.h"
user::user(std::string id, std::string username, std::string password, std::string name) : id(id), username(username),
password(password),
name(name) {}
boost::property_tree::ptree user::to_json() {
boost::property_tree::ptree ptree;
ptree.put("id", id);
ptree.put("username", username);
ptree.put("password", "******");
ptree.put("name", name);
return ptree;
}
在这篇博文中,我们一同探索了使用C++的asio网络库手动编写简易的Web服务器的过程。从创建服务器、处理会话到业务逻辑的实现,我们逐步领略了网络编程的精妙之处。这个过程中,我们回想起学生时代的幻想,梦想着有朝一日能够亲手搭建属于自己的Web服务。时隔多年,通过对计算机的深入了解,我们终于有了实现这个梦想的机会。于是,我们义无反顾地着手实现了这个最简单的Web服务器。
通过本文的学习,我们不仅掌握了搭建基本服务器框架的方法,还深入了解了网络通信的原理和机制,以及如何处理不同类型的请求和响应。这为我们打下了坚实的基础,也让我们对网络开发领域有了更深入的理解和认识。
然而,这个小项目还有许多功能未完成,比如图片的处理、数据库等等。这些功能的完善将进一步丰富服务器的功能和提升性能。未来,我们将继续努力,不断完善这个小项目,并探索更多网络编程的奥秘。
最后,感谢您的阅读和支持,希望本文能够为您带来启发和收获。